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