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