Path: blob/main/src/utils/gui/windows/GUISUMOAbstractView.cpp
169684 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GUISUMOAbstractView.cpp14/// @author Daniel Krajzewicz15/// @author Jakob Erdmann16/// @author Michael Behrisch17/// @author Laura Bieker18/// @author Andreas Gaubatz19/// @date Sept 200220///21// The base class for a view22/****************************************************************************/23#include <config.h>2425#include <iostream>26#include <utility>27#include <cmath>28#include <cassert>29#include <limits>30#include <fxkeys.h>31#ifdef HAVE_GL2PS32#include <gl2ps.h>33#endif34#include <foreign/fontstash/fontstash.h>35#include <utils/common/MsgHandler.h>36#include <utils/common/RGBColor.h>37#include <utils/common/StringUtils.h>38#include <utils/common/SysUtils.h>39#include <utils/common/ToString.h>40#include <utils/foxtools/MFXCheckableButton.h>41#include <utils/foxtools/MFXImageHelper.h>42#include <utils/foxtools/MFXSingleEventThread.h>43#include <utils/foxtools/MFXStaticToolTip.h>44#include <utils/geom/GeoConvHelper.h>45#include <utils/geom/GeomHelper.h>46#include <utils/gui/cursors/GUICursorSubSys.h>47#include <utils/gui/div/GLHelper.h>48#include <utils/gui/div/GUIDesigns.h>49#include <utils/gui/div/GUIGlobalSelection.h>50#include <utils/gui/globjects/GLIncludes.h>51#include <utils/gui/globjects/GUICursorDialog.h>52#include <utils/gui/globjects/GUIGlObject.h>53#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>54#include <utils/gui/globjects/GUIGlObjectStorage.h>55#include <utils/gui/globjects/GUIPointOfInterest.h>56#include <utils/gui/globjects/GUIPolygon.h>57#include <utils/gui/images/GUITexturesHelper.h>58#include <utils/gui/settings/GUICompleteSchemeStorage.h>59#include <utils/gui/settings/GUIVisualizationSettings.h>60#include <utils/gui/windows/GUIAppEnum.h>61#include <utils/gui/windows/GUIDialog_ViewSettings.h>62#include <utils/options/OptionsCont.h>63#include <utils/shapes/PointOfInterest.h>6465#include <unordered_set>6667#include "GUISUMOAbstractView.h"68#include "GUIMainWindow.h"69#include "GUIGlChildWindow.h"70#include "GUIDanielPerspectiveChanger.h"71#include "GUIDialog_EditViewport.h"7273#ifdef HAVE_GDAL74#ifdef _MSC_VER75#pragma warning(push)76#pragma warning(disable: 4435 5219 5220)77#endif78#if __GNUC__ > 379#pragma GCC diagnostic push80#pragma GCC diagnostic ignored "-Wpedantic"81#endif82#include <gdal_priv.h>83#if __GNUC__ > 384#pragma GCC diagnostic pop85#endif86#ifdef _MSC_VER87#pragma warning(pop)88#endif89#endif9091// ===========================================================================92// debug constants93// ===========================================================================94//#define DEBUG_SNAPSHOT9596// ===========================================================================97// static members98// ===========================================================================99100const double GUISUMOAbstractView::SENSITIVITY = 0.1; // meters101102// ===========================================================================103// FOX callback mapping104// ===========================================================================105106FXDEFMAP(GUISUMOAbstractView) GUISUMOAbstractViewMap[] = {107FXMAPFUNC(SEL_CONFIGURE, 0, GUISUMOAbstractView::onConfigure),108FXMAPFUNC(SEL_PAINT, 0, GUISUMOAbstractView::onPaint),109FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, GUISUMOAbstractView::onLeftBtnPress),110FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, GUISUMOAbstractView::onLeftBtnRelease),111FXMAPFUNC(SEL_MIDDLEBUTTONPRESS, 0, GUISUMOAbstractView::onMiddleBtnPress),112FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE, 0, GUISUMOAbstractView::onMiddleBtnRelease),113FXMAPFUNC(SEL_RIGHTBUTTONPRESS, 0, GUISUMOAbstractView::onRightBtnPress),114FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, 0, GUISUMOAbstractView::onRightBtnRelease),115FXMAPFUNC(SEL_DOUBLECLICKED, 0, GUISUMOAbstractView::onDoubleClicked),116FXMAPFUNC(SEL_MOUSEWHEEL, 0, GUISUMOAbstractView::onMouseWheel),117FXMAPFUNC(SEL_MOTION, 0, GUISUMOAbstractView::onMouseMove),118FXMAPFUNC(SEL_LEAVE, 0, GUISUMOAbstractView::onMouseLeft),119FXMAPFUNC(SEL_KEYPRESS, 0, GUISUMOAbstractView::onKeyPress),120FXMAPFUNC(SEL_KEYRELEASE, 0, GUISUMOAbstractView::onKeyRelease),121FXMAPFUNC(SEL_COMMAND, MID_CLOSE_LANE, GUISUMOAbstractView::onCmdCloseLane),122FXMAPFUNC(SEL_COMMAND, MID_CLOSE_EDGE, GUISUMOAbstractView::onCmdCloseEdge),123FXMAPFUNC(SEL_COMMAND, MID_ADD_REROUTER, GUISUMOAbstractView::onCmdAddRerouter),124FXMAPFUNC(SEL_COMMAND, MID_REACHABILITY, GUISUMOAbstractView::onCmdShowReachability),125FXMAPFUNC(SEL_COMMAND, MID_REACHABILITY, GUISUMOAbstractView::onCmdShowReachability),126FXMAPFUNC(SEL_CHANGED, MID_SIMPLE_VIEW_COLORCHANGE, GUISUMOAbstractView::onVisualizationChange),127};128129FXIMPLEMENT_ABSTRACT(GUISUMOAbstractView, FXGLCanvas, GUISUMOAbstractViewMap, ARRAYNUMBER(GUISUMOAbstractViewMap))130131// ===========================================================================132// member method definitions133// ===========================================================================134135GUISUMOAbstractView::GUISUMOAbstractView(FXComposite* p, GUIMainWindow& app, GUIGlChildWindow* parent, const SUMORTree& grid, FXGLVisual* glVis, FXGLCanvas* share) :136FXGLCanvas(p, glVis, share, p, MID_GLCANVAS, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y, 0, 0, 0, 0),137myApp(&app),138myGlChildWindowParent(parent),139myGrid(&grid),140myMouseHotspotX(app.getDefaultCursor()->getHotX()),141myMouseHotspotY(app.getDefaultCursor()->getHotY()),142myWindowCursorPositionX(getWidth() / 2),143myWindowCursorPositionY(getHeight() / 2) {144setTarget(this);145enable();146flags |= FLAG_ENABLED;147myChanger = new GUIDanielPerspectiveChanger(*this, *myGrid);148myVisualizationSettings = &gSchemeStorage.getDefault();149myVisualizationSettings->gaming = myApp->isGaming();150gSchemeStorage.setViewport(this);151myDecals = gSchemeStorage.getDecals();152}153154155GUISUMOAbstractView::~GUISUMOAbstractView() {156gSchemeStorage.setDefault(myVisualizationSettings->name);157gSchemeStorage.saveViewport(myChanger->getXPos(), myChanger->getYPos(), myChanger->getZPos(), myChanger->getRotation());158gSchemeStorage.saveDecals(myDecals);159delete myPopup;160delete myChanger;161delete myGUIDialogEditViewport;162delete myGUIDialogViewSettings;163// cleanup decals164for (auto& decal : myDecals) {165delete decal.image;166}167// remove all elements168for (auto& additional : myAdditionallyDrawn) {169additional.first->removeActiveAddVisualisation(this, ~0);170}171}172173174bool175GUISUMOAbstractView::isInEditMode() {176return myInEditMode;177}178179180GUIPerspectiveChanger&181GUISUMOAbstractView::getChanger() const {182return *myChanger;183}184185186void187GUISUMOAbstractView::updateToolTip() {188if (myGlChildWindowParent->getGUIMainWindowParent()->getStaticTooltipView()->isStaticToolTipEnabled()) {189update();190}191}192193194Position195GUISUMOAbstractView::getPositionInformation() const {196return screenPos2NetPos(myWindowCursorPositionX, myWindowCursorPositionY);197}198199200Position201GUISUMOAbstractView::snapToActiveGrid(const Position& pos, bool snapXY) const {202Position result = pos;203if (myVisualizationSettings->showGrid) {204if (snapXY) {205const double xRest = std::fmod(pos.x(), myVisualizationSettings->gridXSize) + (pos.x() < 0 ? myVisualizationSettings->gridXSize : 0);206const double yRest = std::fmod(pos.y(), myVisualizationSettings->gridYSize) + (pos.y() < 0 ? myVisualizationSettings->gridYSize : 0);207result.setx(pos.x() - xRest + (xRest < myVisualizationSettings->gridXSize * 0.5 ? 0 : myVisualizationSettings->gridXSize));208result.sety(pos.y() - yRest + (yRest < myVisualizationSettings->gridYSize * 0.5 ? 0 : myVisualizationSettings->gridYSize));209} else {210// snapZToActiveGrid uses grid Y Size211const double zRest = std::fmod(pos.z(), myVisualizationSettings->gridYSize) + (pos.z() < 0 ? myVisualizationSettings->gridYSize : 0);212result.setz(pos.z() - zRest + (zRest < myVisualizationSettings->gridYSize * 0.5 ? 0 : myVisualizationSettings->gridYSize));213}214}215return result;216}217218219Position220GUISUMOAbstractView::screenPos2NetPos(int x, int y) const {221Boundary bound = myChanger->getViewport();222double xNet = bound.xmin() + bound.getWidth() * x / getWidth();223// cursor origin is in the top-left corner224double yNet = bound.ymin() + bound.getHeight() * (getHeight() - y) / getHeight();225// rotate around the viewport center226if (myChanger->getRotation() != 0) {227return Position(xNet, yNet).rotateAround2D(-DEG2RAD(myChanger->getRotation()), bound.getCenter());228} else {229return Position(xNet, yNet);230}231}232233234void235GUISUMOAbstractView::addDecals(const std::vector<Decal>& decals) {236myDecals.insert(myDecals.end(), decals.begin(), decals.end());237}238239240void241GUISUMOAbstractView::updatePositionInformationLabel() const {242Position pos = getPositionInformation();243// set cartesian position244myApp->getCartesianLabel()->setText(("x:" + toString(pos.x()) + ", y:" + toString(pos.y())).c_str());245// set geo position246GeoConvHelper::getFinal().cartesian2geo(pos);247if (GeoConvHelper::getFinal().usingGeoProjection()) {248myApp->getGeoLabel()->setText(("lat:" + toString(pos.y(), gPrecisionGeo) + ", lon:" + toString(pos.x(), gPrecisionGeo)).c_str());249} else {250myApp->getGeoLabel()->setText(TL("(No projection defined)"));251}252// if enabled, set test position253if (myApp->getTestFrame()) {254if (OptionsCont::getOptions().getBool("gui-testing")) {255myApp->getTestFrame()->show();256// adjust cursor position (24,25) to show exactly the same position as in function netedit.leftClick(match, X, Y)257myApp->getTestLabel()->setText(("Test: x:" + toString(getWindowCursorPosition().x() - 24.0) + " y:" + toString(getWindowCursorPosition().y() - 25.0)).c_str());258} else {259myApp->getTestFrame()->hide();260}261}262}263264265int266GUISUMOAbstractView::doPaintGL(int /*mode*/, const Boundary& /*boundary*/) {267return 0;268}269270271void272GUISUMOAbstractView::doInit() {273}274275276Boundary277GUISUMOAbstractView::getVisibleBoundary() const {278return myChanger->getViewport();279}280281282bool283GUISUMOAbstractView::is3DView() const {284return false;285}286287288void GUISUMOAbstractView::zoom2Pos(Position& /* camera */, Position& /* lookAt */, double /* zoom */) {289}290291292void293GUISUMOAbstractView::paintGL() {294// reset debug counters295GLHelper::resetMatrixCounter();296GLHelper::resetVertexCounter();297if (getWidth() == 0 || getHeight() == 0) {298return;299}300const long start = SysUtils::getCurrentMillis();301302if (getTrackedID() != GUIGlObject::INVALID_ID) {303centerTo(getTrackedID(), false);304}305// draw306glClearColor(307myVisualizationSettings->backgroundColor.red() / 255.f,308myVisualizationSettings->backgroundColor.green() / 255.f,309myVisualizationSettings->backgroundColor.blue() / 255.f,310myVisualizationSettings->backgroundColor.alpha() / 255.f);311glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);312glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);313314if (myVisualizationSettings->dither) {315glEnable(GL_DITHER);316} else {317glDisable(GL_DITHER);318}319glEnable(GL_BLEND);320glDisable(GL_LINE_SMOOTH);321322Boundary bound = applyGLTransform();323doPaintGL(GL_RENDER, bound);324GLHelper::checkCounterMatrix();325GLHelper::checkCounterName();326displayLegends();327const long end = SysUtils::getCurrentMillis();328myFrameDrawTime = end - start;329if (myVisualizationSettings->fps) {330drawFPS();331}332// check if show tooltip333if (myGlChildWindowParent->getGUIMainWindowParent()->getStaticTooltipView()->isStaticToolTipEnabled()) {334showToolTipFor(getToolTipID());335} else {336myGlChildWindowParent->getGUIMainWindowParent()->getStaticTooltipView()->hideStaticToolTip();337}338swapBuffers();339}340341342long343GUISUMOAbstractView::onCmdCloseLane(FXObject*, FXSelector, void*) {344return 1;345}346347348long349GUISUMOAbstractView::onCmdCloseEdge(FXObject*, FXSelector, void*) {350return 1;351}352353354long355GUISUMOAbstractView::onCmdAddRerouter(FXObject*, FXSelector, void*) {356return 1;357}358359360long361GUISUMOAbstractView::onCmdShowReachability(FXObject*, FXSelector, void*) {362return 1;363}364365366long367GUISUMOAbstractView::onVisualizationChange(FXObject*, FXSelector, void*) {368return 1;369}370371372GUILane*373GUISUMOAbstractView::getLaneUnderCursor() {374return nullptr;375}376377378GUIGlID379GUISUMOAbstractView::getToolTipID() {380return getObjectUnderCursor();381}382383384GUIGlID385GUISUMOAbstractView::getObjectUnderCursor(double sensitivity) {386return getObjectAtPosition(getPositionInformation(), sensitivity);387}388389390std::vector<GUIGlID>391GUISUMOAbstractView::getObjectsUnderCursor() {392return getObjectsAtPosition(getPositionInformation(), SENSITIVITY);393}394395396397std::vector<GUIGlObject*>398GUISUMOAbstractView::getGUIGlObjectsUnderCursor() {399return getGUIGlObjectsAtPosition(getPositionInformation(), SENSITIVITY);400}401402403std::vector<GUIGlObject*>404GUISUMOAbstractView::getGUIGlObjectsUnderSnappedCursor() {405return getGUIGlObjectsAtPosition(snapToActiveGrid(getPositionInformation()), SENSITIVITY);406}407408409GUIGlID410GUISUMOAbstractView::getObjectAtPosition(Position pos, double sensitivity) {411// calculate a boundary for the given position412Boundary positionBoundary;413positionBoundary.add(pos);414positionBoundary.grow(sensitivity);415const std::vector<GUIGlID> ids = getObjectsInBoundary(positionBoundary);416// Interpret results417int idMax = 0;418double maxLayer = -std::numeric_limits<double>::max();419double minDist = std::numeric_limits<double>::max();420// iterate over obtained GUIGlIDs421for (const auto& i : ids) {422// obtain GUIGlObject423GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(i);424// check that GUIGlObject exist425if (o == nullptr) {426continue;427}428// check that GUIGlObject isn't the network429if (o->getGlID() == 0) {430continue;431}432//std::cout << "point selection hit " << o->getMicrosimID() << "\n";433double layer = o->getClickPriority();434double dist = o->getCenter().distanceTo2D(pos);435// check whether the current object is above a previous one436if (layer > maxLayer) {437idMax = i;438maxLayer = layer;439minDist = dist;440} else if (layer == maxLayer && dist < minDist) {441idMax = i;442minDist = dist;443}444// unblock object445GUIGlObjectStorage::gIDStorage.unblockObject(i);446}447return idMax;448}449450451std::vector<GUIGlID>452GUISUMOAbstractView::getObjectsAtPosition(Position pos, double radius) {453// declare result vector454std::vector<GUIGlID> result;455// calculate boundary456Boundary selection;457selection.add(pos);458selection.grow(radius);459// obtain GUIGlID of objects in boundary460const std::vector<GUIGlID> ids = getObjectsInBoundary(selection);461// iterate over obtained GUIGlIDs462for (const auto& i : ids) {463// obtain GUIGlObject464GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(i);465// check that GUIGlObject exist466if (o == nullptr) {467continue;468}469// check that GUIGlObject isn't the network470if (o->getGlID() == 0) {471continue;472}473//std::cout << "point selection hit " << o->getMicrosimID() << "\n";474GUIGlObjectType type = o->getType();475// avoid network476if (type != GLO_NETWORK) {477result.push_back(i);478}479// unblock object480GUIGlObjectStorage::gIDStorage.unblockObject(i);481}482return result;483}484485486std::vector<GUIGlObject*>487GUISUMOAbstractView::getGUIGlObjectsAtPosition(Position pos, double radius) {488// declare result vector489std::vector<GUIGlObject*> result;490// calculate boundary491Boundary selection;492selection.add(pos);493selection.grow(radius);494// obtain GUIGlID of objects in boundary495const std::vector<GUIGlID> ids = getObjectsInBoundary(selection);496// iterate over obtained GUIGlIDs497for (const auto& i : ids) {498// obtain GUIGlObject499GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(i);500// check that GUIGlObject exist501if (o == nullptr) {502continue;503}504// check that GUIGlObject isn't the network505if (o->getGlID() == 0) {506continue;507}508result.push_back(o);509// unblock object510GUIGlObjectStorage::gIDStorage.unblockObject(i);511}512return result;513}514515516std::vector<GUIGlID>517GUISUMOAbstractView::getObjectsInBoundary(Boundary bound) {518const int NB_HITS_MAX = 1024 * 1024;519// Prepare the selection mode520static GUIGlID hits[NB_HITS_MAX];521static GLint nb_hits = 0;522glSelectBuffer(NB_HITS_MAX, hits);523glInitNames();524525myVisualizationSettings->scale = m2p(SUMO_const_laneWidth);526Boundary oldViewPort = myChanger->getViewport(false); // backup the actual viewPort527myChanger->setViewport(bound);528bound = applyGLTransform(false);529// enable draw for selecting (to draw objects with less details)530myVisualizationSettings->drawForRectangleSelection = true;531int hits2 = doPaintGL(GL_SELECT, bound);532// reset flags533myVisualizationSettings->drawForRectangleSelection = false;534// Get the results535nb_hits = glRenderMode(GL_RENDER);536if (nb_hits == -1) {537myApp->setStatusBarText("Selection in boundary failed. Try to select fewer than " + toString(hits2) + " items");538}539std::vector<GUIGlID> result;540GLuint numNames;541GLuint* ptr = hits;542for (int i = 0; i < nb_hits; ++i) {543numNames = *ptr;544ptr += 3;545for (int j = 0; j < (int)numNames; j++) {546result.push_back(*ptr);547ptr++;548}549}550// switch viewport back to normal551myChanger->setViewport(oldViewPort);552return result;553}554555556std::vector<GUIGlObject*>557GUISUMOAbstractView::filterInternalLanes(const std::vector<GUIGlObject*>& objects) const {558// count number of internal lanes559size_t internalLanes = 0;560for (const auto& object : objects) {561if ((object->getType() == GLO_LANE) && (object->getMicrosimID().find(':') != std::string::npos)) {562internalLanes++;563}564}565// if all objects are internal lanes, return it all566if (objects.size() == internalLanes || !myVisualizationSettings->drawJunctionShape) {567return objects;568}569// in other case filter internal lanes570std::vector<GUIGlObject*> filteredObjects;571for (const auto& object : objects) {572if ((object->getType() == GLO_LANE) && (object->getMicrosimID().find(':') != std::string::npos)) {573continue;574}575filteredObjects.push_back(object);576}577return filteredObjects;578}579580581bool582GUISUMOAbstractView::showToolTipFor(const GUIGlID idToolTip) {583if (idToolTip != GUIGlObject::INVALID_ID) {584const GUIGlObject* object = GUIGlObjectStorage::gIDStorage.getObjectBlocking(idToolTip);585if (object != nullptr) {586myGlChildWindowParent->getGUIMainWindowParent()->getStaticTooltipView()->showStaticToolTip(object->getFullName().c_str());587return true;588}589}590// nothing to show591myGlChildWindowParent->getGUIMainWindowParent()->getStaticTooltipView()->hideStaticToolTip();592return false;593}594595596void597GUISUMOAbstractView::paintGLGrid() const {598// obtain minimum grid599const double minimumSizeGrid = (myVisualizationSettings->gridXSize < myVisualizationSettings->gridYSize) ? myVisualizationSettings->gridXSize : myVisualizationSettings->gridYSize;600// Check if the distance is enough to draw grid601if (myVisualizationSettings->scale * myVisualizationSettings->addSize.getExaggeration(*myVisualizationSettings, nullptr) >= (25 / minimumSizeGrid)) {602glEnable(GL_DEPTH_TEST);603glLineWidth(1);604// get multiplication values (2 is the margin)605const int multXmin = (int)(myChanger->getViewport().xmin() / myVisualizationSettings->gridXSize) - 2;606const int multYmin = (int)(myChanger->getViewport().ymin() / myVisualizationSettings->gridYSize) - 2;607const int multXmax = (int)(myChanger->getViewport().xmax() / myVisualizationSettings->gridXSize) + 2;608const int multYmax = (int)(myChanger->getViewport().ymax() / myVisualizationSettings->gridYSize) + 2;609// obtain references610const double xmin = myVisualizationSettings->gridXSize * multXmin;611const double ymin = myVisualizationSettings->gridYSize * multYmin;612const double xmax = myVisualizationSettings->gridXSize * multXmax;613const double ymax = myVisualizationSettings->gridYSize * multYmax;614double xp = xmin;615double yp = ymin;616// move drawing matrix617glTranslated(0, 0, .55);618glColor3d(0.5, 0.5, 0.5);619// draw horizontal lines620glBegin(GL_LINES);621while (yp <= ymax) {622glVertex2d(xmin, yp);623glVertex2d(xmax, yp);624yp += myVisualizationSettings->gridYSize;625}626// draw vertical lines627while (xp <= xmax) {628glVertex2d(xp, ymin);629glVertex2d(xp, ymax);630xp += myVisualizationSettings->gridXSize;631}632glEnd();633glTranslated(0, 0, -.55);634}635}636637638void639GUISUMOAbstractView::displayLegend() {640// compute the scale bar length641int length = 1;642const std::string text("10000000000");643int noDigits = 1;644int pixelSize = (int) m2p((double) length);645while (pixelSize <= 20) {646length *= 10;647noDigits++;648if (noDigits > (int)text.length()) {649return;650}651pixelSize = (int) m2p((double) length);652}653glLineWidth(1.0);654655glMatrixMode(GL_PROJECTION);656GLHelper::pushMatrix();657glLoadIdentity();658glMatrixMode(GL_MODELVIEW);659GLHelper::pushMatrix();660glLoadIdentity();661662// draw the scale bar663const double z = -1;664glDisable(GL_TEXTURE_2D);665glDisable(GL_ALPHA_TEST);666glDisable(GL_BLEND);667glEnable(GL_DEPTH_TEST);668GLHelper::pushMatrix();669glTranslated(0, 0, z);670671double len = (double) pixelSize / (double)(getWidth() - 1) * (double) 2.0;672glColor3d(0, 0, 0);673double o = double(15) / double(getHeight());674double o2 = o + o;675double oo = double(5) / double(getHeight());676glBegin(GL_LINES);677// vertical678glVertex2d(-.98, -1. + o);679glVertex2d(-.98 + len, -1. + o);680// tick at begin681glVertex2d(-.98, -1. + o);682glVertex2d(-.98, -1. + o2);683// tick at end684glVertex2d(-.98 + len, -1. + o);685glVertex2d(-.98 + len, -1. + o2);686glEnd();687GLHelper::popMatrix();688689const double fontHeight = 0.1 * 300. / getHeight();690const double fontWidth = 0.1 * 300. / getWidth();691// draw 0692GLHelper::drawText("0", Position(-.99, -0.99 + o2 + oo), z, fontHeight, RGBColor::BLACK, 0, FONS_ALIGN_LEFT, fontWidth);693694// draw current scale695GLHelper::drawText((text.substr(0, noDigits) + "m").c_str(), Position(-.99 + len, -0.99 + o2 + oo), z, fontHeight, RGBColor::BLACK, 0, FONS_ALIGN_LEFT, fontWidth);696697// restore matrices698glMatrixMode(GL_PROJECTION);699GLHelper::popMatrix();700glMatrixMode(GL_MODELVIEW);701GLHelper::popMatrix();702}703704void705GUISUMOAbstractView::displayLegends() {706if (myVisualizationSettings->showSizeLegend) {707displayLegend();708}709std::string key = "";710if (myVisualizationSettings->showColorLegend) {711auto const& scheme = myVisualizationSettings->getLaneEdgeScheme();712if (scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_EDGEDATA_NUMERICAL) {713key = myVisualizationSettings->edgeData;714} else if (scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_EDGE_PARAM_NUMERICAL) {715key = myVisualizationSettings->edgeParam;716} else if (scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_LANE_PARAM_NUMERICAL) {717key = myVisualizationSettings->laneParam;718}719displayColorLegend(scheme, false, key);720}721if (myVisualizationSettings->showVehicleColorLegend) {722auto const& scheme = myVisualizationSettings->vehicleColorer.getScheme();723if (scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_PARAM_NUMERICAL) {724key = myVisualizationSettings->vehicleParam;725}726displayColorLegend(myVisualizationSettings->vehicleColorer.getScheme(), true, key);727}728}729730void731GUISUMOAbstractView::displayColorLegend(const GUIColorScheme& scheme, bool leftSide, const std::string& key) {732// compute the scale bar length733glLineWidth(1.0);734glMatrixMode(GL_PROJECTION);735GLHelper::pushMatrix();736glLoadIdentity();737glMatrixMode(GL_MODELVIEW);738GLHelper::pushMatrix();739glLoadIdentity();740741const double z = -1;742glEnable(GL_DEPTH_TEST);743glEnable(GL_BLEND);744GLHelper::pushMatrix();745glTranslated(0, 0, z);746747const bool fixed = scheme.isFixed();748const int numColors = (int)scheme.getColors().size();749750// vertical751double right = 0.98;752double left = 0.95;753double textX = left - 0.01;754double textDir = 1;755FONSalign textAlign = FONS_ALIGN_RIGHT;756const double top = -0.7;757const double bot = 0.9;758const double dy = (top - bot) / numColors;759const double bot2 = fixed ? bot : bot + dy / 2;760// legend placement761if (leftSide) {762right = -right;763left = -left;764std::swap(right, left);765textX = right + 0.01;766textDir *= -1;767textAlign = FONS_ALIGN_LEFT;768}769// draw black boundary around legend colors770glColor3d(0, 0, 0);771glBegin(GL_LINES);772glVertex2d(right, top);773glVertex2d(right, bot2);774glVertex2d(left, bot2);775glVertex2d(left, top);776glVertex2d(right, top);777glVertex2d(left, top);778glVertex2d(right, bot2);779glVertex2d(left, bot2);780glEnd();781782const double fontHeight = 0.20 * 300. / getHeight();783const double fontWidth = 0.20 * 300. / getWidth();784785const int fadeSteps = fixed ? 1 : 10;786double colorStep = dy / fadeSteps;787for (int i = 0; i < numColors; i++) {788RGBColor col = scheme.getColors()[i];789const double topi = top - i * dy;790//const double boti = top - (i + 1) * dy;791//std::cout << " col=" << scheme.getColors()[i] << " i=" << i << " topi=" << topi << " boti=" << boti << "\n";792if (i + 1 < numColors) {793// fade794RGBColor col2 = scheme.getColors()[i + 1];795double thresh2 = scheme.getThresholds()[i + 1];796if (!fixed && thresh2 == GUIVisualizationSettings::MISSING_DATA) {797// draw scale end before missing data798GLHelper::setColor(col);799glBegin(GL_QUADS);800glVertex2d(left, topi);801glVertex2d(right, topi);802glVertex2d(right, topi - 5 * colorStep);803glVertex2d(left, topi - 5 * colorStep);804glEnd();805glColor3d(0, 0, 0);806glBegin(GL_LINES);807glVertex2d(right, topi - 10 * colorStep);808glVertex2d(left, topi - 10 * colorStep);809glEnd();810glBegin(GL_LINES);811glVertex2d(right, topi - 5 * colorStep);812glVertex2d(left, topi - 5 * colorStep);813glEnd();814} else {815// fade colors816for (double j = 0.0; j < fadeSteps; j++) {817GLHelper::setColor(RGBColor::interpolate(col, col2, j / fadeSteps));818glBegin(GL_QUADS);819glVertex2d(left, topi - j * colorStep);820glVertex2d(right, topi - j * colorStep);821glVertex2d(right, topi - (j + 1) * colorStep);822glVertex2d(left, topi - (j + 1) * colorStep);823glEnd();824}825}826} else {827GLHelper::setColor(col);828glBegin(GL_QUADS);829glVertex2d(left, topi);830glVertex2d(right, topi);831glVertex2d(right, bot2);832glVertex2d(left, bot2);833glEnd();834}835836const double threshold = scheme.getThresholds()[i];837std::string name = scheme.getNames()[i];838std::string text = fixed || threshold == GUIVisualizationSettings::MISSING_DATA ? name : toString(threshold);839840const double bgShift = 0.0;841const double textShift = 0.01;842const double textXShift = -0.005;843844GLHelper::setColor(RGBColor::WHITE);845glTranslated(0, 0, 0.1);846glBegin(GL_QUADS);847glVertex2d(textX, topi + fontHeight * bgShift);848glVertex2d(textX - textDir * fontWidth * (double)text.size() / 2.1, topi + fontHeight * bgShift);849glVertex2d(textX - textDir * fontWidth * (double)text.size() / 2.1, topi + fontHeight * (0.8 + bgShift));850glVertex2d(textX, topi + fontHeight * (0.8 + bgShift));851glEnd();852glTranslated(0, 0, -0.1);853GLHelper::drawText(text, Position(textX + textDir * textXShift, topi + textShift), 0, fontHeight, RGBColor::BLACK, 0, textAlign, fontWidth);854}855// draw scheme name856std::string name = scheme.getName();857if (name == GUIVisualizationSettings::SCHEME_NAME_EDGEDATA_NUMERICAL) {858name = "edgeData (" + key + ")";859} else if (name == GUIVisualizationSettings::SCHEME_NAME_EDGE_PARAM_NUMERICAL) {860name = "edgeParam (" + key + ")";861} else if (name == GUIVisualizationSettings::SCHEME_NAME_LANE_PARAM_NUMERICAL) {862name = "laneParam (" + key + ")";863} else if (name == GUIVisualizationSettings::SCHEME_NAME_PARAM_NUMERICAL) {864name = "param (" + key + ")";865} else if (name == GUIVisualizationSettings::SCHEME_NAME_DATA_ATTRIBUTE_NUMERICAL) {866name = "attribute (" + key + ")";867} else if (StringUtils::startsWith(name, "by ")) {868name = name.substr(3);869}870const double topN = -0.8;871const double bgShift = 0.0;872GLHelper::setColor(RGBColor::WHITE);873glTranslated(0, 0, 0.1);874glBegin(GL_QUADS);875glVertex2d(textX + textDir * 0.04, topN + fontHeight * bgShift - 0.01);876glVertex2d(textX + textDir * 0.04 - textDir * fontWidth * (double)name.size() / 2.3, topN + fontHeight * bgShift - 0.01);877glVertex2d(textX + textDir * 0.04 - textDir * fontWidth * (double)name.size() / 2.3, topN + fontHeight * (0.8 + bgShift));878glVertex2d(textX + textDir * 0.04, topN + fontHeight * (0.8 + bgShift));879glEnd();880glTranslated(0, 0, -0.1);881GLHelper::drawText(name, Position(textX + textDir * 0.04, topN), 0, fontHeight, RGBColor::BLACK, 0, textAlign, fontWidth);882883GLHelper::popMatrix();884// restore matrices885glMatrixMode(GL_PROJECTION);886GLHelper::popMatrix();887glMatrixMode(GL_MODELVIEW);888GLHelper::popMatrix();889}890891892double893GUISUMOAbstractView::getFPS() const {894return 1000.0 / MAX2((long)1, myFrameDrawTime);895}896897898GUIGlChildWindow*899GUISUMOAbstractView::getGUIGlChildWindow() {900return myGlChildWindowParent;901}902903904void905GUISUMOAbstractView::drawFPS() {906glMatrixMode(GL_PROJECTION);907GLHelper::pushMatrix();908glLoadIdentity();909glMatrixMode(GL_MODELVIEW);910GLHelper::pushMatrix();911glLoadIdentity();912const double fontHeight = 0.2 * 300. / getHeight();913const double fontWidth = 0.2 * 300. / getWidth();914GLHelper::drawText(toString((int)getFPS()) + " FPS", Position(0.82, 0.88), -1, fontHeight, RGBColor::RED, 0, FONS_ALIGN_LEFT, fontWidth);915#ifdef CHECK_ELEMENTCOUNTER916GLHelper::drawText(toString(GLHelper::getMatrixCounter()) + " matrix", Position(0.82, 0.79), -1, fontHeight, RGBColor::RED, 0, FONS_ALIGN_LEFT, fontWidth);917GLHelper::drawText(toString(GLHelper::getVertexCounter()) + " vertex", Position(0.82, 0.71), -1, fontHeight, RGBColor::RED, 0, FONS_ALIGN_LEFT, fontWidth);918#endif919// restore matrices920glMatrixMode(GL_PROJECTION);921GLHelper::popMatrix();922glMatrixMode(GL_MODELVIEW);923GLHelper::popMatrix();924}925926927double928GUISUMOAbstractView::m2p(double meter) const {929return meter * getWidth() / myChanger->getViewport().getWidth();930}931932933double934GUISUMOAbstractView::p2m(double pixel) const {935return pixel * myChanger->getViewport().getWidth() / getWidth();936}937938939void940GUISUMOAbstractView::recenterView() {941myChanger->setViewport(*myGrid);942}943944945void946GUISUMOAbstractView::centerTo(GUIGlID id, bool applyZoom, double zoomDist) {947GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(id);948if (o != nullptr && dynamic_cast<GUIGlObject*>(o) != nullptr) {949const Boundary& b = o->getCenteringBoundary();950if (b.getCenter() != Position::INVALID) {951if (applyZoom && zoomDist < 0) {952myChanger->setViewport(b);953update(); // only update when centering onto an object once954} else {955// called during tracking. update is triggered somewhere else956myChanger->centerTo(b.getCenter(), zoomDist, applyZoom);957updatePositionInformationLabel();958}959}960}961GUIGlObjectStorage::gIDStorage.unblockObject(id);962}963964965void966GUISUMOAbstractView::centerTo(const Position& pos, bool applyZoom, double zoomDist) {967// called during tracking. update is triggered somewhere else968myChanger->centerTo(pos, zoomDist, applyZoom);969updatePositionInformationLabel();970}971972973void974GUISUMOAbstractView::centerTo(const Boundary& bound) {975myChanger->setViewport(bound);976update();977}978979980GUIMainWindow*981GUISUMOAbstractView::getMainWindow() const {982return myApp;983}984985986Position987GUISUMOAbstractView::getWindowCursorPosition() const {988return Position(myWindowCursorPositionX, myWindowCursorPositionY);989}990991992void993GUISUMOAbstractView::setWindowCursorPosition(FXint x, FXint y) {994myWindowCursorPositionX = x + myMouseHotspotX;995myWindowCursorPositionY = y + myMouseHotspotY;996}997998999FXbool1000GUISUMOAbstractView::makeCurrent() {1001FXbool ret = FXGLCanvas::makeCurrent();1002return ret;1003}100410051006long1007GUISUMOAbstractView::onConfigure(FXObject*, FXSelector, void*) {1008if (makeCurrent()) {1009glViewport(0, 0, getWidth() - 1, getHeight() - 1);1010glClearColor(1011myVisualizationSettings->backgroundColor.red() / 255.f,1012myVisualizationSettings->backgroundColor.green() / 255.f,1013myVisualizationSettings->backgroundColor.blue() / 255.f,1014myVisualizationSettings->backgroundColor.alpha() / 255.f);1015doInit();1016myAmInitialised = true;1017makeNonCurrent();1018checkSnapshots();1019}1020return 1;1021}102210231024long1025GUISUMOAbstractView::onPaint(FXObject*, FXSelector, void*) {1026if (!isEnabled() || !myAmInitialised) {1027return 1;1028}1029if (makeCurrent()) {1030paintGL();1031makeNonCurrent();1032}1033// run tests1034myApp->handle(this, FXSEL(SEL_COMMAND, MID_RUNTESTS), nullptr);1035return 1;1036}103710381039GUIGLObjectPopupMenu*1040GUISUMOAbstractView::getPopup() const {1041return myPopup;1042}104310441045const Position&1046GUISUMOAbstractView::getPopupPosition() const {1047return myPopupPosition;1048}104910501051void1052GUISUMOAbstractView::destroyPopup() {1053if (myPopup != nullptr) {1054myPopup->removePopupFromObject();1055delete myPopup;1056myPopupPosition.set(0, 0);1057myPopup = nullptr;1058myCurrentObjectsDialog.clear();1059}1060}106110621063void1064GUISUMOAbstractView::replacePopup(GUIGLObjectPopupMenu* popUp) {1065// use the same position of old popUp1066popUp->move(myPopup->getX(), myPopup->getY());1067// delete and replace popup1068myPopup->removePopupFromObject();1069delete myPopup;1070myPopup = popUp;1071// create and show popUp1072myPopup->create();1073myPopup->show();1074myChanger->onRightBtnRelease(nullptr);1075setFocus();1076}107710781079long1080GUISUMOAbstractView::onLeftBtnPress(FXObject*, FXSelector, void* ptr) {1081destroyPopup();1082setFocus();1083FXEvent* e = (FXEvent*) ptr;1084// check whether the selection-mode is activated1085if ((e->state & CONTROLMASK) != 0) {1086// toggle selection of object under cursor1087if (makeCurrent()) {1088int id = getObjectUnderCursor();1089if (id != 0) {1090gSelected.toggleSelection(id);1091}1092makeNonCurrent();1093if (id != 0) {1094// possibly, the selection-coloring is used,1095// so we should update the screen again...1096update();1097}1098}1099}1100if ((e->state & SHIFTMASK) != 0) {1101// track vehicle or person under cursor1102if (makeCurrent()) {1103int id = getObjectUnderCursor();1104if (id != 0) {1105GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(id);1106if (o != nullptr) {1107if (!myApp->isGaming() && (o->getType() == GLO_VEHICLE || o->getType() == GLO_PERSON)) {1108startTrack(id);1109}1110}1111}1112makeNonCurrent();1113}1114}1115myChanger->onLeftBtnPress(ptr);1116grab();1117// Check there are double click1118if (e->click_count == 2) {1119handle(this, FXSEL(SEL_DOUBLECLICKED, 0), ptr);1120}1121return 1;1122}112311241125long1126GUISUMOAbstractView::onLeftBtnRelease(FXObject*, FXSelector, void* ptr) {1127destroyPopup();1128myChanger->onLeftBtnRelease(ptr);1129if (myApp->isGaming()) {1130onGamingClick(getPositionInformation());1131}1132ungrab();1133return 1;1134}113511361137long1138GUISUMOAbstractView::onMiddleBtnPress(FXObject*, FXSelector, void* ptr) {1139destroyPopup();1140setFocus();1141if (!myApp->isGaming()) {1142myChanger->onMiddleBtnPress(ptr);1143}1144grab();1145// enable panning1146myPanning = true;1147// set cursors1148setDefaultCursor(GUICursorSubSys::getCursor(GUICursor::MOVEVIEW));1149setDragCursor(GUICursorSubSys::getCursor(GUICursor::MOVEVIEW));1150return 1;1151}115211531154long1155GUISUMOAbstractView::onMiddleBtnRelease(FXObject*, FXSelector, void* ptr) {1156destroyPopup();1157if (!myApp->isGaming()) {1158myChanger->onMiddleBtnRelease(ptr);1159}1160ungrab();1161// disable panning1162myPanning = false;1163// restore cursors1164setDefaultCursor(GUICursorSubSys::getCursor(GUICursor::DEFAULT));1165setDragCursor(GUICursorSubSys::getCursor(GUICursor::DEFAULT));1166return 1;1167}116811691170long1171GUISUMOAbstractView::onRightBtnPress(FXObject*, FXSelector, void* ptr) {1172destroyPopup();1173if (!myApp->isGaming()) {1174myChanger->onRightBtnPress(ptr);1175}1176grab();1177return 1;1178}117911801181long1182GUISUMOAbstractView::onRightBtnRelease(FXObject* o, FXSelector sel, void* ptr) {1183destroyPopup();1184onMouseMove(o, sel, ptr);1185if (!myChanger->onRightBtnRelease(ptr) && !myApp->isGaming()) {1186openObjectDialogAtCursor((FXEvent*)ptr);1187}1188if (myApp->isGaming()) {1189onGamingRightClick(getPositionInformation());1190}1191ungrab();1192return 1;1193}119411951196long1197GUISUMOAbstractView::onDoubleClicked(FXObject*, FXSelector, void*) {1198return 1;1199}120012011202long1203GUISUMOAbstractView::onMouseWheel(FXObject*, FXSelector, void* ptr) {1204if (!myApp->isGaming()) {1205myChanger->onMouseWheel(ptr);1206// upddate viewport1207if (myGUIDialogEditViewport != nullptr) {1208myGUIDialogEditViewport->setValues(myChanger->getZoom(),1209myChanger->getXPos(), myChanger->getYPos(),1210myChanger->getRotation());1211}1212updatePositionInformationLabel();1213}1214return 1;1215}121612171218long1219GUISUMOAbstractView::onMouseMove(FXObject*, FXSelector, void* ptr) {1220// check if popup exist1221if (myPopup) {1222// check if handle front element1223if (myPopupPosition == getPositionInformation()) {1224myPopupPosition = Position::INVALID;1225myPopup->handle(this, FXSEL(SEL_COMMAND, MID_CURSORDIALOG_FRONT), nullptr);1226destroyPopup();1227} else if (!myPopup->shown()) {1228destroyPopup();1229}1230}1231if (myPopup == nullptr) {1232if (myGUIDialogEditViewport == nullptr || !myGUIDialogEditViewport->haveGrabbed()) {1233myChanger->onMouseMove(ptr);1234}1235if (myGUIDialogEditViewport != nullptr) {1236myGUIDialogEditViewport->setValues(myChanger->getZoom(),1237myChanger->getXPos(), myChanger->getYPos(),1238myChanger->getRotation());1239}1240updatePositionInformationLabel();1241}1242return 1;1243}124412451246long1247GUISUMOAbstractView::onMouseLeft(FXObject*, FXSelector, void* /*data*/) {1248return 1;1249}12501251std::vector<GUIGlObject*>1252GUISUMOAbstractView::filterContextObjects(const std::vector<GUIGlObject*>& objects) {1253// assume input is sorted with ComparatorClickPriority1254std::vector<GUIGlObject*> result;1255for (GUIGlObject* o : objects) {1256if (o->getClickPriority() != GUIGlObject::INVALID_PRIORITY && (result.empty() || result.back() != o)) {1257result.push_back(o);1258}1259}1260return result;1261}126212631264void1265GUISUMOAbstractView::openObjectDialogAtCursor(const FXEvent* ev) {1266// release the mouse grab1267ungrab();1268// check if alt key is pressed1269const bool altKeyPressed = ((ev->state & ALTMASK) != 0);1270// check if SUMO is enabled, initialised and Make OpenGL context current1271if (isEnabled() && myAmInitialised && makeCurrent()) {1272auto objectsUnderCursor = getGUIGlObjectsUnderCursor();1273if (objectsUnderCursor.empty()) {1274myPopup = GUIGlObjectStorage::gIDStorage.getNetObject()->getPopUpMenu(*myApp, *this);1275} else {1276std::sort(objectsUnderCursor.begin(), objectsUnderCursor.end(), ComparatorClickPriority());1277std::vector<GUIGlObject*> filtered = filterContextObjects(objectsUnderCursor);1278if (filtered.size() > 1 && (altKeyPressed1279|| filtered[0]->getClickPriority() == filtered[1]->getClickPriority())) {1280// open dialog for picking among objects (without duplicates)1281myPopup = new GUICursorDialog(GUIGLObjectPopupMenu::PopupType::PROPERTIES, this, filtered);1282} else {1283myPopup = objectsUnderCursor.front()->getPopUpMenu(*myApp, *this);1284}1285}1286openPopupDialog();1287makeNonCurrent();1288}1289}129012911292void1293GUISUMOAbstractView::openObjectDialog(const std::vector<GUIGlObject*>& objects, const bool filter) {1294if (objects.size() > 0) {1295// create cursor popup dialog1296if (objects.size() == 1) {1297myCurrentObjectsDialog = objects;1298} else if (filter) {1299// declare filtered objects1300std::vector<GUIGlObject*> filteredGLObjects;1301// fill filtered objects1302for (const auto& glObject : objects) {1303// compare type with first element type1304if (glObject->getType() == objects.front()->getType()) {1305filteredGLObjects.push_back(glObject);1306}1307}1308myCurrentObjectsDialog = filteredGLObjects;1309} else {1310myCurrentObjectsDialog = objects;1311}1312if (myCurrentObjectsDialog.size() > 1) {1313myPopup = new GUICursorDialog(GUIGLObjectPopupMenu::PopupType::PROPERTIES, this, myCurrentObjectsDialog);1314} else {1315myPopup = myCurrentObjectsDialog.front()->getPopUpMenu(*myApp, *this);1316}1317// open popup dialog1318openPopupDialog();1319}1320}132113221323long1324GUISUMOAbstractView::onKeyPress(FXObject* o, FXSelector sel, void* ptr) {1325const FXEvent* e = (FXEvent*) ptr;1326// check if process canvas or popup1327if (myPopup != nullptr) {1328return myPopup->onKeyPress(o, sel, ptr);1329} else {1330if (e->state & CONTROLMASK) {1331if (e->code == FX::KEY_Page_Up) {1332myVisualizationSettings->gridXSize *= 2;1333myVisualizationSettings->gridYSize *= 2;1334update();1335return 1;1336} else if (e->code == FX::KEY_Page_Down) {1337myVisualizationSettings->gridXSize /= 2;1338myVisualizationSettings->gridYSize /= 2;1339update();1340return 1;1341}1342}1343FXGLCanvas::onKeyPress(o, sel, ptr);1344return myChanger->onKeyPress(ptr);1345}1346}134713481349long1350GUISUMOAbstractView::onKeyRelease(FXObject* o, FXSelector sel, void* ptr) {1351// check if process canvas or popup1352if (myPopup != nullptr) {1353return myPopup->onKeyRelease(o, sel, ptr);1354} else {1355FXGLCanvas::onKeyRelease(o, sel, ptr);1356return myChanger->onKeyRelease(ptr);1357}1358}13591360// ------------ Dealing with snapshots13611362void1363GUISUMOAbstractView::addSnapshot(SUMOTime time, const std::string& file, const int w, const int h) {1364#ifdef DEBUG_SNAPSHOT1365std::cout << "add snapshot time=" << time << " file=" << file << "\n";1366#endif1367FXMutexLock lock(mySnapshotsMutex);1368mySnapshots[time].push_back(std::make_tuple(file, w, h));1369}137013711372std::string1373GUISUMOAbstractView::makeSnapshot(const std::string& destFile, const int w, const int h) {1374if (w >= 0) {1375resize(w, h);1376repaint();1377}1378std::string errorMessage;1379FXString ext = FXPath::extension(destFile.c_str());1380const bool useGL2PS = ext == "ps" || ext == "eps" || ext == "pdf" || ext == "svg" || ext == "tex" || ext == "pgf";1381#ifdef HAVE_FFMPEG1382const bool useVideo = destFile == "" || ext == "h264" || ext == "hevc" || ext == "mp4";1383#endif1384for (int i = 0; i < 10 && !makeCurrent(); ++i) {1385MFXSingleEventThread::sleep(100);1386}1387// draw1388glClearColor(1389myVisualizationSettings->backgroundColor.red() / 255.f,1390myVisualizationSettings->backgroundColor.green() / 255.f,1391myVisualizationSettings->backgroundColor.blue() / 255.f,1392myVisualizationSettings->backgroundColor.alpha() / 255.f);1393glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);1394glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);13951396if (myVisualizationSettings->dither) {1397glEnable(GL_DITHER);1398} else {1399glDisable(GL_DITHER);1400}1401glEnable(GL_BLEND);1402glDisable(GL_LINE_SMOOTH);14031404applyGLTransform();14051406if (useGL2PS) {1407#ifdef HAVE_GL2PS1408GLint format = GL2PS_PS;1409if (ext == "ps") {1410format = GL2PS_PS;1411} else if (ext == "eps") {1412format = GL2PS_EPS;1413} else if (ext == "pdf") {1414format = GL2PS_PDF;1415} else if (ext == "tex") {1416format = GL2PS_TEX;1417} else if (ext == "svg") {1418format = GL2PS_SVG;1419} else if (ext == "pgf") {1420format = GL2PS_PGF;1421} else {1422return "Could not save '" + destFile + "'.\n Unrecognized format '" + std::string(ext.text()) + "'.";1423}1424FILE* fp = fopen(destFile.c_str(), "wb");1425if (fp == 0) {1426return "Could not save '" + destFile + "'.\n Could not open file for writing";1427}1428GLHelper::setGL2PS();1429GLint buffsize = 0, state = GL2PS_OVERFLOW;1430GLint viewport[4];1431glGetIntegerv(GL_VIEWPORT, viewport);1432while (state == GL2PS_OVERFLOW) {1433buffsize += 1024 * 1024;1434gl2psBeginPage(destFile.c_str(), "sumo-gui; https://sumo.dlr.de", viewport, format, GL2PS_SIMPLE_SORT,1435GL2PS_DRAW_BACKGROUND | GL2PS_USE_CURRENT_VIEWPORT,1436GL_RGBA, 0, NULL, 0, 0, 0, buffsize, fp, "out.eps");1437glMatrixMode(GL_MODELVIEW);1438GLHelper::pushMatrix();1439glDisable(GL_TEXTURE_2D);1440glDisable(GL_ALPHA_TEST);1441glDisable(GL_BLEND);1442glEnable(GL_DEPTH_TEST);1443// draw decals (if not in grabbing mode)14441445drawDecals();1446if (myVisualizationSettings->showGrid) {1447paintGLGrid();1448}14491450glLineWidth(1);1451glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);1452Boundary viewPort = myChanger->getViewport();1453const float minB[2] = { (float)viewPort.xmin(), (float)viewPort.ymin() };1454const float maxB[2] = { (float)viewPort.xmax(), (float)viewPort.ymax() };1455myVisualizationSettings->scale = m2p(SUMO_const_laneWidth);1456glEnable(GL_POLYGON_OFFSET_FILL);1457glEnable(GL_POLYGON_OFFSET_LINE);1458myGrid->Search(minB, maxB, *myVisualizationSettings);14591460displayLegends();1461state = gl2psEndPage();1462glFinish();1463}1464GLHelper::setGL2PS(false);1465fclose(fp);1466#else1467return "Could not save '" + destFile + "', gl2ps was not enabled at compile time.";1468#endif1469} else {1470doPaintGL(GL_RENDER, myChanger->getViewport());1471displayLegends();1472swapBuffers();1473glFinish();1474FXColor* buf;1475FXMALLOC(&buf, FXColor, getWidth()*getHeight());1476// read from the back buffer1477glReadBuffer(GL_BACK);1478// Read the pixels1479glReadPixels(0, 0, getWidth(), getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)buf);1480makeNonCurrent();1481update();1482// mirror1483int mwidth = getWidth();1484int mheight = getHeight();1485FXColor* paa = buf;1486FXColor* pbb = buf + mwidth * (mheight - 1);1487do {1488FXColor* pa = paa;1489paa += mwidth;1490FXColor* pb = pbb;1491pbb -= mwidth;1492do {1493FXColor t = *pa;1494*pa++ = *pb;1495*pb++ = t;1496} while (pa < paa);1497} while (paa < pbb);1498try {1499#ifdef HAVE_FFMPEG1500if (useVideo) {1501try {1502saveFrame(destFile, buf);1503errorMessage = "video";1504} catch (std::runtime_error& err) {1505errorMessage = err.what();1506}1507} else1508#endif1509if (!MFXImageHelper::saveImage(destFile, getWidth(), getHeight(), buf)) {1510errorMessage = "Could not save '" + destFile + "'.";1511}1512} catch (InvalidArgument& e) {1513errorMessage = "Could not save '" + destFile + "'.\n" + e.what();1514}1515FXFREE(&buf);1516}1517return errorMessage;1518}151915201521void1522GUISUMOAbstractView::saveFrame(const std::string& destFile, FXColor* buf) {1523UNUSED_PARAMETER(destFile);1524UNUSED_PARAMETER(buf);1525}152615271528void1529GUISUMOAbstractView::checkSnapshots() {1530const SUMOTime time = getCurrentTimeStep() - DELTA_T;1531#ifdef DEBUG_SNAPSHOT1532std::cout << "check snapshots time=" << time << " registeredTimes=" << mySnapshots.size() << "\n";1533#endif1534FXMutexLock lock(mySnapshotsMutex);1535const auto snapIt = mySnapshots.find(time);1536if (snapIt == mySnapshots.end()) {1537return;1538}1539std::vector<std::tuple<std::string, int, int> > files = snapIt->second;1540lock.unlock();1541// decouple map access and painting to avoid deadlock1542for (const auto& entry : files) {1543#ifdef DEBUG_SNAPSHOT1544std::cout << "make snapshot time=" << time << " file=" << file << "\n";1545#endif1546const std::string& error = makeSnapshot(std::get<0>(entry), std::get<1>(entry), std::get<2>(entry));1547if (error != "" && error != "video") {1548WRITE_WARNING(error);1549}1550}1551// synchronization with a waiting run thread1552lock.lock();1553mySnapshots.erase(time);1554mySnapshotCondition.signal();1555#ifdef DEBUG_SNAPSHOT1556std::cout << " files=" << toString(files) << " myApplicationSnapshots=" << joinToString(*myApplicationSnapshots, ",") << "\n";1557#endif1558}155915601561void1562GUISUMOAbstractView::waitForSnapshots(const SUMOTime snapshotTime) {1563FXMutexLock lock(mySnapshotsMutex);1564if (mySnapshots.count(snapshotTime) > 0) {1565mySnapshotCondition.wait(mySnapshotsMutex);1566}1567}156815691570SUMOTime1571GUISUMOAbstractView::getCurrentTimeStep() const {1572return 0;1573}157415751576void1577GUISUMOAbstractView::showViewschemeEditor() {1578if (myGUIDialogViewSettings == nullptr) {1579myGUIDialogViewSettings = new GUIDialog_ViewSettings(this, myVisualizationSettings);1580myGUIDialogViewSettings->create();1581} else {1582myGUIDialogViewSettings->setCurrent(myVisualizationSettings);1583}1584setFocus();1585myGUIDialogViewSettings->show();1586}158715881589GUIDialog_EditViewport*1590GUISUMOAbstractView::getViewportEditor() {1591if (myGUIDialogEditViewport == nullptr) {1592myGUIDialogEditViewport = new GUIDialog_EditViewport(this, TLC("Labels", "Edit Viewport"));1593myGUIDialogEditViewport->create();1594}1595updateViewportValues();1596return myGUIDialogEditViewport;1597}159815991600void GUISUMOAbstractView::updateViewportValues() {1601myGUIDialogEditViewport->setValues(myChanger->getZoom(),1602myChanger->getXPos(), myChanger->getYPos(),1603myChanger->getRotation());1604}160516061607void1608GUISUMOAbstractView::showViewportEditor() {1609getViewportEditor(); // make sure it exists;1610Position p(myChanger->getXPos(), myChanger->getYPos(), myChanger->getZPos());1611myGUIDialogEditViewport->setOldValues(p, Position::INVALID, myChanger->getRotation());1612myGUIDialogEditViewport->show();1613}161416151616void1617GUISUMOAbstractView::setViewportFromToRot(const Position& lookFrom, const Position& /* lookAt */, double rotation) {1618myChanger->setViewportFrom(lookFrom.x(), lookFrom.y(), lookFrom.z());1619myChanger->setRotation(rotation);1620update();1621}162216231624void1625GUISUMOAbstractView::copyViewportTo(GUISUMOAbstractView* view) {1626// look straight down1627view->setViewportFromToRot(Position(myChanger->getXPos(), myChanger->getYPos(), myChanger->getZPos()),1628Position(myChanger->getXPos(), myChanger->getYPos(), 0),1629myChanger->getRotation());1630}163116321633bool1634GUISUMOAbstractView::setColorScheme(const std::string&) {1635return true;1636}163716381639const GUIVisualizationSettings&1640GUISUMOAbstractView::getVisualisationSettings() const {1641return *myVisualizationSettings;1642}164316441645GUIVisualizationSettings*1646GUISUMOAbstractView::editVisualisationSettings() const {1647return myVisualizationSettings;1648}164916501651void1652GUISUMOAbstractView::remove(GUIDialog_EditViewport*) {1653myGUIDialogEditViewport = nullptr;1654}165516561657void1658GUISUMOAbstractView::remove(GUIDialog_ViewSettings*) {1659myGUIDialogViewSettings = nullptr;1660}166116621663double1664GUISUMOAbstractView::getGridWidth() const {1665return myGrid->getWidth();1666}166716681669double1670GUISUMOAbstractView::getGridHeight() const {1671return myGrid->getHeight();1672}167316741675void1676GUISUMOAbstractView::startTrack(int /*id*/) {1677}167816791680void1681GUISUMOAbstractView::stopTrack() {1682}168316841685GUIGlID1686GUISUMOAbstractView::getTrackedID() const {1687return GUIGlObject::INVALID_ID;1688}168916901691void1692GUISUMOAbstractView::onGamingClick(Position /*pos*/) {1693}16941695void1696GUISUMOAbstractView::onGamingRightClick(Position /*pos*/) {1697}169816991700std::vector<GUISUMOAbstractView::Decal>&1701GUISUMOAbstractView::getDecals() {1702return myDecals;1703}170417051706FXMutex&1707GUISUMOAbstractView::getDecalsLockMutex() {1708return myDecalsLockMutex;1709}171017111712MFXComboBoxIcon*1713GUISUMOAbstractView::getColoringSchemesCombo() {1714return myGlChildWindowParent->getColoringSchemesCombo();1715}171617171718FXImage*1719GUISUMOAbstractView::checkGDALImage(Decal& d) {1720#ifdef HAVE_GDAL1721GDALAllRegister();1722GDALDataset* poDataset = (GDALDataset*)GDALOpen(d.filename.c_str(), GA_ReadOnly);1723if (poDataset == 0) {1724return 0;1725}1726const int xSize = poDataset->GetRasterXSize();1727const int ySize = poDataset->GetRasterYSize();1728// checking for geodata in the picture and try to adapt position and scale1729if (d.width <= 0.) {1730double adfGeoTransform[6];1731if (poDataset->GetGeoTransform(adfGeoTransform) == CE_None) {1732Position topLeft(adfGeoTransform[0], adfGeoTransform[3]);1733const double horizontalSize = xSize * adfGeoTransform[1];1734const double verticalSize = ySize * adfGeoTransform[5];1735Position bottomRight(topLeft.x() + horizontalSize, topLeft.y() + verticalSize);1736if (GeoConvHelper::getFinal().x2cartesian_const(topLeft) && GeoConvHelper::getFinal().x2cartesian_const(bottomRight)) {1737//WRITE_MESSAGE("proj: " + toString(poDataset->GetProjectionRef()) + " dim: " + toString(d.width) + "," + toString(d.height) + " center: " + toString(d.centerX) + "," + toString(d.centerY));1738} else {1739WRITE_WARNINGF(TL("Could not transform coordinates from WGS84 in decal %, assuming UTM."), d.filename);1740topLeft = topLeft + GeoConvHelper::getFinal().getOffset();1741bottomRight = bottomRight + GeoConvHelper::getFinal().getOffset();1742}1743d.width = bottomRight.x() - topLeft.x();1744d.height = topLeft.y() - bottomRight.y();1745d.centerX = (topLeft.x() + bottomRight.x()) / 2;1746d.centerY = (topLeft.y() + bottomRight.y()) / 2;1747}1748}1749#endif1750if (d.width <= 0.) {1751d.width = getGridWidth();1752d.height = getGridHeight();1753}17541755// trying to read the picture1756#ifdef HAVE_GDAL1757const int picSize = xSize * ySize;1758FXColor* result;1759if (!FXMALLOC(&result, FXColor, picSize)) {1760WRITE_WARNINGF("Could not allocate memory for %.", d.filename);1761return 0;1762}1763for (int j = 0; j < picSize; j++) {1764result[j] = GUIDesignTextColorBlack;1765}1766bool valid = true;1767for (int i = 1; i <= poDataset->GetRasterCount(); i++) {1768GDALRasterBand* poBand = poDataset->GetRasterBand(i);1769int shift = -1;1770if (poBand->GetColorInterpretation() == GCI_RedBand) {1771shift = 0;1772} else if (poBand->GetColorInterpretation() == GCI_GreenBand) {1773shift = 1;1774} else if (poBand->GetColorInterpretation() == GCI_BlueBand) {1775shift = 2;1776} else if (poBand->GetColorInterpretation() == GCI_AlphaBand) {1777shift = 3;1778} else {1779valid = false;1780break;1781}1782assert(xSize == poBand->GetXSize() && ySize == poBand->GetYSize());1783if (poBand->RasterIO(GF_Read, 0, 0, xSize, ySize, ((unsigned char*)result) + shift, xSize, ySize, GDT_Byte, 4, 4 * xSize) == CE_Failure) {1784valid = false;1785break;1786}1787}1788GDALClose(poDataset);1789if (valid) {1790return new FXImage(getApp(), result, IMAGE_OWNED | IMAGE_KEEP | IMAGE_SHMI | IMAGE_SHMP, xSize, ySize);1791}1792FXFREE(&result);1793#endif1794return nullptr;1795}179617971798void1799GUISUMOAbstractView::drawDecals() {1800GLHelper::pushName(0);1801myDecalsLockMutex.lock();1802for (auto& decal : myDecals) {1803if (decal.skip2D || decal.filename.empty()) {1804continue;1805}1806if (!decal.initialised) {1807try {1808FXImage* img = checkGDALImage(decal);1809if (img == nullptr) {1810img = MFXImageHelper::loadImage(getApp(), decal.filename);1811}1812MFXImageHelper::scalePower2(img, GUITexturesHelper::getMaxTextureSize());1813decal.glID = GUITexturesHelper::add(img);1814decal.initialised = true;1815decal.image = img;1816} catch (InvalidArgument& e) {1817WRITE_ERROR("Could not load '" + decal.filename + "'.\n" + e.what());1818decal.skip2D = true;1819}1820}1821GLHelper::pushMatrix();1822if (decal.screenRelative) {1823Position center = screenPos2NetPos((int)decal.centerX, (int)decal.centerY);1824glTranslated(center.x(), center.y(), decal.layer);1825} else {1826glTranslated(decal.centerX, decal.centerY, decal.layer);1827}1828glRotated(decal.rot, 0, 0, 1);1829glColor3d(1, 1, 1);1830double halfWidth = decal.width / 2.;1831double halfHeight = decal.height / 2.;1832if (decal.screenRelative) {1833halfWidth = p2m(halfWidth);1834halfHeight = p2m(halfHeight);1835}1836GUITexturesHelper::drawTexturedBox(decal.glID, -halfWidth, -halfHeight, halfWidth, halfHeight);1837GLHelper::popMatrix();1838}1839myDecalsLockMutex.unlock();1840GLHelper::popName();1841}184218431844void1845GUISUMOAbstractView::openPopupDialog() {1846int x, y;1847FXuint b;1848myApp->getCursorPosition(x, y, b);1849int appX = myApp->getX();1850int popX = x + appX;1851int popY = y + myApp->getY();1852myPopup->setX(popX);1853myPopup->setY(popY);1854myPopup->create();1855myPopup->show();1856// TODO: try to stay on screen even on a right secondary screen in multi-monitor setup1857const int rootWidth = getApp()->getRootWindow()->getWidth();1858const int rootHeight = getApp()->getRootWindow()->getHeight();1859if (popX <= rootWidth) {1860const int maxX = (appX < 0) ? 0 : rootWidth;1861popX = MIN2(popX, maxX - myPopup->getWidth() - 10);1862}1863popY = MIN2(popY, rootHeight - myPopup->getHeight() - 50);1864myPopup->move(popX, popY);1865myPopupPosition = getPositionInformation();1866myChanger->onRightBtnRelease(nullptr);1867setFocus();1868}18691870// ------------ Additional visualisations18711872bool1873GUISUMOAbstractView::addAdditionalGLVisualisation(GUIGlObject* const which) {1874if (myAdditionallyDrawn.find(which) == myAdditionallyDrawn.end()) {1875myAdditionallyDrawn[which] = 1;1876} else {1877myAdditionallyDrawn[which] = myAdditionallyDrawn[which] + 1;1878}1879update();1880return true;1881}188218831884bool1885GUISUMOAbstractView::removeAdditionalGLVisualisation(GUIGlObject* const which) {1886if (myAdditionallyDrawn.find(which) == myAdditionallyDrawn.end()) {1887return false;1888}1889int cnt = myAdditionallyDrawn[which];1890if (cnt == 1) {1891myAdditionallyDrawn.erase(which);1892} else {1893myAdditionallyDrawn[which] = myAdditionallyDrawn[which] - 1;1894}1895update();1896return true;1897}189818991900bool1901GUISUMOAbstractView::isAdditionalGLVisualisationEnabled(GUIGlObject* const which) const {1902if (myAdditionallyDrawn.find(which) == myAdditionallyDrawn.end()) {1903return false;1904} else {1905return true;1906}1907}190819091910Boundary1911GUISUMOAbstractView::applyGLTransform(bool fixRatio) {1912Boundary bound = myChanger->getViewport(fixRatio);1913glMatrixMode(GL_PROJECTION);1914glLoadIdentity();1915// as a rough rule, each GLObject is drawn at z = -GUIGlObjectType1916// thus, objects with a higher value will be closer (drawn on top)1917// // @todo last param should be 0 after modifying all glDraw methods1918glOrtho(0, getWidth(), 0, getHeight(), -GLO_MAX - 1, GLO_MAX + 1);1919glMatrixMode(GL_MODELVIEW);1920glLoadIdentity();1921double scaleX = (double)getWidth() / bound.getWidth();1922double scaleY = (double)getHeight() / bound.getHeight();1923glScaled(scaleX, scaleY, 1);1924glTranslated(-bound.xmin(), -bound.ymin(), 0);1925// rotate around the center of the screen1926//double angle = -90;1927if (myChanger->getRotation() != 0) {1928glTranslated(bound.getCenter().x(), bound.getCenter().y(), 0);1929glRotated(myChanger->getRotation(), 0, 0, 1);1930glTranslated(-bound.getCenter().x(), -bound.getCenter().y(), 0);1931Boundary rotBound;1932double rad = -DEG2RAD(myChanger->getRotation());1933rotBound.add(Position(bound.xmin(), bound.ymin()).rotateAround2D(rad, bound.getCenter()));1934rotBound.add(Position(bound.xmin(), bound.ymax()).rotateAround2D(rad, bound.getCenter()));1935rotBound.add(Position(bound.xmax(), bound.ymin()).rotateAround2D(rad, bound.getCenter()));1936rotBound.add(Position(bound.xmax(), bound.ymax()).rotateAround2D(rad, bound.getCenter()));1937bound = rotBound;1938}1939myVisualizationSettings->angle = myChanger->getRotation();1940return bound;1941}194219431944double1945GUISUMOAbstractView::getDelay() const {1946return myApp->getDelay();1947}194819491950void1951GUISUMOAbstractView::setDelay(double delay) {1952myApp->setDelay(delay);1953}195419551956void1957GUISUMOAbstractView::setBreakpoints(const std::vector<SUMOTime>& breakpoints) {1958myApp->setBreakpoints(breakpoints);1959}196019611962void1963GUISUMOAbstractView::buildMinMaxRainbow(const GUIVisualizationSettings& s, GUIColorScheme& scheme,1964const GUIVisualizationRainbowSettings& rs, double minValue, double maxValue, bool hasMissingData) {1965if (rs.hideMin && rs.hideMax && minValue == std::numeric_limits<double>::infinity()) {1966minValue = rs.minThreshold;1967maxValue = rs.maxThreshold;1968}1969if (rs.fixRange) {1970if (rs.hideMin) {1971minValue = rs.minThreshold;1972}1973if (rs.hideMax) {1974maxValue = rs.maxThreshold;1975}1976}1977if (minValue != std::numeric_limits<double>::infinity()) {1978scheme.clear();1979// add new thresholds1980if (scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_EDGEDATA_NUMERICAL1981|| scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_EDGE_PARAM_NUMERICAL1982|| scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_LANE_PARAM_NUMERICAL1983|| scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_DATA_ATTRIBUTE_NUMERICAL1984|| scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_PARAM_NUMERICAL1985|| hasMissingData) {1986scheme.addColor(s.COL_MISSING_DATA, s.MISSING_DATA, "missing data");1987}1988if (rs.hideMin && !rs.fixRange) {1989const double rawRange = maxValue - minValue;1990minValue = MAX2(rs.minThreshold + MIN2(1.0, rawRange / 100.0), minValue);1991scheme.addColor(RGBColor(204, 204, 204), rs.minThreshold);1992}1993if (rs.hideMax && !rs.fixRange) {1994const double rawRange = maxValue - minValue;1995maxValue = MIN2(rs.maxThreshold - MIN2(1.0, rawRange / 100.0), maxValue);1996scheme.addColor(RGBColor(204, 204, 204), rs.maxThreshold);1997}1998const double range = maxValue - minValue;1999scheme.addColor(rs.colors.front(), minValue);2000const int steps = (int)rs.colors.size() - 1;2001if (rs.setNeutral) {2002const int steps1 = steps / 2;2003const int steps2 = steps - steps1;2004const double range1 = rs.neutralThreshold - minValue;2005const double range2 = maxValue - rs.neutralThreshold;2006for (int i = 1; i < steps1; i++) {2007scheme.addColor(rs.colors[i], (minValue + range1 * i / steps1));2008}2009scheme.addColor(rs.colors[steps1], rs.neutralThreshold);2010for (int i = 1; i < steps2; i++) {2011scheme.addColor(rs.colors[steps1 + i], (rs.neutralThreshold + range2 * i / steps2));2012}2013} else {2014for (int i = 1; i < steps; i++) {2015scheme.addColor(rs.colors[i], (minValue + range * i / steps));2016}2017}2018scheme.addColor(rs.colors.back(), maxValue);2019}2020}202120222023GUISUMOAbstractView::LayerObject::LayerObject(double layer, GUIGlObject* object) :2024myGLObject(object) {2025first = layer;2026second.first = object->getType();2027second.second = object->getMicrosimID();2028}202920302031GUISUMOAbstractView::LayerObject::LayerObject(GUIGlObject* object) :2032myGLObject(object) {2033first = object->getType();2034second.first = object->getType();2035second.second = object->getMicrosimID();2036}203720382039GUIGlObject*2040GUISUMOAbstractView::LayerObject::getGLObject() const {2041return myGLObject;2042}20432044/****************************************************************************/204520462047