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