Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/gui/globjects/GUIPolygon.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 GUIPolygon.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Michael Behrisch
18
/// @author Laura Bieker
19
/// @date June 2006
20
///
21
// The GUI-version of a polygon
22
/****************************************************************************/
23
#include <config.h>
24
25
#include <string>
26
#include <utils/geom/GeomHelper.h>
27
#include <utils/gui/images/GUITexturesHelper.h>
28
#include <utils/gui/globjects/GUIGlObject.h>
29
#include <utils/gui/div/GUIParameterTableWindow.h>
30
#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>
31
#include <utils/gui/settings/GUIVisualizationSettings.h>
32
#include <utils/gui/div/GUIGlobalSelection.h>
33
#include <utils/gui/div/GLHelper.h>
34
#include <utils/gui/div/GUIDesigns.h>
35
36
#include "GUIPolygon.h"
37
38
#ifndef CALLBACK
39
#define CALLBACK
40
#endif
41
42
// ===========================================================================
43
// static members
44
// ===========================================================================
45
46
// minimum number of extra vertices per shape before tesselation artefacts occur
47
// (new vertices are needed for some concave polygons)
48
#define MAX_COMBINE_INDEX 1024
49
// ring buffer to store temporary vertices (x,y,z) needed by combineCallback
50
GLdouble myCombineVertices[MAX_COMBINE_INDEX][3];
51
// array index for above array; incremented inside combineCallback
52
int myCombineIndex = 0;
53
GLenum myCurrentType = 0;
54
std::vector<Position> myCurrentPoints;
55
const TesselatedPolygon* myCurrentTesselated = nullptr;
56
57
// ===========================================================================
58
// callbacks definitions
59
// ===========================================================================
60
61
void CALLBACK beginCallback(GLenum which) {
62
//std::cout << " beginCallback id=" << Named::getIDSecure(myCurrentTesselated) << " type=" << which << "\n";
63
myCurrentType = which;
64
myCurrentPoints.clear();
65
}
66
67
68
void CALLBACK endCallback(void) {
69
myCurrentTesselated->myTesselation.emplace_back(GLPrimitive());
70
GLPrimitive& glp = myCurrentTesselated->myTesselation.back();
71
glp.type = myCurrentType;
72
glp.vert = myCurrentPoints;
73
myCurrentPoints.clear();
74
}
75
76
77
void CALLBACK vertexCallback(GLvoid* vertex) {
78
GLdouble* p3 = (GLdouble*) vertex;
79
//std::cout << " vertexCallback id=" << Named::getIDSecure(myCurrentTesselated) << " point=" << p3 << "\n";
80
myCurrentPoints.push_back(Position(p3[0], p3[1], p3[2]));
81
}
82
83
84
void CALLBACK combineCallback(GLdouble coords[3],
85
GLdouble* vertex_data[4],
86
GLfloat weight[4], GLdouble** dataOut) {
87
UNUSED_PARAMETER(weight);
88
UNUSED_PARAMETER(*vertex_data);
89
myCombineIndex = (myCombineIndex + 1) % MAX_COMBINE_INDEX;
90
myCombineVertices[myCombineIndex][0] = coords[0];
91
myCombineVertices[myCombineIndex][1] = coords[1];
92
myCombineVertices[myCombineIndex][2] = coords[2];
93
*dataOut = myCombineVertices[myCombineIndex];
94
}
95
96
void CALLBACK errorCallback(GLenum errorCode) {
97
const GLubyte* estring;
98
99
estring = gluErrorString(errorCode);
100
fprintf(stderr, "Tessellation Error: %s\n", estring);
101
exit(0);
102
}
103
104
105
106
static const GLdouble INV_POLY_TEX_DIM = 1.0 / 256.0;
107
static const GLdouble xPlane[] = {INV_POLY_TEX_DIM, 0.0, 0.0, 0.0};
108
static const GLdouble yPlane[] = {0.0, INV_POLY_TEX_DIM, 0.0, 0.0};
109
110
111
// ===========================================================================
112
// TesselatedPolygon method definitions
113
// ===========================================================================
114
115
116
void
117
TesselatedPolygon::drawTesselation(const PositionVector& shape) const {
118
if (myTesselation.empty()) {
119
myCurrentTesselated = this;
120
// draw the tesselated shape
121
size_t numPoints = shape.size() * 3;
122
for (const PositionVector& hole : myHoles) {
123
numPoints += hole.size() * 3;
124
}
125
double* points = new double[numPoints];
126
GLUtesselator* tobj = gluNewTess();
127
#ifdef _MSC_VER
128
#pragma warning(push)
129
#pragma warning(disable: 4191)
130
#endif
131
#if defined(__GNUC__) && __GNUC__ >= 8
132
#pragma GCC diagnostic push
133
#pragma GCC diagnostic ignored "-Wcast-function-type"
134
#endif
135
gluTessCallback(tobj, GLU_TESS_VERTEX, (GLvoid(CALLBACK*)()) &vertexCallback);
136
gluTessCallback(tobj, GLU_TESS_BEGIN, (GLvoid(CALLBACK*)()) &beginCallback);
137
gluTessCallback(tobj, GLU_TESS_END, (GLvoid(CALLBACK*)()) &endCallback);
138
//gluTessCallback(tobj, GLU_TESS_ERROR, (GLvoid (CALLBACK*) ()) &errorCallback);
139
gluTessCallback(tobj, GLU_TESS_COMBINE, (GLvoid(CALLBACK*)()) &combineCallback);
140
#if defined(__GNUC__) && __GNUC__ >= 8
141
#pragma GCC diagnostic pop
142
#endif
143
#ifdef _MSC_VER
144
#pragma warning(pop)
145
#endif
146
gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
147
gluTessBeginPolygon(tobj, nullptr);
148
gluTessBeginContour(tobj);
149
for (int i = 0; i < (int)shape.size(); i++) {
150
points[3 * i] = shape[i].x();
151
points[3 * i + 1] = shape[i].y();
152
points[3 * i + 2] = 0.;
153
gluTessVertex(tobj, points + 3 * i, points + 3 * i);
154
}
155
gluTessEndContour(tobj);
156
size_t startIndex = shape.size() * 3;
157
for (const PositionVector& hole : myHoles) {
158
gluTessBeginContour(tobj);
159
for (int i = 0; i < (int)hole.size(); i++) {
160
points[startIndex + 3 * i] = hole[i].x();
161
points[startIndex + 3 * i + 1] = hole[i].y();
162
points[startIndex + 3 * i + 2] = 0.;
163
gluTessVertex(tobj, points + startIndex + 3 * i, points + startIndex + 3 * i);
164
}
165
startIndex += hole.size() * 3;
166
gluTessEndContour(tobj);
167
}
168
gluTessEndPolygon(tobj);
169
gluDeleteTess(tobj);
170
delete[] points;
171
}
172
for (GLPrimitive& pr : myTesselation) {
173
// XXX change to glDrawArrays
174
glBegin(pr.type);
175
for (const Position& p : pr.vert) {
176
glVertex3d(p.x(), p.y(), p.z());
177
}
178
glEnd();
179
}
180
}
181
182
// ===========================================================================
183
// GUIPolygon method definitions
184
// ===========================================================================
185
186
GUIPolygon::GUIPolygon(const std::string& id, const std::string& type, const RGBColor& color,
187
const PositionVector& shape, bool geo, bool fill, double lineWidth,
188
double layer, double angle, const std::string& imgFile, const std::string& name):
189
TesselatedPolygon(id, type, color, shape, geo, fill, lineWidth, layer, angle, imgFile, name),
190
GUIGlObject_AbstractAdd(GLO_POLYGON, id, GUIIconSubSys::getIcon(GUIIcon::POLY)),
191
myRotatedShape(nullptr) {
192
if (angle != 0.) {
193
setShape(shape);
194
}
195
}
196
197
198
GUIPolygon::~GUIPolygon() {
199
delete myRotatedShape;
200
}
201
202
203
GUIGLObjectPopupMenu*
204
GUIPolygon::getPopUpMenu(GUIMainWindow& app,
205
GUISUMOAbstractView& parent) {
206
GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);
207
buildPopupHeader(ret, app, false);
208
GUIDesigns::buildFXMenuCommand(ret, "(" + getShapeType() + ")", nullptr, nullptr, 0);
209
new FXMenuSeparator(ret);
210
buildCenterPopupEntry(ret);
211
buildNameCopyPopupEntry(ret);
212
buildSelectionPopupEntry(ret);
213
buildShowParamsPopupEntry(ret, false);
214
buildPositionCopyEntry(ret, app);
215
return ret;
216
}
217
218
219
GUIParameterTableWindow*
220
GUIPolygon::getParameterWindow(GUIMainWindow& app,
221
GUISUMOAbstractView&) {
222
GUIParameterTableWindow* ret = new GUIParameterTableWindow(app, *this);
223
// add items
224
ret->mkItem("type", false, getShapeType());
225
ret->mkItem("layer", false, toString(getShapeLayer()));
226
ret->mkItem("name", false, toString(getShapeName()));
227
ret->closeBuilding(this);
228
return ret;
229
}
230
231
232
double
233
GUIPolygon::getExaggeration(const GUIVisualizationSettings& s) const {
234
return s.polySize.getExaggeration(s, this);
235
}
236
237
238
Boundary
239
GUIPolygon::getCenteringBoundary() const {
240
const PositionVector& shape = myRotatedShape != nullptr ? *myRotatedShape : myShape;
241
Boundary b;
242
b.add(shape.getBoxBoundary());
243
b.grow(2);
244
return b;
245
}
246
247
248
void
249
GUIPolygon::drawGL(const GUIVisualizationSettings& s) const {
250
// first check if polygon can be drawn
251
if (myIsActive && checkDraw(s, this, this)) {
252
FXMutexLock locker(myLock);
253
// push name (needed for getGUIGlObjectsUnderCursor(...)
254
GLHelper::pushName(getGlID());
255
// draw inner polygon
256
if (myRotatedShape) {
257
drawInnerPolygon(s, this, this, *myRotatedShape, s.polyUseCustomLayer ? s.polyCustomLayer : getShapeLayer(), getFill());
258
} else {
259
drawInnerPolygon(s, this, this, myShape, s.polyUseCustomLayer ? s.polyCustomLayer : getShapeLayer(), getFill());
260
}
261
// pop name
262
GLHelper::popName();
263
}
264
}
265
266
267
void
268
GUIPolygon::setShape(const PositionVector& shape) {
269
FXMutexLock locker(myLock);
270
SUMOPolygon::setShape(shape);
271
if (getShapeNaviDegree() != 0.) {
272
if (myRotatedShape == nullptr) {
273
myRotatedShape = new PositionVector();
274
}
275
const Position& centroid = myShape.getCentroid();
276
*myRotatedShape = myShape;
277
myRotatedShape->sub(centroid);
278
myRotatedShape->rotate2D(-DEG2RAD(getShapeNaviDegree()));
279
myRotatedShape->add(centroid);
280
} else {
281
delete myRotatedShape;
282
myRotatedShape = nullptr;
283
}
284
myTesselation.clear();
285
}
286
287
288
RGBColor
289
GUIPolygon::setColor(const GUIVisualizationSettings& s, const SUMOPolygon* polygon, const GUIGlObject* o, bool disableSelectionColor, int alphaOverride) {
290
const GUIColorer& c = s.polyColorer;
291
const int active = c.getActive();
292
RGBColor color;
293
if (s.netedit && active != 1 && gSelected.isSelected(o->getType(), o->getGlID()) && disableSelectionColor) {
294
// override with special selection colors (unless the color scheme is based on selection)
295
color = RGBColor(0, 0, 204);
296
} else if (active == 0) {
297
color = polygon->getShapeColor();
298
} else if (active == 1) {
299
color = c.getScheme().getColor(gSelected.isSelected(o->getType(), o->getGlID()));
300
} else if (active == 2) {
301
color = c.getScheme().getColor(0);
302
} else {
303
// color randomly (by pointer hash)
304
std::hash<const SUMOPolygon*> ptr_hash;
305
const double hue = (double)(ptr_hash(polygon) % 360); // [0-360]
306
const double sat = (double)((ptr_hash(polygon) / 360) % 67) / 100.0 + 0.33; // [0.33-1]
307
color = RGBColor::fromHSV(hue, sat, 1.);
308
}
309
if (alphaOverride >= 0 && alphaOverride <= 255) {
310
color.setAlpha((unsigned char)alphaOverride);
311
}
312
GLHelper::setColor(color);
313
return color;
314
}
315
316
317
bool
318
GUIPolygon::checkDraw(const GUIVisualizationSettings& s, const SUMOPolygon* polygon, const GUIGlObject* o) {
319
if (o->getExaggeration(s) == 0) {
320
return false;
321
}
322
Boundary boundary = polygon->getShape().getBoxBoundary();
323
if (s.scale * MAX2(boundary.getWidth(), boundary.getHeight()) < s.polySize.minSize) {
324
return false;
325
}
326
if (polygon->getFill()) {
327
if (polygon->getShape().size() < 3) {
328
return false;
329
}
330
} else {
331
if (polygon->getShape().size() < 2) {
332
return false;
333
}
334
}
335
return true;
336
}
337
338
339
void
340
GUIPolygon::drawInnerPolygon(const GUIVisualizationSettings& s, const TesselatedPolygon* polygon, const GUIGlObject* o,
341
const PositionVector shape, const double layer, const bool fill,
342
const bool disableSelectionColor, const int alphaOverride, const bool disableText) {
343
GLHelper::pushMatrix();
344
glTranslated(0, 0, layer);
345
setColor(s, polygon, o, disableSelectionColor, alphaOverride);
346
int textureID = -1;
347
if (fill) {
348
const std::string& file = polygon->getShapeImgFile();
349
if (file != "") {
350
textureID = GUITexturesHelper::getTextureID(file, true);
351
}
352
}
353
// init generation of texture coordinates
354
if (textureID >= 0) {
355
glEnable(GL_TEXTURE_2D);
356
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
357
glDisable(GL_CULL_FACE);
358
glDisable(GL_DEPTH_TEST); // without DEPTH_TEST vehicles may be drawn below roads
359
glDisable(GL_LIGHTING);
360
glDisable(GL_COLOR_MATERIAL);
361
glDisable(GL_ALPHA_TEST);
362
glEnable(GL_BLEND);
363
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
364
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
365
glBindTexture(GL_TEXTURE_2D, textureID);
366
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
367
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
368
// http://www.gamedev.net/topic/133564-glutesselation-and-texture-mapping/
369
glEnable(GL_TEXTURE_GEN_S);
370
glEnable(GL_TEXTURE_GEN_T);
371
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
372
glTexGendv(GL_S, GL_OBJECT_PLANE, xPlane);
373
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
374
glTexGendv(GL_T, GL_OBJECT_PLANE, yPlane);
375
}
376
if (fill) {
377
polygon->drawTesselation(shape);
378
} else {
379
GLHelper::drawLine(shape);
380
GLHelper::drawBoxLines(shape, polygon->getLineWidth() * o->getExaggeration(s));
381
}
382
383
// de-init generation of texture coordinates
384
if (textureID >= 0) {
385
glEnable(GL_DEPTH_TEST);
386
glBindTexture(GL_TEXTURE_2D, 0);
387
glDisable(GL_TEXTURE_2D);
388
glDisable(GL_TEXTURE_GEN_S);
389
glDisable(GL_TEXTURE_GEN_T);
390
}
391
GLHelper::popMatrix();
392
if (s.geometryIndices.show(o)) {
393
GLHelper::debugVertices(shape, s.geometryIndices, s.scale);
394
}
395
if (!disableText) {
396
const Position& namePos = shape.getPolygonCenter();
397
o->drawName(namePos, s.scale, s.polyName, s.angle);
398
if (s.polyType.show(o)) {
399
const Position p = namePos + Position(0, -0.6 * s.polyType.size / s.scale);
400
GLHelper::drawTextSettings(s.polyType, polygon->getShapeType(), p, s.scale, s.angle);
401
}
402
}
403
}
404
405
/****************************************************************************/
406
407