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  markedEdge = nullptr;
44  markedLane = nullptr;
45  markedTAZ = nullptr;
46  markedRoute = nullptr;
47  markedFirstGeometryPoint = nullptr;
48  markedSecondGeometryPoint = nullptr;
49 }
50 
51 
52 const Position&
54  return mySelectionPosition;
55 }
56 
57 
58 const Boundary&
60  return mySelectionBoundary;
61 }
62 
63 
64 void
66  // set position selection
67  mySelectionPosition = pos;
68  // invalidate selection boundary
71 }
72 
73 
74 void
76  // invalidate position selection
78  // set selection boundary
79  mySelectionBoundary = boundary;
80  mySelectionBoundaryShape = boundary.getShape(false);
81 }
82 
83 
84 bool
86  return mySelectedObjects.find(GLObject) != mySelectedObjects.end();
87 }
88 
89 
90 bool
92  // first check if we're selecting for boundary
94  return false;
95  }
96  // try to find parent in seleted object
97  auto finder = mySelectedObjects.find(parent);
98  // if parent was found and was inserted with full boundary, insert it
99  if (finder != mySelectedObjects.end() && finder->second && !isElementSelected(GLObject)) {
100  // insert element with full boundary
101  return addElementUnderCursor(GLObject, false, true);
102  } else {
103  return false;
104  }
105 }
106 
107 
108 bool
110  const Position& center, const double radius, const Boundary& circleBoundary) {
111  // first check that object doesn't exist
112  if (isElementSelected(GLObject)) {
113  return false;
114  } else {
115  // declare squared radius
116  const double squaredRadius = (radius * radius);
117  // continue depending if we're selecting a position or a boundary
119  // continue depending of detail level
121  // avoid empty boundaries
122  if (!circleBoundary.isInitialised()) {
123  return false;
124  }
125  // check if selection boundary contains the centering boundary of object
127  return addElementUnderCursor(GLObject, false, true);
128  }
129  // check if boundary overlaps
130  if (mySelectionBoundary.overlapsWith(circleBoundary)) {
131  return addElementUnderCursor(GLObject, false, false);
132  }
133  // check if the four boundary vertex are within circle
134  for (const auto& vertex : mySelectionBoundaryShape) {
135  if (vertex.distanceSquaredTo2D(center) <= squaredRadius) {
136  return addElementUnderCursor(GLObject, false, false);
137  }
138  }
139  // no intersection, then return false
140  return false;
141  } else {
142  // check if center is within mySelectionBoundary
143  if (mySelectionBoundary.around2D(center)) {
144  return addElementUnderCursor(GLObject, false, false);
145  } else {
146  return false;
147  }
148  }
149  } else if (mySelectionPosition != Position::INVALID) {
150  // check distance between selection position and center
151  if (mySelectionPosition.distanceSquaredTo2D(center) <= squaredRadius) {
152  return addElementUnderCursor(GLObject, false, false);
153  } else {
154  return false;
155  }
156  } else {
157  return false;
158  }
159  }
160 }
161 
162 
163 bool
165  const PositionVector& shape, const int index, const double radius) {
166  // obtain geometry point pos
167  const auto geometryPointPos = shape[index];
168  // declare squared radius
169  const double squaredRadius = (radius * radius);
170  // continue depending if we're selecting a position or a boundary
172  // continue depending of detail level
174  // make a boundary using center and radius
175  Boundary geometryPointBoundary;
176  geometryPointBoundary.add(geometryPointPos);
177  geometryPointBoundary.grow(radius);
178  // check if boundary is whithin selection boundary
179  if (mySelectionBoundary.contains(geometryPointBoundary)) {
180  return addGeometryPointUnderCursor(GLObject, index);
181  } else if (mySelectionBoundary.overlapsWith(geometryPointBoundary)) {
182  return addGeometryPointUnderCursor(GLObject, index);
183  } else {
184  // check if the four boundary vertex are within circle
185  for (const auto& vertex : mySelectionBoundaryShape) {
186  if (vertex.distanceSquaredTo2D(geometryPointPos) <= squaredRadius) {
187  return addGeometryPointUnderCursor(GLObject, index);
188  }
189  }
190  // no intersection, then return false
191  return false;
192  }
193  } else {
194  // check if center is within mySelectionBoundary
195  if (mySelectionBoundary.around2D(geometryPointPos)) {
196  return addGeometryPointUnderCursor(GLObject, index);
197  } else {
198  return false;
199  }
200  }
201  } else if (mySelectionPosition != Position::INVALID) {
202  // check distance between selection position and center
203  if (mySelectionPosition.distanceSquaredTo2D(geometryPointPos) <= squaredRadius) {
204  return addGeometryPointUnderCursor(GLObject, index);
205  } else {
206  return false;
207  }
208  } else {
209  return false;
210  }
211 }
212 
213 
214 bool
216  const PositionVector& shape, const double distance) {
217  // only process if we're selecting a precise position
219  // obtain nearest position over shape
220  const auto nearestOffset = shape.nearest_offset_to_point2D(mySelectionPosition);
221  const auto nearestPos = shape.positionAtOffset2D(nearestOffset);
222  // check distance nearest position and pos
223  if (mySelectionPosition.distanceSquaredTo2D(nearestPos) <= (distance * distance)) {
224  return addPositionOverShape(GLObject, nearestPos, nearestOffset);
225  } else {
226  return false;
227  }
228  } else {
229  return false;
230  }
231 }
232 
233 
234 bool
236  const Boundary& shapeBoundary) {
237  // first check that object doesn't exist
238  if (isElementSelected(GLObject)) {
239  return false;
240  } else if (mySelectionBoundary.isInitialised()) {
241  // avoid invalid boundaries
242  if (!shapeBoundary.isInitialised()) {
243  return false;
244  }
245  // check if selection boundary contains the centering boundary of object
246  if (mySelectionBoundary.contains(shapeBoundary)) {
247  return addElementUnderCursor(GLObject, false, true);
248  }
249  // check if shape crosses to selection boundary
250  for (int i = 1; i < (int)shape.size(); i++) {
251  if (mySelectionBoundary.crosses(shape[i - 1], shape[i])) {
252  return addElementUnderCursor(GLObject, false, false);
253  }
254  }
255  // no intersection, then return false
256  return false;
257  } else if (mySelectionPosition != Position::INVALID) {
258  // check if selection position is around shape
259  if (shape.around(mySelectionPosition)) {
260  return addElementUnderCursor(GLObject, false, false);
261  } else {
262  return false;
263  }
264  } else {
265  return false;
266  }
267 }
268 
269 
270 bool
271 GUIViewObjectsHandler::addElementUnderCursor(const GUIGlObject* GLObject, const bool checkDuplicated, const bool fullBoundary) {
272  // first check that object doesn't exist
273  if (checkDuplicated && isElementSelected(GLObject)) {
274  return false;
275  } else {
276  // check if this is an element with an associated layer
277  const auto layer = dynamic_cast<const Shape*>(GLObject);
278  if (layer) {
279  auto& layerContainer = mySortedSelectedObjects[layer->getShapeLayer() * -1];
280  layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
281  } else if (GLObject) {
282  auto& layerContainer = mySortedSelectedObjects[GLObject->getType() * -1];
283  layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
284  }
285  mySelectedObjects[GLObject] = fullBoundary;
286  return true;
287  }
288 }
289 
290 
291 bool
293  // avoid to insert duplicated elements
294  for (auto& elementLayer : mySortedSelectedObjects) {
295  for (auto& element : elementLayer.second) {
296  if (element.object == GLObject) {
297  // avoid double points
298  for (auto& index : element.geometryPoints) {
299  if (index == newIndex) {
300  return false;
301  }
302  }
303  // add new index
304  element.geometryPoints.push_back(newIndex);
305  return true;
306  }
307  }
308  }
309  // no element found then add it
310  const auto layer = dynamic_cast<const Shape*>(GLObject);
311  if (layer) {
312  auto& layerContainer = mySortedSelectedObjects[layer->getShapeLayer() * -1];
313  auto it = layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
314  it->geometryPoints.push_back(newIndex);
315  } else if (GLObject) {
316  auto& layerContainer = mySortedSelectedObjects[GLObject->getType() * -1];
317  auto it = layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
318  it->geometryPoints.push_back(newIndex);
319  }
320  mySelectedObjects[GLObject] = false;
321  return true;
322 }
323 
324 
325 bool
326 GUIViewObjectsHandler::addPositionOverShape(const GUIGlObject* GLObject, const Position& pos, const double offset) {
327  // avoid to insert duplicated elements
328  for (auto& elementLayer : mySortedSelectedObjects) {
329  for (auto& element : elementLayer.second) {
330  if (element.object == GLObject) {
331  if (element.posOverShape != Position::INVALID) {
332  return false;
333  } else {
334  // set position and offset over shape
335  element.posOverShape = pos;
336  element.offset = offset;
337  return true;
338  }
339  }
340  }
341  }
342  // no element found then add it
343  const auto layer = dynamic_cast<const Shape*>(GLObject);
344  if (layer) {
345  auto& layerContainer = mySortedSelectedObjects[layer->getShapeLayer() * -1];
346  auto it = layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
347  it->posOverShape = pos;
348  } else if (GLObject) {
349  auto& layerContainer = mySortedSelectedObjects[GLObject->getType() * -1];
350  auto it = layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
351  it->posOverShape = pos;
352  }
353  mySelectedObjects[GLObject] = false;
354  return true;
355 }
356 
357 
361 }
362 
363 
364 const std::vector<int>&
366  // avoid to insert duplicated elements
367  for (auto& elementLayer : mySortedSelectedObjects) {
368  for (auto& element : elementLayer.second) {
369  if (element.object == GLObject) {
370  return element.geometryPoints;
371  }
372  }
373  }
374  return myEmptyGeometryPoints;
375 }
376 
377 
378 const Position&
380  // avoid to insert duplicated elements
381  for (auto& elementLayer : mySortedSelectedObjects) {
382  for (auto& element : elementLayer.second) {
383  if (element.object == GLObject) {
384  return element.posOverShape;
385  }
386  }
387  }
388  return Position::INVALID;
389 }
390 
391 
392 void
394  ObjectContainer frontElement(nullptr);
395  // extract element
396  for (auto& elementLayer : mySortedSelectedObjects) {
397  auto it = elementLayer.second.begin();
398  while (it != elementLayer.second.end()) {
399  if (it->object == GLObject) {
400  // copy element to front element
401  frontElement.object = it->object;
402  frontElement.geometryPoints = it->geometryPoints;
403  // remove element from myElementsUnderCursor
404  it = elementLayer.second.erase(it);
405  } else {
406  it++;
407  }
408  }
409  }
410  // add element again wit a new layer
411  if (frontElement.object) {
412  mySortedSelectedObjects[(double)GLO_FRONTELEMENT].push_back(frontElement);
413  }
414 }
415 
416 
417 void
419  // declare object container for edge
420  ObjectContainer edgeWithGeometryPoints(nullptr);
421  // check if there are edges with geometry points in mySortedSelectedObjects
422  for (auto& elementLayer : mySortedSelectedObjects) {
423  for (auto element : elementLayer.second) {
424  if ((element.object->getType() == GLO_EDGE) && (element.geometryPoints.size() > 0)) {
425  edgeWithGeometryPoints = element;
426  }
427  }
428  }
429  // continue if something was found
430  if (edgeWithGeometryPoints.object != nullptr) {
431  // clear all selected objects
432  mySortedSelectedObjects.clear();
433  // add edge with geometry points as front element
434  mySortedSelectedObjects[(double)GLO_FRONTELEMENT].push_back(edgeWithGeometryPoints);
435  }
436 }
437 
438 /****************************************************************************/
@ 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
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
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
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:276
static const Position INVALID
used to indicate that a position is valid
Definition: Position.h:317
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