LCOV - code coverage report
Current view: top level - src/utils/gui/globjects - GUIPolygon.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 149 196 76.0 %
Date: 2024-05-06 15:32:35 Functions: 15 18 83.3 %

          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        4531 : void CALLBACK beginCallback(GLenum which) {
      62             :     //std::cout << " beginCallback id=" << Named::getIDSecure(myCurrentTesselated) << " type=" << which << "\n";
      63        4531 :     myCurrentType = which;
      64             :     myCurrentPoints.clear();
      65        4531 : }
      66             : 
      67             : 
      68        4531 : void CALLBACK endCallback(void) {
      69        4531 :     myCurrentTesselated->myTesselation.emplace_back(GLPrimitive());
      70        4531 :     GLPrimitive& glp = myCurrentTesselated->myTesselation.back();
      71        4531 :     glp.type = myCurrentType;
      72        4531 :     glp.vert = myCurrentPoints;
      73             :     myCurrentPoints.clear();
      74        4531 : }
      75             : 
      76             : 
      77       31893 : void CALLBACK vertexCallback(GLvoid* vertex) {
      78             :     GLdouble* p3 = (GLdouble*) vertex;
      79             :     //std::cout << " vertexCallback id=" << Named::getIDSecure(myCurrentTesselated) << " point=" << p3 << "\n";
      80       31893 :     myCurrentPoints.push_back(Position(p3[0], p3[1], p3[2]));
      81       31893 : }
      82             : 
      83             : 
      84         752 : 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         752 :     myCombineIndex = (myCombineIndex + 1) % MAX_COMBINE_INDEX;
      90         752 :     myCombineVertices[myCombineIndex][0] = coords[0];
      91         752 :     myCombineVertices[myCombineIndex][1] = coords[1];
      92         752 :     myCombineVertices[myCombineIndex][2] = coords[2];
      93         752 :     *dataOut = myCombineVertices[myCombineIndex];
      94         752 : }
      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      152055 : TesselatedPolygon::drawTesselation(const PositionVector& shape) const {
     118      152055 :     if (myTesselation.empty()) {
     119       50772 :         myCurrentTesselated = this;
     120             :         // draw the tesselated shape
     121       50772 :         size_t numPoints = shape.size() * 3;
     122       50772 :         for (const PositionVector& hole : myHoles) {
     123           0 :             numPoints += hole.size() * 3;
     124             :         }
     125       50772 :         double* points = new double[numPoints];
     126       50772 :         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       50772 :         gluTessCallback(tobj, GLU_TESS_VERTEX, (GLvoid(CALLBACK*)()) &vertexCallback);
     136       50772 :         gluTessCallback(tobj, GLU_TESS_BEGIN, (GLvoid(CALLBACK*)()) &beginCallback);
     137       50772 :         gluTessCallback(tobj, GLU_TESS_END, (GLvoid(CALLBACK*)()) &endCallback);
     138             :         //gluTessCallback(tobj, GLU_TESS_ERROR, (GLvoid (CALLBACK*) ()) &errorCallback);
     139       50772 :         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       50772 :         gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
     147       50772 :         gluTessBeginPolygon(tobj, nullptr);
     148       50772 :         gluTessBeginContour(tobj);
     149      241839 :         for (int i = 0; i < (int)shape.size(); i++) {
     150      191067 :             points[3 * i]  = shape[i].x();
     151      191067 :             points[3 * i + 1]  = shape[i].y();
     152      191067 :             points[3 * i + 2]  = 0.;
     153      191067 :             gluTessVertex(tobj, points + 3 * i, points + 3 * i);
     154             :         }
     155       50772 :         gluTessEndContour(tobj);
     156       50772 :         size_t startIndex = shape.size() * 3;
     157       50772 :         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       50772 :         gluTessEndPolygon(tobj);
     169       50772 :         gluDeleteTess(tobj);
     170       50772 :         delete[] points;
     171             :     }
     172      389995 :     for (GLPrimitive& pr : myTesselation) {
     173             :         // XXX change to glDrawArrays
     174      237940 :         glBegin(pr.type);
     175     1921866 :         for (const Position& p : pr.vert) {
     176     1683926 :             glVertex3d(p.x(), p.y(), p.z());
     177             :         }
     178      237940 :         glEnd();
     179             :     }
     180      152055 : }
     181             : 
     182             : 
     183             : // ===========================================================================
     184             : // GUIPolygon method definitions
     185             : // ===========================================================================
     186             : 
     187         121 : 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         121 :                        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         121 :     myRotatedShape(nullptr) {
     194         121 :     if (angle != 0.) {
     195           2 :         setShape(shape);
     196             :     }
     197         121 : }
     198             : 
     199             : 
     200         242 : GUIPolygon::~GUIPolygon() {
     201         121 :     delete myRotatedShape;
     202         242 : }
     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       12196 : GUIPolygon::getExaggeration(const GUIVisualizationSettings& s) const {
     236       12196 :     return s.polySize.getExaggeration(s, this);
     237             : }
     238             : 
     239             : 
     240             : Boundary
     241        6014 : GUIPolygon::getCenteringBoundary() const {
     242        6014 :     const PositionVector& shape = myRotatedShape != nullptr ? *myRotatedShape : myShape;
     243        6014 :     Boundary b;
     244        6014 :     b.add(shape.getBoxBoundary());
     245        6014 :     b.grow(2);
     246        6014 :     return b;
     247           0 : }
     248             : 
     249             : 
     250             : void
     251        6489 : GUIPolygon::drawGL(const GUIVisualizationSettings& s) const {
     252             :     // first check if polygon can be drawn
     253        6489 :     if (myIsActive && checkDraw(s, this, this)) {
     254        6489 :         FXMutexLock locker(myLock);
     255             :         // push name (needed for getGUIGlObjectsUnderCursor(...)
     256        6489 :         GLHelper::pushName(getGlID());
     257             :         // draw inner polygon
     258        6489 :         if (myRotatedShape) {
     259           0 :             drawInnerPolygon(s, this, this, *myRotatedShape, s.altKeyPressed ? 0 : getShapeLayer(), getFill());
     260             :         } else {
     261       12978 :             drawInnerPolygon(s, this, this, myShape, s.altKeyPressed ? 0 : getShapeLayer(), getFill());
     262             :         }
     263             :         // pop name
     264        6489 :         GLHelper::popName();
     265             :     }
     266        6489 : }
     267             : 
     268             : 
     269             : void
     270        2823 : GUIPolygon::setShape(const PositionVector& shape) {
     271        2823 :     FXMutexLock locker(myLock);
     272        2823 :     SUMOPolygon::setShape(shape);
     273        2823 :     if (getShapeNaviDegree() != 0.) {
     274           2 :         if (myRotatedShape == nullptr) {
     275           4 :             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        2821 :         delete myRotatedShape;
     284        2821 :         myRotatedShape = nullptr;
     285             :     }
     286             :     myTesselation.clear();
     287        2823 : }
     288             : 
     289             : 
     290             : RGBColor
     291        6489 : 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        6489 :     RGBColor color;
     295        6489 :     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        6489 :     } else if (active == 0) {
     299        6489 :         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        6489 :     if (alphaOverride >= 0 && alphaOverride <= 255) {
     312           0 :         color.setAlpha((unsigned char)alphaOverride);
     313             :     }
     314        6489 :     GLHelper::setColor(color);
     315        6489 :     return color;
     316             : }
     317             : 
     318             : 
     319             : bool
     320        6489 : GUIPolygon::checkDraw(const GUIVisualizationSettings& s, const SUMOPolygon* polygon, const GUIGlObject* o) {
     321        6489 :     if (o->getExaggeration(s) == 0) {
     322             :         return false;
     323             :     }
     324        6489 :     Boundary boundary = polygon->getShape().getBoxBoundary();
     325       12978 :     if (s.scale * MAX2(boundary.getWidth(), boundary.getHeight()) < s.polySize.minSize) {
     326             :         return false;
     327             :     }
     328        6489 :     if (polygon->getFill()) {
     329         782 :         if (polygon->getShape().size() < 3) {
     330           0 :             return false;
     331             :         }
     332             :     } else {
     333        5707 :         if (polygon->getShape().size() < 2) {
     334           0 :             return false;
     335             :         }
     336             :     }
     337             :     return true;
     338        6489 : }
     339             : 
     340             : 
     341             : void
     342        6489 : 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        6489 :     GLHelper::pushMatrix();
     346        6489 :     glTranslated(0, 0, layer);
     347        6489 :     setColor(s, polygon, o, disableSelectionColor, alphaOverride);
     348             :     int textureID = -1;
     349        6489 :     if (fill) {
     350             :         const std::string& file = polygon->getShapeImgFile();
     351         782 :         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        6489 :     if (fill) {
     379         782 :         polygon->drawTesselation(shape);
     380             :     } else {
     381        5707 :         GLHelper::drawLine(shape);
     382        5707 :         GLHelper::drawBoxLines(shape, polygon->getLineWidth() * o->getExaggeration(s));
     383             :     }
     384             : 
     385             :     // de-init generation of texture coordinates
     386        6489 :     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        6489 :     GLHelper::popMatrix();
     394        6489 :     if (s.geometryIndices.show(o)) {
     395           0 :         GLHelper::debugVertices(shape, s.geometryIndices, s.scale);
     396             :     }
     397        6489 :     if (!disableText) {
     398        6489 :         const Position& namePos = shape.getPolygonCenter();
     399        6489 :         o->drawName(namePos, s.scale, s.polyName, s.angle);
     400        6489 :         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        6489 : }
     406             : 
     407             : /****************************************************************************/

Generated by: LCOV version 1.14