Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/gui/div/GUIGeometry.cpp
169684 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 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
GUIGeometry::GUIGeometry() {
40
}
41
42
43
GUIGeometry::GUIGeometry(const PositionVector& shape) :
44
myShape(shape) {
45
// calculate shape rotation and lengths
46
calculateShapeRotationsAndLengths();
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
59
GUIGeometry::updateGeometry(const PositionVector& shape) {
60
// clear geometry
61
clearGeometry();
62
// update shape
63
myShape = shape;
64
// calculate shape rotation and lengths
65
calculateShapeRotationsAndLengths();
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
124
calculateShapeRotationsAndLengths();
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
170
calculateShapeRotationsAndLengths();
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 GUIGeometry::clearGeometry() {
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&
211
GUIGeometry::getShape() const {
212
return myShape;
213
}
214
215
216
const std::vector<double>&
217
GUIGeometry::getShapeRotations() const {
218
return myShapeRotations;
219
}
220
221
222
const std::vector<double>&
223
GUIGeometry::getShapeLengths() const {
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
277
GUIGeometry::drawGeometry(const GUIVisualizationSettings::Detail d, const GUIGeometry& geometry,
278
const double width, double offset) {
279
// continue depending of detail level
280
if (d <= GUIVisualizationSettings::Detail::GeometryBoxLines) {
281
GLHelper::drawBoxLines(geometry.getShape(), geometry.getShapeRotations(), geometry.getShapeLengths(), width, 0, offset);
282
} else if (d < GUIVisualizationSettings::Detail::GeometryBoxSimpleLine) {
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
297
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
if (d <= GUIVisualizationSettings::Detail::GeometryBoxLines) {
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
306
if (d < GUIVisualizationSettings::Detail::GeometryBoxSimpleLine) {
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
349
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
if (editingElevation || (d <= GUIVisualizationSettings::Detail::GeometryPoint)) {
354
// get exaggeratedRadio
355
const double exaggeratedRadio = (radius * exaggeration);
356
// iterate over geometryPoints
357
for (const auto& geometryPos : shape) {
358
// push geometry point matrix
359
GLHelper::pushMatrix();
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
367
GLHelper::popMatrix();
368
// draw elevation or special symbols (Start, End and Block)
369
if (d <= GUIVisualizationSettings::Detail::Text) {
370
if (editingElevation) {
371
// Push Z matrix
372
GLHelper::pushMatrix();
373
// draw Z (elevation)
374
GLHelper::drawText(toString(geometryPos.z()), geometryPos, 0.3, 0.7, color.invertedColor());
375
// pop Z matrix
376
GLHelper::popMatrix();
377
} else if (geometryPos == shape.front()) {
378
// push "S" matrix
379
GLHelper::pushMatrix();
380
// draw a "s" over first point
381
GLHelper::drawText("S", geometryPos, 0.3, 2 * exaggeratedRadio, color.invertedColor());
382
// pop "S" matrix
383
GLHelper::popMatrix();
384
} else if (geometryPos == shape.back()) {
385
// push "E" matrix
386
GLHelper::pushMatrix();
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
390
GLHelper::popMatrix();
391
}
392
}
393
}
394
}
395
}
396
397
398
void
399
GUIGeometry::drawParentLine(const GUIVisualizationSettings& s, const Position& parent, const Position& child,
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
407
GLHelper::pushMatrix();
408
// move back
409
glTranslated(0, 0, -1);
410
// draw box line
411
if (drawEntire) {
412
// draw first box line
413
GLHelper::setColor(color.changedBrightness(-50));
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
422
GLHelper::setColor(color.changedBrightness(-50));
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
433
GLHelper::setColor(color.changedBrightness(-50));
434
GLHelper::drawTriangleAtEnd(parent,
435
vector.positionAtOffset2D(5),
436
s.additionalSettings.arrowWidth,
437
s.additionalSettings.arrowLength,
438
s.additionalSettings.arrowOffset);
439
// move front
440
glTranslated(0, 0, 0.1);
441
// draw second arrow at end
442
GLHelper::setColor(color);
443
GLHelper::drawTriangleAtEnd(parent,
444
vector.positionAtOffset2D(5),
445
s.additionalSettings.arrowWidth - 0.01,
446
s.additionalSettings.arrowLength - 0.01,
447
s.additionalSettings.arrowOffset - 0.01);
448
}
449
}
450
// pop draw matrix
451
GLHelper::popMatrix();
452
}
453
}
454
455
456
void
457
GUIGeometry::drawChildLine(const GUIVisualizationSettings& s, const Position& child, const Position& parent,
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
467
GLHelper::pushMatrix();
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
477
GLHelper::setColor(color.changedBrightness(-50));
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
486
GLHelper::setColor(color.changedBrightness(-50));
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
497
GLHelper::setColor(color.changedBrightness(-50));
498
GLHelper::drawTriangleAtEnd(child,
499
vector.positionAtOffset2D(5),
500
s.additionalSettings.arrowWidth,
501
s.additionalSettings.arrowLength,
502
s.additionalSettings.arrowOffset);
503
// move front
504
glTranslated(0, 0, 0.1);
505
// draw second arrow at end
506
GLHelper::setColor(color);
507
GLHelper::drawTriangleAtEnd(child,
508
vector.positionAtOffset2D(5),
509
s.additionalSettings.arrowWidth - 0.01,
510
s.additionalSettings.arrowLength - 0.01,
511
s.additionalSettings.arrowOffset - 0.01);
512
}
513
}
514
// pop draw matrix
515
GLHelper::popMatrix();
516
}
517
}
518
519
520
PositionVector
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
563
GUIGeometry::calculateShapeRotationsAndLengths() {
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
/****************************************************************************/
583
584