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 8154 : GUIViewObjectsHandler::GUIViewObjectsHandler() {}
33 :
34 :
35 : void
36 0 : GUIViewObjectsHandler::reset() {
37 : // reset recompute boundaries
38 0 : recomputeBoundaries = GLO_NETWORK;
39 : // clear objects containers
40 : mySortedSelectedObjects.clear();
41 : mySelectedObjects.clear();
42 0 : myNumberOfSelectedObjects = 0;
43 : myRedrawPathElements.clear();
44 : // reset marked elements
45 : myMergingJunctions.clear();
46 0 : markedEdge = nullptr;
47 0 : markedLane = nullptr;
48 0 : markedTAZ = nullptr;
49 0 : markedRoute = nullptr;
50 0 : markedFirstGeometryPoint = nullptr;
51 0 : markedSecondGeometryPoint = nullptr;
52 0 : }
53 :
54 :
55 : const Position&
56 0 : GUIViewObjectsHandler::getSelectionPosition() const {
57 0 : return mySelectionPosition;
58 : }
59 :
60 :
61 : const Boundary&
62 0 : GUIViewObjectsHandler::getSelectionBoundary() const {
63 0 : return mySelectionBoundary;
64 : }
65 :
66 :
67 : void
68 0 : GUIViewObjectsHandler::setSelectionPosition(const Position& pos) {
69 : // set position selection
70 0 : mySelectionPosition = pos;
71 : // invalidate selection boundary
72 0 : mySelectionBoundary.reset();
73 : mySelectionBoundaryShape.clear();
74 0 : }
75 :
76 :
77 : void
78 0 : GUIViewObjectsHandler::setSelectionBoundary(const Boundary& boundary) {
79 : // invalidate position selection
80 0 : mySelectionPosition = Position::INVALID;
81 : // set selection boundary
82 : mySelectionBoundary = boundary;
83 0 : mySelectionBoundaryShape = boundary.getShape(false);
84 0 : }
85 :
86 :
87 : bool
88 0 : GUIViewObjectsHandler::checkBoundaryParentObject(const GUIGlObject* GLObject, const double layer,
89 : const GUIGlObject* parent) {
90 : // first check if we're selecting for boundary
91 0 : if (!mySelectionBoundary.isInitialised()) {
92 : return false;
93 : }
94 : // try to find parent in seleted object
95 : auto finder = mySelectedObjects.find(parent);
96 : // if parent was found and was inserted with full boundary, insert it
97 0 : if (finder != mySelectedObjects.end() && finder->second.first && !isObjectSelected(GLObject)) {
98 : // insert element with full boundary
99 0 : return selectObject(GLObject, layer, false, true, nullptr);
100 : } else {
101 0 : return false;
102 : }
103 : }
104 :
105 :
106 : bool
107 0 : GUIViewObjectsHandler::checkCircleObject(const GUIVisualizationSettings::Detail d, const GUIGlObject* GLObject,
108 : const Position& center, const double radius, const Boundary& circleBoundary, const double layer) {
109 : // first check that object doesn't exist
110 0 : if (isObjectSelected(GLObject)) {
111 : return false;
112 : } else {
113 : // declare squared radius
114 0 : const double squaredRadius = (radius * radius);
115 : // continue depending if we're selecting a position or a boundary
116 0 : if (mySelectionBoundary.isInitialised()) {
117 : // continue depending of detail level
118 0 : if (d <= GUIVisualizationSettings::Detail::PreciseSelection) {
119 : // avoid empty boundaries
120 0 : if (!circleBoundary.isInitialised()) {
121 : return false;
122 : }
123 : // check if selection boundary contains the centering boundary of object
124 0 : if (mySelectionBoundary.contains2D(GLObject->getCenteringBoundary())) {
125 0 : return selectObject(GLObject, layer, false, true, nullptr);
126 : }
127 : // check if boundary overlaps
128 0 : if (mySelectionBoundary.overlapsWith(circleBoundary)) {
129 0 : return selectObject(GLObject, layer, false, false, nullptr);
130 : }
131 : // check if the four boundary vertex are within circle
132 0 : for (const auto& vertex : mySelectionBoundaryShape) {
133 0 : if (vertex.distanceSquaredTo2D(center) <= squaredRadius) {
134 0 : return selectObject(GLObject, layer, false, false, nullptr);
135 : }
136 : }
137 : // no intersection, then return false
138 : return false;
139 : } else {
140 : // check if center is within mySelectionBoundary
141 0 : if (mySelectionBoundary.around2D(center)) {
142 0 : return selectObject(GLObject, layer, false, false, nullptr);
143 : } else {
144 : return false;
145 : }
146 : }
147 : } else if (mySelectionPosition != Position::INVALID) {
148 : // check distance between selection position and center
149 0 : if (mySelectionPosition.distanceSquaredTo2D(center) <= squaredRadius) {
150 0 : return selectObject(GLObject, layer, false, false, nullptr);
151 : } else {
152 : return false;
153 : }
154 : } else {
155 : return false;
156 : }
157 : }
158 : }
159 :
160 :
161 : bool
162 0 : GUIViewObjectsHandler::checkGeometryPoint(const GUIVisualizationSettings::Detail d, const GUIGlObject* GLObject,
163 : const PositionVector& shape, const int index, const double layer, const double radius) {
164 : // obtain geometry point pos
165 0 : const auto geometryPointPos = shape[index];
166 : // declare squared radius
167 0 : const double squaredRadius = (radius * radius);
168 : // continue depending if we're selecting a position or a boundary
169 0 : if (mySelectionBoundary.isInitialised()) {
170 : // continue depending of detail level
171 0 : if (d <= GUIVisualizationSettings::Detail::PreciseSelection) {
172 : // make a boundary using center and radius
173 0 : Boundary geometryPointBoundary;
174 0 : geometryPointBoundary.add(geometryPointPos);
175 0 : geometryPointBoundary.grow(radius);
176 : // check if boundary is whithin selection boundary
177 0 : if (mySelectionBoundary.contains2D(geometryPointBoundary)) {
178 0 : return selectGeometryPoint(GLObject, index, layer);
179 0 : } else if (mySelectionBoundary.overlapsWith(geometryPointBoundary)) {
180 0 : return selectGeometryPoint(GLObject, index, layer);
181 : } else {
182 : // check if the four boundary vertex are within circle
183 0 : for (const auto& vertex : mySelectionBoundaryShape) {
184 0 : if (vertex.distanceSquaredTo2D(geometryPointPos) <= squaredRadius) {
185 0 : return selectGeometryPoint(GLObject, index, layer);
186 : }
187 : }
188 : // no intersection, then return false
189 : return false;
190 : }
191 0 : } else {
192 : // check if center is within mySelectionBoundary
193 0 : if (mySelectionBoundary.around2D(geometryPointPos)) {
194 0 : return selectGeometryPoint(GLObject, index, layer);
195 : } else {
196 : return false;
197 : }
198 : }
199 : } else if (mySelectionPosition != Position::INVALID) {
200 : // check distance between selection position and center
201 0 : if (mySelectionPosition.distanceSquaredTo2D(geometryPointPos) <= squaredRadius) {
202 0 : return selectGeometryPoint(GLObject, index, layer);
203 : } else {
204 : return false;
205 : }
206 : } else {
207 : return false;
208 : }
209 : }
210 :
211 :
212 : bool
213 0 : GUIViewObjectsHandler::checkPositionOverShape(const GUIVisualizationSettings::Detail d, const GUIGlObject* GLObject,
214 : const PositionVector& shape, const double layer, const double distance) {
215 : // only process if we're selecting a precise position
216 0 : if ((mySelectionPosition != Position::INVALID) && (d <= GUIVisualizationSettings::Detail::PreciseSelection)) {
217 : // obtain nearest position over shape
218 0 : const auto nearestOffset = shape.nearest_offset_to_point2D(mySelectionPosition);
219 0 : const auto nearestPos = shape.positionAtOffset2D(nearestOffset);
220 : // check distance nearest position and pos
221 0 : if (mySelectionPosition.distanceSquaredTo2D(nearestPos) <= (distance * distance)) {
222 0 : return selectPositionOverShape(GLObject, nearestPos, layer, nearestOffset);
223 : } else {
224 : return false;
225 : }
226 : } else {
227 : return false;
228 : }
229 : }
230 :
231 :
232 : bool
233 0 : GUIViewObjectsHandler::checkShapeObject(const GUIGlObject* GLObject, const PositionVector& shape, const Boundary& shapeBoundary,
234 : const double layer, const GNESegment* segment) {
235 : // first check that object doesn't exist
236 0 : if (isObjectSelected(GLObject)) {
237 : return false;
238 0 : } else if (mySelectionBoundary.isInitialised()) {
239 : // avoid invalid boundaries
240 0 : if (!shapeBoundary.isInitialised()) {
241 : return false;
242 : }
243 : // check if selection boundary contains the centering boundary of object
244 0 : if (mySelectionBoundary.contains2D(shapeBoundary)) {
245 0 : return selectObject(GLObject, layer, false, true, segment);
246 : }
247 : // check if shape crosses to selection boundary
248 0 : for (int i = 1; i < (int)shape.size(); i++) {
249 0 : if (mySelectionBoundary.crosses(shape[i - 1], shape[i])) {
250 0 : return selectObject(GLObject, layer, false, false, segment);
251 : }
252 : }
253 : // no intersection, then return false
254 : return false;
255 : } else if (mySelectionPosition != Position::INVALID) {
256 : // check if selection position is around shape
257 0 : if (shape.around(mySelectionPosition)) {
258 0 : return selectObject(GLObject, layer, false, false, segment);
259 : } else {
260 : return false;
261 : }
262 : } else {
263 : return false;
264 : }
265 : }
266 :
267 :
268 : bool
269 0 : GUIViewObjectsHandler::selectObject(const GUIGlObject* GLObject, const double layer, const bool checkDuplicated,
270 : const bool fullBoundary, const GNESegment* segment) {
271 : // first check that object doesn't exist
272 0 : if (checkDuplicated && isObjectSelected(GLObject)) {
273 : return false;
274 : } else {
275 0 : auto& layerContainer = mySortedSelectedObjects[layer * -1];
276 0 : layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
277 : mySelectedObjects[GLObject] = std::make_pair(fullBoundary, segment);
278 0 : myNumberOfSelectedObjects++;
279 0 : return true;
280 : }
281 : }
282 :
283 :
284 : bool
285 0 : GUIViewObjectsHandler::selectGeometryPoint(const GUIGlObject* GLObject, const int newIndex,
286 : const double layer) {
287 : // avoid to insert duplicated elements
288 0 : for (auto& elementLayer : mySortedSelectedObjects) {
289 0 : for (auto& element : elementLayer.second) {
290 0 : if (element.object == GLObject) {
291 : // avoid double points
292 0 : for (auto& index : element.geometryPoints) {
293 0 : if (index == newIndex) {
294 : return false;
295 : }
296 : }
297 : // add new index
298 0 : element.geometryPoints.push_back(newIndex);
299 0 : return true;
300 : }
301 : }
302 : }
303 : // no element found then add it
304 0 : auto& layerContainer = mySortedSelectedObjects[layer * -1];
305 0 : auto it = layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
306 0 : it->geometryPoints.push_back(newIndex);
307 : mySelectedObjects[GLObject] = std::make_pair(false, nullptr);
308 0 : myNumberOfSelectedObjects++;
309 0 : return true;
310 : }
311 :
312 :
313 : bool
314 0 : GUIViewObjectsHandler::selectPositionOverShape(const GUIGlObject* GLObject, const Position& pos, const double layer,
315 : const double offset) {
316 : // avoid to insert duplicated elements
317 0 : for (auto& elementLayer : mySortedSelectedObjects) {
318 0 : for (auto& element : elementLayer.second) {
319 0 : if (element.object == GLObject) {
320 : if (element.posOverShape != Position::INVALID) {
321 : return false;
322 : } else {
323 : // set position and offset over shape
324 0 : element.posOverShape = pos;
325 0 : element.offset = offset;
326 0 : return true;
327 : }
328 : }
329 : }
330 : }
331 : // no element found then add it
332 0 : auto& layerContainer = mySortedSelectedObjects[layer * -1];
333 0 : auto it = layerContainer.insert(layerContainer.begin(), ObjectContainer(GLObject));
334 0 : it->posOverShape = pos;
335 : mySelectedObjects[GLObject] = std::make_pair(false, nullptr);
336 0 : myNumberOfSelectedObjects++;
337 0 : return true;
338 : }
339 :
340 :
341 : bool
342 0 : GUIViewObjectsHandler::isObjectSelected(const GUIGlObject* GLObject) const {
343 0 : return mySelectedObjects.find(GLObject) != mySelectedObjects.end();
344 : }
345 :
346 :
347 : bool
348 0 : GUIViewObjectsHandler::checkRectangleSelection(const GUIVisualizationSettings& s, const GUIGlObject* GLObject,
349 : const double layer, const GUIGlObject* parent) {
350 0 : if (!s.drawForRectangleSelection) {
351 : return false;
352 : } else {
353 0 : return checkBoundaryParentObject(GLObject, layer, parent);
354 : }
355 : }
356 :
357 :
358 : const GUIViewObjectsHandler::GLObjectsSortedContainer&
359 0 : GUIViewObjectsHandler::getSelectedObjects() const {
360 0 : return mySortedSelectedObjects;
361 : }
362 :
363 :
364 : const GNESegment*
365 0 : GUIViewObjectsHandler::getSelectedSegment(const GUIGlObject* GLObject) const {
366 : auto finder = mySelectedObjects.find(GLObject);
367 0 : if (finder != mySelectedObjects.end()) {
368 0 : return finder->second.second;
369 : } else {
370 : return nullptr;
371 : }
372 : }
373 :
374 :
375 : const std::vector<int>&
376 0 : GUIViewObjectsHandler::getSelectedGeometryPoints(const GUIGlObject* GLObject) const {
377 : // avoid to insert duplicated elements
378 0 : for (auto& elementLayer : mySortedSelectedObjects) {
379 0 : for (auto& element : elementLayer.second) {
380 0 : if (element.object == GLObject) {
381 0 : return element.geometryPoints;
382 : }
383 : }
384 : }
385 0 : return myEmptyGeometryPoints;
386 : }
387 :
388 :
389 : const Position&
390 0 : GUIViewObjectsHandler::getSelectedPositionOverShape(const GUIGlObject* GLObject) const {
391 : // avoid to insert duplicated elements
392 0 : for (auto& elementLayer : mySortedSelectedObjects) {
393 0 : for (auto& element : elementLayer.second) {
394 0 : if (element.object == GLObject) {
395 0 : return element.posOverShape;
396 : }
397 : }
398 : }
399 : return Position::INVALID;
400 : }
401 :
402 :
403 : int
404 0 : GUIViewObjectsHandler::getNumberOfSelectedObjects() const {
405 0 : return myNumberOfSelectedObjects;
406 : }
407 :
408 :
409 : const std::set<const GNEPathElement*>&
410 0 : GUIViewObjectsHandler::getRedrawPathElements() const {
411 0 : return myRedrawPathElements;
412 : }
413 :
414 :
415 : bool
416 0 : GUIViewObjectsHandler::isPathElementMarkForRedraw(const GNEPathElement* pathElement) const {
417 0 : if (myRedrawPathElements.empty()) {
418 : return false;
419 : } else {
420 : return myRedrawPathElements.find(pathElement) != myRedrawPathElements.end();
421 : }
422 : }
423 :
424 :
425 : void
426 0 : GUIViewObjectsHandler::addToRedrawPathElements(const GNEPathElement* pathElement) {
427 : myRedrawPathElements.insert(pathElement);
428 0 : }
429 :
430 :
431 : const std::vector<const GNEJunction*>&
432 0 : GUIViewObjectsHandler::getMergingJunctions() const {
433 0 : return myMergingJunctions;
434 : }
435 :
436 :
437 : bool
438 0 : GUIViewObjectsHandler::addMergingJunctions(const GNEJunction* junction) {
439 : // avoid insert duplicated junctions
440 0 : for (const auto mergingJunctions : myMergingJunctions) {
441 0 : if (mergingJunctions == junction) {
442 : return false;
443 : }
444 : }
445 0 : myMergingJunctions.push_back(junction);
446 0 : return true;
447 : }
448 :
449 :
450 : void
451 0 : GUIViewObjectsHandler::updateFrontObject(const GUIGlObject* GLObject) {
452 : ObjectContainer frontElement(nullptr);
453 : // extract element
454 0 : for (auto& elementLayer : mySortedSelectedObjects) {
455 : auto it = elementLayer.second.begin();
456 0 : while (it != elementLayer.second.end()) {
457 0 : if (it->object == GLObject) {
458 : // copy element to front element
459 0 : frontElement.object = it->object;
460 0 : frontElement.geometryPoints = it->geometryPoints;
461 : // remove element from myElementsUnderCursor
462 0 : it = elementLayer.second.erase(it);
463 : } else {
464 : it++;
465 : }
466 : }
467 : }
468 : // add element again wit a new layer
469 0 : if (frontElement.object) {
470 0 : mySortedSelectedObjects[(double)GLO_FRONTELEMENT].push_back(frontElement);
471 : }
472 0 : }
473 :
474 :
475 : void
476 0 : GUIViewObjectsHandler::isolateEdgeGeometryPoints() {
477 : // declare object container for edge
478 : ObjectContainer edgeWithGeometryPoints(nullptr);
479 : // check if there are edges with geometry points in mySortedSelectedObjects
480 0 : for (auto& elementLayer : mySortedSelectedObjects) {
481 0 : for (auto element : elementLayer.second) {
482 0 : if ((element.object->getType() == GLO_EDGE) && (element.geometryPoints.size() > 0)) {
483 : edgeWithGeometryPoints = element;
484 : }
485 : }
486 : }
487 : // continue if something was found
488 0 : if (edgeWithGeometryPoints.object != nullptr) {
489 : // clear all selected objects
490 : mySortedSelectedObjects.clear();
491 : // add edge with geometry points as front element
492 0 : mySortedSelectedObjects[(double)GLO_FRONTELEMENT].push_back(edgeWithGeometryPoints);
493 : }
494 0 : }
495 :
496 : /****************************************************************************/
|