Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 9383 : 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 GUIVisualizationSettings& s, const GUIGlObject* GLObject,
93 : const double layer, 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(s, GLObject, layer, false, nullptr);
104 : } else {
105 0 : return false;
106 : }
107 : }
108 :
109 :
110 : bool
111 0 : GUIViewObjectsHandler::checkCircleObject(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,
112 : const GUIGlObject* GLObject, 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(s, 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(s, 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(s, 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& s, const GUIVisualizationSettings::Detail d,
153 : const GUIGlObject* GLObject, 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(s, 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 GUIVisualizationSettings& s, const GUIGlObject* GLObject, const PositionVector& shape,
214 : const Boundary& shapeBoundary, 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(s, 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(s, 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 GUIVisualizationSettings& s, const GUIGlObject* GLObject, const double layer,
244 : const bool checkDuplicated, const GNESegment* segment) {
245 : // check that object is visible and it doesn't was selected previously
246 0 : if (!GLObject->isVisible(s)) {
247 : return false;
248 0 : } else if (checkDuplicated && isObjectSelected(GLObject)) {
249 : return false;
250 : } else {
251 0 : auto& layerContainer = mySortedSelectedObjects[layer * -1];
252 0 : layerContainer.append(ObjectContainer(GLObject));
253 0 : mySelectedObjects[GLObject] = segment;
254 0 : myNumberOfSelectedObjects++;
255 0 : return true;
256 : }
257 : }
258 :
259 :
260 : bool
261 0 : GUIViewObjectsHandler::selectGeometryPoint(const GUIGlObject* GLObject, const int newIndex,
262 : const double layer) {
263 : // avoid to insert duplicated elements
264 0 : for (auto& elementLayer : mySortedSelectedObjects) {
265 0 : for (auto& element : elementLayer.second) {
266 0 : if (element.object == GLObject) {
267 : // avoid double points
268 0 : for (auto& index : element.geometryPoints) {
269 0 : if (index == newIndex) {
270 : return false;
271 : }
272 : }
273 : // add new index
274 0 : element.geometryPoints.push_back(newIndex);
275 0 : return true;
276 : }
277 : }
278 : }
279 : // no element found then add it
280 0 : auto& layerContainer = mySortedSelectedObjects[layer * -1];
281 0 : layerContainer.append(ObjectContainer(GLObject));
282 0 : layerContainer.back().geometryPoints.push_back(newIndex);
283 0 : mySelectedObjects[GLObject] = nullptr;
284 0 : myNumberOfSelectedObjects++;
285 0 : return true;
286 : }
287 :
288 :
289 : bool
290 0 : GUIViewObjectsHandler::selectPositionOverShape(const GUIGlObject* GLObject, const Position& pos, const double layer,
291 : const double offset) {
292 : // avoid to insert duplicated elements
293 0 : for (auto& elementLayer : mySortedSelectedObjects) {
294 0 : for (auto& element : elementLayer.second) {
295 0 : if (element.object == GLObject) {
296 : if (element.posOverShape != Position::INVALID) {
297 : return false;
298 : } else {
299 : // set position and offset over shape
300 0 : element.posOverShape = pos;
301 0 : element.offset = offset;
302 0 : return true;
303 : }
304 : }
305 : }
306 : }
307 : // no element found then add it
308 0 : auto& layerContainer = mySortedSelectedObjects[layer * -1];
309 0 : layerContainer.append(ObjectContainer(GLObject));
310 0 : layerContainer.back().posOverShape = pos;
311 0 : mySelectedObjects[GLObject] = nullptr;
312 0 : myNumberOfSelectedObjects++;
313 0 : return true;
314 : }
315 :
316 :
317 : bool
318 0 : GUIViewObjectsHandler::isObjectSelected(const GUIGlObject* GLObject) const {
319 0 : return mySelectedObjects.find(GLObject) != mySelectedObjects.end();
320 : }
321 :
322 :
323 : bool
324 0 : GUIViewObjectsHandler::checkRectangleSelection(const GUIVisualizationSettings& s, const GUIGlObject* GLObject,
325 : const double layer, const GUIGlObject* parent) {
326 0 : if (!s.drawForRectangleSelection) {
327 : return false;
328 : } else {
329 0 : return checkBoundaryParentObject(s, GLObject, layer, parent);
330 : }
331 : }
332 :
333 :
334 : const GUIViewObjectsHandler::GLObjectsSortedContainer&
335 0 : GUIViewObjectsHandler::getSelectedObjects() const {
336 0 : return mySortedSelectedObjects;
337 : }
338 :
339 :
340 : const GNESegment*
341 0 : GUIViewObjectsHandler::getSelectedSegment(const GUIGlObject* GLObject) const {
342 : auto finder = mySelectedObjects.find(GLObject);
343 0 : if (finder != mySelectedObjects.end()) {
344 0 : return finder->second;
345 : } else {
346 : return nullptr;
347 : }
348 : }
349 :
350 :
351 : const std::vector<int>&
352 0 : GUIViewObjectsHandler::getSelectedGeometryPoints(const GUIGlObject* GLObject) const {
353 : // avoid to insert duplicated elements
354 0 : for (auto& elementLayer : mySortedSelectedObjects) {
355 0 : for (auto& element : elementLayer.second) {
356 0 : if (element.object == GLObject) {
357 0 : return element.geometryPoints;
358 : }
359 : }
360 : }
361 0 : return myEmptyGeometryPoints;
362 : }
363 :
364 :
365 : const Position&
366 0 : GUIViewObjectsHandler::getSelectedPositionOverShape(const GUIGlObject* GLObject) const {
367 : // avoid to insert duplicated elements
368 0 : for (auto& elementLayer : mySortedSelectedObjects) {
369 0 : for (auto& element : elementLayer.second) {
370 0 : if (element.object == GLObject) {
371 0 : return element.posOverShape;
372 : }
373 : }
374 : }
375 : return Position::INVALID;
376 : }
377 :
378 :
379 : int
380 0 : GUIViewObjectsHandler::getNumberOfSelectedObjects() const {
381 0 : return myNumberOfSelectedObjects;
382 : }
383 :
384 :
385 : void
386 0 : GUIViewObjectsHandler::reverseSelectedObjects() {
387 0 : for (auto& layerContainer : mySortedSelectedObjects) {
388 : std::reverse(layerContainer.second.begin(), layerContainer.second.end());
389 : }
390 0 : }
391 :
392 :
393 : const std::set<const GNEPathElement*>&
394 0 : GUIViewObjectsHandler::getRedrawPathElements() const {
395 0 : return myRedrawPathElements;
396 : }
397 :
398 :
399 : bool
400 0 : GUIViewObjectsHandler::isPathElementMarkForRedraw(const GNEPathElement* pathElement) const {
401 0 : if (myRedrawPathElements.empty()) {
402 : return false;
403 : } else {
404 : return myRedrawPathElements.find(pathElement) != myRedrawPathElements.end();
405 : }
406 : }
407 :
408 :
409 : void
410 0 : GUIViewObjectsHandler::addToRedrawPathElements(const GNEPathElement* pathElement) {
411 : myRedrawPathElements.insert(pathElement);
412 0 : }
413 :
414 :
415 : const std::vector<const GNEJunction*>&
416 0 : GUIViewObjectsHandler::getMergingJunctions() const {
417 0 : return myMergingJunctions;
418 : }
419 :
420 :
421 : bool
422 0 : GUIViewObjectsHandler::addMergingJunctions(const GNEJunction* junction) {
423 : // avoid insert duplicated junctions
424 0 : for (const auto mergingJunctions : myMergingJunctions) {
425 0 : if (mergingJunctions == junction) {
426 : return false;
427 : }
428 : }
429 0 : myMergingJunctions.push_back(junction);
430 0 : return true;
431 : }
432 :
433 :
434 : void
435 0 : GUIViewObjectsHandler::updateFrontObject(const GUIGlObject* GLObject) {
436 : ObjectContainer frontElement(nullptr);
437 : // extract element
438 0 : for (auto& elementLayer : mySortedSelectedObjects) {
439 : auto it = elementLayer.second.begin();
440 0 : while (it != elementLayer.second.end()) {
441 0 : if (it->object == GLObject) {
442 : // copy element to front element
443 0 : frontElement.object = it->object;
444 0 : frontElement.geometryPoints = it->geometryPoints;
445 : // remove element from myElementsUnderCursor
446 0 : it = elementLayer.second.erase(it);
447 : } else {
448 : it++;
449 : }
450 : }
451 : }
452 : // add element again wit a new layer
453 0 : if (frontElement.object) {
454 0 : mySortedSelectedObjects[(double)GLO_FRONTELEMENT].append(frontElement);
455 : }
456 0 : }
457 :
458 :
459 : void
460 0 : GUIViewObjectsHandler::isolateEdgeGeometryPoints() {
461 : // declare object container for edge
462 : ObjectContainer edgeWithGeometryPoints(nullptr);
463 : // check if there are edges with geometry points in mySortedSelectedObjects
464 0 : for (auto& elementLayer : mySortedSelectedObjects) {
465 0 : for (auto element : elementLayer.second) {
466 0 : if ((element.object->getType() == GLO_EDGE) && (element.geometryPoints.size() > 0)) {
467 : edgeWithGeometryPoints = element;
468 : }
469 : }
470 : }
471 : // continue if something was found
472 0 : if (edgeWithGeometryPoints.object != nullptr) {
473 : // clear all selected objects
474 : mySortedSelectedObjects.clear();
475 : // add edge with geometry points as front element
476 0 : mySortedSelectedObjects[(double)GLO_FRONTELEMENT].append(edgeWithGeometryPoints);
477 : }
478 0 : }
479 :
480 :
481 : void
482 0 : GUIViewObjectsHandler::ObjectContainerLayer::append(const ObjectContainer& objectContainer) {
483 0 : if (capacity() == size()) {
484 0 : if (size() < 10) {
485 0 : reserve(size() + 10);
486 : } else {
487 0 : reserve(size() + 1000);
488 : }
489 : }
490 0 : push_back(objectContainer);
491 0 : }
492 :
493 : /****************************************************************************/
|