LCOV - code coverage report
Current view: top level - src/utils/gui/globjects - GUIPolygon.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 76.8 % 194 149
Test Date: 2024-11-22 15:46:21 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-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              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1