Eclipse SUMO - Simulation of Urban MObility
GUIGeometry.cpp
Go to the documentation of this file.
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 /****************************************************************************/
18 // File for geometry classes and functions
19 /****************************************************************************/
20 #include <utils/geom/GeomHelper.h>
21 #include <utils/gui/div/GLHelper.h>
25 
26 #include "GUIGeometry.h"
27 
28 #define CIRCLE_RESOLUTION (double)10 // inverse in degrees
29 
30 // ===========================================================================
31 // static member definitions
32 // ===========================================================================
34 
35 // ===========================================================================
36 // method definitions
37 // ===========================================================================
38 
40 }
41 
42 
44  myShape(shape) {
45  // calculate shape rotation and lengths
47 }
48 
49 
50 GUIGeometry::GUIGeometry(const PositionVector& shape, const std::vector<double>& shapeRotations,
51  const std::vector<double>& shapeLengths) :
52  myShape(shape),
53  myShapeRotations(shapeRotations),
54  myShapeLengths(shapeLengths) {
55 }
56 
57 
58 void
60  // clear geometry
61  clearGeometry();
62  // update shape
63  myShape = shape;
64  // calculate shape rotation and lengths
66 }
67 
68 
69 void
70 GUIGeometry::updateGeometry(const PositionVector& shape, const double posOverShape,
71  const double lateralOffset) {
72  // first clear geometry
73  clearGeometry();
74  // get shape length
75  const double shapeLength = shape.length();
76  // calculate position and rotation
77  if (posOverShape < 0) {
78  myShape.push_back(shape.positionAtOffset(0, lateralOffset));
79  myShapeRotations.push_back(shape.rotationDegreeAtOffset(0));
80  } else if (posOverShape > shapeLength) {
81  myShape.push_back(shape.positionAtOffset(shapeLength, lateralOffset));
82  myShapeRotations.push_back(shape.rotationDegreeAtOffset(shapeLength));
83  } else {
84  myShape.push_back(shape.positionAtOffset(posOverShape, lateralOffset));
85  myShapeRotations.push_back(shape.rotationDegreeAtOffset(posOverShape));
86  }
87 }
88 
89 
90 void
91 GUIGeometry::updateGeometry(const PositionVector& shape, double starPosOverShape,
92  double endPosOverShape, const double lateralOffset) {
93  // first clear geometry
94  clearGeometry();
95  // set new shape
96  myShape = shape;
97  // set lateral offset
98  myShape.move2side(lateralOffset);
99  // get shape length
100  const double shapeLength = myShape.length2D();
101  // set initial beginTrim value
102  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  if (starPosOverShape > (shapeLength - POSITION_EPS)) {
111  endPosOverShape = (shapeLength - POSITION_EPS);
112  }
113  // check maximum endTrim
114  if ((endPosOverShape > shapeLength)) {
115  endPosOverShape = shapeLength;
116  }
117  // check sub-vector
118  if (endPosOverShape <= starPosOverShape) {
119  endPosOverShape = endPosOverShape + POSITION_EPS;
120  }
121  // trim shape
122  myShape = myShape.getSubpart2D(starPosOverShape, endPosOverShape);
123  // calculate shape rotation and lengths
125 }
126 
127 
128 void
129 GUIGeometry::updateGeometry(const PositionVector& shape, double beginTrimPosition, const Position& extraFirstPosition,
130  double endTrimPosition, const Position& extraLastPosition) {
131  // first clear geometry
132  clearGeometry();
133  // set new shape
134  myShape = shape;
135  // check trim values
136  if ((beginTrimPosition != -1) || (endTrimPosition != -1)) {
137  // get shape length
138  const double shapeLength = myShape.length2D();
139  // set initial beginTrim value
140  if (beginTrimPosition < 0) {
141  beginTrimPosition = 0;
142  }
143  // set initial endtrim value
144  if (endTrimPosition < 0) {
145  endTrimPosition = shapeLength;
146  }
147  // check maximum beginTrim
148  if (beginTrimPosition > (shapeLength - POSITION_EPS)) {
149  beginTrimPosition = (shapeLength - POSITION_EPS);
150  }
151  // check maximum endTrim
152  if ((endTrimPosition > shapeLength)) {
153  endTrimPosition = shapeLength;
154  }
155  // check sub-vector
156  if (endTrimPosition <= beginTrimPosition) {
157  endTrimPosition = endTrimPosition + POSITION_EPS;
158  }
159  // trim shape
160  myShape = myShape.getSubpart2D(beginTrimPosition, endTrimPosition);
161  // add extra positions
162  if (extraFirstPosition != Position::INVALID) {
163  myShape.push_front_noDoublePos(extraFirstPosition);
164  }
165  if (extraLastPosition != Position::INVALID) {
166  myShape.push_back_noDoublePos(extraLastPosition);
167  }
168  }
169  // calculate shape rotation and lengths
171 }
172 
173 
174 void
175 GUIGeometry::updateSinglePosGeometry(const Position& position, const double rotation) {
176  // first clear geometry
177  clearGeometry();
178  // set position and rotation
179  myShape.push_back(position);
180  myShapeRotations.push_back(rotation);
181 }
182 
183 
184 void
185 GUIGeometry::moveGeometryToSide(const double amount) {
186  // move shape
187  myShape.move2side(amount);
188 }
189 
190 
191 void
192 GUIGeometry::scaleGeometry(const double scale) {
193  // scale shape and lengths
194  myShape.scaleRelative(scale);
195  // scale lengths
196  for (auto& shapeLength : myShapeLengths) {
197  shapeLength *= scale;
198  }
199 }
200 
201 
202 const PositionVector&
204  return myShape;
205 }
206 
207 
208 const std::vector<double>&
210  return myShapeRotations;
211 }
212 
213 
214 const std::vector<double>&
216  return myShapeLengths;
217 }
218 
219 
220 double
221 GUIGeometry::calculateRotation(const Position& first, const Position& second) {
222  // return rotation (angle) of the vector constructed by points first and second
223  return ((double)atan2((second.x() - first.x()), (first.y() - second.y())) * (double) 180.0 / (double)M_PI);
224 }
225 
226 
227 double
228 GUIGeometry::calculateLength(const Position& first, const Position& second) {
229  // return 2D distance between two points
230  return first.distanceTo2D(second);
231 }
232 
233 
234 void
235 GUIGeometry::adjustStartPosGeometricPath(double& startPos, const PositionVector& startLaneShape,
236  double& endPos, const PositionVector& endLaneShape) {
237  // adjust both, if start and end lane are the same
238  if ((startLaneShape.size() > 0) &&
239  (endLaneShape.size() > 0) &&
240  (startLaneShape == endLaneShape) &&
241  (startPos != -1) &&
242  (endPos != -1)) {
243  if (startPos >= endPos) {
244  endPos = (startPos + POSITION_EPS);
245  }
246  }
247  // adjust startPos
248  if ((startPos != -1) && (startLaneShape.size() > 0)) {
249  if (startPos < POSITION_EPS) {
250  startPos = POSITION_EPS;
251  }
252  if (startPos > (startLaneShape.length() - POSITION_EPS)) {
253  startPos = (startLaneShape.length() - POSITION_EPS);
254  }
255  }
256  // adjust endPos
257  if ((endPos != -1) && (endLaneShape.size() > 0)) {
258  if (endPos < POSITION_EPS) {
259  endPos = POSITION_EPS;
260  }
261  if (endPos > (endLaneShape.length() - POSITION_EPS)) {
262  endPos = (endLaneShape.length() - POSITION_EPS);
263  }
264  }
265 }
266 
267 
268 void
270  const double width, double offset) {
271  // continue depending of detail level
273  GLHelper::drawBoxLines(geometry.getShape(), geometry.getShapeRotations(), geometry.getShapeLengths(), width, 0, offset);
275  // set line width
276  glLineWidth(static_cast<float>(width));
277  // draw a simple line
278  GLHelper::drawLine(geometry.getShape());
279  // restore line width
280  glLineWidth(1);
281  } else {
282  // draw a simple line
283  GLHelper::drawLine(geometry.getShape());
284  }
285 }
286 
287 
288 void
290  const std::vector<RGBColor>& colors, const double width, double offset) {
291  // continue depending of detail level
293  GLHelper::drawBoxLines(geometry.getShape(), geometry.getShapeRotations(), geometry.getShapeLengths(), colors, width, 0, offset);
294  } else {
295  // set first color
296  GLHelper::setColor(*colors.begin());
297  // set width
299  // set line width
300  glLineWidth(static_cast<float>(width));
301  // draw a simple line
302  GLHelper::drawLine(geometry.getShape());
303  // restore line width
304  glLineWidth(1);
305  } else {
306  // draw a simple line
307  GLHelper::drawLine(geometry.getShape());
308  }
309  }
310 }
311 
312 
313 void
314 GUIGeometry::drawContourGeometry(const GUIGeometry& geometry, const double width, const bool drawExtremes) {
315  // get shapes
316  PositionVector shapeA = geometry.getShape();
317  PositionVector shapeB = geometry.getShape();
318  // move both shapes
319  shapeA.move2side((width - 0.1));
320  shapeB.move2side((width - 0.1) * -1);
321  // check if we have to drawn extremes
322  if (drawExtremes) {
323  // reverse shape B
324  shapeB = shapeB.reverse();
325  // append shape B to shape A
326  shapeA.append(shapeB, 0);
327  // close shape A
328  shapeA.closePolygon();
329  // draw box lines using shapeA
330  GLHelper::drawBoxLines(shapeA, 0.1);
331  } else {
332  // draw box lines using shapeA
333  GLHelper::drawBoxLines(shapeA, 0.1);
334  // draw box lines using shapeA
335  GLHelper::drawBoxLines(shapeB, 0.1);
336  }
337 }
338 
339 
340 void
342  const RGBColor& color, const double radius, const double exaggeration,
343  const bool editingElevation) {
344  // check detail level
346  // get exaggeratedRadio
347  const double exaggeratedRadio = (radius * exaggeration);
348  // iterate over geometryPoints
349  for (const auto& geometryPos : shape) {
350  // push geometry point matrix
352  // move to vertex
353  glTranslated(geometryPos.x(), geometryPos.y(), 0.2);
354  // set color
355  GLHelper::setColor(color);
356  // draw circle detailled
357  GLHelper::drawFilledCircleDetailled(d, exaggeratedRadio);
358  // pop geometry point matrix
360  // draw elevation or special symbols (Start, End and Block)
362  if (editingElevation) {
363  // Push Z matrix
365  // draw Z (elevation)
366  GLHelper::drawText(toString(geometryPos.z()), geometryPos, 0.3, 0.7, color.invertedColor());
367  // pop Z matrix
369  } else if (geometryPos == shape.front()) {
370  // push "S" matrix
372  // draw a "s" over first point
373  GLHelper::drawText("S", geometryPos, 0.3, 2 * exaggeratedRadio, color.invertedColor());
374  // pop "S" matrix
376  } else if (geometryPos == shape.back()) {
377  // push "E" matrix
379  // draw a "e" over last point if polygon isn't closed
380  GLHelper::drawText("E", geometryPos, 0.3, 2 * exaggeratedRadio, color.invertedColor());
381  // pop "E" matrix
383  }
384  }
385  }
386  }
387 }
388 
389 
390 void
392  const RGBColor& color, const bool drawEntire, const double lineWidth) {
393  if (!s.drawForRectangleSelection) {
394  // calculate rotation
395  const double rot = RAD2DEG(parent.angleTo2D(child)) + 90;
396  // calculate distance between origin and destination
397  const double distanceSquared = parent.distanceSquaredTo2D(child);
398  // Add a draw matrix for details
400  // move back
401  glTranslated(0, 0, -1);
402  // draw box line
403  if (drawEntire) {
404  // draw first box line
406  GLHelper::drawBoxLine(parent, rot, sqrt(distanceSquared), lineWidth);
407  // move front
408  glTranslated(0, 0, 0.1);
409  // draw second box line
410  GLHelper::setColor(color);
411  GLHelper::drawBoxLine(parent, rot, sqrt(distanceSquared), .04);
412  } else if (distanceSquared > 25) {
413  // draw first box line with length 4.9
415  GLHelper::drawBoxLine(parent, rot, 4.9, lineWidth);
416  glTranslated(0, 0, 0.1);
417  // draw second box line with length 4.9
418  GLHelper::setColor(color);
419  GLHelper::drawBoxLine(parent, rot, 4.9, .04);
420  // draw arrow depending of distanceSquared (10*10)
421  if (distanceSquared > 100) {
422  // calculate positionVector between both points
423  const PositionVector vector = {parent, child};
424  // draw first arrow at end
427  vector.positionAtOffset2D(5),
431  // move front
432  glTranslated(0, 0, 0.1);
433  // draw second arrow at end
434  GLHelper::setColor(color);
436  vector.positionAtOffset2D(5),
440  }
441  }
442  // pop draw matrix
444  }
445 }
446 
447 
448 void
450  const RGBColor& color, const bool drawEntire, const double lineWidth) {
451  if (!s.drawForRectangleSelection) {
452  // calculate distance between origin and destination
453  const double distanceSquared = child.distanceSquaredTo2D(parent);
454  // calculate subline width
455  const double sublineWidth = (lineWidth * 0.8);
456  // calculate rotation
457  const double rot = RAD2DEG(child.angleTo2D(parent)) + 90;
458  // Add a draw matrix for details
460  // move back
461  glTranslated(0, 0, -1);
462  // set color
463  GLHelper::setColor(color);
464  // draw box line
465  if (drawEntire || (distanceSquared < 25)) {
466  // set color
467  GLHelper::setColor(color);
468  // draw first box line
470  GLHelper::drawBoxLine(child, rot, sqrt(distanceSquared), lineWidth);
471  // move front
472  glTranslated(0, 0, 0.1);
473  // draw second box line
474  GLHelper::setColor(color);
475  GLHelper::drawBoxLine(child, rot, sqrt(distanceSquared), sublineWidth);
476  } else {
477  // draw first box line with length 4.9
479  GLHelper::drawBoxLine(child, rot, 4.9, lineWidth);
480  glTranslated(0, 0, 0.1);
481  // draw second box line with length
482  GLHelper::setColor(color);
483  GLHelper::drawBoxLine(child, rot, 4.9, sublineWidth);
484  // draw arrow depending of distanceSquared (10*10)
485  if (distanceSquared > 100) {
486  // calculate positionVector between both points
487  const PositionVector vector = {child, parent};
488  // draw first arrow at end
491  vector.positionAtOffset2D(5),
495  // move front
496  glTranslated(0, 0, 0.1);
497  // draw second arrow at end
498  GLHelper::setColor(color);
500  vector.positionAtOffset2D(5),
504  }
505  }
506  // pop draw matrix
508  }
509 }
510 
511 
513 GUIGeometry::getVertexCircleAroundPosition(const Position& pos, const double width, const int steps) {
514  // first check if we have to fill myCircleCoords (only once)
515  if (myCircleCoords.size() == 0) {
516  for (int i = 0; i <= (int)(360 * CIRCLE_RESOLUTION); ++i) {
517  const double x = (double) sin(DEG2RAD(i / CIRCLE_RESOLUTION));
518  const double y = (double) cos(DEG2RAD(i / CIRCLE_RESOLUTION));
519  myCircleCoords.push_back(Position(x, y));
520  }
521  }
522  PositionVector vertexCircle;
523  const double inc = 360 / (double)steps;
524  // obtain all vertices
525  for (int i = 0; i <= steps; ++i) {
526  const Position& vertex = myCircleCoords[GUIGeometry::angleLookup(i * inc)];
527  vertexCircle.push_back(Position(vertex.x() * width, vertex.y() * width));
528  }
529  // move result using position
530  vertexCircle.add(pos);
531  return vertexCircle;
532 }
533 
534 
535 void
536 GUIGeometry::rotateOverLane(const double rot) {
537  // rotate using rotation calculated in PositionVector
538  glRotated((rot * -1) + 90, 0, 0, 1);
539 }
540 
541 
542 int
543 GUIGeometry::angleLookup(const double angleDeg) {
544  const int numCoords = (int)myCircleCoords.size() - 1;
545  int index = ((int)(floor(angleDeg * CIRCLE_RESOLUTION + 0.5))) % numCoords;
546  if (index < 0) {
547  index += numCoords;
548  }
549  assert(index >= 0);
550  return (int)index;
551 }
552 
553 
555  // clear geometry containers
556  myShape.clear();
557  myShapeRotations.clear();
558  myShapeLengths.clear();
559 }
560 
561 
562 void
564  // clear rotations and lengths
565  myShapeRotations.clear();
566  myShapeLengths.clear();
567  // Get number of parts of the shape
568  int numberOfSegments = (int)myShape.size() - 1;
569  // If number of segments is more than 0
570  if (numberOfSegments >= 0) {
571  // Reserve memory (To improve efficiency)
572  myShapeRotations.reserve(numberOfSegments);
573  myShapeLengths.reserve(numberOfSegments);
574  // Calculate lengths and rotations for every shape
575  for (int i = 0; i < numberOfSegments; i++) {
576  myShapeRotations.push_back(calculateRotation(myShape[i], myShape[i + 1]));
577  myShapeLengths.push_back(calculateLength(myShape[i], myShape[i + 1]));
578  }
579  }
580 }
581 
582 /****************************************************************************/
#define CIRCLE_RESOLUTION
Definition: GUIGeometry.cpp:28
#define DEG2RAD(x)
Definition: GeomHelper.h:35
#define RAD2DEG(x)
Definition: GeomHelper.h:36
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
static void drawLine(const Position &beg, double rot, double visLength)
Draws a thin line.
Definition: GLHelper.cpp:439
static void setColor(const RGBColor &c)
Sets the gl-color to this value.
Definition: GLHelper.cpp:655
static void drawTriangleAtEnd(const Position &p1, const Position &p2, double tLength, double tWidth, const double extraOffset=0)
Draws a triangle at the end of the given line.
Definition: GLHelper.cpp:630
static void popMatrix()
pop matrix
Definition: GLHelper.cpp:130
static void drawBoxLines(const PositionVector &geom, const std::vector< double > &rots, const std::vector< double > &lengths, double width, int cornerDetail=0, double offset=0)
Draws thick lines.
Definition: GLHelper.cpp:347
static void drawBoxLine(const Position &beg, double rot, double visLength, double width, double offset=0)
Draws a thick line.
Definition: GLHelper.cpp:295
static void drawFilledCircleDetailled(const GUIVisualizationSettings::Detail d, const double radius)
Draws a filled circle around (0,0) depending of level of detail.
Definition: GLHelper.cpp:540
static void pushMatrix()
push matrix
Definition: GLHelper.cpp:117
static void drawText(const std::string &text, const Position &pos, const double layer, const double size, const RGBColor &col=RGBColor::BLACK, const double angle=0, const int align=0, double width=-1)
Definition: GLHelper.cpp:757
static void rotateOverLane(const double rot)
rotate over lane (used by Lock icons, detector logos, etc.)
void moveGeometryToSide(const double amount)
move current shape to side
const std::vector< double > & getShapeRotations() const
The rotations of the single shape parts.
static void drawGeometryPoints(const GUIVisualizationSettings::Detail d, const PositionVector &shape, const RGBColor &color, const double radius, const double exaggeration, const bool editingElevation)
draw geometry points
static PositionVector myCircleCoords
Storage for precomputed sin/cos-values describing a circle.
Definition: GUIGeometry.h:144
void scaleGeometry(const double scale)
scale geometry
static PositionVector getVertexCircleAroundPosition(const Position &pos, const double width, const int steps=8)
get a circle around the given position
void calculateShapeRotationsAndLengths()
calculate shape rotations and lengths
std::vector< double > myShapeLengths
The lengths of the shape (note: Always size = myShape.size()-1)
Definition: GUIGeometry.h:140
static void adjustStartPosGeometricPath(double &startPos, const PositionVector &startLaneShape, double &endPos, const PositionVector &endLaneShape)
adjust start and end positions in geometric path
void clearGeometry()
clear geometry
static int angleLookup(const double angleDeg)
normalize angle for lookup in myCircleCoords
static void drawContourGeometry(const GUIGeometry &geometry, const double width, const bool drawExtremes=false)
draw contour geometry
static void drawGeometry(const GUIVisualizationSettings::Detail d, const GUIGeometry &geometry, const double width, double offset=0)
draw geometry
PositionVector myShape
element shape
Definition: GUIGeometry.h:134
void updateSinglePosGeometry(const Position &position, const double rotation)
update position and rotation
static double calculateRotation(const Position &first, const Position &second)
return angle between two points (used in geometric calculations)
static void drawChildLine(const GUIVisualizationSettings &s, const Position &child, const Position &parent, const RGBColor &color, const bool drawEntire, const double lineWidth)
draw line between child and parent (used in netedit)
const PositionVector & getShape() const
The shape of the additional element.
void updateGeometry(const PositionVector &shape)
update entire geometry
Definition: GUIGeometry.cpp:59
const std::vector< double > & getShapeLengths() const
The lengths of the single shape parts.
std::vector< double > myShapeRotations
The rotations of the shape (note: Always size = myShape.size()-1)
Definition: GUIGeometry.h:137
GUIGeometry()
default constructor
Definition: GUIGeometry.cpp:39
static void drawParentLine(const GUIVisualizationSettings &s, const Position &parent, const Position &child, const RGBColor &color, const bool drawEntire, const double lineWidth)
draw line between parent and children (used in netedit)
static double calculateLength(const Position &first, const Position &second)
return length between two points (used in geometric calculations)
Stores the information about how to visualize structures.
bool drawForRectangleSelection
whether drawing is performed for the purpose of selecting objects using a rectangle
GUIVisualizationAdditionalSettings additionalSettings
Additional settings.
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
double distanceSquaredTo2D(const Position &p2) const
returns the square of the distance to another position (Only using x and y positions)
Definition: Position.h:276
static const Position INVALID
used to indicate that a position is valid
Definition: Position.h:317
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:271
double x() const
Returns the x-position.
Definition: Position.h:55
double angleTo2D(const Position &other) const
returns the angle in the plane of the vector pointing from here to the other position (in radians bet...
Definition: Position.h:281
double y() const
Returns the y-position.
Definition: Position.h:60
A list of positions.
double length2D() const
Returns the length.
void append(const PositionVector &v, double sameThreshold=2.0)
double length() const
Returns the length.
void push_front_noDoublePos(const Position &p)
insert in front a non double position
double rotationDegreeAtOffset(double pos) const
Returns the rotation at the given length.
Position positionAtOffset(double pos, double lateralOffset=0) const
Returns the position at the given length.
void add(double xoff, double yoff, double zoff)
void closePolygon()
ensures that the last position equals the first
void move2side(double amount, double maxExtension=100)
move position vector to side using certain amount
PositionVector getSubpart2D(double beginOffset, double endOffset) const
get subpart of a position vector in two dimensions (Z is ignored)
void scaleRelative(double factor)
enlarges/shrinks the polygon by a factor based at the centroid
void push_back_noDoublePos(const Position &p)
insert in back a non double position
PositionVector reverse() const
reverse position vector
Position positionAtOffset2D(double pos, double lateralOffset=0) const
Returns the position at the given length.
RGBColor invertedColor() const
obtain inverted of current RGBColor
Definition: RGBColor.cpp:183
RGBColor changedBrightness(int change, int toChange=3) const
Returns a new color with altered brightness.
Definition: RGBColor.cpp:200
#define M_PI
Definition: odrSpiral.cpp:45
static const double arrowLength
arrow length
static const double arrowWidth
arrow width
static const double arrowOffset
arrow offset