Line data Source code
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 6664 : void CALLBACK beginCallback(GLenum which) {
62 : //std::cout << " beginCallback id=" << Named::getIDSecure(myCurrentTesselated) << " type=" << which << "\n";
63 6664 : myCurrentType = which;
64 : myCurrentPoints.clear();
65 6664 : }
66 :
67 :
68 6664 : void CALLBACK endCallback(void) {
69 6664 : myCurrentTesselated->myTesselation.emplace_back(GLPrimitive());
70 6664 : GLPrimitive& glp = myCurrentTesselated->myTesselation.back();
71 6664 : glp.type = myCurrentType;
72 6664 : glp.vert = myCurrentPoints;
73 : myCurrentPoints.clear();
74 6664 : }
75 :
76 :
77 47643 : void CALLBACK vertexCallback(GLvoid* vertex) {
78 : GLdouble* p3 = (GLdouble*) vertex;
79 : //std::cout << " vertexCallback id=" << Named::getIDSecure(myCurrentTesselated) << " point=" << p3 << "\n";
80 47643 : myCurrentPoints.push_back(Position(p3[0], p3[1], p3[2]));
81 47643 : }
82 :
83 :
84 970 : 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 970 : myCombineIndex = (myCombineIndex + 1) % MAX_COMBINE_INDEX;
90 970 : myCombineVertices[myCombineIndex][0] = coords[0];
91 970 : myCombineVertices[myCombineIndex][1] = coords[1];
92 970 : myCombineVertices[myCombineIndex][2] = coords[2];
93 970 : *dataOut = myCombineVertices[myCombineIndex];
94 970 : }
95 :
96 0 : void CALLBACK errorCallback(GLenum errorCode) {
97 : const GLubyte* estring;
98 :
99 0 : estring = gluErrorString(errorCode);
100 0 : fprintf(stderr, "Tessellation Error: %s\n", estring);
101 0 : 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 433146 : TesselatedPolygon::drawTesselation(const PositionVector& shape) const {
118 433146 : if (myTesselation.empty()) {
119 163652 : myCurrentTesselated = this;
120 : // draw the tesselated shape
121 163652 : size_t numPoints = shape.size() * 3;
122 163652 : for (const PositionVector& hole : myHoles) {
123 0 : numPoints += hole.size() * 3;
124 : }
125 163652 : double* points = new double[numPoints];
126 163652 : 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 163652 : gluTessCallback(tobj, GLU_TESS_VERTEX, (GLvoid(CALLBACK*)()) &vertexCallback);
136 163652 : gluTessCallback(tobj, GLU_TESS_BEGIN, (GLvoid(CALLBACK*)()) &beginCallback);
137 163652 : gluTessCallback(tobj, GLU_TESS_END, (GLvoid(CALLBACK*)()) &endCallback);
138 : //gluTessCallback(tobj, GLU_TESS_ERROR, (GLvoid (CALLBACK*) ()) &errorCallback);
139 163652 : 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 163652 : gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
147 163652 : gluTessBeginPolygon(tobj, nullptr);
148 163652 : gluTessBeginContour(tobj);
149 777745 : for (int i = 0; i < (int)shape.size(); i++) {
150 614093 : points[3 * i] = shape[i].x();
151 614093 : points[3 * i + 1] = shape[i].y();
152 614093 : points[3 * i + 2] = 0.;
153 614093 : gluTessVertex(tobj, points + 3 * i, points + 3 * i);
154 : }
155 163652 : gluTessEndContour(tobj);
156 163652 : size_t startIndex = shape.size() * 3;
157 163652 : for (const PositionVector& hole : myHoles) {
158 0 : gluTessBeginContour(tobj);
159 0 : for (int i = 0; i < (int)hole.size(); i++) {
160 0 : points[startIndex + 3 * i] = hole[i].x();
161 0 : points[startIndex + 3 * i + 1] = hole[i].y();
162 0 : points[startIndex + 3 * i + 2] = 0.;
163 0 : gluTessVertex(tobj, points + startIndex + 3 * i, points + startIndex + 3 * i);
164 : }
165 0 : startIndex += hole.size() * 3;
166 0 : gluTessEndContour(tobj);
167 : }
168 163652 : gluTessEndPolygon(tobj);
169 163652 : gluDeleteTess(tobj);
170 163652 : delete[] points;
171 : }
172 936185 : for (GLPrimitive& pr : myTesselation) {
173 : // XXX change to glDrawArrays
174 503039 : glBegin(pr.type);
175 3980145 : for (const Position& p : pr.vert) {
176 3477106 : glVertex3d(p.x(), p.y(), p.z());
177 : }
178 503039 : glEnd();
179 : }
180 433146 : }
181 :
182 : // ===========================================================================
183 : // GUIPolygon method definitions
184 : // ===========================================================================
185 :
186 124 : GUIPolygon::GUIPolygon(const std::string& id, const std::string& type, const RGBColor& color,
187 : const PositionVector& shape, bool geo, bool fill, double lineWidth,
188 124 : 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 124 : myRotatedShape(nullptr) {
192 124 : if (angle != 0.) {
193 2 : setShape(shape);
194 : }
195 124 : }
196 :
197 :
198 248 : GUIPolygon::~GUIPolygon() {
199 124 : delete myRotatedShape;
200 248 : }
201 :
202 :
203 : GUIGLObjectPopupMenu*
204 0 : GUIPolygon::getPopUpMenu(GUIMainWindow& app,
205 : GUISUMOAbstractView& parent) {
206 0 : GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);
207 0 : buildPopupHeader(ret, app, false);
208 0 : GUIDesigns::buildFXMenuCommand(ret, "(" + getShapeType() + ")", nullptr, nullptr, 0);
209 0 : new FXMenuSeparator(ret);
210 0 : buildCenterPopupEntry(ret);
211 0 : buildNameCopyPopupEntry(ret);
212 0 : buildSelectionPopupEntry(ret);
213 0 : buildShowParamsPopupEntry(ret, false);
214 0 : buildPositionCopyEntry(ret, app);
215 0 : return ret;
216 : }
217 :
218 :
219 : GUIParameterTableWindow*
220 0 : GUIPolygon::getParameterWindow(GUIMainWindow& app,
221 : GUISUMOAbstractView&) {
222 0 : GUIParameterTableWindow* ret = new GUIParameterTableWindow(app, *this);
223 : // add items
224 0 : ret->mkItem("type", false, getShapeType());
225 0 : ret->mkItem("layer", false, toString(getShapeLayer()));
226 0 : ret->mkItem("name", false, toString(getShapeName()));
227 0 : ret->closeBuilding(this);
228 0 : return ret;
229 : }
230 :
231 :
232 : double
233 19627 : GUIPolygon::getExaggeration(const GUIVisualizationSettings& s) const {
234 19627 : return s.polySize.getExaggeration(s, this);
235 : }
236 :
237 :
238 : Boundary
239 4698 : GUIPolygon::getCenteringBoundary() const {
240 4698 : const PositionVector& shape = myRotatedShape != nullptr ? *myRotatedShape : myShape;
241 4698 : Boundary b;
242 4698 : b.add(shape.getBoxBoundary());
243 4698 : b.grow(2);
244 4698 : return b;
245 : }
246 :
247 :
248 : void
249 10442 : GUIPolygon::drawGL(const GUIVisualizationSettings& s) const {
250 : // first check if polygon can be drawn
251 10442 : if (myIsActive && checkDraw(s, this, this)) {
252 10442 : FXMutexLock locker(myLock);
253 : // push name (needed for getGUIGlObjectsUnderCursor(...)
254 10442 : GLHelper::pushName(getGlID());
255 : // draw inner polygon
256 10442 : if (myRotatedShape) {
257 0 : drawInnerPolygon(s, this, this, *myRotatedShape, s.polyUseCustomLayer ? s.polyCustomLayer : getShapeLayer(), getFill());
258 : } else {
259 20884 : drawInnerPolygon(s, this, this, myShape, s.polyUseCustomLayer ? s.polyCustomLayer : getShapeLayer(), getFill());
260 : }
261 : // pop name
262 10442 : GLHelper::popName();
263 : }
264 10442 : }
265 :
266 :
267 : void
268 2188 : GUIPolygon::setShape(const PositionVector& shape) {
269 2188 : FXMutexLock locker(myLock);
270 2188 : SUMOPolygon::setShape(shape);
271 2188 : if (getShapeNaviDegree() != 0.) {
272 2 : if (myRotatedShape == nullptr) {
273 2 : myRotatedShape = new PositionVector();
274 : }
275 2 : const Position& centroid = myShape.getCentroid();
276 2 : *myRotatedShape = myShape;
277 2 : myRotatedShape->sub(centroid);
278 2 : myRotatedShape->rotate2D(-DEG2RAD(getShapeNaviDegree()));
279 2 : myRotatedShape->add(centroid);
280 : } else {
281 2186 : delete myRotatedShape;
282 2186 : myRotatedShape = nullptr;
283 : }
284 : myTesselation.clear();
285 2188 : }
286 :
287 :
288 : RGBColor
289 10442 : 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 10442 : RGBColor color;
293 10442 : 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 0 : color = RGBColor(0, 0, 204);
296 10442 : } else if (active == 0) {
297 10442 : color = polygon->getShapeColor();
298 0 : } else if (active == 1) {
299 0 : color = c.getScheme().getColor(gSelected.isSelected(o->getType(), o->getGlID()));
300 0 : } else if (active == 2) {
301 0 : color = c.getScheme().getColor(0);
302 : } else {
303 : // color randomly (by pointer hash)
304 : std::hash<const SUMOPolygon*> ptr_hash;
305 0 : const double hue = (double)(ptr_hash(polygon) % 360); // [0-360]
306 0 : const double sat = (double)((ptr_hash(polygon) / 360) % 67) / 100.0 + 0.33; // [0.33-1]
307 0 : color = RGBColor::fromHSV(hue, sat, 1.);
308 : }
309 10442 : if (alphaOverride >= 0 && alphaOverride <= 255) {
310 0 : color.setAlpha((unsigned char)alphaOverride);
311 : }
312 10442 : GLHelper::setColor(color);
313 10442 : return color;
314 : }
315 :
316 :
317 : bool
318 10442 : GUIPolygon::checkDraw(const GUIVisualizationSettings& s, const SUMOPolygon* polygon, const GUIGlObject* o) {
319 10442 : if (o->getExaggeration(s) == 0) {
320 : return false;
321 : }
322 10442 : Boundary boundary = polygon->getShape().getBoxBoundary();
323 20884 : if (s.scale * MAX2(boundary.getWidth(), boundary.getHeight()) < s.polySize.minSize) {
324 : return false;
325 : }
326 10442 : if (polygon->getFill()) {
327 1257 : if (polygon->getShape().size() < 3) {
328 : return false;
329 : }
330 : } else {
331 9185 : if (polygon->getShape().size() < 2) {
332 : return false;
333 : }
334 : }
335 : return true;
336 : }
337 :
338 :
339 : void
340 10442 : 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 10442 : GLHelper::pushMatrix();
344 10442 : glTranslated(0, 0, layer);
345 10442 : setColor(s, polygon, o, disableSelectionColor, alphaOverride);
346 : int textureID = -1;
347 10442 : if (fill) {
348 1257 : const std::string& file = polygon->getShapeImgFile();
349 1257 : if (file != "") {
350 1 : textureID = GUITexturesHelper::getTextureID(file, true);
351 : }
352 : }
353 : // init generation of texture coordinates
354 1 : if (textureID >= 0) {
355 1 : glEnable(GL_TEXTURE_2D);
356 1 : glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
357 1 : glDisable(GL_CULL_FACE);
358 1 : glDisable(GL_DEPTH_TEST); // without DEPTH_TEST vehicles may be drawn below roads
359 1 : glDisable(GL_LIGHTING);
360 1 : glDisable(GL_COLOR_MATERIAL);
361 1 : glDisable(GL_ALPHA_TEST);
362 1 : glEnable(GL_BLEND);
363 1 : glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
364 1 : glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
365 1 : glBindTexture(GL_TEXTURE_2D, textureID);
366 1 : glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
367 1 : glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
368 : // http://www.gamedev.net/topic/133564-glutesselation-and-texture-mapping/
369 1 : glEnable(GL_TEXTURE_GEN_S);
370 1 : glEnable(GL_TEXTURE_GEN_T);
371 1 : glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
372 1 : glTexGendv(GL_S, GL_OBJECT_PLANE, xPlane);
373 1 : glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
374 1 : glTexGendv(GL_T, GL_OBJECT_PLANE, yPlane);
375 : }
376 10442 : if (fill) {
377 1257 : polygon->drawTesselation(shape);
378 : } else {
379 9185 : GLHelper::drawLine(shape);
380 9185 : GLHelper::drawBoxLines(shape, polygon->getLineWidth() * o->getExaggeration(s));
381 : }
382 :
383 : // de-init generation of texture coordinates
384 10442 : if (textureID >= 0) {
385 1 : glEnable(GL_DEPTH_TEST);
386 1 : glBindTexture(GL_TEXTURE_2D, 0);
387 1 : glDisable(GL_TEXTURE_2D);
388 1 : glDisable(GL_TEXTURE_GEN_S);
389 1 : glDisable(GL_TEXTURE_GEN_T);
390 : }
391 10442 : GLHelper::popMatrix();
392 10442 : if (s.geometryIndices.show(o)) {
393 0 : GLHelper::debugVertices(shape, s.geometryIndices, s.scale);
394 : }
395 10442 : if (!disableText) {
396 10442 : const Position& namePos = shape.getPolygonCenter();
397 10442 : o->drawName(namePos, s.scale, s.polyName, s.angle);
398 10442 : if (s.polyType.show(o)) {
399 0 : const Position p = namePos + Position(0, -0.6 * s.polyType.size / s.scale);
400 0 : GLHelper::drawTextSettings(s.polyType, polygon->getShapeType(), p, s.scale, s.angle);
401 : }
402 : }
403 10442 : }
404 :
405 : /****************************************************************************/
|