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