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