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 
185  // clear geometry containers
186  myShape.clear();
187  myShapeRotations.clear();
188  myShapeLengths.clear();
189 }
190 
191 
192 void
193 GUIGeometry::moveGeometryToSide(const double amount) {
194  // move shape
195  myShape.move2side(amount);
196 }
197 
198 
199 void
200 GUIGeometry::scaleGeometry(const double scale) {
201  // scale shape and lengths
202  myShape.scaleRelative(scale);
203  // scale lengths
204  for (auto& shapeLength : myShapeLengths) {
205  shapeLength *= scale;
206  }
207 }
208 
209 
210 const PositionVector&
212  return myShape;
213 }
214 
215 
216 const std::vector<double>&
218  return myShapeRotations;
219 }
220 
221 
222 const std::vector<double>&
224  return myShapeLengths;
225 }
226 
227 
228 double
229 GUIGeometry::calculateRotation(const Position& first, const Position& second) {
230  // return rotation (angle) of the vector constructed by points first and second
231  return ((double)atan2((second.x() - first.x()), (first.y() - second.y())) * (double) 180.0 / (double)M_PI);
232 }
233 
234 
235 double
236 GUIGeometry::calculateLength(const Position& first, const Position& second) {
237  // return 2D distance between two points
238  return first.distanceTo2D(second);
239 }
240 
241 
242 void
243 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  if ((startLaneShape.size() > 0) &&
247  (endLaneShape.size() > 0) &&
248  (startLaneShape == endLaneShape) &&
249  (startPos != -1) &&
250  (endPos != -1)) {
251  if (startPos >= endPos) {
252  endPos = (startPos + POSITION_EPS);
253  }
254  }
255  // adjust startPos
256  if ((startPos != -1) && (startLaneShape.size() > 0)) {
257  if (startPos < POSITION_EPS) {
258  startPos = POSITION_EPS;
259  }
260  if (startPos > (startLaneShape.length() - POSITION_EPS)) {
261  startPos = (startLaneShape.length() - POSITION_EPS);
262  }
263  }
264  // adjust endPos
265  if ((endPos != -1) && (endLaneShape.size() > 0)) {
266  if (endPos < POSITION_EPS) {
267  endPos = POSITION_EPS;
268  }
269  if (endPos > (endLaneShape.length() - POSITION_EPS)) {
270  endPos = (endLaneShape.length() - POSITION_EPS);
271  }
272  }
273 }
274 
275 
276 void
278  const double width, double offset) {
279  // continue depending of detail level
281  GLHelper::drawBoxLines(geometry.getShape(), geometry.getShapeRotations(), geometry.getShapeLengths(), width, 0, offset);
283  // set line width
284  glLineWidth(static_cast<float>(width));
285  // draw a simple line
286  GLHelper::drawLine(geometry.getShape());
287  // restore line width
288  glLineWidth(1);
289  } else {
290  // draw a simple line
291  GLHelper::drawLine(geometry.getShape());
292  }
293 }
294 
295 
296 void
298  const std::vector<RGBColor>& colors, const double width, double offset) {
299  // continue depending of detail level
301  GLHelper::drawBoxLines(geometry.getShape(), geometry.getShapeRotations(), geometry.getShapeLengths(), colors, width, 0, offset);
302  } else {
303  // set first color
304  GLHelper::setColor(*colors.begin());
305  // set width
307  // set line width
308  glLineWidth(static_cast<float>(width));
309  // draw a simple line
310  GLHelper::drawLine(geometry.getShape());
311  // restore line width
312  glLineWidth(1);
313  } else {
314  // draw a simple line
315  GLHelper::drawLine(geometry.getShape());
316  }
317  }
318 }
319 
320 
321 void
322 GUIGeometry::drawContourGeometry(const GUIGeometry& geometry, const double width, const bool drawExtremes) {
323  // get shapes
324  PositionVector shapeA = geometry.getShape();
325  PositionVector shapeB = geometry.getShape();
326  // move both shapes
327  shapeA.move2side((width - 0.1));
328  shapeB.move2side((width - 0.1) * -1);
329  // check if we have to drawn extremes
330  if (drawExtremes) {
331  // reverse shape B
332  shapeB = shapeB.reverse();
333  // append shape B to shape A
334  shapeA.append(shapeB, 0);
335  // close shape A
336  shapeA.closePolygon();
337  // draw box lines using shapeA
338  GLHelper::drawBoxLines(shapeA, 0.1);
339  } else {
340  // draw box lines using shapeA
341  GLHelper::drawBoxLines(shapeA, 0.1);
342  // draw box lines using shapeA
343  GLHelper::drawBoxLines(shapeB, 0.1);
344  }
345 }
346 
347 
348 void
350  const RGBColor& color, const double radius, const double exaggeration,
351  const bool editingElevation) {
352  // check detail level
354  // get exaggeratedRadio
355  const double exaggeratedRadio = (radius * exaggeration);
356  // iterate over geometryPoints
357  for (const auto& geometryPos : shape) {
358  // push geometry point matrix
360  // move to vertex
361  glTranslated(geometryPos.x(), geometryPos.y(), 0.2);
362  // set color
363  GLHelper::setColor(color);
364  // draw circle detailled
365  GLHelper::drawFilledCircleDetailled(d, exaggeratedRadio);
366  // pop geometry point matrix
368  // draw elevation or special symbols (Start, End and Block)
370  if (editingElevation) {
371  // Push Z matrix
373  // draw Z (elevation)
374  GLHelper::drawText(toString(geometryPos.z()), geometryPos, 0.3, 0.7, color.invertedColor());
375  // pop Z matrix
377  } else if (geometryPos == shape.front()) {
378  // push "S" matrix
380  // draw a "s" over first point
381  GLHelper::drawText("S", geometryPos, 0.3, 2 * exaggeratedRadio, color.invertedColor());
382  // pop "S" matrix
384  } else if (geometryPos == shape.back()) {
385  // push "E" matrix
387  // draw a "e" over last point if polygon isn't closed
388  GLHelper::drawText("E", geometryPos, 0.3, 2 * exaggeratedRadio, color.invertedColor());
389  // pop "E" matrix
391  }
392  }
393  }
394  }
395 }
396 
397 
398 void
400  const RGBColor& color, const bool drawEntire, const double lineWidth) {
401  if (!s.drawForRectangleSelection) {
402  // calculate rotation
403  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
408  // move back
409  glTranslated(0, 0, -1);
410  // draw box line
411  if (drawEntire) {
412  // draw first box line
414  GLHelper::drawBoxLine(parent, rot, sqrt(distanceSquared), lineWidth);
415  // move front
416  glTranslated(0, 0, 0.1);
417  // draw second box line
418  GLHelper::setColor(color);
419  GLHelper::drawBoxLine(parent, rot, sqrt(distanceSquared), .04);
420  } else if (distanceSquared > 25) {
421  // draw first box line with length 4.9
423  GLHelper::drawBoxLine(parent, rot, 4.9, lineWidth);
424  glTranslated(0, 0, 0.1);
425  // draw second box line with length 4.9
426  GLHelper::setColor(color);
427  GLHelper::drawBoxLine(parent, rot, 4.9, .04);
428  // draw arrow depending of distanceSquared (10*10)
429  if (distanceSquared > 100) {
430  // calculate positionVector between both points
431  const PositionVector vector = {parent, child};
432  // draw first arrow at end
435  vector.positionAtOffset2D(5),
439  // move front
440  glTranslated(0, 0, 0.1);
441  // draw second arrow at end
442  GLHelper::setColor(color);
444  vector.positionAtOffset2D(5),
448  }
449  }
450  // pop draw matrix
452  }
453 }
454 
455 
456 void
458  const RGBColor& color, const bool drawEntire, const double lineWidth) {
459  if (!s.drawForRectangleSelection) {
460  // calculate distance between origin and destination
461  const double distanceSquared = child.distanceSquaredTo2D(parent);
462  // calculate subline width
463  const double sublineWidth = (lineWidth * 0.8);
464  // calculate rotation
465  const double rot = RAD2DEG(child.angleTo2D(parent)) + 90;
466  // Add a draw matrix for details
468  // move back
469  glTranslated(0, 0, -1);
470  // set color
471  GLHelper::setColor(color);
472  // draw box line
473  if (drawEntire || (distanceSquared < 25)) {
474  // set color
475  GLHelper::setColor(color);
476  // draw first box line
478  GLHelper::drawBoxLine(child, rot, sqrt(distanceSquared), lineWidth);
479  // move front
480  glTranslated(0, 0, 0.1);
481  // draw second box line
482  GLHelper::setColor(color);
483  GLHelper::drawBoxLine(child, rot, sqrt(distanceSquared), sublineWidth);
484  } else {
485  // draw first box line with length 4.9
487  GLHelper::drawBoxLine(child, rot, 4.9, lineWidth);
488  glTranslated(0, 0, 0.1);
489  // draw second box line with length
490  GLHelper::setColor(color);
491  GLHelper::drawBoxLine(child, rot, 4.9, sublineWidth);
492  // draw arrow depending of distanceSquared (10*10)
493  if (distanceSquared > 100) {
494  // calculate positionVector between both points
495  const PositionVector vector = {child, parent};
496  // draw first arrow at end
499  vector.positionAtOffset2D(5),
503  // move front
504  glTranslated(0, 0, 0.1);
505  // draw second arrow at end
506  GLHelper::setColor(color);
508  vector.positionAtOffset2D(5),
512  }
513  }
514  // pop draw matrix
516  }
517 }
518 
519 
521 GUIGeometry::getVertexCircleAroundPosition(const Position& pos, const double width, const int steps) {
522  // first check if we have to fill myCircleCoords (only once)
523  if (myCircleCoords.size() == 0) {
524  for (int i = 0; i <= (int)(360 * CIRCLE_RESOLUTION); ++i) {
525  const double x = (double) sin(DEG2RAD(i / CIRCLE_RESOLUTION));
526  const double y = (double) cos(DEG2RAD(i / CIRCLE_RESOLUTION));
527  myCircleCoords.push_back(Position(x, y));
528  }
529  }
530  PositionVector vertexCircle;
531  const double inc = 360 / (double)steps;
532  // obtain all vertices
533  for (int i = 0; i <= steps; ++i) {
534  const Position& vertex = myCircleCoords[GUIGeometry::angleLookup(i * inc)];
535  vertexCircle.push_back(Position(vertex.x() * width, vertex.y() * width));
536  }
537  // move result using position
538  vertexCircle.add(pos);
539  return vertexCircle;
540 }
541 
542 
543 void
544 GUIGeometry::rotateOverLane(const double rot) {
545  // rotate using rotation calculated in PositionVector
546  glRotated((rot * -1) + 90, 0, 0, 1);
547 }
548 
549 
550 int
551 GUIGeometry::angleLookup(const double angleDeg) {
552  const int numCoords = (int)myCircleCoords.size() - 1;
553  int index = ((int)(floor(angleDeg * CIRCLE_RESOLUTION + 0.5))) % numCoords;
554  if (index < 0) {
555  index += numCoords;
556  }
557  assert(index >= 0);
558  return (int)index;
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:438
static void setColor(const RGBColor &c)
Sets the gl-color to this value.
Definition: GLHelper.cpp:654
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:629
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:539
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:756
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:281
static const Position INVALID
used to indicate that a position is valid
Definition: Position.h:322
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:276
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:286
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