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 GUISUMOAbstractView.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Laura Bieker
19 : /// @author Andreas Gaubatz
20 : /// @date Sept 2002
21 : ///
22 : // The base class for a view
23 : /****************************************************************************/
24 : #include <config.h>
25 :
26 : #include <iostream>
27 : #include <utility>
28 : #include <cmath>
29 : #include <cassert>
30 : #include <limits>
31 : #include <fxkeys.h>
32 : #ifdef HAVE_GL2PS
33 : #include <gl2ps.h>
34 : #endif
35 : #include <utils/foxtools/MFXSingleEventThread.h>
36 : #include <utils/foxtools/MFXCheckableButton.h>
37 : #include <utils/foxtools/MFXImageHelper.h>
38 : #include <utils/common/RGBColor.h>
39 : #include <utils/common/ToString.h>
40 : #include <utils/common/StringUtils.h>
41 : #include <utils/common/MsgHandler.h>
42 : #include <utils/common/SysUtils.h>
43 : #include <utils/gui/windows/GUIAppEnum.h>
44 : #include <utils/gui/globjects/GUIGLObjectPopupMenu.h>
45 : #include <utils/gui/globjects/GUICursorDialog.h>
46 : #include <utils/gui/images/GUITexturesHelper.h>
47 : #include <utils/gui/div/GUIGlobalSelection.h>
48 : #include <utils/gui/div/GLHelper.h>
49 : #include <utils/gui/globjects/GUIGlObjectStorage.h>
50 : #include <utils/gui/globjects/GUIGlObject.h>
51 : #include <utils/shapes/PointOfInterest.h>
52 : #include <utils/gui/globjects/GUIPointOfInterest.h>
53 : #include <utils/gui/globjects/GUIPolygon.h>
54 : #include <utils/gui/windows/GUIDialog_ViewSettings.h>
55 : #include <utils/geom/GeoConvHelper.h>
56 : #include <utils/geom/GeomHelper.h>
57 : #include <utils/gui/settings/GUICompleteSchemeStorage.h>
58 : #include <utils/gui/globjects/GLIncludes.h>
59 : #include <utils/gui/settings/GUIVisualizationSettings.h>
60 : #include <foreign/fontstash/fontstash.h>
61 : #include <utils/gui/cursors/GUICursorSubSys.h>
62 : #include <utils/options/OptionsCont.h>
63 :
64 : #include <unordered_set>
65 :
66 : #include "GUISUMOAbstractView.h"
67 : #include "GUIMainWindow.h"
68 : #include "GUIGlChildWindow.h"
69 : #include "GUIDanielPerspectiveChanger.h"
70 : #include "GUIDialog_EditViewport.h"
71 :
72 : #ifdef HAVE_GDAL
73 : #ifdef _MSC_VER
74 : #pragma warning(push)
75 : #pragma warning(disable: 4435 5219 5220)
76 : #endif
77 : #if __GNUC__ > 3
78 : #pragma GCC diagnostic push
79 : #pragma GCC diagnostic ignored "-Wpedantic"
80 : #endif
81 : #include <gdal_priv.h>
82 : #if __GNUC__ > 3
83 : #pragma GCC diagnostic pop
84 : #endif
85 : #ifdef _MSC_VER
86 : #pragma warning(pop)
87 : #endif
88 : #endif
89 :
90 :
91 : // ===========================================================================
92 : // debug constants
93 : // ===========================================================================
94 : //#define DEBUG_SNAPSHOT
95 :
96 : // ===========================================================================
97 : // static members
98 : // ===========================================================================
99 :
100 : const double GUISUMOAbstractView::SENSITIVITY = 0.1; // meters
101 :
102 :
103 : // ===========================================================================
104 : // member method definitions
105 : // ===========================================================================
106 : /* -------------------------------------------------------------------------
107 : * GUISUMOAbstractView - FOX callback mapping
108 : * ----------------------------------------------------------------------- */
109 : FXDEFMAP(GUISUMOAbstractView) GUISUMOAbstractViewMap[] = {
110 : FXMAPFUNC(SEL_CONFIGURE, 0, GUISUMOAbstractView::onConfigure),
111 : FXMAPFUNC(SEL_PAINT, 0, GUISUMOAbstractView::onPaint),
112 : FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, GUISUMOAbstractView::onLeftBtnPress),
113 : FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, GUISUMOAbstractView::onLeftBtnRelease),
114 : FXMAPFUNC(SEL_MIDDLEBUTTONPRESS, 0, GUISUMOAbstractView::onMiddleBtnPress),
115 : FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE, 0, GUISUMOAbstractView::onMiddleBtnRelease),
116 : FXMAPFUNC(SEL_RIGHTBUTTONPRESS, 0, GUISUMOAbstractView::onRightBtnPress),
117 : FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, 0, GUISUMOAbstractView::onRightBtnRelease),
118 : FXMAPFUNC(SEL_DOUBLECLICKED, 0, GUISUMOAbstractView::onDoubleClicked),
119 : FXMAPFUNC(SEL_MOUSEWHEEL, 0, GUISUMOAbstractView::onMouseWheel),
120 : FXMAPFUNC(SEL_MOTION, 0, GUISUMOAbstractView::onMouseMove),
121 : FXMAPFUNC(SEL_LEAVE, 0, GUISUMOAbstractView::onMouseLeft),
122 : FXMAPFUNC(SEL_KEYPRESS, 0, GUISUMOAbstractView::onKeyPress),
123 : FXMAPFUNC(SEL_KEYRELEASE, 0, GUISUMOAbstractView::onKeyRelease),
124 : FXMAPFUNC(SEL_COMMAND, MID_CLOSE_LANE, GUISUMOAbstractView::onCmdCloseLane),
125 : FXMAPFUNC(SEL_COMMAND, MID_CLOSE_EDGE, GUISUMOAbstractView::onCmdCloseEdge),
126 : FXMAPFUNC(SEL_COMMAND, MID_ADD_REROUTER, GUISUMOAbstractView::onCmdAddRerouter),
127 : FXMAPFUNC(SEL_COMMAND, MID_REACHABILITY, GUISUMOAbstractView::onCmdShowReachability),
128 : FXMAPFUNC(SEL_COMMAND, MID_REACHABILITY, GUISUMOAbstractView::onCmdShowReachability),
129 : FXMAPFUNC(SEL_CHANGED, MID_SIMPLE_VIEW_COLORCHANGE, GUISUMOAbstractView::onVisualizationChange),
130 : };
131 :
132 :
133 1827538 : FXIMPLEMENT_ABSTRACT(GUISUMOAbstractView, FXGLCanvas, GUISUMOAbstractViewMap, ARRAYNUMBER(GUISUMOAbstractViewMap))
134 :
135 :
136 : /* -------------------------------------------------------------------------
137 : * GUISUMOAbstractView - methods
138 : * ----------------------------------------------------------------------- */
139 7103 : GUISUMOAbstractView::GUISUMOAbstractView(FXComposite* p, GUIMainWindow& app, GUIGlChildWindow* parent, const SUMORTree& grid, FXGLVisual* glVis, FXGLCanvas* share) :
140 : FXGLCanvas(p, glVis, share, p, MID_GLCANVAS, LAYOUT_SIDE_TOP | LAYOUT_FILL_X | LAYOUT_FILL_Y, 0, 0, 0, 0),
141 7103 : myApp(&app),
142 7103 : myGlChildWindowParent(parent),
143 7103 : myGrid(&grid),
144 7103 : myMouseHotspotX(app.getDefaultCursor()->getHotX()),
145 7103 : myMouseHotspotY(app.getDefaultCursor()->getHotY()),
146 7103 : myWindowCursorPositionX(getWidth() / 2),
147 7103 : myWindowCursorPositionY(getHeight() / 2) {
148 7103 : setTarget(this);
149 7103 : enable();
150 7103 : flags |= FLAG_ENABLED;
151 7103 : myChanger = new GUIDanielPerspectiveChanger(*this, *myGrid);
152 7103 : myVisualizationSettings = &gSchemeStorage.getDefault();
153 7103 : myVisualizationSettings->gaming = myApp->isGaming();
154 7103 : gSchemeStorage.setViewport(this);
155 7103 : myDecals = gSchemeStorage.getDecals();
156 7103 : }
157 :
158 :
159 7084 : GUISUMOAbstractView::~GUISUMOAbstractView() {
160 7084 : gSchemeStorage.setDefault(myVisualizationSettings->name);
161 7084 : gSchemeStorage.saveViewport(myChanger->getXPos(), myChanger->getYPos(), myChanger->getZPos(), myChanger->getRotation());
162 7084 : gSchemeStorage.saveDecals(myDecals);
163 7084 : delete myPopup;
164 7084 : delete myChanger;
165 7084 : delete myGUIDialogEditViewport;
166 7084 : delete myGUIDialogViewSettings;
167 : // cleanup decals
168 7091 : for (auto& decal : myDecals) {
169 7 : delete decal.image;
170 : }
171 : // remove all elements
172 7084 : for (auto& additional : myAdditionallyDrawn) {
173 0 : additional.first->removeActiveAddVisualisation(this, ~0);
174 : }
175 14168 : }
176 :
177 :
178 : bool
179 0 : GUISUMOAbstractView::isInEditMode() {
180 0 : return myInEditMode;
181 : }
182 :
183 :
184 : GUIPerspectiveChanger&
185 6 : GUISUMOAbstractView::getChanger() const {
186 6 : return *myChanger;
187 : }
188 :
189 :
190 : void
191 0 : GUISUMOAbstractView::updateToolTip() {
192 0 : if (myGlChildWindowParent->getGUIMainWindowParent()->getStaticTooltipView()->isStaticToolTipEnabled()) {
193 0 : update();
194 : }
195 0 : }
196 :
197 :
198 : Position
199 0 : GUISUMOAbstractView::getPositionInformation() const {
200 0 : return screenPos2NetPos(myWindowCursorPositionX, myWindowCursorPositionY);
201 : }
202 :
203 :
204 : Position
205 0 : GUISUMOAbstractView::snapToActiveGrid(const Position& pos, bool snapXY) const {
206 0 : Position result = pos;
207 0 : if (myVisualizationSettings->showGrid) {
208 0 : if (snapXY) {
209 0 : const double xRest = std::fmod(pos.x(), myVisualizationSettings->gridXSize) + (pos.x() < 0 ? myVisualizationSettings->gridXSize : 0);
210 0 : const double yRest = std::fmod(pos.y(), myVisualizationSettings->gridYSize) + (pos.y() < 0 ? myVisualizationSettings->gridYSize : 0);
211 0 : result.setx(pos.x() - xRest + (xRest < myVisualizationSettings->gridXSize * 0.5 ? 0 : myVisualizationSettings->gridXSize));
212 0 : result.sety(pos.y() - yRest + (yRest < myVisualizationSettings->gridYSize * 0.5 ? 0 : myVisualizationSettings->gridYSize));
213 : } else {
214 : // snapZToActiveGrid uses grid Y Size
215 0 : const double zRest = std::fmod(pos.z(), myVisualizationSettings->gridYSize) + (pos.z() < 0 ? myVisualizationSettings->gridYSize : 0);
216 0 : result.setz(pos.z() - zRest + (zRest < myVisualizationSettings->gridYSize * 0.5 ? 0 : myVisualizationSettings->gridYSize));
217 : }
218 : }
219 0 : return result;
220 : }
221 :
222 :
223 : Position
224 0 : GUISUMOAbstractView::screenPos2NetPos(int x, int y) const {
225 0 : Boundary bound = myChanger->getViewport();
226 0 : double xNet = bound.xmin() + bound.getWidth() * x / getWidth();
227 : // cursor origin is in the top-left corner
228 0 : double yNet = bound.ymin() + bound.getHeight() * (getHeight() - y) / getHeight();
229 : // rotate around the viewport center
230 0 : if (myChanger->getRotation() != 0) {
231 0 : return Position(xNet, yNet).rotateAround2D(-DEG2RAD(myChanger->getRotation()), bound.getCenter());
232 : } else {
233 : return Position(xNet, yNet);
234 : }
235 0 : }
236 :
237 :
238 : void
239 35 : GUISUMOAbstractView::addDecals(const std::vector<Decal>& decals) {
240 35 : myDecals.insert(myDecals.end(), decals.begin(), decals.end());
241 35 : }
242 :
243 :
244 : void
245 0 : GUISUMOAbstractView::updatePositionInformationLabel() const {
246 0 : Position pos = getPositionInformation();
247 : // set cartesian position
248 0 : myApp->getCartesianLabel()->setText(("x:" + toString(pos.x()) + ", y:" + toString(pos.y())).c_str());
249 : // set geo position
250 0 : GeoConvHelper::getFinal().cartesian2geo(pos);
251 0 : if (GeoConvHelper::getFinal().usingGeoProjection()) {
252 0 : myApp->getGeoLabel()->setText(("lat:" + toString(pos.y(), gPrecisionGeo) + ", lon:" + toString(pos.x(), gPrecisionGeo)).c_str());
253 : } else {
254 0 : myApp->getGeoLabel()->setText(TL("(No projection defined)"));
255 : }
256 : // if enabled, set test position
257 0 : if (myApp->getTestFrame()) {
258 0 : if (OptionsCont::getOptions().getBool("gui-testing")) {
259 0 : myApp->getTestFrame()->show();
260 : // adjust cursor position (24,25) to show exactly the same position as in function netedit.leftClick(match, X, Y)
261 0 : myApp->getTestLabel()->setText(("Test: x:" + toString(getWindowCursorPosition().x() - 24.0) + " y:" + toString(getWindowCursorPosition().y() - 25.0)).c_str());
262 : } else {
263 0 : myApp->getTestFrame()->hide();
264 : }
265 : }
266 0 : }
267 :
268 :
269 : int
270 0 : GUISUMOAbstractView::doPaintGL(int /*mode*/, const Boundary& /*boundary*/) {
271 0 : return 0;
272 : }
273 :
274 :
275 : void
276 6605 : GUISUMOAbstractView::doInit() {
277 6605 : }
278 :
279 :
280 : Boundary
281 0 : GUISUMOAbstractView::getVisibleBoundary() const {
282 0 : return myChanger->getViewport();
283 : }
284 :
285 :
286 : bool
287 18 : GUISUMOAbstractView::is3DView() const {
288 18 : return false;
289 : }
290 :
291 :
292 0 : void GUISUMOAbstractView::zoom2Pos(Position& /* camera */, Position& /* lookAt */, double /* zoom */) {
293 0 : }
294 :
295 :
296 : void
297 532603 : GUISUMOAbstractView::paintGL() {
298 : // reset debug counters
299 532603 : GLHelper::resetMatrixCounter();
300 532603 : GLHelper::resetVertexCounter();
301 532603 : if (getWidth() == 0 || getHeight() == 0) {
302 0 : return;
303 : }
304 532603 : const long start = SysUtils::getCurrentMillis();
305 :
306 532603 : if (getTrackedID() != GUIGlObject::INVALID_ID) {
307 0 : centerTo(getTrackedID(), false);
308 : }
309 : // draw
310 2130412 : glClearColor(
311 532603 : myVisualizationSettings->backgroundColor.red() / 255.f,
312 532603 : myVisualizationSettings->backgroundColor.green() / 255.f,
313 532603 : myVisualizationSettings->backgroundColor.blue() / 255.f,
314 532603 : myVisualizationSettings->backgroundColor.alpha() / 255.f);
315 532603 : glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
316 532603 : glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
317 :
318 532603 : if (myVisualizationSettings->dither) {
319 0 : glEnable(GL_DITHER);
320 : } else {
321 532603 : glDisable(GL_DITHER);
322 : }
323 532603 : glEnable(GL_BLEND);
324 532603 : glDisable(GL_LINE_SMOOTH);
325 :
326 532603 : Boundary bound = applyGLTransform();
327 532603 : doPaintGL(GL_RENDER, bound);
328 532602 : GLHelper::checkCounterMatrix();
329 532602 : GLHelper::checkCounterName();
330 532602 : displayLegends();
331 532602 : const long end = SysUtils::getCurrentMillis();
332 532602 : myFrameDrawTime = end - start;
333 532602 : if (myVisualizationSettings->fps) {
334 0 : drawFPS();
335 : }
336 : // check if show tooltip
337 532602 : if (myGlChildWindowParent->getGUIMainWindowParent()->getStaticTooltipView()->isStaticToolTipEnabled()) {
338 0 : showToolTipFor(getToolTipID());
339 : } else {
340 532602 : myGlChildWindowParent->getGUIMainWindowParent()->getStaticTooltipView()->hideStaticToolTip();
341 : }
342 532602 : swapBuffers();
343 532602 : }
344 :
345 :
346 : long
347 0 : GUISUMOAbstractView::onCmdCloseLane(FXObject*, FXSelector, void*) {
348 0 : return 1;
349 : }
350 :
351 :
352 : long
353 0 : GUISUMOAbstractView::onCmdCloseEdge(FXObject*, FXSelector, void*) {
354 0 : return 1;
355 : }
356 :
357 :
358 : long
359 0 : GUISUMOAbstractView::onCmdAddRerouter(FXObject*, FXSelector, void*) {
360 0 : return 1;
361 : }
362 :
363 :
364 : long
365 0 : GUISUMOAbstractView::onCmdShowReachability(FXObject*, FXSelector, void*) {
366 0 : return 1;
367 : }
368 :
369 :
370 : long
371 0 : GUISUMOAbstractView::onVisualizationChange(FXObject*, FXSelector, void*) {
372 0 : return 1;
373 : }
374 :
375 :
376 : GUILane*
377 0 : GUISUMOAbstractView::getLaneUnderCursor() {
378 0 : return nullptr;
379 : }
380 :
381 :
382 : GUIGlID
383 0 : GUISUMOAbstractView::getToolTipID() {
384 0 : return getObjectUnderCursor();
385 : }
386 :
387 :
388 : GUIGlID
389 0 : GUISUMOAbstractView::getObjectUnderCursor() {
390 0 : return getObjectAtPosition(getPositionInformation());
391 : }
392 :
393 :
394 : std::vector<GUIGlID>
395 0 : GUISUMOAbstractView::getObjectsUnderCursor() {
396 0 : return getObjectsAtPosition(getPositionInformation(), SENSITIVITY);
397 : }
398 :
399 :
400 :
401 : std::vector<GUIGlObject*>
402 0 : GUISUMOAbstractView::getGUIGlObjectsUnderCursor() {
403 0 : return getGUIGlObjectsAtPosition(getPositionInformation(), SENSITIVITY);
404 : }
405 :
406 :
407 : std::vector<GUIGlObject*>
408 0 : GUISUMOAbstractView::getGUIGlObjectsUnderSnappedCursor() {
409 0 : return getGUIGlObjectsAtPosition(snapToActiveGrid(getPositionInformation()), SENSITIVITY);
410 : }
411 :
412 :
413 : GUIGlID
414 0 : GUISUMOAbstractView::getObjectAtPosition(Position pos) {
415 : // calculate a boundary for the given position
416 0 : Boundary positionBoundary;
417 0 : positionBoundary.add(pos);
418 0 : positionBoundary.grow(SENSITIVITY);
419 0 : const std::vector<GUIGlID> ids = getObjectsInBoundary(positionBoundary);
420 : // Interpret results
421 : int idMax = 0;
422 : double maxLayer = -std::numeric_limits<double>::max();
423 : // iterate over obtained GUIGlIDs
424 0 : for (const auto& i : ids) {
425 : // obtain GUIGlObject
426 0 : GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(i);
427 : // check that GUIGlObject exist
428 0 : if (o == nullptr) {
429 0 : continue;
430 : }
431 : // check that GUIGlObject isn't the network
432 0 : if (o->getGlID() == 0) {
433 0 : continue;
434 : }
435 : //std::cout << "point selection hit " << o->getMicrosimID() << "\n";
436 0 : double layer = o->getClickPriority();
437 : // check whether the current object is above a previous one
438 0 : if (layer > maxLayer) {
439 0 : idMax = i;
440 : maxLayer = layer;
441 : }
442 : // unblock object
443 0 : GUIGlObjectStorage::gIDStorage.unblockObject(i);
444 : }
445 0 : return idMax;
446 0 : }
447 :
448 :
449 : std::vector<GUIGlID>
450 0 : GUISUMOAbstractView::getObjectsAtPosition(Position pos, double radius) {
451 : // declare result vector
452 : std::vector<GUIGlID> result;
453 : // calculate boundary
454 0 : Boundary selection;
455 0 : selection.add(pos);
456 0 : selection.grow(radius);
457 : // obtain GUIGlID of objects in boundary
458 0 : const std::vector<GUIGlID> ids = getObjectsInBoundary(selection);
459 : // iterate over obtained GUIGlIDs
460 0 : for (const auto& i : ids) {
461 : // obtain GUIGlObject
462 0 : GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(i);
463 : // check that GUIGlObject exist
464 0 : if (o == nullptr) {
465 0 : continue;
466 : }
467 : // check that GUIGlObject isn't the network
468 0 : if (o->getGlID() == 0) {
469 0 : continue;
470 : }
471 : //std::cout << "point selection hit " << o->getMicrosimID() << "\n";
472 : GUIGlObjectType type = o->getType();
473 : // avoid network
474 0 : if (type != GLO_NETWORK) {
475 0 : result.push_back(i);
476 : }
477 : // unblock object
478 0 : GUIGlObjectStorage::gIDStorage.unblockObject(i);
479 : }
480 0 : return result;
481 0 : }
482 :
483 :
484 : std::vector<GUIGlObject*>
485 0 : GUISUMOAbstractView::getGUIGlObjectsAtPosition(Position pos, double radius) {
486 : // declare result vector
487 : std::vector<GUIGlObject*> result;
488 : // calculate boundary
489 0 : Boundary selection;
490 0 : selection.add(pos);
491 0 : selection.grow(radius);
492 : // obtain GUIGlID of objects in boundary
493 0 : const std::vector<GUIGlID> ids = getObjectsInBoundary(selection);
494 : // iterate over obtained GUIGlIDs
495 0 : for (const auto& i : ids) {
496 : // obtain GUIGlObject
497 0 : GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(i);
498 : // check that GUIGlObject exist
499 0 : if (o == nullptr) {
500 0 : continue;
501 : }
502 : // check that GUIGlObject isn't the network
503 0 : if (o->getGlID() == 0) {
504 0 : continue;
505 : }
506 0 : result.push_back(o);
507 : // unblock object
508 0 : GUIGlObjectStorage::gIDStorage.unblockObject(i);
509 : }
510 0 : return result;
511 0 : }
512 :
513 :
514 : std::vector<GUIGlID>
515 0 : GUISUMOAbstractView::getObjectsInBoundary(Boundary bound) {
516 : const int NB_HITS_MAX = 1024 * 1024;
517 : // Prepare the selection mode
518 : static GUIGlID hits[NB_HITS_MAX];
519 : static GLint nb_hits = 0;
520 0 : glSelectBuffer(NB_HITS_MAX, hits);
521 0 : glInitNames();
522 :
523 0 : myVisualizationSettings->scale = m2p(SUMO_const_laneWidth);
524 0 : Boundary oldViewPort = myChanger->getViewport(false); // backup the actual viewPort
525 0 : myChanger->setViewport(bound);
526 0 : bound = applyGLTransform(false);
527 : // enable draw for selecting (to draw objects with less details)
528 0 : myVisualizationSettings->drawForRectangleSelection = true;
529 0 : int hits2 = doPaintGL(GL_SELECT, bound);
530 : // reset flags
531 0 : myVisualizationSettings->drawForRectangleSelection = false;
532 : // Get the results
533 0 : nb_hits = glRenderMode(GL_RENDER);
534 0 : if (nb_hits == -1) {
535 0 : myApp->setStatusBarText("Selection in boundary failed. Try to select fewer than " + toString(hits2) + " items");
536 : }
537 : std::vector<GUIGlID> result;
538 : GLuint numNames;
539 : GLuint* ptr = hits;
540 0 : for (int i = 0; i < nb_hits; ++i) {
541 0 : numNames = *ptr;
542 0 : ptr += 3;
543 0 : for (int j = 0; j < (int)numNames; j++) {
544 0 : result.push_back(*ptr);
545 0 : ptr++;
546 : }
547 : }
548 : // switch viewport back to normal
549 0 : myChanger->setViewport(oldViewPort);
550 0 : return result;
551 0 : }
552 :
553 :
554 : std::vector<GUIGlObject*>
555 0 : GUISUMOAbstractView::filterInternalLanes(const std::vector<GUIGlObject*>& objects) const {
556 : // count number of internal lanes
557 : size_t internalLanes = 0;
558 0 : for (const auto& object : objects) {
559 0 : if ((object->getType() == GLO_LANE) && (object->getMicrosimID().find(':') != std::string::npos)) {
560 0 : internalLanes++;
561 : }
562 : }
563 : // if all objects are internal lanes, return it all
564 0 : if (objects.size() == internalLanes || !myVisualizationSettings->drawJunctionShape) {
565 0 : return objects;
566 : }
567 : // in other case filter internal lanes
568 : std::vector<GUIGlObject*> filteredObjects;
569 0 : for (const auto& object : objects) {
570 0 : if ((object->getType() == GLO_LANE) && (object->getMicrosimID().find(':') != std::string::npos)) {
571 0 : continue;
572 : }
573 0 : filteredObjects.push_back(object);
574 : }
575 : return filteredObjects;
576 0 : }
577 :
578 :
579 : bool
580 0 : GUISUMOAbstractView::showToolTipFor(const GUIGlID idToolTip) {
581 0 : if (idToolTip != GUIGlObject::INVALID_ID) {
582 0 : const GUIGlObject* object = GUIGlObjectStorage::gIDStorage.getObjectBlocking(idToolTip);
583 0 : if (object != nullptr) {
584 0 : myGlChildWindowParent->getGUIMainWindowParent()->getStaticTooltipView()->showStaticToolTip(object->getFullName().c_str());
585 0 : return true;
586 : }
587 : }
588 : // nothing to show
589 0 : myGlChildWindowParent->getGUIMainWindowParent()->getStaticTooltipView()->hideStaticToolTip();
590 0 : return false;
591 : }
592 :
593 :
594 : void
595 0 : GUISUMOAbstractView::paintGLGrid() const {
596 : // obtain minimum grid
597 0 : const double minimumSizeGrid = (myVisualizationSettings->gridXSize < myVisualizationSettings->gridYSize) ? myVisualizationSettings->gridXSize : myVisualizationSettings->gridYSize;
598 : // Check if the distance is enough to draw grid
599 0 : if (myVisualizationSettings->scale * myVisualizationSettings->addSize.getExaggeration(*myVisualizationSettings, nullptr) >= (25 / minimumSizeGrid)) {
600 0 : glEnable(GL_DEPTH_TEST);
601 0 : glLineWidth(1);
602 : // get multiplication values (2 is the margin)
603 0 : const int multXmin = (int)(myChanger->getViewport().xmin() / myVisualizationSettings->gridXSize) - 2;
604 0 : const int multYmin = (int)(myChanger->getViewport().ymin() / myVisualizationSettings->gridYSize) - 2;
605 0 : const int multXmax = (int)(myChanger->getViewport().xmax() / myVisualizationSettings->gridXSize) + 2;
606 0 : const int multYmax = (int)(myChanger->getViewport().ymax() / myVisualizationSettings->gridYSize) + 2;
607 : // obtain references
608 0 : const double xmin = myVisualizationSettings->gridXSize * multXmin;
609 0 : const double ymin = myVisualizationSettings->gridYSize * multYmin;
610 0 : const double xmax = myVisualizationSettings->gridXSize * multXmax;
611 0 : const double ymax = myVisualizationSettings->gridYSize * multYmax;
612 : double xp = xmin;
613 : double yp = ymin;
614 : // move drawing matrix
615 0 : glTranslated(0, 0, .55);
616 0 : glColor3d(0.5, 0.5, 0.5);
617 : // draw horizontal lines
618 0 : glBegin(GL_LINES);
619 0 : while (yp <= ymax) {
620 0 : glVertex2d(xmin, yp);
621 0 : glVertex2d(xmax, yp);
622 0 : yp += myVisualizationSettings->gridYSize;
623 : }
624 : // draw vertical lines
625 0 : while (xp <= xmax) {
626 0 : glVertex2d(xp, ymin);
627 0 : glVertex2d(xp, ymax);
628 0 : xp += myVisualizationSettings->gridXSize;
629 : }
630 0 : glEnd();
631 0 : glTranslated(0, 0, -.55);
632 : }
633 0 : }
634 :
635 :
636 : void
637 532904 : GUISUMOAbstractView::displayLegend() {
638 : // compute the scale bar length
639 : int length = 1;
640 532904 : const std::string text("10000000000");
641 : int noDigits = 1;
642 532904 : int pixelSize = (int) m2p((double) length);
643 1752743 : while (pixelSize <= 20) {
644 1219839 : length *= 10;
645 1219839 : noDigits++;
646 1219839 : if (noDigits > (int)text.length()) {
647 : return;
648 : }
649 1219839 : pixelSize = (int) m2p((double) length);
650 : }
651 532904 : glLineWidth(1.0);
652 :
653 532904 : glMatrixMode(GL_PROJECTION);
654 532904 : GLHelper::pushMatrix();
655 532904 : glLoadIdentity();
656 532904 : glMatrixMode(GL_MODELVIEW);
657 532904 : GLHelper::pushMatrix();
658 532904 : glLoadIdentity();
659 :
660 : // draw the scale bar
661 : const double z = -1;
662 532904 : glDisable(GL_TEXTURE_2D);
663 532904 : glDisable(GL_ALPHA_TEST);
664 532904 : glDisable(GL_BLEND);
665 532904 : glEnable(GL_DEPTH_TEST);
666 532904 : GLHelper::pushMatrix();
667 532904 : glTranslated(0, 0, z);
668 :
669 532904 : double len = (double) pixelSize / (double)(getWidth() - 1) * (double) 2.0;
670 532904 : glColor3d(0, 0, 0);
671 532904 : double o = double(15) / double(getHeight());
672 532904 : double o2 = o + o;
673 532904 : double oo = double(5) / double(getHeight());
674 532904 : glBegin(GL_LINES);
675 : // vertical
676 532904 : glVertex2d(-.98, -1. + o);
677 532904 : glVertex2d(-.98 + len, -1. + o);
678 : // tick at begin
679 532904 : glVertex2d(-.98, -1. + o);
680 532904 : glVertex2d(-.98, -1. + o2);
681 : // tick at end
682 532904 : glVertex2d(-.98 + len, -1. + o);
683 532904 : glVertex2d(-.98 + len, -1. + o2);
684 532904 : glEnd();
685 532904 : GLHelper::popMatrix();
686 :
687 532904 : const double fontHeight = 0.1 * 300. / getHeight();
688 532904 : const double fontWidth = 0.1 * 300. / getWidth();
689 : // draw 0
690 1065808 : GLHelper::drawText("0", Position(-.99, -0.99 + o2 + oo), z, fontHeight, RGBColor::BLACK, 0, FONS_ALIGN_LEFT, fontWidth);
691 :
692 : // draw current scale
693 1598712 : GLHelper::drawText((text.substr(0, noDigits) + "m").c_str(), Position(-.99 + len, -0.99 + o2 + oo), z, fontHeight, RGBColor::BLACK, 0, FONS_ALIGN_LEFT, fontWidth);
694 :
695 : // restore matrices
696 532904 : glMatrixMode(GL_PROJECTION);
697 532904 : GLHelper::popMatrix();
698 532904 : glMatrixMode(GL_MODELVIEW);
699 532904 : GLHelper::popMatrix();
700 : }
701 :
702 : void
703 532904 : GUISUMOAbstractView::displayLegends() {
704 532904 : if (myVisualizationSettings->showSizeLegend) {
705 532904 : displayLegend();
706 : }
707 532904 : if (myVisualizationSettings->showColorLegend) {
708 0 : displayColorLegend(myVisualizationSettings->getLaneEdgeScheme(), false);
709 : }
710 532904 : if (myVisualizationSettings->showVehicleColorLegend) {
711 0 : displayColorLegend(myVisualizationSettings->vehicleColorer.getScheme(), true);
712 : }
713 532904 : }
714 :
715 : void
716 0 : GUISUMOAbstractView::displayColorLegend(const GUIColorScheme& scheme, bool leftSide) {
717 : // compute the scale bar length
718 0 : glLineWidth(1.0);
719 0 : glMatrixMode(GL_PROJECTION);
720 0 : GLHelper::pushMatrix();
721 0 : glLoadIdentity();
722 0 : glMatrixMode(GL_MODELVIEW);
723 0 : GLHelper::pushMatrix();
724 0 : glLoadIdentity();
725 :
726 : const double z = -1;
727 0 : glEnable(GL_DEPTH_TEST);
728 0 : glEnable(GL_BLEND);
729 0 : GLHelper::pushMatrix();
730 0 : glTranslated(0, 0, z);
731 :
732 : const bool fixed = scheme.isFixed();
733 0 : const int numColors = (int)scheme.getColors().size();
734 :
735 : // vertical
736 : double right = 0.98;
737 : double left = 0.95;
738 : double textX = left - 0.01;
739 : double textDir = 1;
740 : FONSalign textAlign = FONS_ALIGN_RIGHT;
741 : const double top = -0.7;
742 : const double bot = 0.9;
743 0 : const double dy = (top - bot) / numColors;
744 0 : const double bot2 = fixed ? bot : bot + dy / 2;
745 : // legend placement
746 0 : if (leftSide) {
747 : right = -right;
748 : left = -left;
749 : std::swap(right, left);
750 : textX = right + 0.01;
751 : textDir *= -1;
752 : textAlign = FONS_ALIGN_LEFT;
753 : }
754 : // draw black boundary around legend colors
755 0 : glColor3d(0, 0, 0);
756 0 : glBegin(GL_LINES);
757 0 : glVertex2d(right, top);
758 0 : glVertex2d(right, bot2);
759 0 : glVertex2d(left, bot2);
760 0 : glVertex2d(left, top);
761 0 : glVertex2d(right, top);
762 0 : glVertex2d(left, top);
763 0 : glVertex2d(right, bot2);
764 0 : glVertex2d(left, bot2);
765 0 : glEnd();
766 :
767 0 : const double fontHeight = 0.20 * 300. / getHeight();
768 0 : const double fontWidth = 0.20 * 300. / getWidth();
769 :
770 0 : const int fadeSteps = fixed ? 1 : 10;
771 0 : double colorStep = dy / fadeSteps;
772 0 : for (int i = 0; i < numColors; i++) {
773 0 : RGBColor col = scheme.getColors()[i];
774 0 : const double topi = top - i * dy;
775 : //const double boti = top - (i + 1) * dy;
776 : //std::cout << " col=" << scheme.getColors()[i] << " i=" << i << " topi=" << topi << " boti=" << boti << "\n";
777 0 : if (i + 1 < numColors) {
778 : // fade
779 0 : RGBColor col2 = scheme.getColors()[i + 1];
780 0 : double thresh2 = scheme.getThresholds()[i + 1];
781 0 : if (!fixed && thresh2 == GUIVisualizationSettings::MISSING_DATA) {
782 : // draw scale end before missing data
783 0 : GLHelper::setColor(col);
784 0 : glBegin(GL_QUADS);
785 0 : glVertex2d(left, topi);
786 0 : glVertex2d(right, topi);
787 0 : glVertex2d(right, topi - 5 * colorStep);
788 0 : glVertex2d(left, topi - 5 * colorStep);
789 0 : glEnd();
790 0 : glColor3d(0, 0, 0);
791 0 : glBegin(GL_LINES);
792 0 : glVertex2d(right, topi - 10 * colorStep);
793 0 : glVertex2d(left, topi - 10 * colorStep);
794 0 : glEnd();
795 0 : glBegin(GL_LINES);
796 0 : glVertex2d(right, topi - 5 * colorStep);
797 0 : glVertex2d(left, topi - 5 * colorStep);
798 0 : glEnd();
799 : } else {
800 : // fade colors
801 0 : for (double j = 0.0; j < fadeSteps; j++) {
802 0 : GLHelper::setColor(RGBColor::interpolate(col, col2, j / fadeSteps));
803 0 : glBegin(GL_QUADS);
804 0 : glVertex2d(left, topi - j * colorStep);
805 0 : glVertex2d(right, topi - j * colorStep);
806 0 : glVertex2d(right, topi - (j + 1) * colorStep);
807 0 : glVertex2d(left, topi - (j + 1) * colorStep);
808 0 : glEnd();
809 : }
810 : }
811 : } else {
812 0 : GLHelper::setColor(col);
813 0 : glBegin(GL_QUADS);
814 0 : glVertex2d(left, topi);
815 0 : glVertex2d(right, topi);
816 0 : glVertex2d(right, bot2);
817 0 : glVertex2d(left, bot2);
818 0 : glEnd();
819 : }
820 :
821 0 : const double threshold = scheme.getThresholds()[i];
822 : std::string name = scheme.getNames()[i];
823 0 : std::string text = fixed || threshold == GUIVisualizationSettings::MISSING_DATA ? name : toString(threshold);
824 :
825 : const double bgShift = 0.0;
826 : const double textShift = 0.01;
827 : const double textXShift = -0.005;
828 :
829 0 : GLHelper::setColor(RGBColor::WHITE);
830 0 : glTranslated(0, 0, 0.1);
831 0 : glBegin(GL_QUADS);
832 0 : glVertex2d(textX, topi + fontHeight * bgShift);
833 0 : glVertex2d(textX - textDir * fontWidth * (double)text.size() / 2.1, topi + fontHeight * bgShift);
834 0 : glVertex2d(textX - textDir * fontWidth * (double)text.size() / 2.1, topi + fontHeight * (0.8 + bgShift));
835 0 : glVertex2d(textX, topi + fontHeight * (0.8 + bgShift));
836 0 : glEnd();
837 0 : glTranslated(0, 0, -0.1);
838 0 : GLHelper::drawText(text, Position(textX + textDir * textXShift, topi + textShift), 0, fontHeight, RGBColor::BLACK, 0, textAlign, fontWidth);
839 : }
840 : // draw scheme name
841 : std::string name = scheme.getName();
842 0 : if (StringUtils::startsWith(name, "by ")) {
843 0 : name = name.substr(3);
844 : }
845 : const double topN = -0.8;
846 : const double bgShift = 0.0;
847 0 : GLHelper::setColor(RGBColor::WHITE);
848 0 : glTranslated(0, 0, 0.1);
849 0 : glBegin(GL_QUADS);
850 0 : glVertex2d(textX + textDir * 0.04, topN + fontHeight * bgShift - 0.01);
851 0 : glVertex2d(textX + textDir * 0.04 - textDir * fontWidth * (double)name.size() / 2.3, topN + fontHeight * bgShift - 0.01);
852 0 : glVertex2d(textX + textDir * 0.04 - textDir * fontWidth * (double)name.size() / 2.3, topN + fontHeight * (0.8 + bgShift));
853 0 : glVertex2d(textX + textDir * 0.04, topN + fontHeight * (0.8 + bgShift));
854 0 : glEnd();
855 0 : glTranslated(0, 0, -0.1);
856 0 : GLHelper::drawText(name, Position(textX + textDir * 0.04, topN), 0, fontHeight, RGBColor::BLACK, 0, textAlign, fontWidth);
857 :
858 0 : GLHelper::popMatrix();
859 : // restore matrices
860 0 : glMatrixMode(GL_PROJECTION);
861 0 : GLHelper::popMatrix();
862 0 : glMatrixMode(GL_MODELVIEW);
863 0 : GLHelper::popMatrix();
864 0 : }
865 :
866 :
867 : double
868 0 : GUISUMOAbstractView::getFPS() const {
869 0 : return 1000.0 / MAX2((long)1, myFrameDrawTime);
870 : }
871 :
872 :
873 : GUIGlChildWindow*
874 0 : GUISUMOAbstractView::getGUIGlChildWindow() {
875 0 : return myGlChildWindowParent;
876 : }
877 :
878 :
879 : void
880 0 : GUISUMOAbstractView::drawFPS() {
881 0 : glMatrixMode(GL_PROJECTION);
882 0 : GLHelper::pushMatrix();
883 0 : glLoadIdentity();
884 0 : glMatrixMode(GL_MODELVIEW);
885 0 : GLHelper::pushMatrix();
886 0 : glLoadIdentity();
887 0 : const double fontHeight = 0.2 * 300. / getHeight();
888 0 : const double fontWidth = 0.2 * 300. / getWidth();
889 0 : GLHelper::drawText(toString((int)getFPS()) + " FPS", Position(0.82, 0.88), -1, fontHeight, RGBColor::RED, 0, FONS_ALIGN_LEFT, fontWidth);
890 : #ifdef CHECK_ELEMENTCOUNTER
891 : GLHelper::drawText(toString(GLHelper::getMatrixCounter()) + " matrix", Position(0.82, 0.79), -1, fontHeight, RGBColor::RED, 0, FONS_ALIGN_LEFT, fontWidth);
892 : GLHelper::drawText(toString(GLHelper::getVertexCounter()) + " vertex", Position(0.82, 0.71), -1, fontHeight, RGBColor::RED, 0, FONS_ALIGN_LEFT, fontWidth);
893 : #endif
894 : // restore matrices
895 0 : glMatrixMode(GL_PROJECTION);
896 0 : GLHelper::popMatrix();
897 0 : glMatrixMode(GL_MODELVIEW);
898 0 : GLHelper::popMatrix();
899 0 : }
900 :
901 :
902 : double
903 2285648 : GUISUMOAbstractView::m2p(double meter) const {
904 2285648 : return meter * getWidth() / myChanger->getViewport().getWidth();
905 : }
906 :
907 :
908 : double
909 0 : GUISUMOAbstractView::p2m(double pixel) const {
910 0 : return pixel * myChanger->getViewport().getWidth() / getWidth();
911 : }
912 :
913 :
914 : void
915 7103 : GUISUMOAbstractView::recenterView() {
916 7103 : myChanger->setViewport(*myGrid);
917 7103 : }
918 :
919 :
920 : void
921 0 : GUISUMOAbstractView::centerTo(GUIGlID id, bool applyZoom, double zoomDist) {
922 0 : GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(id);
923 0 : if (o != nullptr && dynamic_cast<GUIGlObject*>(o) != nullptr) {
924 0 : const Boundary& b = o->getCenteringBoundary();
925 0 : if (b.getCenter() != Position::INVALID) {
926 0 : if (applyZoom && zoomDist < 0) {
927 0 : myChanger->setViewport(b);
928 0 : update(); // only update when centering onto an object once
929 : } else {
930 : // called during tracking. update is triggered somewhere else
931 0 : myChanger->centerTo(b.getCenter(), zoomDist, applyZoom);
932 0 : updatePositionInformationLabel();
933 : }
934 : }
935 0 : }
936 0 : GUIGlObjectStorage::gIDStorage.unblockObject(id);
937 0 : }
938 :
939 :
940 : void
941 0 : GUISUMOAbstractView::centerTo(const Position& pos, bool applyZoom, double zoomDist) {
942 : // called during tracking. update is triggered somewhere else
943 0 : myChanger->centerTo(pos, zoomDist, applyZoom);
944 0 : updatePositionInformationLabel();
945 0 : }
946 :
947 :
948 : void
949 0 : GUISUMOAbstractView::centerTo(const Boundary& bound) {
950 0 : myChanger->setViewport(bound);
951 0 : update();
952 0 : }
953 :
954 :
955 : GUIMainWindow*
956 0 : GUISUMOAbstractView::getMainWindow() const {
957 0 : return myApp;
958 : }
959 :
960 :
961 : Position
962 0 : GUISUMOAbstractView::getWindowCursorPosition() const {
963 0 : return Position(myWindowCursorPositionX, myWindowCursorPositionY);
964 : }
965 :
966 :
967 : void
968 0 : GUISUMOAbstractView::setWindowCursorPosition(FXint x, FXint y) {
969 0 : myWindowCursorPositionX = x + myMouseHotspotX;
970 0 : myWindowCursorPositionY = y + myMouseHotspotY;
971 0 : }
972 :
973 :
974 : FXbool
975 579957 : GUISUMOAbstractView::makeCurrent() {
976 579957 : FXbool ret = FXGLCanvas::makeCurrent();
977 579957 : return ret;
978 : }
979 :
980 :
981 : long
982 6605 : GUISUMOAbstractView::onConfigure(FXObject*, FXSelector, void*) {
983 6605 : if (makeCurrent()) {
984 6605 : glViewport(0, 0, getWidth() - 1, getHeight() - 1);
985 26420 : glClearColor(
986 6605 : myVisualizationSettings->backgroundColor.red() / 255.f,
987 6605 : myVisualizationSettings->backgroundColor.green() / 255.f,
988 6605 : myVisualizationSettings->backgroundColor.blue() / 255.f,
989 6605 : myVisualizationSettings->backgroundColor.alpha() / 255.f);
990 6605 : doInit();
991 6605 : myAmInitialised = true;
992 6605 : makeNonCurrent();
993 6605 : checkSnapshots();
994 : }
995 6605 : return 1;
996 : }
997 :
998 :
999 : long
1000 539313 : GUISUMOAbstractView::onPaint(FXObject*, FXSelector, void*) {
1001 539313 : if (!isEnabled() || !myAmInitialised) {
1002 : return 1;
1003 : }
1004 532603 : if (makeCurrent()) {
1005 532603 : paintGL();
1006 532602 : makeNonCurrent();
1007 : }
1008 : return 1;
1009 : }
1010 :
1011 :
1012 : const Position&
1013 0 : GUISUMOAbstractView::getPopupPosition() const {
1014 0 : return myPopupPosition;
1015 : }
1016 :
1017 :
1018 : void
1019 0 : GUISUMOAbstractView::destroyPopup() {
1020 0 : if (myPopup != nullptr) {
1021 0 : myPopup->removePopupFromObject();
1022 0 : delete myPopup;
1023 : myPopupPosition.set(0, 0);
1024 0 : myPopup = nullptr;
1025 : myCurrentObjectsDialog.clear();
1026 : }
1027 0 : }
1028 :
1029 :
1030 : void
1031 0 : GUISUMOAbstractView::replacePopup(GUIGLObjectPopupMenu* popUp) {
1032 : // use the same position of old popUp
1033 0 : popUp->move(myPopup->getX(), myPopup->getY());
1034 : // delete and replace popup
1035 0 : myPopup->removePopupFromObject();
1036 0 : delete myPopup;
1037 0 : myPopup = popUp;
1038 : // create and show popUp
1039 0 : myPopup->create();
1040 0 : myPopup->show();
1041 0 : myChanger->onRightBtnRelease(nullptr);
1042 0 : setFocus();
1043 0 : }
1044 :
1045 :
1046 : long
1047 0 : GUISUMOAbstractView::onLeftBtnPress(FXObject*, FXSelector, void* ptr) {
1048 0 : destroyPopup();
1049 0 : setFocus();
1050 : FXEvent* e = (FXEvent*) ptr;
1051 : // check whether the selection-mode is activated
1052 0 : if ((e->state & CONTROLMASK) != 0) {
1053 : // toggle selection of object under cursor
1054 0 : if (makeCurrent()) {
1055 0 : int id = getObjectUnderCursor();
1056 0 : if (id != 0) {
1057 0 : gSelected.toggleSelection(id);
1058 : }
1059 0 : makeNonCurrent();
1060 0 : if (id != 0) {
1061 : // possibly, the selection-coloring is used,
1062 : // so we should update the screen again...
1063 0 : update();
1064 : }
1065 : }
1066 : }
1067 0 : if ((e->state & SHIFTMASK) != 0) {
1068 : // track vehicle or person under cursor
1069 0 : if (makeCurrent()) {
1070 0 : int id = getObjectUnderCursor();
1071 0 : if (id != 0) {
1072 0 : GUIGlObject* o = GUIGlObjectStorage::gIDStorage.getObjectBlocking(id);
1073 0 : if (o != nullptr) {
1074 0 : if (o->getType() == GLO_VEHICLE || o->getType() == GLO_PERSON) {
1075 0 : startTrack(id);
1076 0 : } else if (o->getType() == GLO_REROUTER_EDGE) {
1077 0 : o->onLeftBtnPress(ptr);
1078 0 : update();
1079 : }
1080 : }
1081 : }
1082 0 : makeNonCurrent();
1083 : }
1084 : }
1085 0 : myChanger->onLeftBtnPress(ptr);
1086 0 : grab();
1087 : // Check there are double click
1088 0 : if (e->click_count == 2) {
1089 0 : handle(this, FXSEL(SEL_DOUBLECLICKED, 0), ptr);
1090 : }
1091 0 : return 1;
1092 : }
1093 :
1094 :
1095 : long
1096 0 : GUISUMOAbstractView::onLeftBtnRelease(FXObject*, FXSelector, void* ptr) {
1097 0 : destroyPopup();
1098 0 : myChanger->onLeftBtnRelease(ptr);
1099 0 : if (myApp->isGaming()) {
1100 0 : onGamingClick(getPositionInformation());
1101 : }
1102 0 : ungrab();
1103 0 : return 1;
1104 : }
1105 :
1106 :
1107 : long
1108 0 : GUISUMOAbstractView::onMiddleBtnPress(FXObject*, FXSelector, void* ptr) {
1109 0 : destroyPopup();
1110 0 : setFocus();
1111 0 : if (!myApp->isGaming()) {
1112 0 : myChanger->onMiddleBtnPress(ptr);
1113 : }
1114 0 : grab();
1115 : // enable panning
1116 0 : myPanning = true;
1117 : // set cursors
1118 0 : setDefaultCursor(GUICursorSubSys::getCursor(GUICursor::MOVEVIEW));
1119 0 : setDragCursor(GUICursorSubSys::getCursor(GUICursor::MOVEVIEW));
1120 0 : return 1;
1121 : }
1122 :
1123 :
1124 : long
1125 0 : GUISUMOAbstractView::onMiddleBtnRelease(FXObject*, FXSelector, void* ptr) {
1126 0 : destroyPopup();
1127 0 : if (!myApp->isGaming()) {
1128 0 : myChanger->onMiddleBtnRelease(ptr);
1129 : }
1130 0 : ungrab();
1131 : // disable panning
1132 0 : myPanning = false;
1133 : // restore cursors
1134 0 : setDefaultCursor(GUICursorSubSys::getCursor(GUICursor::DEFAULT));
1135 0 : setDragCursor(GUICursorSubSys::getCursor(GUICursor::DEFAULT));
1136 0 : return 1;
1137 : }
1138 :
1139 :
1140 : long
1141 0 : GUISUMOAbstractView::onRightBtnPress(FXObject*, FXSelector, void* ptr) {
1142 0 : destroyPopup();
1143 0 : if (!myApp->isGaming()) {
1144 0 : myChanger->onRightBtnPress(ptr);
1145 : }
1146 0 : grab();
1147 0 : return 1;
1148 : }
1149 :
1150 :
1151 : long
1152 0 : GUISUMOAbstractView::onRightBtnRelease(FXObject* o, FXSelector sel, void* ptr) {
1153 0 : destroyPopup();
1154 0 : onMouseMove(o, sel, ptr);
1155 0 : if (!myChanger->onRightBtnRelease(ptr) && !myApp->isGaming()) {
1156 0 : openObjectDialogAtCursor((FXEvent*)ptr);
1157 : }
1158 0 : if (myApp->isGaming()) {
1159 0 : onGamingRightClick(getPositionInformation());
1160 : }
1161 0 : ungrab();
1162 0 : return 1;
1163 : }
1164 :
1165 :
1166 : long
1167 0 : GUISUMOAbstractView::onDoubleClicked(FXObject*, FXSelector, void*) {
1168 0 : return 1;
1169 : }
1170 :
1171 :
1172 : long
1173 0 : GUISUMOAbstractView::onMouseWheel(FXObject*, FXSelector, void* ptr) {
1174 0 : if (!myApp->isGaming()) {
1175 0 : myChanger->onMouseWheel(ptr);
1176 : // upddate viewport
1177 0 : if (myGUIDialogEditViewport != nullptr) {
1178 0 : myGUIDialogEditViewport->setValues(myChanger->getZoom(),
1179 0 : myChanger->getXPos(), myChanger->getYPos(),
1180 0 : myChanger->getRotation());
1181 : }
1182 0 : updatePositionInformationLabel();
1183 : }
1184 0 : return 1;
1185 : }
1186 :
1187 :
1188 : long
1189 0 : GUISUMOAbstractView::onMouseMove(FXObject*, FXSelector, void* ptr) {
1190 : // check if popup exist
1191 0 : if (myPopup) {
1192 : // check if handle front element
1193 0 : if (myPopupPosition == getPositionInformation()) {
1194 0 : myPopupPosition = Position::INVALID;
1195 0 : myPopup->handle(this, FXSEL(SEL_COMMAND, MID_CURSORDIALOG_FRONT), nullptr);
1196 0 : destroyPopup();
1197 0 : } else if (!myPopup->shown()) {
1198 0 : destroyPopup();
1199 : }
1200 : }
1201 0 : if (myPopup == nullptr) {
1202 0 : if (myGUIDialogEditViewport == nullptr || !myGUIDialogEditViewport->haveGrabbed()) {
1203 0 : myChanger->onMouseMove(ptr);
1204 : }
1205 0 : if (myGUIDialogEditViewport != nullptr) {
1206 0 : myGUIDialogEditViewport->setValues(myChanger->getZoom(),
1207 0 : myChanger->getXPos(), myChanger->getYPos(),
1208 0 : myChanger->getRotation());
1209 : }
1210 0 : updatePositionInformationLabel();
1211 : }
1212 0 : return 1;
1213 : }
1214 :
1215 :
1216 : long
1217 0 : GUISUMOAbstractView::onMouseLeft(FXObject*, FXSelector, void* /*data*/) {
1218 0 : return 1;
1219 : }
1220 :
1221 : std::vector<GUIGlObject*>
1222 0 : GUISUMOAbstractView::filterContextObjects(const std::vector<GUIGlObject*>& objects) {
1223 : // assume input is sorted with ComparatorClickPriority
1224 : std::vector<GUIGlObject*> result;
1225 0 : for (GUIGlObject* o : objects) {
1226 0 : if (o->getClickPriority() != GUIGlObject::INVALID_PRIORITY && (result.empty() || result.back() != o)) {
1227 0 : result.push_back(o);
1228 : }
1229 : }
1230 0 : return result;
1231 0 : }
1232 :
1233 :
1234 : void
1235 0 : GUISUMOAbstractView::openObjectDialogAtCursor(const FXEvent* ev) {
1236 : // release the mouse grab
1237 0 : ungrab();
1238 : // check if alt key is pressed
1239 0 : const bool altKeyPressed = ((ev->state & ALTMASK) != 0);
1240 : // check if SUMO is enabled, initialised and Make OpenGL context current
1241 0 : if (isEnabled() && myAmInitialised && makeCurrent()) {
1242 0 : auto objectsUnderCursor = getGUIGlObjectsUnderCursor();
1243 0 : if (objectsUnderCursor.empty()) {
1244 0 : myPopup = GUIGlObjectStorage::gIDStorage.getNetObject()->getPopUpMenu(*myApp, *this);
1245 : } else {
1246 0 : std::sort(objectsUnderCursor.begin(), objectsUnderCursor.end(), ComparatorClickPriority());
1247 0 : std::vector<GUIGlObject*> filtered = filterContextObjects(objectsUnderCursor);
1248 0 : if (filtered.size() > 1 && (altKeyPressed
1249 0 : || filtered[0]->getClickPriority() == filtered[1]->getClickPriority())) {
1250 : // open dialog for picking among objects (without duplicates)
1251 0 : myPopup = new GUICursorDialog(GUIGLObjectPopupMenu::PopupType::PROPERTIES, this, filtered);
1252 : } else {
1253 0 : myPopup = objectsUnderCursor.front()->getPopUpMenu(*myApp, *this);
1254 : }
1255 0 : }
1256 0 : openPopupDialog();
1257 0 : makeNonCurrent();
1258 0 : }
1259 0 : }
1260 :
1261 :
1262 : void
1263 0 : GUISUMOAbstractView::openObjectDialog(const std::vector<GUIGlObject*>& objects, const bool filter) {
1264 0 : if (objects.size() > 0) {
1265 : // create cursor popup dialog
1266 0 : if (objects.size() == 1) {
1267 0 : myCurrentObjectsDialog = objects;
1268 0 : } else if (filter) {
1269 : // declare filtered objects
1270 : std::vector<GUIGlObject*> filteredGLObjects;
1271 : // fill filtered objects
1272 0 : for (const auto& glObject : objects) {
1273 : // compare type with first element type
1274 0 : if (glObject->getType() == objects.front()->getType()) {
1275 0 : filteredGLObjects.push_back(glObject);
1276 : }
1277 : }
1278 0 : myCurrentObjectsDialog = filteredGLObjects;
1279 0 : } else {
1280 0 : myCurrentObjectsDialog = objects;
1281 : }
1282 0 : if (myCurrentObjectsDialog.size() > 1) {
1283 0 : myPopup = new GUICursorDialog(GUIGLObjectPopupMenu::PopupType::PROPERTIES, this, myCurrentObjectsDialog);
1284 : } else {
1285 0 : myPopup = myCurrentObjectsDialog.front()->getPopUpMenu(*myApp, *this);
1286 : }
1287 : // open popup dialog
1288 0 : openPopupDialog();
1289 : }
1290 0 : }
1291 :
1292 :
1293 : long
1294 0 : GUISUMOAbstractView::onKeyPress(FXObject* o, FXSelector sel, void* ptr) {
1295 : const FXEvent* e = (FXEvent*) ptr;
1296 : // check if process canvas or popup
1297 0 : if (myPopup != nullptr) {
1298 0 : return myPopup->onKeyPress(o, sel, ptr);
1299 : } else {
1300 0 : if (e->state & CONTROLMASK) {
1301 0 : if (e->code == FX::KEY_Page_Up) {
1302 0 : myVisualizationSettings->gridXSize *= 2;
1303 0 : myVisualizationSettings->gridYSize *= 2;
1304 0 : update();
1305 0 : return 1;
1306 0 : } else if (e->code == FX::KEY_Page_Down) {
1307 0 : myVisualizationSettings->gridXSize /= 2;
1308 0 : myVisualizationSettings->gridYSize /= 2;
1309 0 : update();
1310 0 : return 1;
1311 : }
1312 : }
1313 0 : FXGLCanvas::onKeyPress(o, sel, ptr);
1314 0 : return myChanger->onKeyPress(ptr);
1315 : }
1316 : }
1317 :
1318 :
1319 : long
1320 0 : GUISUMOAbstractView::onKeyRelease(FXObject* o, FXSelector sel, void* ptr) {
1321 : // check if process canvas or popup
1322 0 : if (myPopup != nullptr) {
1323 0 : return myPopup->onKeyRelease(o, sel, ptr);
1324 : } else {
1325 0 : FXGLCanvas::onKeyRelease(o, sel, ptr);
1326 0 : return myChanger->onKeyRelease(ptr);
1327 : }
1328 : }
1329 :
1330 : // ------------ Dealing with snapshots
1331 :
1332 : void
1333 302 : GUISUMOAbstractView::addSnapshot(SUMOTime time, const std::string& file, const int w, const int h) {
1334 : #ifdef DEBUG_SNAPSHOT
1335 : std::cout << "add snapshot time=" << time << " file=" << file << "\n";
1336 : #endif
1337 302 : FXMutexLock lock(mySnapshotsMutex);
1338 604 : mySnapshots[time].push_back(std::make_tuple(file, w, h));
1339 302 : }
1340 :
1341 :
1342 : std::string
1343 302 : GUISUMOAbstractView::makeSnapshot(const std::string& destFile, const int w, const int h) {
1344 302 : if (w >= 0) {
1345 0 : resize(w, h);
1346 0 : repaint();
1347 : }
1348 : std::string errorMessage;
1349 302 : FXString ext = FXPath::extension(destFile.c_str());
1350 302 : const bool useGL2PS = ext == "ps" || ext == "eps" || ext == "pdf" || ext == "svg" || ext == "tex" || ext == "pgf";
1351 : #ifdef HAVE_FFMPEG
1352 302 : const bool useVideo = destFile == "" || ext == "h264" || ext == "hevc" || ext == "mp4";
1353 : #endif
1354 302 : for (int i = 0; i < 10 && !makeCurrent(); ++i) {
1355 0 : MFXSingleEventThread::sleep(100);
1356 : }
1357 : // draw
1358 1208 : glClearColor(
1359 302 : myVisualizationSettings->backgroundColor.red() / 255.f,
1360 302 : myVisualizationSettings->backgroundColor.green() / 255.f,
1361 302 : myVisualizationSettings->backgroundColor.blue() / 255.f,
1362 302 : myVisualizationSettings->backgroundColor.alpha() / 255.f);
1363 302 : glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
1364 302 : glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1365 :
1366 302 : if (myVisualizationSettings->dither) {
1367 0 : glEnable(GL_DITHER);
1368 : } else {
1369 302 : glDisable(GL_DITHER);
1370 : }
1371 302 : glEnable(GL_BLEND);
1372 302 : glDisable(GL_LINE_SMOOTH);
1373 :
1374 302 : applyGLTransform();
1375 :
1376 302 : if (useGL2PS) {
1377 : #ifdef HAVE_GL2PS
1378 : GLint format = GL2PS_PS;
1379 0 : if (ext == "ps") {
1380 : format = GL2PS_PS;
1381 0 : } else if (ext == "eps") {
1382 : format = GL2PS_EPS;
1383 0 : } else if (ext == "pdf") {
1384 : format = GL2PS_PDF;
1385 0 : } else if (ext == "tex") {
1386 : format = GL2PS_TEX;
1387 0 : } else if (ext == "svg") {
1388 : format = GL2PS_SVG;
1389 0 : } else if (ext == "pgf") {
1390 : format = GL2PS_PGF;
1391 : } else {
1392 0 : return "Could not save '" + destFile + "'.\n Unrecognized format '" + std::string(ext.text()) + "'.";
1393 : }
1394 0 : FILE* fp = fopen(destFile.c_str(), "wb");
1395 0 : if (fp == 0) {
1396 0 : return "Could not save '" + destFile + "'.\n Could not open file for writing";
1397 : }
1398 0 : GLHelper::setGL2PS();
1399 : GLint buffsize = 0, state = GL2PS_OVERFLOW;
1400 : GLint viewport[4];
1401 0 : glGetIntegerv(GL_VIEWPORT, viewport);
1402 0 : while (state == GL2PS_OVERFLOW) {
1403 0 : buffsize += 1024 * 1024;
1404 0 : gl2psBeginPage(destFile.c_str(), "sumo-gui; https://sumo.dlr.de", viewport, format, GL2PS_SIMPLE_SORT,
1405 : GL2PS_DRAW_BACKGROUND | GL2PS_USE_CURRENT_VIEWPORT,
1406 : GL_RGBA, 0, NULL, 0, 0, 0, buffsize, fp, "out.eps");
1407 0 : glMatrixMode(GL_MODELVIEW);
1408 0 : GLHelper::pushMatrix();
1409 0 : glDisable(GL_TEXTURE_2D);
1410 0 : glDisable(GL_ALPHA_TEST);
1411 0 : glDisable(GL_BLEND);
1412 0 : glEnable(GL_DEPTH_TEST);
1413 : // draw decals (if not in grabbing mode)
1414 :
1415 0 : drawDecals();
1416 0 : if (myVisualizationSettings->showGrid) {
1417 0 : paintGLGrid();
1418 : }
1419 :
1420 0 : glLineWidth(1);
1421 0 : glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1422 0 : Boundary viewPort = myChanger->getViewport();
1423 0 : const float minB[2] = { (float)viewPort.xmin(), (float)viewPort.ymin() };
1424 0 : const float maxB[2] = { (float)viewPort.xmax(), (float)viewPort.ymax() };
1425 0 : myVisualizationSettings->scale = m2p(SUMO_const_laneWidth);
1426 0 : glEnable(GL_POLYGON_OFFSET_FILL);
1427 0 : glEnable(GL_POLYGON_OFFSET_LINE);
1428 0 : myGrid->Search(minB, maxB, *myVisualizationSettings);
1429 :
1430 0 : displayLegends();
1431 0 : state = gl2psEndPage();
1432 0 : glFinish();
1433 0 : }
1434 0 : GLHelper::setGL2PS(false);
1435 0 : fclose(fp);
1436 : #else
1437 : return "Could not save '" + destFile + "', gl2ps was not enabled at compile time.";
1438 : #endif
1439 : } else {
1440 302 : doPaintGL(GL_RENDER, myChanger->getViewport());
1441 302 : displayLegends();
1442 302 : swapBuffers();
1443 302 : glFinish();
1444 : FXColor* buf;
1445 302 : FXMALLOC(&buf, FXColor, getWidth()*getHeight());
1446 : // read from the back buffer
1447 302 : glReadBuffer(GL_BACK);
1448 : // Read the pixels
1449 302 : glReadPixels(0, 0, getWidth(), getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)buf);
1450 302 : makeNonCurrent();
1451 302 : update();
1452 : // mirror
1453 : int mwidth = getWidth();
1454 : int mheight = getHeight();
1455 302 : FXColor* paa = buf;
1456 302 : FXColor* pbb = buf + mwidth * (mheight - 1);
1457 : do {
1458 : FXColor* pa = paa;
1459 38656 : paa += mwidth;
1460 : FXColor* pb = pbb;
1461 38656 : pbb -= mwidth;
1462 : do {
1463 18941440 : FXColor t = *pa;
1464 18941440 : *pa++ = *pb;
1465 18941440 : *pb++ = t;
1466 18941440 : } while (pa < paa);
1467 38656 : } while (paa < pbb);
1468 : try {
1469 : #ifdef HAVE_FFMPEG
1470 302 : if (useVideo) {
1471 : try {
1472 0 : saveFrame(destFile, buf);
1473 : errorMessage = "video";
1474 0 : } catch (std::runtime_error& err) {
1475 0 : errorMessage = err.what();
1476 0 : }
1477 : } else
1478 : #endif
1479 302 : if (!MFXImageHelper::saveImage(destFile, getWidth(), getHeight(), buf)) {
1480 0 : errorMessage = "Could not save '" + destFile + "'.";
1481 : }
1482 0 : } catch (InvalidArgument& e) {
1483 0 : errorMessage = "Could not save '" + destFile + "'.\n" + e.what();
1484 0 : }
1485 302 : FXFREE(&buf);
1486 : }
1487 302 : return errorMessage;
1488 302 : }
1489 :
1490 :
1491 : void
1492 0 : GUISUMOAbstractView::saveFrame(const std::string& destFile, FXColor* buf) {
1493 : UNUSED_PARAMETER(destFile);
1494 : UNUSED_PARAMETER(buf);
1495 0 : }
1496 :
1497 :
1498 : void
1499 5831290 : GUISUMOAbstractView::checkSnapshots() {
1500 5831290 : const SUMOTime time = getCurrentTimeStep() - DELTA_T;
1501 : #ifdef DEBUG_SNAPSHOT
1502 : std::cout << "check snapshots time=" << time << " registeredTimes=" << mySnapshots.size() << "\n";
1503 : #endif
1504 5831290 : FXMutexLock lock(mySnapshotsMutex);
1505 : const auto snapIt = mySnapshots.find(time);
1506 5831290 : if (snapIt == mySnapshots.end()) {
1507 : return;
1508 : }
1509 31 : std::vector<std::tuple<std::string, int, int> > files = snapIt->second;
1510 : lock.unlock();
1511 : // decouple map access and painting to avoid deadlock
1512 333 : for (const auto& entry : files) {
1513 : #ifdef DEBUG_SNAPSHOT
1514 : std::cout << "make snapshot time=" << time << " file=" << file << "\n";
1515 : #endif
1516 302 : const std::string& error = makeSnapshot(std::get<0>(entry), std::get<1>(entry), std::get<2>(entry));
1517 302 : if (error != "" && error != "video") {
1518 0 : WRITE_WARNING(error);
1519 : }
1520 : }
1521 : // synchronization with a waiting run thread
1522 : lock.lock();
1523 : mySnapshots.erase(time);
1524 31 : mySnapshotCondition.signal();
1525 : #ifdef DEBUG_SNAPSHOT
1526 : std::cout << " files=" << toString(files) << " myApplicationSnapshots=" << joinToString(*myApplicationSnapshots, ",") << "\n";
1527 : #endif
1528 31 : }
1529 :
1530 :
1531 : void
1532 5830972 : GUISUMOAbstractView::waitForSnapshots(const SUMOTime snapshotTime) {
1533 5830972 : FXMutexLock lock(mySnapshotsMutex);
1534 : if (mySnapshots.count(snapshotTime) > 0) {
1535 31 : mySnapshotCondition.wait(mySnapshotsMutex);
1536 : }
1537 5830972 : }
1538 :
1539 :
1540 : SUMOTime
1541 0 : GUISUMOAbstractView::getCurrentTimeStep() const {
1542 0 : return 0;
1543 : }
1544 :
1545 :
1546 : void
1547 0 : GUISUMOAbstractView::showViewschemeEditor() {
1548 0 : if (myGUIDialogViewSettings == nullptr) {
1549 0 : myGUIDialogViewSettings = new GUIDialog_ViewSettings(this, myVisualizationSettings);
1550 0 : myGUIDialogViewSettings->create();
1551 : } else {
1552 0 : myGUIDialogViewSettings->setCurrent(myVisualizationSettings);
1553 : }
1554 0 : setFocus();
1555 0 : myGUIDialogViewSettings->show();
1556 0 : }
1557 :
1558 :
1559 : GUIDialog_EditViewport*
1560 0 : GUISUMOAbstractView::getViewportEditor() {
1561 0 : if (myGUIDialogEditViewport == nullptr) {
1562 0 : myGUIDialogEditViewport = new GUIDialog_EditViewport(this, TLC("Labels", "Edit Viewport"));
1563 0 : myGUIDialogEditViewport->create();
1564 : }
1565 0 : updateViewportValues();
1566 0 : return myGUIDialogEditViewport;
1567 : }
1568 :
1569 :
1570 0 : void GUISUMOAbstractView::updateViewportValues() {
1571 0 : myGUIDialogEditViewport->setValues(myChanger->getZoom(),
1572 0 : myChanger->getXPos(), myChanger->getYPos(),
1573 0 : myChanger->getRotation());
1574 0 : }
1575 :
1576 :
1577 : void
1578 0 : GUISUMOAbstractView::showViewportEditor() {
1579 0 : getViewportEditor(); // make sure it exists;
1580 0 : Position p(myChanger->getXPos(), myChanger->getYPos(), myChanger->getZPos());
1581 0 : myGUIDialogEditViewport->setOldValues(p, Position::INVALID, myChanger->getRotation());
1582 0 : myGUIDialogEditViewport->show();
1583 0 : }
1584 :
1585 :
1586 : void
1587 7 : GUISUMOAbstractView::setViewportFromToRot(const Position& lookFrom, const Position& /* lookAt */, double rotation) {
1588 7 : myChanger->setViewportFrom(lookFrom.x(), lookFrom.y(), lookFrom.z());
1589 7 : myChanger->setRotation(rotation);
1590 7 : update();
1591 7 : }
1592 :
1593 :
1594 : void
1595 1 : GUISUMOAbstractView::copyViewportTo(GUISUMOAbstractView* view) {
1596 : // look straight down
1597 1 : view->setViewportFromToRot(Position(myChanger->getXPos(), myChanger->getYPos(), myChanger->getZPos()),
1598 1 : Position(myChanger->getXPos(), myChanger->getYPos(), 0),
1599 1 : myChanger->getRotation());
1600 1 : }
1601 :
1602 :
1603 : bool
1604 0 : GUISUMOAbstractView::setColorScheme(const std::string&) {
1605 0 : return true;
1606 : }
1607 :
1608 :
1609 : const GUIVisualizationSettings&
1610 0 : GUISUMOAbstractView::getVisualisationSettings() const {
1611 0 : return *myVisualizationSettings;
1612 : }
1613 :
1614 :
1615 : GUIVisualizationSettings*
1616 0 : GUISUMOAbstractView::editVisualisationSettings() const {
1617 0 : return myVisualizationSettings;
1618 : }
1619 :
1620 :
1621 : void
1622 0 : GUISUMOAbstractView::remove(GUIDialog_EditViewport*) {
1623 0 : myGUIDialogEditViewport = nullptr;
1624 0 : }
1625 :
1626 :
1627 : void
1628 0 : GUISUMOAbstractView::remove(GUIDialog_ViewSettings*) {
1629 0 : myGUIDialogViewSettings = nullptr;
1630 0 : }
1631 :
1632 :
1633 : double
1634 0 : GUISUMOAbstractView::getGridWidth() const {
1635 0 : return myGrid->getWidth();
1636 : }
1637 :
1638 :
1639 : double
1640 0 : GUISUMOAbstractView::getGridHeight() const {
1641 0 : return myGrid->getHeight();
1642 : }
1643 :
1644 :
1645 : void
1646 0 : GUISUMOAbstractView::startTrack(int /*id*/) {
1647 0 : }
1648 :
1649 :
1650 : void
1651 0 : GUISUMOAbstractView::stopTrack() {
1652 0 : }
1653 :
1654 :
1655 : GUIGlID
1656 0 : GUISUMOAbstractView::getTrackedID() const {
1657 0 : return GUIGlObject::INVALID_ID;
1658 : }
1659 :
1660 :
1661 : void
1662 0 : GUISUMOAbstractView::onGamingClick(Position /*pos*/) {
1663 0 : }
1664 :
1665 : void
1666 0 : GUISUMOAbstractView::onGamingRightClick(Position /*pos*/) {
1667 0 : }
1668 :
1669 :
1670 : std::vector<GUISUMOAbstractView::Decal>&
1671 0 : GUISUMOAbstractView::getDecals() {
1672 0 : return myDecals;
1673 : }
1674 :
1675 :
1676 : FXMutex&
1677 0 : GUISUMOAbstractView::getDecalsLockMutex() {
1678 0 : return myDecalsLockMutex;
1679 : }
1680 :
1681 :
1682 : MFXComboBoxIcon*
1683 35 : GUISUMOAbstractView::getColoringSchemesCombo() {
1684 35 : return myGlChildWindowParent->getColoringSchemesCombo();
1685 : }
1686 :
1687 :
1688 : FXImage*
1689 7 : GUISUMOAbstractView::checkGDALImage(Decal& d) {
1690 : #ifdef HAVE_GDAL
1691 7 : GDALAllRegister();
1692 7 : GDALDataset* poDataset = (GDALDataset*)GDALOpen(d.filename.c_str(), GA_ReadOnly);
1693 7 : if (poDataset == 0) {
1694 : return 0;
1695 : }
1696 7 : const int xSize = poDataset->GetRasterXSize();
1697 7 : const int ySize = poDataset->GetRasterYSize();
1698 : // checking for geodata in the picture and try to adapt position and scale
1699 7 : if (d.width <= 0.) {
1700 : double adfGeoTransform[6];
1701 0 : if (poDataset->GetGeoTransform(adfGeoTransform) == CE_None) {
1702 0 : Position topLeft(adfGeoTransform[0], adfGeoTransform[3]);
1703 0 : const double horizontalSize = xSize * adfGeoTransform[1];
1704 0 : const double verticalSize = ySize * adfGeoTransform[5];
1705 0 : Position bottomRight(topLeft.x() + horizontalSize, topLeft.y() + verticalSize);
1706 0 : if (GeoConvHelper::getProcessing().x2cartesian(topLeft) && GeoConvHelper::getProcessing().x2cartesian(bottomRight)) {
1707 0 : d.width = bottomRight.x() - topLeft.x();
1708 0 : d.height = topLeft.y() - bottomRight.y();
1709 0 : d.centerX = (topLeft.x() + bottomRight.x()) / 2;
1710 0 : d.centerY = (topLeft.y() + bottomRight.y()) / 2;
1711 : //WRITE_MESSAGE("proj: " + toString(poDataset->GetProjectionRef()) + " dim: " + toString(d.width) + "," + toString(d.height) + " center: " + toString(d.centerX) + "," + toString(d.centerY));
1712 : } else {
1713 0 : WRITE_WARNINGF(TL("Could not convert coordinates in %."), d.filename);
1714 : }
1715 : }
1716 : }
1717 : #endif
1718 7 : if (d.width <= 0.) {
1719 0 : d.width = getGridWidth();
1720 0 : d.height = getGridHeight();
1721 : }
1722 :
1723 : // trying to read the picture
1724 : #ifdef HAVE_GDAL
1725 7 : const int picSize = xSize * ySize;
1726 : FXColor* result;
1727 7 : if (!FXMALLOC(&result, FXColor, picSize)) {
1728 0 : WRITE_WARNINGF("Could not allocate memory for %.", d.filename);
1729 0 : return 0;
1730 : }
1731 544775 : for (int j = 0; j < picSize; j++) {
1732 544768 : result[j] = FXRGB(0, 0, 0);
1733 : }
1734 : bool valid = true;
1735 7 : for (int i = 1; i <= poDataset->GetRasterCount(); i++) {
1736 7 : GDALRasterBand* poBand = poDataset->GetRasterBand(i);
1737 : int shift = -1;
1738 7 : if (poBand->GetColorInterpretation() == GCI_RedBand) {
1739 : shift = 0;
1740 7 : } else if (poBand->GetColorInterpretation() == GCI_GreenBand) {
1741 : shift = 1;
1742 7 : } else if (poBand->GetColorInterpretation() == GCI_BlueBand) {
1743 : shift = 2;
1744 7 : } else if (poBand->GetColorInterpretation() == GCI_AlphaBand) {
1745 : shift = 3;
1746 : } else {
1747 : valid = false;
1748 : break;
1749 : }
1750 : assert(xSize == poBand->GetXSize() && ySize == poBand->GetYSize());
1751 0 : if (poBand->RasterIO(GF_Read, 0, 0, xSize, ySize, ((unsigned char*)result) + shift, xSize, ySize, GDT_Byte, 4, 4 * xSize) == CE_Failure) {
1752 : valid = false;
1753 : break;
1754 : }
1755 : }
1756 7 : GDALClose(poDataset);
1757 7 : if (valid) {
1758 0 : return new FXImage(getApp(), result, IMAGE_OWNED | IMAGE_KEEP | IMAGE_SHMI | IMAGE_SHMP, xSize, ySize);
1759 : }
1760 7 : FXFREE(&result);
1761 : #endif
1762 : return nullptr;
1763 : }
1764 :
1765 :
1766 : void
1767 532905 : GUISUMOAbstractView::drawDecals() {
1768 532905 : GLHelper::pushName(0);
1769 532905 : myDecalsLockMutex.lock();
1770 535138 : for (auto& decal : myDecals) {
1771 2233 : if (decal.skip2D || decal.filename.empty()) {
1772 0 : continue;
1773 : }
1774 2233 : if (!decal.initialised) {
1775 : try {
1776 7 : FXImage* img = checkGDALImage(decal);
1777 7 : if (img == nullptr) {
1778 7 : img = MFXImageHelper::loadImage(getApp(), decal.filename);
1779 : }
1780 7 : MFXImageHelper::scalePower2(img, GUITexturesHelper::getMaxTextureSize());
1781 7 : decal.glID = GUITexturesHelper::add(img);
1782 7 : decal.initialised = true;
1783 7 : decal.image = img;
1784 0 : } catch (InvalidArgument& e) {
1785 0 : WRITE_ERROR("Could not load '" + decal.filename + "'.\n" + e.what());
1786 0 : decal.skip2D = true;
1787 0 : }
1788 : }
1789 2233 : GLHelper::pushMatrix();
1790 2233 : if (decal.screenRelative) {
1791 0 : Position center = screenPos2NetPos((int)decal.centerX, (int)decal.centerY);
1792 0 : glTranslated(center.x(), center.y(), decal.layer);
1793 : } else {
1794 2233 : glTranslated(decal.centerX, decal.centerY, decal.layer);
1795 : }
1796 2233 : glRotated(decal.rot, 0, 0, 1);
1797 2233 : glColor3d(1, 1, 1);
1798 2233 : double halfWidth = decal.width / 2.;
1799 2233 : double halfHeight = decal.height / 2.;
1800 2233 : if (decal.screenRelative) {
1801 0 : halfWidth = p2m(halfWidth);
1802 0 : halfHeight = p2m(halfHeight);
1803 : }
1804 2233 : GUITexturesHelper::drawTexturedBox(decal.glID, -halfWidth, -halfHeight, halfWidth, halfHeight);
1805 2233 : GLHelper::popMatrix();
1806 : }
1807 532905 : myDecalsLockMutex.unlock();
1808 532905 : GLHelper::popName();
1809 532905 : }
1810 :
1811 :
1812 : void
1813 0 : GUISUMOAbstractView::openPopupDialog() {
1814 : int x, y;
1815 : FXuint b;
1816 0 : myApp->getCursorPosition(x, y, b);
1817 0 : int appX = myApp->getX();
1818 0 : int popX = x + appX;
1819 0 : int popY = y + myApp->getY();
1820 0 : myPopup->setX(popX);
1821 0 : myPopup->setY(popY);
1822 0 : myPopup->create();
1823 0 : myPopup->show();
1824 : // TODO: try to stay on screen even on a right secondary screen in multi-monitor setup
1825 : const int rootWidth = getApp()->getRootWindow()->getWidth();
1826 : const int rootHeight = getApp()->getRootWindow()->getHeight();
1827 0 : if (popX <= rootWidth) {
1828 0 : const int maxX = (appX < 0) ? 0 : rootWidth;
1829 0 : popX = MIN2(popX, maxX - myPopup->getWidth() - 10);
1830 : }
1831 0 : popY = MIN2(popY, rootHeight - myPopup->getHeight() - 50);
1832 0 : myPopup->move(popX, popY);
1833 0 : myPopupPosition = getPositionInformation();
1834 0 : myChanger->onRightBtnRelease(nullptr);
1835 0 : setFocus();
1836 0 : }
1837 :
1838 : // ------------ Additional visualisations
1839 :
1840 : bool
1841 8 : GUISUMOAbstractView::addAdditionalGLVisualisation(GUIGlObject* const which) {
1842 8 : if (myAdditionallyDrawn.find(which) == myAdditionallyDrawn.end()) {
1843 8 : myAdditionallyDrawn[which] = 1;
1844 : } else {
1845 0 : myAdditionallyDrawn[which] = myAdditionallyDrawn[which] + 1;
1846 : }
1847 8 : update();
1848 8 : return true;
1849 : }
1850 :
1851 :
1852 : bool
1853 24 : GUISUMOAbstractView::removeAdditionalGLVisualisation(GUIGlObject* const which) {
1854 24 : if (myAdditionallyDrawn.find(which) == myAdditionallyDrawn.end()) {
1855 : return false;
1856 : }
1857 8 : int cnt = myAdditionallyDrawn[which];
1858 8 : if (cnt == 1) {
1859 : myAdditionallyDrawn.erase(which);
1860 : } else {
1861 0 : myAdditionallyDrawn[which] = myAdditionallyDrawn[which] - 1;
1862 : }
1863 8 : update();
1864 8 : return true;
1865 : }
1866 :
1867 :
1868 : bool
1869 0 : GUISUMOAbstractView::isAdditionalGLVisualisationEnabled(GUIGlObject* const which) const {
1870 0 : if (myAdditionallyDrawn.find(which) == myAdditionallyDrawn.end()) {
1871 : return false;
1872 : } else {
1873 0 : return true;
1874 : }
1875 : }
1876 :
1877 :
1878 : Boundary
1879 532905 : GUISUMOAbstractView::applyGLTransform(bool fixRatio) {
1880 532905 : Boundary bound = myChanger->getViewport(fixRatio);
1881 532905 : glMatrixMode(GL_PROJECTION);
1882 532905 : glLoadIdentity();
1883 : // as a rough rule, each GLObject is drawn at z = -GUIGlObjectType
1884 : // thus, objects with a higher value will be closer (drawn on top)
1885 : // // @todo last param should be 0 after modifying all glDraw methods
1886 532905 : glOrtho(0, getWidth(), 0, getHeight(), -GLO_MAX - 1, GLO_MAX + 1);
1887 532905 : glMatrixMode(GL_MODELVIEW);
1888 532905 : glLoadIdentity();
1889 532905 : double scaleX = (double)getWidth() / bound.getWidth();
1890 532905 : double scaleY = (double)getHeight() / bound.getHeight();
1891 532905 : glScaled(scaleX, scaleY, 1);
1892 532905 : glTranslated(-bound.xmin(), -bound.ymin(), 0);
1893 : // rotate around the center of the screen
1894 : //double angle = -90;
1895 532905 : if (myChanger->getRotation() != 0) {
1896 0 : glTranslated(bound.getCenter().x(), bound.getCenter().y(), 0);
1897 0 : glRotated(myChanger->getRotation(), 0, 0, 1);
1898 0 : glTranslated(-bound.getCenter().x(), -bound.getCenter().y(), 0);
1899 0 : Boundary rotBound;
1900 0 : double rad = -DEG2RAD(myChanger->getRotation());
1901 0 : rotBound.add(Position(bound.xmin(), bound.ymin()).rotateAround2D(rad, bound.getCenter()));
1902 0 : rotBound.add(Position(bound.xmin(), bound.ymax()).rotateAround2D(rad, bound.getCenter()));
1903 0 : rotBound.add(Position(bound.xmax(), bound.ymin()).rotateAround2D(rad, bound.getCenter()));
1904 0 : rotBound.add(Position(bound.xmax(), bound.ymax()).rotateAround2D(rad, bound.getCenter()));
1905 : bound = rotBound;
1906 0 : }
1907 532905 : myVisualizationSettings->angle = myChanger->getRotation();
1908 532905 : return bound;
1909 0 : }
1910 :
1911 :
1912 : double
1913 0 : GUISUMOAbstractView::getDelay() const {
1914 0 : return myApp->getDelay();
1915 : }
1916 :
1917 :
1918 : void
1919 0 : GUISUMOAbstractView::setDelay(double delay) {
1920 0 : myApp->setDelay(delay);
1921 0 : }
1922 :
1923 :
1924 : void
1925 0 : GUISUMOAbstractView::setBreakpoints(const std::vector<SUMOTime>& breakpoints) {
1926 0 : myApp->setBreakpoints(breakpoints);
1927 0 : }
1928 :
1929 :
1930 : void
1931 0 : GUISUMOAbstractView::buildMinMaxRainbow(const GUIVisualizationSettings& s, GUIColorScheme& scheme,
1932 : const GUIVisualizationRainbowSettings& rs, double minValue, double maxValue, bool hasMissingData) {
1933 0 : if (rs.hideMin && rs.hideMax && minValue == std::numeric_limits<double>::infinity()) {
1934 0 : minValue = rs.minThreshold;
1935 0 : maxValue = rs.maxThreshold;
1936 : }
1937 0 : if (rs.fixRange) {
1938 0 : if (rs.hideMin) {
1939 0 : minValue = rs.minThreshold;
1940 : }
1941 0 : if (rs.hideMax) {
1942 0 : maxValue = rs.maxThreshold;
1943 : }
1944 : }
1945 0 : if (minValue != std::numeric_limits<double>::infinity()) {
1946 0 : scheme.clear();
1947 : // add new thresholds
1948 0 : if (scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_EDGEDATA_NUMERICAL
1949 0 : || scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_EDGE_PARAM_NUMERICAL
1950 0 : || scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_LANE_PARAM_NUMERICAL
1951 0 : || scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_DATA_ATTRIBUTE_NUMERICAL
1952 0 : || scheme.getName() == GUIVisualizationSettings::SCHEME_NAME_PARAM_NUMERICAL
1953 0 : || hasMissingData) {
1954 0 : scheme.addColor(s.COL_MISSING_DATA, s.MISSING_DATA, "missing data");
1955 : }
1956 0 : if (rs.hideMin && !rs.fixRange) {
1957 0 : const double rawRange = maxValue - minValue;
1958 0 : minValue = MAX2(rs.minThreshold + MIN2(1.0, rawRange / 100.0), minValue);
1959 0 : scheme.addColor(RGBColor(204, 204, 204), rs.minThreshold);
1960 : }
1961 0 : if (rs.hideMax && !rs.fixRange) {
1962 0 : const double rawRange = maxValue - minValue;
1963 0 : maxValue = MIN2(rs.maxThreshold - MIN2(1.0, rawRange / 100.0), maxValue);
1964 0 : scheme.addColor(RGBColor(204, 204, 204), rs.maxThreshold);
1965 : }
1966 0 : const double range = maxValue - minValue;
1967 0 : scheme.addColor(rs.colors.front(), minValue);
1968 0 : const int steps = (int)rs.colors.size() - 1;
1969 0 : if (rs.setNeutral) {
1970 0 : const int steps1 = steps / 2;
1971 0 : const int steps2 = steps - steps1;
1972 0 : const double range1 = rs.neutralThreshold - minValue;
1973 0 : const double range2 = maxValue - rs.neutralThreshold;
1974 0 : for (int i = 1; i < steps1; i++) {
1975 0 : scheme.addColor(rs.colors[i], (minValue + range1 * i / steps1));
1976 : }
1977 0 : scheme.addColor(rs.colors[steps1], rs.neutralThreshold);
1978 0 : for (int i = 1; i < steps2; i++) {
1979 0 : scheme.addColor(rs.colors[steps1 + i], (rs.neutralThreshold + range2 * i / steps2));
1980 : }
1981 : } else {
1982 0 : for (int i = 1; i < steps; i++) {
1983 0 : scheme.addColor(rs.colors[i], (minValue + range * i / steps));
1984 : }
1985 : }
1986 0 : scheme.addColor(rs.colors.back(), maxValue);
1987 : }
1988 0 : }
1989 :
1990 :
1991 0 : GUISUMOAbstractView::LayerObject::LayerObject(double layer, GUIGlObject* object) :
1992 0 : myGLObject(object) {
1993 0 : first = layer;
1994 0 : second.first = object->getType();
1995 0 : second.second = object->getMicrosimID();
1996 0 : }
1997 :
1998 :
1999 0 : GUISUMOAbstractView::LayerObject::LayerObject(GUIGlObject* object) :
2000 0 : myGLObject(object) {
2001 0 : first = object->getType();
2002 0 : second.first = object->getType();
2003 0 : second.second = object->getMicrosimID();
2004 0 : }
2005 :
2006 :
2007 : GUIGlObject*
2008 0 : GUISUMOAbstractView::LayerObject::getGLObject() const {
2009 0 : return myGLObject;
2010 : }
2011 :
2012 : /****************************************************************************/
|