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 GUIGeometry.cpp
15 : /// @author Pablo Alvarez Lopez
16 : /// @date Oct 2020
17 : ///
18 : // File for geometry classes and functions
19 : /****************************************************************************/
20 : #include <utils/geom/GeomHelper.h>
21 : #include <utils/gui/div/GLHelper.h>
22 : #include <utils/gui/globjects/GLIncludes.h>
23 : #include <utils/gui/globjects/GUIGlObjectTypes.h>
24 : #include <utils/gui/div/GUIGlobalViewObjectsHandler.h>
25 :
26 : #include "GUIGeometry.h"
27 :
28 : #define CIRCLE_RESOLUTION (double)10 // inverse in degrees
29 :
30 : // ===========================================================================
31 : // static member definitions
32 : // ===========================================================================
33 : PositionVector GUIGeometry::myCircleCoords;
34 :
35 : // ===========================================================================
36 : // method definitions
37 : // ===========================================================================
38 :
39 0 : GUIGeometry::GUIGeometry() {
40 0 : }
41 :
42 :
43 51126 : GUIGeometry::GUIGeometry(const PositionVector& shape) :
44 : myShape(shape) {
45 : // calculate shape rotation and lengths
46 25563 : calculateShapeRotationsAndLengths();
47 25563 : }
48 :
49 :
50 0 : GUIGeometry::GUIGeometry(const PositionVector& shape, const std::vector<double>& shapeRotations,
51 0 : const std::vector<double>& shapeLengths) :
52 : myShape(shape),
53 0 : myShapeRotations(shapeRotations),
54 0 : myShapeLengths(shapeLengths) {
55 0 : }
56 :
57 :
58 : void
59 0 : GUIGeometry::updateGeometry(const PositionVector& shape) {
60 : // clear geometry
61 0 : clearGeometry();
62 : // update shape
63 : myShape = shape;
64 : // calculate shape rotation and lengths
65 0 : calculateShapeRotationsAndLengths();
66 0 : }
67 :
68 :
69 : void
70 0 : GUIGeometry::updateGeometry(const PositionVector& shape, const double posOverShape,
71 : const double lateralOffset) {
72 : // first clear geometry
73 0 : clearGeometry();
74 : // get shape length
75 0 : const double shapeLength = shape.length();
76 : // calculate position and rotation
77 0 : if (posOverShape < 0) {
78 0 : myShape.push_back(shape.positionAtOffset(0, lateralOffset));
79 0 : myShapeRotations.push_back(shape.rotationDegreeAtOffset(0));
80 0 : } else if (posOverShape > shapeLength) {
81 0 : myShape.push_back(shape.positionAtOffset(shapeLength, lateralOffset));
82 0 : myShapeRotations.push_back(shape.rotationDegreeAtOffset(shapeLength));
83 : } else {
84 0 : myShape.push_back(shape.positionAtOffset(posOverShape, lateralOffset));
85 0 : myShapeRotations.push_back(shape.rotationDegreeAtOffset(posOverShape));
86 : }
87 0 : }
88 :
89 :
90 : void
91 0 : GUIGeometry::updateGeometry(const PositionVector& shape, double starPosOverShape,
92 : double endPosOverShape, const double lateralOffset) {
93 : // first clear geometry
94 0 : clearGeometry();
95 : // set new shape
96 : myShape = shape;
97 : // set lateral offset
98 0 : myShape.move2side(lateralOffset);
99 : // get shape length
100 0 : const double shapeLength = myShape.length2D();
101 : // set initial beginTrim value
102 0 : if (starPosOverShape < 0) {
103 : endPosOverShape = 0;
104 : }
105 : // set initial endtrim value
106 : if (starPosOverShape < 0) {
107 : endPosOverShape = shapeLength;
108 : }
109 : // check maximum beginTrim
110 0 : if (starPosOverShape > (shapeLength - POSITION_EPS)) {
111 : endPosOverShape = (shapeLength - POSITION_EPS);
112 : }
113 : // check maximum endTrim
114 0 : if ((endPosOverShape > shapeLength)) {
115 : endPosOverShape = shapeLength;
116 : }
117 : // check sub-vector
118 0 : if (endPosOverShape <= starPosOverShape) {
119 0 : endPosOverShape = endPosOverShape + POSITION_EPS;
120 : }
121 : // trim shape
122 0 : myShape = myShape.getSubpart2D(starPosOverShape, endPosOverShape);
123 : // calculate shape rotation and lengths
124 0 : calculateShapeRotationsAndLengths();
125 0 : }
126 :
127 :
128 : void
129 0 : GUIGeometry::updateGeometry(const PositionVector& shape, double beginTrimPosition, const Position& extraFirstPosition,
130 : double endTrimPosition, const Position& extraLastPosition) {
131 : // first clear geometry
132 0 : clearGeometry();
133 : // set new shape
134 : myShape = shape;
135 : // check trim values
136 0 : if ((beginTrimPosition != -1) || (endTrimPosition != -1)) {
137 : // get shape length
138 0 : const double shapeLength = myShape.length2D();
139 : // set initial beginTrim value
140 0 : if (beginTrimPosition < 0) {
141 : beginTrimPosition = 0;
142 : }
143 : // set initial endtrim value
144 0 : if (endTrimPosition < 0) {
145 : endTrimPosition = shapeLength;
146 : }
147 : // check maximum beginTrim
148 0 : if (beginTrimPosition > (shapeLength - POSITION_EPS)) {
149 : beginTrimPosition = (shapeLength - POSITION_EPS);
150 : }
151 : // check maximum endTrim
152 0 : if ((endTrimPosition > shapeLength)) {
153 : endTrimPosition = shapeLength;
154 : }
155 : // check sub-vector
156 0 : if (endTrimPosition <= beginTrimPosition) {
157 0 : endTrimPosition = endTrimPosition + POSITION_EPS;
158 : }
159 : // trim shape
160 0 : myShape = myShape.getSubpart2D(beginTrimPosition, endTrimPosition);
161 : // add extra positions
162 : if (extraFirstPosition != Position::INVALID) {
163 0 : myShape.push_front_noDoublePos(extraFirstPosition);
164 : }
165 : if (extraLastPosition != Position::INVALID) {
166 0 : myShape.push_back_noDoublePos(extraLastPosition);
167 : }
168 : }
169 : // calculate shape rotation and lengths
170 0 : calculateShapeRotationsAndLengths();
171 0 : }
172 :
173 :
174 : void
175 0 : GUIGeometry::updateSinglePosGeometry(const Position& position, const double rotation) {
176 : // first clear geometry
177 0 : clearGeometry();
178 : // set position and rotation
179 0 : myShape.push_back(position);
180 0 : myShapeRotations.push_back(rotation);
181 0 : }
182 :
183 :
184 0 : void GUIGeometry::clearGeometry() {
185 : // clear geometry containers
186 : myShape.clear();
187 : myShapeRotations.clear();
188 : myShapeLengths.clear();
189 0 : }
190 :
191 :
192 : void
193 0 : GUIGeometry::moveGeometryToSide(const double amount) {
194 : // move shape
195 0 : myShape.move2side(amount);
196 0 : }
197 :
198 :
199 : void
200 0 : GUIGeometry::scaleGeometry(const double scale) {
201 : // scale shape and lengths
202 0 : myShape.scaleRelative(scale);
203 : // scale lengths
204 0 : for (auto& shapeLength : myShapeLengths) {
205 0 : shapeLength *= scale;
206 : }
207 0 : }
208 :
209 :
210 : const PositionVector&
211 25563 : GUIGeometry::getShape() const {
212 25563 : return myShape;
213 : }
214 :
215 :
216 : const std::vector<double>&
217 25563 : GUIGeometry::getShapeRotations() const {
218 25563 : return myShapeRotations;
219 : }
220 :
221 :
222 : const std::vector<double>&
223 25563 : GUIGeometry::getShapeLengths() const {
224 25563 : return myShapeLengths;
225 : }
226 :
227 :
228 : double
229 87242 : GUIGeometry::calculateRotation(const Position& first, const Position& second) {
230 : // return rotation (angle) of the vector constructed by points first and second
231 87242 : return ((double)atan2((second.x() - first.x()), (first.y() - second.y())) * (double) 180.0 / (double)M_PI);
232 : }
233 :
234 :
235 : double
236 87242 : GUIGeometry::calculateLength(const Position& first, const Position& second) {
237 : // return 2D distance between two points
238 87242 : return first.distanceTo2D(second);
239 : }
240 :
241 :
242 : void
243 0 : GUIGeometry::adjustStartPosGeometricPath(double& startPos, const PositionVector& startLaneShape,
244 : double& endPos, const PositionVector& endLaneShape) {
245 : // adjust both, if start and end lane are the same
246 0 : if ((startLaneShape.size() > 0) &&
247 0 : (endLaneShape.size() > 0) &&
248 0 : (startLaneShape == endLaneShape) &&
249 0 : (startPos != -1) &&
250 0 : (endPos != -1)) {
251 0 : if (startPos >= endPos) {
252 0 : endPos = (startPos + POSITION_EPS);
253 : }
254 : }
255 : // adjust startPos
256 0 : if ((startPos != -1) && (startLaneShape.size() > 0)) {
257 0 : if (startPos < POSITION_EPS) {
258 0 : startPos = POSITION_EPS;
259 : }
260 0 : if (startPos > (startLaneShape.length() - POSITION_EPS)) {
261 0 : startPos = (startLaneShape.length() - POSITION_EPS);
262 : }
263 : }
264 : // adjust endPos
265 0 : if ((endPos != -1) && (endLaneShape.size() > 0)) {
266 0 : if (endPos < POSITION_EPS) {
267 0 : endPos = POSITION_EPS;
268 : }
269 0 : if (endPos > (endLaneShape.length() - POSITION_EPS)) {
270 0 : endPos = (endLaneShape.length() - POSITION_EPS);
271 : }
272 : }
273 0 : }
274 :
275 :
276 : void
277 0 : GUIGeometry::drawGeometry(const GUIVisualizationSettings::Detail d, const GUIGeometry& geometry,
278 : const double width, double offset) {
279 : // continue depending of detail level
280 0 : if (d <= GUIVisualizationSettings::Detail::GeometryBoxLines) {
281 0 : GLHelper::drawBoxLines(geometry.getShape(), geometry.getShapeRotations(), geometry.getShapeLengths(), width, 0, offset);
282 0 : } else if (d < GUIVisualizationSettings::Detail::GeometryBoxSimpleLine) {
283 : // set line width
284 0 : glLineWidth(static_cast<float>(width));
285 : // draw a simple line
286 0 : GLHelper::drawLine(geometry.getShape());
287 : // restore line width
288 0 : glLineWidth(1);
289 : } else {
290 : // draw a simple line
291 0 : GLHelper::drawLine(geometry.getShape());
292 : }
293 0 : }
294 :
295 :
296 : void
297 0 : GUIGeometry::drawGeometry(const GUIVisualizationSettings::Detail d, const GUIGeometry& geometry,
298 : const std::vector<RGBColor>& colors, const double width, double offset) {
299 : // continue depending of detail level
300 0 : if (d <= GUIVisualizationSettings::Detail::GeometryBoxLines) {
301 0 : GLHelper::drawBoxLines(geometry.getShape(), geometry.getShapeRotations(), geometry.getShapeLengths(), colors, width, 0, offset);
302 : } else {
303 : // set first color
304 0 : GLHelper::setColor(*colors.begin());
305 : // set width
306 0 : if (d < GUIVisualizationSettings::Detail::GeometryBoxSimpleLine) {
307 : // set line width
308 0 : glLineWidth(static_cast<float>(width));
309 : // draw a simple line
310 0 : GLHelper::drawLine(geometry.getShape());
311 : // restore line width
312 0 : glLineWidth(1);
313 : } else {
314 : // draw a simple line
315 0 : GLHelper::drawLine(geometry.getShape());
316 : }
317 : }
318 0 : }
319 :
320 :
321 : void
322 0 : GUIGeometry::drawContourGeometry(const GUIGeometry& geometry, const double width, const bool drawExtremes) {
323 : // get shapes
324 0 : PositionVector shapeA = geometry.getShape();
325 0 : PositionVector shapeB = geometry.getShape();
326 : // move both shapes
327 0 : shapeA.move2side((width - 0.1));
328 0 : shapeB.move2side((width - 0.1) * -1);
329 : // check if we have to drawn extremes
330 0 : if (drawExtremes) {
331 : // reverse shape B
332 0 : shapeB = shapeB.reverse();
333 : // append shape B to shape A
334 0 : shapeA.append(shapeB, 0);
335 : // close shape A
336 0 : shapeA.closePolygon();
337 : // draw box lines using shapeA
338 0 : GLHelper::drawBoxLines(shapeA, 0.1);
339 : } else {
340 : // draw box lines using shapeA
341 0 : GLHelper::drawBoxLines(shapeA, 0.1);
342 : // draw box lines using shapeA
343 0 : GLHelper::drawBoxLines(shapeB, 0.1);
344 : }
345 0 : }
346 :
347 :
348 : void
349 0 : GUIGeometry::drawGeometryPoints(const GUIVisualizationSettings::Detail d, const PositionVector& shape,
350 : const RGBColor& color, const double radius, const double exaggeration,
351 : const bool editingElevation) {
352 : // check detail level
353 0 : if (d <= GUIVisualizationSettings::Detail::GeometryPoint) {
354 : // get exaggeratedRadio
355 0 : const double exaggeratedRadio = (radius * exaggeration);
356 : // iterate over geometryPoints
357 0 : for (const auto& geometryPos : shape) {
358 : // push geometry point matrix
359 0 : GLHelper::pushMatrix();
360 : // move to vertex
361 0 : glTranslated(geometryPos.x(), geometryPos.y(), 0.2);
362 : // set color
363 0 : GLHelper::setColor(color);
364 : // draw circle detailled
365 0 : GLHelper::drawFilledCircleDetailled(d, exaggeratedRadio);
366 : // pop geometry point matrix
367 0 : GLHelper::popMatrix();
368 : // draw elevation or special symbols (Start, End and Block)
369 0 : if (d <= GUIVisualizationSettings::Detail::Text) {
370 0 : if (editingElevation) {
371 : // Push Z matrix
372 0 : GLHelper::pushMatrix();
373 : // draw Z (elevation)
374 0 : GLHelper::drawText(toString(geometryPos.z()), geometryPos, 0.3, 0.7, color.invertedColor());
375 : // pop Z matrix
376 0 : GLHelper::popMatrix();
377 : } else if (geometryPos == shape.front()) {
378 : // push "S" matrix
379 0 : GLHelper::pushMatrix();
380 : // draw a "s" over first point
381 0 : GLHelper::drawText("S", geometryPos, 0.3, 2 * exaggeratedRadio, color.invertedColor());
382 : // pop "S" matrix
383 0 : GLHelper::popMatrix();
384 : } else if (geometryPos == shape.back()) {
385 : // push "E" matrix
386 0 : GLHelper::pushMatrix();
387 : // draw a "e" over last point if polygon isn't closed
388 0 : GLHelper::drawText("E", geometryPos, 0.3, 2 * exaggeratedRadio, color.invertedColor());
389 : // pop "E" matrix
390 0 : GLHelper::popMatrix();
391 : }
392 : }
393 : }
394 : }
395 0 : }
396 :
397 :
398 : void
399 0 : GUIGeometry::drawParentLine(const GUIVisualizationSettings& s, const Position& parent, const Position& child,
400 : const RGBColor& color, const bool drawEntire, const double lineWidth) {
401 0 : if (!s.drawForRectangleSelection) {
402 : // calculate rotation
403 0 : const double rot = RAD2DEG(parent.angleTo2D(child)) + 90;
404 : // calculate distance between origin and destination
405 : const double distanceSquared = parent.distanceSquaredTo2D(child);
406 : // Add a draw matrix for details
407 0 : GLHelper::pushMatrix();
408 : // move back
409 0 : glTranslated(0, 0, -1);
410 : // draw box line
411 0 : if (drawEntire) {
412 : // draw first box line
413 0 : GLHelper::setColor(color.changedBrightness(-50));
414 0 : GLHelper::drawBoxLine(parent, rot, sqrt(distanceSquared), lineWidth);
415 : // move front
416 0 : glTranslated(0, 0, 0.1);
417 : // draw second box line
418 0 : GLHelper::setColor(color);
419 0 : GLHelper::drawBoxLine(parent, rot, sqrt(distanceSquared), .04);
420 0 : } else if (distanceSquared > 25) {
421 : // draw first box line with length 4.9
422 0 : GLHelper::setColor(color.changedBrightness(-50));
423 0 : GLHelper::drawBoxLine(parent, rot, 4.9, lineWidth);
424 0 : glTranslated(0, 0, 0.1);
425 : // draw second box line with length 4.9
426 0 : GLHelper::setColor(color);
427 0 : GLHelper::drawBoxLine(parent, rot, 4.9, .04);
428 : // draw arrow depending of distanceSquared (10*10)
429 0 : if (distanceSquared > 100) {
430 : // calculate positionVector between both points
431 0 : const PositionVector vector = {parent, child};
432 : // draw first arrow at end
433 0 : GLHelper::setColor(color.changedBrightness(-50));
434 0 : GLHelper::drawTriangleAtEnd(parent,
435 0 : vector.positionAtOffset2D(5),
436 : s.additionalSettings.arrowWidth,
437 : s.additionalSettings.arrowLength,
438 : s.additionalSettings.arrowOffset);
439 : // move front
440 0 : glTranslated(0, 0, 0.1);
441 : // draw second arrow at end
442 0 : GLHelper::setColor(color);
443 0 : GLHelper::drawTriangleAtEnd(parent,
444 0 : vector.positionAtOffset2D(5),
445 : s.additionalSettings.arrowWidth - 0.01,
446 : s.additionalSettings.arrowLength - 0.01,
447 : s.additionalSettings.arrowOffset - 0.01);
448 0 : }
449 : }
450 : // pop draw matrix
451 0 : GLHelper::popMatrix();
452 : }
453 0 : }
454 :
455 :
456 : void
457 0 : GUIGeometry::drawChildLine(const GUIVisualizationSettings& s, const Position& child, const Position& parent,
458 : const RGBColor& color, const bool drawEntire, const double lineWidth) {
459 0 : if (!s.drawForRectangleSelection) {
460 : // calculate distance between origin and destination
461 : const double distanceSquared = child.distanceSquaredTo2D(parent);
462 : // calculate subline width
463 0 : const double sublineWidth = (lineWidth * 0.8);
464 : // calculate rotation
465 0 : const double rot = RAD2DEG(child.angleTo2D(parent)) + 90;
466 : // Add a draw matrix for details
467 0 : GLHelper::pushMatrix();
468 : // move back
469 0 : glTranslated(0, 0, -1);
470 : // set color
471 0 : GLHelper::setColor(color);
472 : // draw box line
473 0 : if (drawEntire || (distanceSquared < 25)) {
474 : // set color
475 0 : GLHelper::setColor(color);
476 : // draw first box line
477 0 : GLHelper::setColor(color.changedBrightness(-50));
478 0 : GLHelper::drawBoxLine(child, rot, sqrt(distanceSquared), lineWidth);
479 : // move front
480 0 : glTranslated(0, 0, 0.1);
481 : // draw second box line
482 0 : GLHelper::setColor(color);
483 0 : GLHelper::drawBoxLine(child, rot, sqrt(distanceSquared), sublineWidth);
484 : } else {
485 : // draw first box line with length 4.9
486 0 : GLHelper::setColor(color.changedBrightness(-50));
487 0 : GLHelper::drawBoxLine(child, rot, 4.9, lineWidth);
488 0 : glTranslated(0, 0, 0.1);
489 : // draw second box line with length
490 0 : GLHelper::setColor(color);
491 0 : GLHelper::drawBoxLine(child, rot, 4.9, sublineWidth);
492 : // draw arrow depending of distanceSquared (10*10)
493 0 : if (distanceSquared > 100) {
494 : // calculate positionVector between both points
495 0 : const PositionVector vector = {child, parent};
496 : // draw first arrow at end
497 0 : GLHelper::setColor(color.changedBrightness(-50));
498 0 : GLHelper::drawTriangleAtEnd(child,
499 0 : vector.positionAtOffset2D(5),
500 : s.additionalSettings.arrowWidth,
501 : s.additionalSettings.arrowLength,
502 : s.additionalSettings.arrowOffset);
503 : // move front
504 0 : glTranslated(0, 0, 0.1);
505 : // draw second arrow at end
506 0 : GLHelper::setColor(color);
507 0 : GLHelper::drawTriangleAtEnd(child,
508 0 : vector.positionAtOffset2D(5),
509 : s.additionalSettings.arrowWidth - 0.01,
510 : s.additionalSettings.arrowLength - 0.01,
511 : s.additionalSettings.arrowOffset - 0.01);
512 0 : }
513 : }
514 : // pop draw matrix
515 0 : GLHelper::popMatrix();
516 : }
517 0 : }
518 :
519 :
520 : PositionVector
521 0 : GUIGeometry::getVertexCircleAroundPosition(const Position& pos, const double width, const int steps) {
522 : // first check if we have to fill myCircleCoords (only once)
523 0 : if (myCircleCoords.size() == 0) {
524 0 : for (int i = 0; i <= (int)(360 * CIRCLE_RESOLUTION); ++i) {
525 0 : const double x = (double) sin(DEG2RAD(i / CIRCLE_RESOLUTION));
526 0 : const double y = (double) cos(DEG2RAD(i / CIRCLE_RESOLUTION));
527 0 : myCircleCoords.push_back(Position(x, y));
528 : }
529 : }
530 0 : PositionVector vertexCircle;
531 0 : const double inc = 360 / (double)steps;
532 : // obtain all vertices
533 0 : for (int i = 0; i <= steps; ++i) {
534 0 : const Position& vertex = myCircleCoords[GUIGeometry::angleLookup(i * inc)];
535 0 : vertexCircle.push_back(Position(vertex.x() * width, vertex.y() * width));
536 : }
537 : // move result using position
538 0 : vertexCircle.add(pos);
539 0 : return vertexCircle;
540 0 : }
541 :
542 :
543 : void
544 0 : GUIGeometry::rotateOverLane(const double rot) {
545 : // rotate using rotation calculated in PositionVector
546 0 : glRotated((rot * -1) + 90, 0, 0, 1);
547 0 : }
548 :
549 :
550 : int
551 0 : GUIGeometry::angleLookup(const double angleDeg) {
552 0 : const int numCoords = (int)myCircleCoords.size() - 1;
553 0 : int index = ((int)(floor(angleDeg * CIRCLE_RESOLUTION + 0.5))) % numCoords;
554 0 : if (index < 0) {
555 0 : index += numCoords;
556 : }
557 : assert(index >= 0);
558 0 : return (int)index;
559 : }
560 :
561 :
562 : void
563 25563 : GUIGeometry::calculateShapeRotationsAndLengths() {
564 : // clear rotations and lengths
565 : myShapeRotations.clear();
566 : myShapeLengths.clear();
567 : // Get number of parts of the shape
568 25563 : int numberOfSegments = (int)myShape.size() - 1;
569 : // If number of segments is more than 0
570 25563 : if (numberOfSegments >= 0) {
571 : // Reserve memory (To improve efficiency)
572 25563 : myShapeRotations.reserve(numberOfSegments);
573 25563 : myShapeLengths.reserve(numberOfSegments);
574 : // Calculate lengths and rotations for every shape
575 112805 : for (int i = 0; i < numberOfSegments; i++) {
576 87242 : myShapeRotations.push_back(calculateRotation(myShape[i], myShape[i + 1]));
577 87242 : myShapeLengths.push_back(calculateLength(myShape[i], myShape[i + 1]));
578 : }
579 : }
580 25563 : }
581 :
582 : /****************************************************************************/
|