Eclipse SUMO - Simulation of Urban MObility
GUIViewObjectsHandler.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials are made available under the
5 // terms of the Eclipse Public License 2.0 which is available at
6 // https://www.eclipse.org/legal/epl-2.0/
7 // This Source Code may also be made available under the following Secondary
8 // Licenses when the conditions for such availability set forth in the Eclipse
9 // Public License 2.0 are satisfied: GNU General Public License, version 2
10 // or later which is available at
11 // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 /****************************************************************************/
18 // class used for handle objects over view
19 /****************************************************************************/
20 #include <config.h>
21 #include <algorithm>
22 
23 #include <utils/shapes/Shape.h>
24 
25 #include "GUIViewObjectsHandler.h"
26 
27 
28 // ===========================================================================
29 // method definitions
30 // ===========================================================================
31 
33 
34 
35 void
37  // reset recompute boundaries
39  // clear objects under cursor
41  mySelectedObjects.clear();
42  // reset marked elements
43  myMergingJunctions.clear();
44  markedEdge = nullptr;
45  markedLane = nullptr;
46  markedTAZ = nullptr;
47  markedRoute = nullptr;
48  markedFirstGeometryPoint = nullptr;
49  markedSecondGeometryPoint = nullptr;
50 }
51 
52 
53 const Position&
55  return mySelectionPosition;
56 }
57 
58 
59 const Boundary&
61  return mySelectionBoundary;
62 }
63 
64 
65 void
67  // set position selection
68  mySelectionPosition = pos;
69  // invalidate selection boundary
72 }
73 
74 
75 void
77  // invalidate position selection
79  // set selection boundary
80  mySelectionBoundary = boundary;
81  mySelectionBoundaryShape = boundary.getShape(false);
82 }
83 
84 
85 bool
87  return mySelectedObjects.find(GLObject) != mySelectedObjects.end();
88 }
89 
90 
91 bool
93  // first check if we're selecting for boundary
95  return false;
96  }
97  // try to find parent in seleted object
98  auto finder = mySelectedObjects.find(parent);
99  // if parent was found and was inserted with full boundary, insert it
100  if (finder != mySelectedObjects.end() && finder->second && !isElementSelected(GLObject)) {
101  // insert element with full boundary
102  return addElementUnderCursor(GLObject, false, true);
103  } else {
104  return false;
105  }
106 }
107 
108 
109 bool
111  const Position& center, const double radius, const Boundary& circleBoundary) {
112  // first check that object doesn't exist
113  if (isElementSelected(GLObject)) {
114  return false;
115  } else {
116  // declare squared radius
117  const double squaredRadius = (radius * radius);
118  // continue depending if we're selecting a position or a boundary
120  // continue depending of detail level
122  // avoid empty boundaries
123  if (!circleBoundary.isInitialised()) {
124  return false;
125  }
126  // check if selection boundary contains the centering boundary of object
128  return addElementUnderCursor(GLObject, false, true);
129  }
130  // check if boundary overlaps
131  if (mySelectionBoundary.overlapsWith(circleBoundary)) {
132  return addElementUnderCursor(GLObject, false, false);
133  }
134  // check if the four boundary vertex are within circle
135  for (const auto& vertex : mySelectionBoundaryShape) {
136  if (vertex.distanceSquaredTo2D(center) <= squaredRadius) {
137  return addElementUnderCursor(GLObject, false, false);
138  }
139  }
140  // no intersection, then return false
141  return false;
142  } else {
143  // check if center is within mySelectionBoundary
144  if (mySelectionBoundary.around2D(center)) {
145  return addElementUnderCursor(GLObject, false, false);
146  } else {
147  return false;
148  }
149  }
150  } else if (mySelectionPosition != Position::INVALID) {
151  // check distance between selection position and center
152  if (mySelectionPosition.distanceSquaredTo2D(center) <= squaredRadius) {
153  return addElementUnderCursor(GLObject, false, false);
154  } else {
155  return false;
156  }
157  } else {
158  return false;
159  }
160  }
161 }
162 
163 
164 bool
166  const PositionVector& shape, const int index, const double radius) {
167  // obtain geometry point pos
168  const auto geometryPointPos = shape[index];
169  // declare squared radius
170  const double squaredRadius = (radius * radius);
171  // continue depending if we're selecting a position or a boundary
173  // continue depending of detail level
175  // make a boundary using center and radius
176  Boundary geometryPointBoundary;
177  geometryPointBoundary.add(geometryPointPos);
178  geometryPointBoundary.grow(radius);
179  // check if boundary is whithin selection boundary
180  if (mySelectionBoundary.contains(geometryPointBoundary)) {
181  return addGeometryPointUnderCursor(GLObject, index);
182  } else if (mySelectionBoundary.overlapsWith(geometryPointBoundary)) {
183  return addGeometryPointUnderCursor(GLObject, index);
184  } else {
185  // check if the four boundary vertex are within circle
186  for (const auto& vertex : mySelectionBoundaryShape) {
187  if (vertex.distanceSquaredTo2D(geometryPointPos) <= squaredRadius) {
188  return addGeometryPointUnderCursor(GLObject, index);
189  }
190  }
191  // no intersection, then return false
192  return false;
193  }
194  } else {
195  // check if center is within mySelectionBoundary
196  if (mySelectionBoundary.around2D(geometryPointPos)) {
197  return addGeometryPointUnderCursor(GLObject, index);
198  } else {
199  return false;
200  }
201  }
202  } else if (mySelectionPosition != Position::INVALID) {
203  // check distance between selection position and center
204  if (mySelectionPosition.distanceSquaredTo2D(geometryPointPos) <= squaredRadius) {
205  return addGeometryPointUnderCursor(GLObject, index);
206  } else {
207  return false;
208  }
209  } else {
210  return false;
211  }
212 }
213 
214 
215 bool
217  const PositionVector& shape, const double distance) {
218  // only process if we're selecting a precise position
220  // obtain nearest position over shape
221  const auto nearestOffset = shape.nearest_offset_to_point2D(mySelectionPosition);
222  const auto nearestPos = shape.positionAtOffset2D(nearestOffset);
223  // check distance nearest position and pos
224  if (mySelectionPosition.distanceSquaredTo2D(nearestPos) <= (distance * distance)) {
225  return addPositionOverShape(GLObject, nearestPos, nearestOffset);
226  } else {
227  return false;
228  }
229  } else {
230  return false;
231  }
232 }
233 
234 
235 bool
237  const Boundary& shapeBoundary) {
238  // first check that object doesn't exist
239  if (isElementSelected(GLObject)) {
240  return false;
241  } else if (mySelectionBoundary.isInitialised()) {
242  // avoid invalid boundaries
243  if (!shapeBoundary.isInitialised()) {
244  return false;
245  }
246  // check if selection boundary contains the centering boundary of object
247  if (mySelectionBoundary.contains(shapeBoundary)) {
248  return addElementUnderCursor(GLObject, false, true);
249  }
250  // check if shape crosses to selection boundary
251  for (int i = 1; i < (int)shape.size(); i++) {
252  if (mySelectionBoundary.crosses(shape[i - 1], shape[i])) {
253  return addElementUnderCursor(GLObject, false, false);
254  }
255  }
256  // no intersection, then return false
257  return false;
258  } else if (mySelectionPosition != Position::INVALID) {
259  // check if selection position is around shape
260  if (shape.around(mySelectionPosition)) {
261  return addElementUnderCursor(GLObject, false, false);
262  } else {
263  return false;
264  }
265  } else {
266  return false;
267  }
268 }
269 
270 
271 bool
272 GUIViewObjectsHandler::addElementUnderCursor(const GUIGlObject* GLObject, const bool checkDuplicated, const bool fullBoundary) {
273  // first check that object doesn't exist
274  if (checkDuplicated && isElementSelected(GLObject)) {
275  return false;
276  } else {
277  // check if this is an element with an associated layer
278  const auto layer = dynamic_cast<const Shape*>(GLObject);
279  if (layer) {
280  auto& layerContainer = mySortedSelectedObjects[layer->getShapeLayer() * -1];
281  layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
282  } else if (GLObject) {
283  auto& layerContainer = mySortedSelectedObjects[GLObject->getType() * -1];
284  layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
285  }
286  mySelectedObjects[GLObject] = fullBoundary;
287  return true;
288  }
289 }
290 
291 
292 bool
294  // avoid to insert duplicated elements
295  for (auto& elementLayer : mySortedSelectedObjects) {
296  for (auto& element : elementLayer.second) {
297  if (element.object == GLObject) {
298  // avoid double points
299  for (auto& index : element.geometryPoints) {
300  if (index == newIndex) {
301  return false;
302  }
303  }
304  // add new index
305  element.geometryPoints.push_back(newIndex);
306  return true;
307  }
308  }
309  }
310  // no element found then add it
311  const auto layer = dynamic_cast<const Shape*>(GLObject);
312  if (layer) {
313  auto& layerContainer = mySortedSelectedObjects[layer->getShapeLayer() * -1];
314  auto it = layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
315  it->geometryPoints.push_back(newIndex);
316  } else if (GLObject) {
317  auto& layerContainer = mySortedSelectedObjects[GLObject->getType() * -1];
318  auto it = layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
319  it->geometryPoints.push_back(newIndex);
320  }
321  mySelectedObjects[GLObject] = false;
322  return true;
323 }
324 
325 
326 bool
327 GUIViewObjectsHandler::addPositionOverShape(const GUIGlObject* GLObject, const Position& pos, const double offset) {
328  // avoid to insert duplicated elements
329  for (auto& elementLayer : mySortedSelectedObjects) {
330  for (auto& element : elementLayer.second) {
331  if (element.object == GLObject) {
332  if (element.posOverShape != Position::INVALID) {
333  return false;
334  } else {
335  // set position and offset over shape
336  element.posOverShape = pos;
337  element.offset = offset;
338  return true;
339  }
340  }
341  }
342  }
343  // no element found then add it
344  const auto layer = dynamic_cast<const Shape*>(GLObject);
345  if (layer) {
346  auto& layerContainer = mySortedSelectedObjects[layer->getShapeLayer() * -1];
347  auto it = layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
348  it->posOverShape = pos;
349  } else if (GLObject) {
350  auto& layerContainer = mySortedSelectedObjects[GLObject->getType() * -1];
351  auto it = layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
352  it->posOverShape = pos;
353  }
354  mySelectedObjects[GLObject] = false;
355  return true;
356 }
357 
358 
362 }
363 
364 
365 const std::vector<int>&
367  // avoid to insert duplicated elements
368  for (auto& elementLayer : mySortedSelectedObjects) {
369  for (auto& element : elementLayer.second) {
370  if (element.object == GLObject) {
371  return element.geometryPoints;
372  }
373  }
374  }
375  return myEmptyGeometryPoints;
376 }
377 
378 
379 const Position&
381  // avoid to insert duplicated elements
382  for (auto& elementLayer : mySortedSelectedObjects) {
383  for (auto& element : elementLayer.second) {
384  if (element.object == GLObject) {
385  return element.posOverShape;
386  }
387  }
388  }
389  return Position::INVALID;
390 }
391 
392 
393 void
395  ObjectContainer frontElement(nullptr);
396  // extract element
397  for (auto& elementLayer : mySortedSelectedObjects) {
398  auto it = elementLayer.second.begin();
399  while (it != elementLayer.second.end()) {
400  if (it->object == GLObject) {
401  // copy element to front element
402  frontElement.object = it->object;
403  frontElement.geometryPoints = it->geometryPoints;
404  // remove element from myElementsUnderCursor
405  it = elementLayer.second.erase(it);
406  } else {
407  it++;
408  }
409  }
410  }
411  // add element again wit a new layer
412  if (frontElement.object) {
413  mySortedSelectedObjects[(double)GLO_FRONTELEMENT].push_back(frontElement);
414  }
415 }
416 
417 
418 void
420  // declare object container for edge
421  ObjectContainer edgeWithGeometryPoints(nullptr);
422  // check if there are edges with geometry points in mySortedSelectedObjects
423  for (auto& elementLayer : mySortedSelectedObjects) {
424  for (auto element : elementLayer.second) {
425  if ((element.object->getType() == GLO_EDGE) && (element.geometryPoints.size() > 0)) {
426  edgeWithGeometryPoints = element;
427  }
428  }
429  }
430  // continue if something was found
431  if (edgeWithGeometryPoints.object != nullptr) {
432  // clear all selected objects
433  mySortedSelectedObjects.clear();
434  // add edge with geometry points as front element
435  mySortedSelectedObjects[(double)GLO_FRONTELEMENT].push_back(edgeWithGeometryPoints);
436  }
437 }
438 
439 
440 const std::vector<const GNEJunction*>&
442  return myMergingJunctions;
443 }
444 
445 
446 bool
448  // avoid insert duplicated junctions
449  for (const auto mergingJunctions : myMergingJunctions) {
450  if (mergingJunctions == junction) {
451  return false;
452  }
453  }
454  myMergingJunctions.push_back(junction);
455  return true;
456 }
457 
458 /****************************************************************************/
@ GLO_FRONTELEMENT
front element (used in netedit)
@ GLO_EDGE
an edge
@ GLO_NETWORK
The network - empty.
A class that stores a 2D geometrical boundary.
Definition: Boundary.h:39
void add(double x, double y, double z=0)
Makes the boundary include the given coordinate.
Definition: Boundary.cpp:78
bool isInitialised() const
check if Boundary is Initialised
Definition: Boundary.cpp:235
void reset()
Resets the boundary.
Definition: Boundary.cpp:66
Boundary & grow(double by)
extends the boundary by the given amount
Definition: Boundary.cpp:319
bool overlapsWith(const AbstractPoly &poly, double offset=0) const
Returns whether the boundary overlaps with the given polygon.
Definition: Boundary.cpp:189
PositionVector getShape(const bool closeShape) const
get position vector (shape) based on this boundary
Definition: Boundary.cpp:423
bool around2D(const Position &p, double offset=0) const
Returns whether the boundary contains the given 2D coordinate.
Definition: Boundary.cpp:181
bool crosses(const Position &p1, const Position &p2) const
Returns whether the boundary crosses the given line.
Definition: Boundary.cpp:210
double contains(const Boundary &b) const
return true if this boundary contains the given boundary (only X-Y)
Definition: Boundary.cpp:224
virtual Boundary getCenteringBoundary() const =0
GUIGlObjectType getType() const
Returns the type of the object as coded in GUIGlObjectType.
Definition: GUIGlObject.h:156
Boundary mySelectionBoundary
selection boundary
bool addPositionOverShape(const GUIGlObject *GLObject, const Position &pos, const double offset)
add position over shape
void updateFrontElement(const GUIGlObject *GLObject)
move front element in elements under cursor (currently used only in netedit)
PositionVector mySelectionBoundaryShape
selection boundary (shape)
bool isElementSelected(const GUIGlObject *GLObject) const
check if element was already selected
const Boundary & getSelectionBoundary() const
get selection boundary (usually the mouse position)
void setSelectionPosition(const Position &pos)
set selection position (usually the mouse position)
const GNERoute * markedRoute
marked route (used in create vehicle mode)
bool checkBoundaryParentElement(const GUIGlObject *GLObject, const GUIGlObject *parent)
check boundary parent element
std::vector< const GNEJunction * > myMergingJunctions
merging junctions
const Position & getSelectionPosition() const
get selection position (usually the mouse position)
bool checkShapeElement(const GUIGlObject *GLObject, const PositionVector &shape, const Boundary &shapeBoundary)
check (closed) shape element
void isolateEdgeGeometryPoints()
isolate edge geometry points (used for moving)
const GUIGlObject * markedTAZ
marked TAZ (used in create TAZRel mode)
std::map< double, std::vector< ObjectContainer > > GLObjectsSortedContainer
typedef
bool addMergingJunctions(const GNEJunction *junction)
add to merging junctions (used for marking junctions to merge)
void setSelectionBoundary(const Boundary &boundary)
set selection boundary (usually the mouse position)
std::map< const GUIGlObject *, bool > mySelectedObjects
map with selected elements and if was selected with full boundary (used only to avoid double seletion...
GUIGlObjectType recomputeBoundaries
recompute boundaries
const GLObjectsSortedContainer & getSelectedObjects() const
get all elements under cursor sorted by layer
void clearSelectedElements()
clear selected elements
const std::vector< int > & getGeometryPoints(const GUIGlObject *GLObject) const
get geometry points for the given glObject
const GNEEdge * markedEdge
marked edge (used in create edge mode, for splitting)
const GNELane * markedLane
marked lane (used in create edge mode, for splitting)
Position mySelectionPosition
position
const GUIGlObject * markedSecondGeometryPoint
marked first geometry point (used for moving/delete geometry points)
bool addElementUnderCursor(const GUIGlObject *GLObject, const bool checkDuplicated, const bool fullBoundary)
add element into list of elements under cursor
const Position & getPositionOverShape(const GUIGlObject *GLObject) const
get position over shape
bool checkCircleElement(const GUIVisualizationSettings::Detail d, const GUIGlObject *GLObject, const Position &center, const double radius, const Boundary &circleBoundary)
check if mouse is within elements geometry (for circles)
GLObjectsSortedContainer mySortedSelectedObjects
selected element sorted by layer
bool checkGeometryPoint(const GUIVisualizationSettings::Detail d, const GUIGlObject *GLObject, const PositionVector &shape, const int index, const double radius)
check if mouse is within geometry point
bool checkPositionOverShape(const GUIVisualizationSettings::Detail d, const GUIGlObject *GLObject, const PositionVector &shape, const double distance)
check if mouse is within geometry point
std::vector< int > myEmptyGeometryPoints
empty geometry points
const GUIGlObject * markedFirstGeometryPoint
marked first geometry point (used for moving/delete geometry points)
bool addGeometryPointUnderCursor(const GUIGlObject *GLObject, const int newIndex)
add geometryPoint into list of elements under cursor
const std::vector< const GNEJunction * > & getMergingJunctions() const
get merging junctions
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
double distanceSquaredTo2D(const Position &p2) const
returns the square of the distance to another position (Only using x and y positions)
Definition: Position.h:281
static const Position INVALID
used to indicate that a position is valid
Definition: Position.h:322
A list of positions.
double nearest_offset_to_point2D(const Position &p, bool perpendicular=true) const
return the nearest offest to point 2D
Position positionAtOffset2D(double pos, double lateralOffset=0) const
Returns the position at the given length.
bool around(const Position &p, double offset=0) const
Returns the information whether the position vector describes a polygon lying around the given point.
A 2D- or 3D-Shape.
Definition: Shape.h:38
std::vector< int > geometryPoints
vector with geometry points