Path: blob/main/src/utils/gui/windows/GUIDanielPerspectiveChanger.cpp
169684 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GUIDanielPerspectiveChanger.cpp14/// @author Daniel Krajzewicz15/// @author Jakob Erdmann16/// @author Michael Behrisch17/// @date Sept 200218///19// A class that allows to steer the visual output in dependence to20/****************************************************************************/21#include <config.h>2223#include <fxkeys.h>24#include <utils/geom/Boundary.h>25#include <utils/geom/Position.h>26#include <utils/geom/GeomHelper.h>27#include <utils/gui/settings/GUICompleteSchemeStorage.h>28#include "GUIPerspectiveChanger.h"29#include "GUIDanielPerspectiveChanger.h"303132// ===========================================================================33// method definitions34// ===========================================================================35GUIDanielPerspectiveChanger::GUIDanielPerspectiveChanger(36GUISUMOAbstractView& callBack, const Boundary& viewPort) :37GUIPerspectiveChanger(callBack, viewPort),38myOrigWidth(viewPort.getWidth()),39myOrigHeight(viewPort.getHeight()),40myRotation(0),41myMouseButtonState(MOUSEBTN_NONE),42myMoveOnClick(false),43myZoomBase(viewPort.getCenter()),44myDragDelay(0) {45}464748GUIDanielPerspectiveChanger::~GUIDanielPerspectiveChanger() {}495051void52GUIDanielPerspectiveChanger::move(int xdiff, int ydiff) {53myViewPort.moveby(myCallback.p2m(xdiff), -myCallback.p2m(ydiff));54myCallback.update();55}565758void59GUIDanielPerspectiveChanger::zoom(double factor) {60if (myCallback.getApp()->reg().readIntEntry("gui", "zoomAtCenter", 0)) {61myZoomBase = myViewPort.getCenter();62}63if (factor > 0) {64myViewPort = Boundary(65myZoomBase.x() - (myZoomBase.x() - myViewPort.xmin()) / factor,66myZoomBase.y() - (myZoomBase.y() - myViewPort.ymin()) / factor,67myZoomBase.x() - (myZoomBase.x() - myViewPort.xmax()) / factor,68myZoomBase.y() - (myZoomBase.y() - myViewPort.ymax()) / factor);69myCallback.update();70}71}727374void75GUIDanielPerspectiveChanger::rotate(int /* diff */) {76/*77if (myCallback.allowRotation()) {78myRotation += (double) diff / (double) 10.0;79myCallback.update();80}81*/82}838485double86GUIDanielPerspectiveChanger::getRotation() const {87return myRotation;88}899091double92GUIDanielPerspectiveChanger::getXPos() const {93return myViewPort.getCenter().x();94}959697double98GUIDanielPerspectiveChanger::getYPos() const {99return myViewPort.getCenter().y();100}101102103double104GUIDanielPerspectiveChanger::getZoom() const {105return myOrigWidth / myViewPort.getWidth() * 100;106}107108109double110GUIDanielPerspectiveChanger::getZPos() const {111return myViewPort.getWidth();112}113114115double116GUIDanielPerspectiveChanger::zoom2ZPos(double zoom) const {117return myOrigWidth / (zoom / 100);118}119120121double122GUIDanielPerspectiveChanger::zPos2Zoom(double zPos) const {123return (myOrigWidth / zPos) * 100;124}125126127void128GUIDanielPerspectiveChanger::centerTo(const Position& pos, double radius,129bool applyZoom) {130if (applyZoom) {131myViewPort = Boundary();132myViewPort.add(pos);133myViewPort.grow(radius);134} else {135myViewPort.moveby(pos.x() - getXPos(), pos.y() - getYPos());136}137}138139140void141GUIDanielPerspectiveChanger::onLeftBtnPress(void* data) {142myMouseButtonState |= MOUSEBTN_LEFT;143FXEvent* e = (FXEvent*) data;144myMouseXPosition = e->win_x;145myMouseYPosition = e->win_y;146myMoveOnClick = false;147myMouseDownTime = FXThread::time();148}149150151bool152GUIDanielPerspectiveChanger::onLeftBtnRelease(void* data) {153myMouseButtonState &= ~MOUSEBTN_LEFT;154FXEvent* e = (FXEvent*) data;155myMouseXPosition = e->win_x;156myMouseYPosition = e->win_y;157return myMoveOnClick;158}159160161void162GUIDanielPerspectiveChanger::onMiddleBtnPress(void* data) {163myMouseButtonState |= MOUSEBTN_MIDDLE;164FXEvent* e = (FXEvent*) data;165myMouseXPosition = e->win_x;166myMouseYPosition = e->win_y;167myMoveOnClick = false;168myMouseDownTime = FXThread::time();169myZoomBase = myCallback.getPositionInformation();170}171172173bool174GUIDanielPerspectiveChanger::onMiddleBtnRelease(void* data) {175myMouseButtonState &= ~MOUSEBTN_MIDDLE;176FXEvent* e = (FXEvent*) data;177myMouseXPosition = e->win_x;178myMouseYPosition = e->win_y;179return myMoveOnClick;180}181182183void184GUIDanielPerspectiveChanger::onRightBtnPress(void* data) {185myMouseButtonState |= MOUSEBTN_RIGHT;186FXEvent* e = (FXEvent*) data;187myMouseXPosition = e->win_x;188myMouseYPosition = e->win_y;189myMoveOnClick = false;190myMouseDownTime = FXThread::time();191myZoomBase = myCallback.getPositionInformation();192}193194195bool196GUIDanielPerspectiveChanger::onRightBtnRelease(void* data) {197myMouseButtonState &= ~MOUSEBTN_RIGHT;198if (data != nullptr) {199FXEvent* e = (FXEvent*) data;200myMouseXPosition = e->win_x;201myMouseYPosition = e->win_y;202}203return myMoveOnClick;204}205206207void208GUIDanielPerspectiveChanger::onMouseWheel(void* data) {209FXEvent* e = (FXEvent*) data;210// catch empty ghost events after scroll (seem to occur only on Ubuntu)211if (e->code == 0) {212return;213}214// zoom scale relative delta and its inverse; is optimized (all literals)215const double zScale_rDelta_norm = 0.1;216const double zScale_rDelta_inv = -zScale_rDelta_norm / (1. + zScale_rDelta_norm);217double zScale_rDelta = zScale_rDelta_norm ;218if (e->code < 0) {219// for inverse zooming direction220zScale_rDelta = zScale_rDelta_inv;221}222// keyboard modifier: slow, fast mouse-zoom223if ((e->state & CONTROLMASK) != 0) {224zScale_rDelta /= 4;225} else if ((e->state & SHIFTMASK) != 0) {226zScale_rDelta *= 4;227}228myZoomBase = myCallback.getPositionInformation();229zoom(1.0 + zScale_rDelta);230myCallback.updateToolTip();231}232233234void235GUIDanielPerspectiveChanger::onMouseMove(void* data) {236FXEvent* e = (FXEvent*) data;237myCallback.setWindowCursorPosition(e->win_x, e->win_y);238const int xdiff = myMouseXPosition - e->win_x;239const int ydiff = myMouseYPosition - e->win_y;240const bool moved = xdiff != 0 || ydiff != 0;241const bool pastDelay = !gSchemeStorage.getDefault().gaming && FXThread::time() > (myMouseDownTime + myDragDelay);242switch (myMouseButtonState) {243case MOUSEBTN_LEFT:244case MOUSEBTN_MIDDLE:245if (pastDelay) {246if (myRotation != 0) {247Position diffRot = Position(xdiff, ydiff).rotateAround2D(248DEG2RAD(myRotation), Position(0, 0));249move((int)diffRot.x(), (int)diffRot.y());250} else {251move(xdiff, ydiff);252}253if (moved) {254myMoveOnClick = true;255}256}257break;258case MOUSEBTN_RIGHT:259if (pastDelay) {260zoom(1 + 10.0 * ydiff / myCallback.getWidth());261rotate(xdiff);262if (moved) {263myMoveOnClick = true;264}265}266break;267default:268if (moved) {269myCallback.updateToolTip();270}271break;272}273myMouseXPosition = e->win_x;274myMouseYPosition = e->win_y;275}276277278void279GUIDanielPerspectiveChanger::setViewport(double zoom,280double xPos, double yPos) {281const double zoomFactor = zoom / 50; // /100 to normalize, *2 because growth is added on both sides282myViewPort = Boundary();283myViewPort.add(Position(xPos, yPos));284myViewPort.growHeight(myOrigHeight / zoomFactor);285myViewPort.growWidth(myOrigWidth / zoomFactor);286myCallback.update();287}288289290void291GUIDanielPerspectiveChanger::setViewportFrom(double xPos, double yPos, double zPos) {292setViewport(zPos2Zoom(zPos), xPos, yPos);293}294295296void297GUIDanielPerspectiveChanger::setRotation(double rotation) {298myRotation = rotation;299}300301void302GUIDanielPerspectiveChanger::changeCanvasSizeLeft(int change) {303myViewPort = Boundary(304myViewPort.xmin() - myCallback.p2m(change),305myViewPort.ymin(),306myViewPort.xmax(),307myViewPort.ymax());308}309310311long312GUIDanielPerspectiveChanger::onKeyPress(void* data) {313// ignore key events in gaming mode314if (gSchemeStorage.getDefault().gaming) {315return 0;316}317FXEvent* e = (FXEvent*) data;318double zoomDiff = 0.1;319double moveX = 0;320double moveY = 0;321double moveFactor = 1;322if (e->state & CONTROLMASK) {323zoomDiff /= 2;324moveFactor /= 10;325} else if (e->state & SHIFTMASK) {326zoomDiff *= 2;327} else if (e->state & ALTMASK) {328moveFactor *= 10;329}330switch (e->code) {331case FX::KEY_Left:332moveX = -1;333moveFactor /= 10;334break;335case FX::KEY_Right:336moveX = 1;337moveFactor /= 10;338break;339case FX::KEY_Up:340moveY = -1;341moveFactor /= 10;342break;343case FX::KEY_Down:344moveY = 1;345moveFactor /= 10;346break;347case FX::KEY_plus:348case FX::KEY_KP_Add:349myZoomBase = myCallback.getPositionInformation();350zoom(1.0 + zoomDiff);351myCallback.updateToolTip();352return 1;353case FX::KEY_minus:354case FX::KEY_KP_Subtract:355zoomDiff = -zoomDiff;356myZoomBase = myCallback.getPositionInformation();357zoom(1.0 + zoomDiff);358myCallback.updateToolTip();359return 1;360case FX::KEY_Home:361case FX::KEY_KP_Home:362myCallback.recenterView();363myCallback.update();364return 1;365default:366return 0;367}368myViewPort.moveby(moveX * moveFactor * myViewPort.getWidth(),369-moveY * moveFactor * myViewPort.getHeight());370myCallback.update();371return 1;372}373374375/****************************************************************************/376377378