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