LCOV - code coverage report
Current view: top level - src/utils/gui/globjects - GUIPolygon.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 77.2 % 193 149
Test Date: 2025-12-06 15:35:27 Functions: 83.3 % 18 15

            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              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1