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