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 : /****************************************************************************/
|