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