Rev 1220 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/********************************************************************************************
** iLand - an individual based forest landscape and disturbance model
** http://iland.boku.ac.at
** Copyright (C) 2009- Werner Rammer, Rupert Seidl
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
********************************************************************************************/
#include "global.h"
#include "viewport.h"
/** @class Viewport
Handles coordinaive transforation between grids (based on real-world metric coordinates).
The visible part of the grid is defined by the "viewport" (defaults to 100% of the grid).
The result coordinates are mapped into a "ScreenRect", which is a pixel-based viewing window.
*/
/// toWorld() converts the pixel-information (e.g. by an mouse event) to the corresponding real world coordinates (defined by viewport).
const QPointF Viewport::toWorld(const QPoint pixel)
{
QPointF p;
p.setX( pixel.x()/m_scale_worldtoscreen + m_delta_worldtoscreen.x());
p.setY( (m_screen.height() - pixel.y() )/m_scale_worldtoscreen + m_delta_worldtoscreen.y() );
return p;
}
/// toScreen() converts world coordinates in screen coordinates using the defined viewport.
const QPoint Viewport::toScreen(const QPointF p)
{
QPoint pixel;
pixel.setX( qRound( (p.x()-m_delta_worldtoscreen.x())* m_scale_worldtoscreen ) );
pixel.setY( m_screen.height() - 1 - qRound( (p.y()-m_delta_worldtoscreen.y() ) * m_scale_worldtoscreen ));
return pixel;
}
/// sets the screen rect; this also modifies the viewport.
void Viewport::setScreenRect(const QRect &viewrect)
{
if (m_screen!=viewrect) {
m_screen = viewrect;
m_viewport = viewrect;
zoomToAll();
}
}
/// show the full extent of the world.
void Viewport::zoomToAll()
{
// calculate move/scale so that world-rect maps entirely onto screen
double scale_x = m_screen.width() / m_world.width(); // pixel per meter in x
double scale_y = m_screen.height() / m_world.height(); // pixel per meter in y
double scale = qMin(scale_x, scale_y);
QPointF d;
if (scale_x < scale_y) {
// x-axis fills the screen; center in y-axis
d.setX(m_world.left());
int py_mid = m_screen.height()/2;
double world_mid = m_world.center().y();
d.setY( world_mid - py_mid/scale );
} else {
d.setY(m_world.top());
int px_mid = m_screen.width()/2;
double world_mid = m_world.center().x();
d.setX( world_mid - px_mid/scale );
}
m_delta_worldtoscreen = d;
m_scale_worldtoscreen = scale;
m_viewport.setBottomLeft(toWorld(m_screen.topLeft()));
m_viewport.setTopRight(toWorld(m_screen.bottomRight()));
}
/// zoom using a factor of @p factor. Values > 1 means zoom out, < 1 zoom in. (factor=1 would have no effect).
/// after zooming, the world-point under the mouse @p screen_point is still under the mouse.
void Viewport::zoomTo(const QPoint &screen_point, const double factor)
{
QPointF focus_point = toWorld(screen_point); // point under the mouse
m_viewport.setWidth(m_viewport.width() * factor);
m_viewport.setHeight(m_viewport.height() * factor);
m_scale_worldtoscreen /= factor;
// get scale/delta
QPointF new_focus = toWorld(screen_point);
m_delta_worldtoscreen -= (new_focus - focus_point);
m_viewport.setBottomLeft(toWorld(m_screen.topLeft()));
m_viewport.setTopRight(toWorld(m_screen.bottomRight()));
//qDebug() <<"oldf"<< new_focus << "newf" << focus_point << "m_delta" << m_delta_worldtoscreen << "m_scale:" << m_scale_worldtoscreen << "viewport:"<<m_viewport;
}
/// move the viewport. @p screen_from and @p screen_to give mouse positions (in pixel) from dragging the mouse.
void Viewport::moveTo(const QPoint &screen_from, const QPoint &screen_to)
{
QPointF p1 = toWorld(screen_from);
QPointF p2 = toWorld(screen_to);
m_delta_worldtoscreen -= (p2-p1);
// correct the viewport
m_viewport.setBottomLeft(toWorld(m_screen.topLeft()));
m_viewport.setTopRight(toWorld(m_screen.bottomRight()));
}
/// set 'world_center' as the new center point of the viewport
void Viewport::setViewPoint(const QPointF &world_center, const double px_per_meter)
{
QPoint p = toScreen(world_center); // point where world_center would be
QPoint target = m_screen.center();
moveTo(p,target);
double px_p_m = qMax(px_per_meter, 0.001);
double factor = m_scale_worldtoscreen / px_p_m;
zoomTo(target, factor);
}
bool Viewport::isVisible(const QPointF &world_coord) const
{
return m_viewport.contains(world_coord);
}
bool Viewport::isVisible(const QRectF &world_rect) const
{
return m_viewport.contains(world_rect)
|| m_viewport.intersects(world_rect);
}