LCOV - code coverage report
Current view: top level - src/utils/gui/windows - GUIDanielPerspectiveChanger.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 23.9 % 180 43
Test Date: 2026-06-15 15:46:12 Functions: 51.9 % 27 14

            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    GUIDanielPerspectiveChanger.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Michael Behrisch
      18              : /// @date    Sept 2002
      19              : ///
      20              : // A class that allows to steer the visual output in dependence to
      21              : /****************************************************************************/
      22              : #include <config.h>
      23              : 
      24              : #include <fxkeys.h>
      25              : #include <utils/geom/Boundary.h>
      26              : #include <utils/geom/Position.h>
      27              : #include <utils/geom/GeomHelper.h>
      28              : #include <utils/gui/settings/GUICompleteSchemeStorage.h>
      29              : #include "GUIMainWindow.h"
      30              : #include "GUIPerspectiveChanger.h"
      31              : #include "GUIDanielPerspectiveChanger.h"
      32              : 
      33              : 
      34              : // ===========================================================================
      35              : // method definitions
      36              : // ===========================================================================
      37         8339 : GUIDanielPerspectiveChanger::GUIDanielPerspectiveChanger(
      38         8339 :     GUISUMOAbstractView& callBack, const Boundary& viewPort) :
      39              :     GUIPerspectiveChanger(callBack, viewPort),
      40        16678 :     myOrigWidth(viewPort.getWidth()),
      41         8339 :     myOrigHeight(viewPort.getHeight()),
      42         8339 :     myRotation(0),
      43         8339 :     myMouseButtonState(MOUSEBTN_NONE),
      44         8339 :     myMoveOnClick(false),
      45         8339 :     myZoomBase(viewPort.getCenter()),
      46         8339 :     myDragDelay(0) {
      47         8339 : }
      48              : 
      49              : 
      50        16622 : GUIDanielPerspectiveChanger::~GUIDanielPerspectiveChanger() {}
      51              : 
      52              : 
      53              : void
      54            0 : GUIDanielPerspectiveChanger::move(int xdiff, int ydiff) {
      55            0 :     myViewPort.moveby(myCallback.p2m(xdiff), -myCallback.p2m(ydiff));
      56            0 :     myCallback.update();
      57            0 : }
      58              : 
      59              : 
      60              : void
      61            0 : GUIDanielPerspectiveChanger::zoom(double factor) {
      62            0 :     if (myCallback.getApp()->reg().readIntEntry("gui", "zoomAtCenter", 0)) {
      63            0 :         myZoomBase = myViewPort.getCenter();
      64              :     }
      65            0 :     if (factor > 0) {
      66            0 :         myViewPort = Boundary(
      67            0 :                          myZoomBase.x() - (myZoomBase.x() - myViewPort.xmin()) / factor,
      68            0 :                          myZoomBase.y() - (myZoomBase.y() - myViewPort.ymin()) / factor,
      69            0 :                          myZoomBase.x() - (myZoomBase.x() - myViewPort.xmax()) / factor,
      70            0 :                          myZoomBase.y() - (myZoomBase.y() - myViewPort.ymax()) / factor);
      71            0 :         myCallback.update();
      72              :     }
      73            0 : }
      74              : 
      75              : 
      76              : void
      77            0 : GUIDanielPerspectiveChanger::rotate(int /* diff */) {
      78              :     /*
      79              :     if (myCallback.allowRotation()) {
      80              :         myRotation += (double) diff / (double) 10.0;
      81              :         myCallback.update();
      82              :     }
      83              :     */
      84            0 : }
      85              : 
      86              : 
      87              : double
      88      1736126 : GUIDanielPerspectiveChanger::getRotation() const {
      89      1736126 :     return myRotation;
      90              : }
      91              : 
      92              : 
      93              : double
      94         8378 : GUIDanielPerspectiveChanger::getXPos() const {
      95         8378 :     return myViewPort.getCenter().x();
      96              : }
      97              : 
      98              : 
      99              : double
     100         8378 : GUIDanielPerspectiveChanger::getYPos() const {
     101         8378 :     return myViewPort.getCenter().y();
     102              : }
     103              : 
     104              : 
     105              : double
     106            3 : GUIDanielPerspectiveChanger::getZoom() const {
     107            3 :     return myOrigWidth / myViewPort.getWidth() * 100;
     108              : }
     109              : 
     110              : 
     111              : double
     112         7869 : GUIDanielPerspectiveChanger::getZPos() const {
     113         7869 :     return myViewPort.getWidth();
     114              : }
     115              : 
     116              : 
     117              : double
     118            9 : GUIDanielPerspectiveChanger::zoom2ZPos(double zoom) const {
     119            9 :     return myOrigWidth / (zoom / 100);
     120              : }
     121              : 
     122              : 
     123              : double
     124           16 : GUIDanielPerspectiveChanger::zPos2Zoom(double zPos) const {
     125           16 :     return (myOrigWidth / zPos) * 100;
     126              : }
     127              : 
     128              : 
     129              : void
     130          502 : GUIDanielPerspectiveChanger::centerTo(const Position& pos, double radius,
     131              :                                       bool applyZoom) {
     132          502 :     if (applyZoom) {
     133            0 :         myViewPort = Boundary();
     134            0 :         myViewPort.add(pos);
     135            0 :         myViewPort.grow(radius);
     136              :     } else {
     137          502 :         myViewPort.moveby(pos.x() - getXPos(), pos.y() - getYPos());
     138              :     }
     139          502 : }
     140              : 
     141              : 
     142              : void
     143            0 : GUIDanielPerspectiveChanger::onLeftBtnPress(void* data) {
     144            0 :     myMouseButtonState |= MOUSEBTN_LEFT;
     145              :     FXEvent* e = (FXEvent*) data;
     146            0 :     myMouseXPosition = e->win_x;
     147            0 :     myMouseYPosition = e->win_y;
     148            0 :     myMoveOnClick = false;
     149            0 :     myMouseDownTime = FXThread::time();
     150            0 : }
     151              : 
     152              : 
     153              : bool
     154            0 : GUIDanielPerspectiveChanger::onLeftBtnRelease(void* data) {
     155            0 :     myMouseButtonState &= ~MOUSEBTN_LEFT;
     156              :     FXEvent* e = (FXEvent*) data;
     157            0 :     myMouseXPosition = e->win_x;
     158            0 :     myMouseYPosition = e->win_y;
     159            0 :     return myMoveOnClick;
     160              : }
     161              : 
     162              : 
     163              : void
     164            0 : GUIDanielPerspectiveChanger::onMiddleBtnPress(void* data) {
     165            0 :     myMouseButtonState |= MOUSEBTN_MIDDLE;
     166              :     FXEvent* e = (FXEvent*) data;
     167            0 :     myMouseXPosition = e->win_x;
     168            0 :     myMouseYPosition = e->win_y;
     169            0 :     myMoveOnClick = false;
     170            0 :     myMouseDownTime = FXThread::time();
     171            0 :     myZoomBase = myCallback.getPositionInformation();
     172            0 : }
     173              : 
     174              : 
     175              : bool
     176            0 : GUIDanielPerspectiveChanger::onMiddleBtnRelease(void* data) {
     177            0 :     myMouseButtonState &= ~MOUSEBTN_MIDDLE;
     178              :     FXEvent* e = (FXEvent*) data;
     179            0 :     myMouseXPosition = e->win_x;
     180            0 :     myMouseYPosition = e->win_y;
     181            0 :     return myMoveOnClick;
     182              : }
     183              : 
     184              : 
     185              : void
     186            0 : GUIDanielPerspectiveChanger::onRightBtnPress(void* data) {
     187            0 :     myMouseButtonState |= MOUSEBTN_RIGHT;
     188              :     FXEvent* e = (FXEvent*) data;
     189            0 :     myMouseXPosition = e->win_x;
     190            0 :     myMouseYPosition = e->win_y;
     191            0 :     myMoveOnClick = false;
     192            0 :     myMouseDownTime = FXThread::time();
     193            0 :     myZoomBase = myCallback.getPositionInformation();
     194            0 : }
     195              : 
     196              : 
     197              : bool
     198            0 : GUIDanielPerspectiveChanger::onRightBtnRelease(void* data) {
     199            0 :     myMouseButtonState &= ~MOUSEBTN_RIGHT;
     200            0 :     if (data != nullptr) {
     201              :         FXEvent* e = (FXEvent*) data;
     202            0 :         myMouseXPosition = e->win_x;
     203            0 :         myMouseYPosition = e->win_y;
     204              :     }
     205            0 :     return myMoveOnClick;
     206              : }
     207              : 
     208              : 
     209              : void
     210            0 : GUIDanielPerspectiveChanger::onMouseWheel(void* data) {
     211              :     FXEvent* e = (FXEvent*) data;
     212              :     // catch empty ghost events after scroll (seem to occur only on Ubuntu)
     213            0 :     if (e->code == 0) {
     214              :         return;
     215              :     }
     216              :     // zoom scale relative delta and its inverse; is optimized (all literals)
     217              :     const double zScale_rDelta_norm = 0.1;
     218              :     const double zScale_rDelta_inv = -zScale_rDelta_norm / (1. + zScale_rDelta_norm);
     219              :     double zScale_rDelta = zScale_rDelta_norm ;
     220            0 :     if (e->code < 0) {
     221              :         // for inverse zooming direction
     222              :         zScale_rDelta = zScale_rDelta_inv;
     223              :     }
     224              :     // keyboard modifier: slow, fast mouse-zoom
     225            0 :     if ((e->state & CONTROLMASK) != 0) {
     226            0 :         zScale_rDelta /= 4;
     227            0 :     } else if ((e->state & SHIFTMASK) != 0) {
     228            0 :         zScale_rDelta *= 4;
     229              :     }
     230            0 :     myZoomBase = myCallback.getPositionInformation();
     231            0 :     zoom(1.0 + zScale_rDelta);
     232            0 :     myCallback.updateToolTip();
     233              : }
     234              : 
     235              : 
     236              : void
     237            0 : GUIDanielPerspectiveChanger::onMouseMove(void* data) {
     238              :     FXEvent* e = (FXEvent*) data;
     239            0 :     myCallback.setWindowCursorPosition(e->win_x, e->win_y);
     240            0 :     const int xdiff = myMouseXPosition - e->win_x;
     241            0 :     const int ydiff = myMouseYPosition - e->win_y;
     242            0 :     const bool moved = xdiff != 0 || ydiff != 0;
     243              :     // no drag events in gaming mode
     244            0 :     const bool pastDelay = !GUIMainWindow::getInstance()->isGaming() && FXThread::time() > (myMouseDownTime + myDragDelay);
     245            0 :     switch (myMouseButtonState) {
     246            0 :         case MOUSEBTN_LEFT:
     247              :         case MOUSEBTN_MIDDLE:
     248            0 :             if (pastDelay) {
     249            0 :                 if (myRotation != 0) {
     250            0 :                     Position diffRot = Position(xdiff, ydiff).rotateAround2D(
     251            0 :                                            DEG2RAD(myRotation), Position(0, 0));
     252            0 :                     move((int)diffRot.x(), (int)diffRot.y());
     253              :                 } else {
     254            0 :                     move(xdiff, ydiff);
     255              :                 }
     256            0 :                 if (moved) {
     257            0 :                     myMoveOnClick = true;
     258              :                 }
     259              :             }
     260              :             break;
     261            0 :         case MOUSEBTN_RIGHT:
     262            0 :             if (pastDelay) {
     263            0 :                 zoom(1 + 10.0 * ydiff / myCallback.getWidth());
     264            0 :                 rotate(xdiff);
     265            0 :                 if (moved) {
     266            0 :                     myMoveOnClick = true;
     267              :                 }
     268              :             }
     269              :             break;
     270            0 :         default:
     271            0 :             if (moved) {
     272            0 :                 myCallback.updateToolTip();
     273              :             }
     274              :             break;
     275              :     }
     276            0 :     myMouseXPosition = e->win_x;
     277            0 :     myMouseYPosition = e->win_y;
     278            0 : }
     279              : 
     280              : 
     281              : void
     282           16 : GUIDanielPerspectiveChanger::setViewport(double zoom,
     283              :         double xPos, double yPos) {
     284           16 :     const double zoomFactor = zoom / 50; // /100 to normalize, *2 because growth is added on both sides
     285           16 :     myViewPort = Boundary();
     286           16 :     myViewPort.add(Position(xPos, yPos));
     287           16 :     myViewPort.growHeight(myOrigHeight / zoomFactor);
     288           16 :     myViewPort.growWidth(myOrigWidth / zoomFactor);
     289           16 :     myCallback.update();
     290           16 : }
     291              : 
     292              : 
     293              : void
     294           16 : GUIDanielPerspectiveChanger::setViewportFrom(double xPos, double yPos, double zPos) {
     295           16 :     setViewport(zPos2Zoom(zPos), xPos, yPos);
     296           16 : }
     297              : 
     298              : 
     299              : void
     300           16 : GUIDanielPerspectiveChanger::setRotation(double rotation) {
     301           16 :     myRotation = rotation;
     302           16 : }
     303              : 
     304              : void
     305            0 : GUIDanielPerspectiveChanger::changeCanvasSizeLeft(int change) {
     306            0 :     myViewPort = Boundary(
     307            0 :                      myViewPort.xmin() - myCallback.p2m(change),
     308              :                      myViewPort.ymin(),
     309              :                      myViewPort.xmax(),
     310              :                      myViewPort.ymax());
     311            0 : }
     312              : 
     313              : 
     314              : long
     315            0 : GUIDanielPerspectiveChanger::onKeyPress(void* data) {
     316              :     // ignore key events in gaming mode
     317            0 :     if (GUIMainWindow::getInstance()->isGaming()) {
     318              :         return 0;
     319              :     }
     320              :     FXEvent* e = (FXEvent*) data;
     321              :     double zoomDiff = 0.1;
     322              :     double moveX = 0;
     323              :     double moveY = 0;
     324              :     double moveFactor = 1;
     325            0 :     if (e->state & CONTROLMASK) {
     326              :         zoomDiff /= 2;
     327              :         moveFactor /= 10;
     328            0 :     } else if (e->state & SHIFTMASK) {
     329              :         zoomDiff *= 2;
     330            0 :     } else if (e->state & ALTMASK) {
     331              :         moveFactor *= 10;
     332              :     }
     333            0 :     switch (e->code) {
     334            0 :         case FX::KEY_Left:
     335              :             moveX = -1;
     336            0 :             moveFactor /= 10;
     337            0 :             break;
     338            0 :         case FX::KEY_Right:
     339              :             moveX = 1;
     340            0 :             moveFactor /= 10;
     341            0 :             break;
     342            0 :         case FX::KEY_Up:
     343              :             moveY = -1;
     344            0 :             moveFactor /= 10;
     345            0 :             break;
     346            0 :         case FX::KEY_Down:
     347              :             moveY = 1;
     348            0 :             moveFactor /= 10;
     349            0 :             break;
     350            0 :         case FX::KEY_plus:
     351              :         case FX::KEY_KP_Add:
     352            0 :             myZoomBase = myCallback.getPositionInformation();
     353            0 :             zoom(1.0 + zoomDiff);
     354            0 :             myCallback.updateToolTip();
     355            0 :             return 1;
     356            0 :         case FX::KEY_minus:
     357              :         case FX::KEY_KP_Subtract:
     358              :             zoomDiff = -zoomDiff;
     359            0 :             myZoomBase = myCallback.getPositionInformation();
     360            0 :             zoom(1.0 + zoomDiff);
     361            0 :             myCallback.updateToolTip();
     362            0 :             return 1;
     363            0 :         case FX::KEY_Home:
     364              :         case FX::KEY_KP_Home:
     365            0 :             myCallback.recenterView();
     366            0 :             myCallback.update();
     367            0 :             return 1;
     368              :         default:
     369              :             return 0;
     370              :     }
     371            0 :     myViewPort.moveby(moveX * moveFactor * myViewPort.getWidth(),
     372            0 :                       -moveY * moveFactor * myViewPort.getHeight());
     373            0 :     myCallback.update();
     374            0 :     return 1;
     375              : }
     376              : 
     377              : 
     378              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1