Rev 1221 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1211 | werner | 1 | /******************************************************************************************** |
2 | ** iLand - an individual based forest landscape and disturbance model |
||
3 | ** http://iland.boku.ac.at |
||
4 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl |
||
5 | ** |
||
6 | ** This program is free software: you can redistribute it and/or modify |
||
7 | ** it under the terms of the GNU General Public License as published by |
||
8 | ** the Free Software Foundation, either version 3 of the License, or |
||
9 | ** (at your option) any later version. |
||
10 | ** |
||
11 | ** This program is distributed in the hope that it will be useful, |
||
12 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | ** GNU General Public License for more details. |
||
15 | ** |
||
16 | ** You should have received a copy of the GNU General Public License |
||
17 | ** along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
18 | ********************************************************************************************/ |
||
808 | werner | 19 | #include "global.h" |
20 | #include "viewport.h" |
||
21 | |||
22 | |||
23 | /** @class Viewport |
||
24 | Handles coordinaive transforation between grids (based on real-world metric coordinates). |
||
25 | The visible part of the grid is defined by the "viewport" (defaults to 100% of the grid). |
||
26 | The result coordinates are mapped into a "ScreenRect", which is a pixel-based viewing window. |
||
27 | */ |
||
28 | |||
29 | /// toWorld() converts the pixel-information (e.g. by an mouse event) to the corresponding real world coordinates (defined by viewport). |
||
30 | const QPointF Viewport::toWorld(const QPoint pixel) |
||
31 | { |
||
32 | QPointF p; |
||
33 | p.setX( pixel.x()/m_scale_worldtoscreen + m_delta_worldtoscreen.x()); |
||
34 | p.setY( (m_screen.height() - pixel.y() )/m_scale_worldtoscreen + m_delta_worldtoscreen.y() ); |
||
35 | return p; |
||
36 | |||
37 | } |
||
38 | |||
39 | /// toScreen() converts world coordinates in screen coordinates using the defined viewport. |
||
40 | const QPoint Viewport::toScreen(const QPointF p) |
||
41 | { |
||
42 | QPoint pixel; |
||
43 | pixel.setX( qRound( (p.x()-m_delta_worldtoscreen.x())* m_scale_worldtoscreen ) ); |
||
44 | pixel.setY( m_screen.height() - 1 - qRound( (p.y()-m_delta_worldtoscreen.y() ) * m_scale_worldtoscreen )); |
||
45 | return pixel; |
||
46 | } |
||
47 | |||
48 | /// sets the screen rect; this also modifies the viewport. |
||
49 | void Viewport::setScreenRect(const QRect &viewrect) |
||
50 | { |
||
51 | if (m_screen!=viewrect) { |
||
52 | m_screen = viewrect; |
||
53 | m_viewport = viewrect; |
||
54 | zoomToAll(); |
||
55 | } |
||
56 | } |
||
57 | |||
58 | /// show the full extent of the world. |
||
59 | void Viewport::zoomToAll() |
||
60 | { |
||
61 | // calculate move/scale so that world-rect maps entirely onto screen |
||
62 | double scale_x = m_screen.width() / m_world.width(); // pixel per meter in x |
||
63 | double scale_y = m_screen.height() / m_world.height(); // pixel per meter in y |
||
64 | double scale = qMin(scale_x, scale_y); |
||
65 | QPointF d; |
||
66 | if (scale_x < scale_y) { |
||
67 | // x-axis fills the screen; center in y-axis |
||
68 | d.setX(m_world.left()); |
||
69 | int py_mid = m_screen.height()/2; |
||
70 | double world_mid = m_world.center().y(); |
||
71 | d.setY( world_mid - py_mid/scale ); |
||
72 | } else { |
||
73 | d.setY(m_world.top()); |
||
74 | int px_mid = m_screen.width()/2; |
||
75 | double world_mid = m_world.center().x(); |
||
76 | d.setX( world_mid - px_mid/scale ); |
||
77 | } |
||
78 | m_delta_worldtoscreen = d; |
||
79 | m_scale_worldtoscreen = scale; |
||
80 | m_viewport.setBottomLeft(toWorld(m_screen.topLeft())); |
||
81 | m_viewport.setTopRight(toWorld(m_screen.bottomRight())); |
||
82 | } |
||
83 | |||
84 | /// zoom using a factor of @p factor. Values > 1 means zoom out, < 1 zoom in. (factor=1 would have no effect). |
||
85 | /// after zooming, the world-point under the mouse @p screen_point is still under the mouse. |
||
86 | void Viewport::zoomTo(const QPoint &screen_point, const double factor) |
||
87 | { |
||
88 | QPointF focus_point = toWorld(screen_point); // point under the mouse |
||
89 | |||
90 | m_viewport.setWidth(m_viewport.width() * factor); |
||
91 | m_viewport.setHeight(m_viewport.height() * factor); |
||
92 | |||
93 | m_scale_worldtoscreen /= factor; |
||
94 | |||
95 | // get scale/delta |
||
96 | QPointF new_focus = toWorld(screen_point); |
||
97 | m_delta_worldtoscreen -= (new_focus - focus_point); |
||
98 | |||
99 | m_viewport.setBottomLeft(toWorld(m_screen.topLeft())); |
||
100 | m_viewport.setTopRight(toWorld(m_screen.bottomRight())); |
||
101 | |||
102 | //qDebug() <<"oldf"<< new_focus << "newf" << focus_point << "m_delta" << m_delta_worldtoscreen << "m_scale:" << m_scale_worldtoscreen << "viewport:"<<m_viewport; |
||
103 | } |
||
104 | |||
105 | /// move the viewport. @p screen_from and @p screen_to give mouse positions (in pixel) from dragging the mouse. |
||
106 | void Viewport::moveTo(const QPoint &screen_from, const QPoint &screen_to) |
||
107 | { |
||
108 | QPointF p1 = toWorld(screen_from); |
||
109 | QPointF p2 = toWorld(screen_to); |
||
110 | m_delta_worldtoscreen -= (p2-p1); |
||
111 | // correct the viewport |
||
112 | m_viewport.setBottomLeft(toWorld(m_screen.topLeft())); |
||
113 | m_viewport.setTopRight(toWorld(m_screen.bottomRight())); |
||
114 | } |
||
115 | |||
116 | /// set 'world_center' as the new center point of the viewport |
||
117 | void Viewport::setViewPoint(const QPointF &world_center, const double px_per_meter) |
||
118 | { |
||
119 | QPoint p = toScreen(world_center); // point where world_center would be |
||
120 | QPoint target = m_screen.center(); |
||
121 | moveTo(p,target); |
||
1016 | werner | 122 | double px_p_m = qMax(px_per_meter, 0.001); |
123 | double factor = m_scale_worldtoscreen / px_p_m; |
||
808 | werner | 124 | zoomTo(target, factor); |
125 | } |
||
126 | |||
127 | bool Viewport::isVisible(const QPointF &world_coord) const |
||
128 | { |
||
129 | return m_viewport.contains(world_coord); |
||
130 | } |
||
131 | bool Viewport::isVisible(const QRectF &world_rect) const |
||
132 | { |
||
133 | return m_viewport.contains(world_rect) |
||
134 | || m_viewport.intersects(world_rect); |
||
135 | } |
||
136 |