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 7103 : GUIDanielPerspectiveChanger::GUIDanielPerspectiveChanger(
37 7103 : GUISUMOAbstractView& callBack, const Boundary& viewPort) :
38 : GUIPerspectiveChanger(callBack, viewPort),
39 14206 : myOrigWidth(viewPort.getWidth()),
40 7103 : myOrigHeight(viewPort.getHeight()),
41 7103 : myRotation(0),
42 7103 : myMouseButtonState(MOUSEBTN_NONE),
43 7103 : myMoveOnClick(false),
44 7103 : myZoomBase(viewPort.getCenter()),
45 7103 : myDragDelay(0) {
46 7103 : }
47 :
48 :
49 14168 : 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 1072484 : GUIDanielPerspectiveChanger::getRotation() const {
88 1072484 : return myRotation;
89 : }
90 :
91 :
92 : double
93 6675 : GUIDanielPerspectiveChanger::getXPos() const {
94 6675 : return myViewPort.getCenter().x();
95 : }
96 :
97 :
98 : double
99 6675 : GUIDanielPerspectiveChanger::getYPos() const {
100 6675 : return myViewPort.getCenter().y();
101 : }
102 :
103 :
104 : double
105 0 : GUIDanielPerspectiveChanger::getZoom() const {
106 0 : return myOrigWidth / myViewPort.getWidth() * 100;
107 : }
108 :
109 :
110 : double
111 6674 : GUIDanielPerspectiveChanger::getZPos() const {
112 6674 : return myViewPort.getWidth();
113 : }
114 :
115 :
116 : double
117 6 : GUIDanielPerspectiveChanger::zoom2ZPos(double zoom) const {
118 6 : return myOrigWidth / (zoom / 100);
119 : }
120 :
121 :
122 : double
123 7 : GUIDanielPerspectiveChanger::zPos2Zoom(double zPos) const {
124 7 : return (myOrigWidth / zPos) * 100;
125 : }
126 :
127 :
128 : void
129 0 : GUIDanielPerspectiveChanger::centerTo(const Position& pos, double radius,
130 : bool applyZoom) {
131 0 : if (applyZoom) {
132 0 : myViewPort = Boundary();
133 0 : myViewPort.add(pos);
134 0 : myViewPort.grow(radius);
135 : } else {
136 0 : myViewPort.moveby(pos.x() - getXPos(), pos.y() - getYPos());
137 : }
138 0 : }
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 7 : GUIDanielPerspectiveChanger::setViewport(double zoom,
281 : double xPos, double yPos) {
282 7 : const double zoomFactor = zoom / 50; // /100 to normalize, *2 because growth is added on both sides
283 7 : myViewPort = Boundary();
284 7 : myViewPort.add(Position(xPos, yPos));
285 7 : myViewPort.growHeight(myOrigHeight / zoomFactor);
286 7 : myViewPort.growWidth(myOrigWidth / zoomFactor);
287 7 : myCallback.update();
288 7 : }
289 :
290 :
291 : void
292 7 : GUIDanielPerspectiveChanger::setViewportFrom(double xPos, double yPos, double zPos) {
293 7 : setViewport(zPos2Zoom(zPos), xPos, yPos);
294 7 : }
295 :
296 :
297 : void
298 7 : GUIDanielPerspectiveChanger::setRotation(double rotation) {
299 7 : myRotation = rotation;
300 7 : }
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 : /****************************************************************************/
|