extract the world<->widget transform into WorldCoordinates
This commit is contained in:
@@ -14,6 +14,7 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DisplayName.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BeltDragPath.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TunnelCompletion.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/WorldCoordinates.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -25,6 +26,7 @@ SET(SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DisplayName.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BeltDragPath.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/TunnelCompletion.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/WorldCoordinates.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
|
||||
78
src/lib/core/WorldCoordinates.cpp
Normal file
78
src/lib/core/WorldCoordinates.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "WorldCoordinates.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
WorldCoordinates::WorldCoordinates(QSize widgetSize_px, int worldHeight_tiles,
|
||||
float viewCenterX_tiles)
|
||||
: m_tilePx(1.0f)
|
||||
, m_viewportWidthTiles(0.0f)
|
||||
, m_viewLeftTiles(0.0f)
|
||||
, m_worldHeightTiles(worldHeight_tiles)
|
||||
{
|
||||
if (worldHeight_tiles > 0)
|
||||
{
|
||||
m_tilePx = static_cast<float>(widgetSize_px.height())
|
||||
/ static_cast<float>(worldHeight_tiles);
|
||||
}
|
||||
m_viewportWidthTiles = static_cast<float>(widgetSize_px.width()) / m_tilePx;
|
||||
m_viewLeftTiles = viewCenterX_tiles - m_viewportWidthTiles / 2.0f;
|
||||
}
|
||||
|
||||
float WorldCoordinates::getTilePx() const
|
||||
{
|
||||
return m_tilePx;
|
||||
}
|
||||
|
||||
float WorldCoordinates::getViewportWidthTiles() const
|
||||
{
|
||||
return m_viewportWidthTiles;
|
||||
}
|
||||
|
||||
float WorldCoordinates::getViewLeftTiles() const
|
||||
{
|
||||
return m_viewLeftTiles;
|
||||
}
|
||||
|
||||
QPointF WorldCoordinates::worldToWidget(QVector2D worldPos) const
|
||||
{
|
||||
return QPointF(
|
||||
static_cast<qreal>((worldPos.x() - m_viewLeftTiles) * m_tilePx),
|
||||
static_cast<qreal>(worldPos.y() * m_tilePx));
|
||||
}
|
||||
|
||||
QPointF WorldCoordinates::tileToWidget(QPoint tile) const
|
||||
{
|
||||
return worldToWidget(QVector2D(static_cast<float>(tile.x()),
|
||||
static_cast<float>(tile.y())));
|
||||
}
|
||||
|
||||
QPoint WorldCoordinates::widgetToTile(QPoint widgetPoint) const
|
||||
{
|
||||
const QVector2D world = widgetToWorld(widgetPoint);
|
||||
return QPoint(static_cast<int>(std::floor(world.x())),
|
||||
static_cast<int>(std::floor(world.y())));
|
||||
}
|
||||
|
||||
QVector2D WorldCoordinates::widgetToWorld(QPoint widgetPoint) const
|
||||
{
|
||||
return QVector2D(
|
||||
static_cast<float>(widgetPoint.x()) / m_tilePx + m_viewLeftTiles,
|
||||
static_cast<float>(widgetPoint.y()) / m_tilePx);
|
||||
}
|
||||
|
||||
QRectF WorldCoordinates::tileRect(QPoint tile) const
|
||||
{
|
||||
const QPointF topLeft = tileToWidget(tile);
|
||||
return QRectF(topLeft.x(), topLeft.y(),
|
||||
static_cast<qreal>(m_tilePx), static_cast<qreal>(m_tilePx));
|
||||
}
|
||||
|
||||
QRect WorldCoordinates::getViewportRect() const
|
||||
{
|
||||
const int left = static_cast<int>(std::floor(m_viewLeftTiles)) - 1;
|
||||
const int top = 0;
|
||||
const int right = static_cast<int>(
|
||||
std::ceil(m_viewLeftTiles + m_viewportWidthTiles)) + 1;
|
||||
const int bottom = m_worldHeightTiles;
|
||||
return QRect(left, top, right - left, bottom - top);
|
||||
}
|
||||
51
src/lib/core/WorldCoordinates.h
Normal file
51
src/lib/core/WorldCoordinates.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <QPoint>
|
||||
#include <QPointF>
|
||||
#include <QRect>
|
||||
#include <QRectF>
|
||||
#include <QSize>
|
||||
#include <QVector2D>
|
||||
|
||||
// Immutable snapshot of the world <-> widget transform for one viewport state
|
||||
// (REQ-GW-COORDS, REQ-GW-TILE-SIZE). Tiles are square and sized so the world
|
||||
// height exactly fills the viewport height; there is no zoom (REQ-UI-NO-ZOOM),
|
||||
// so the only free parameter is the horizontal scroll position.
|
||||
//
|
||||
// The transform is a value: it is constructed from the viewport size, the world
|
||||
// height, and the view center, and never observes them again. A caller therefore
|
||||
// builds one per frame (or per event) rather than holding one across a resize or
|
||||
// a scroll, which would silently go stale.
|
||||
class WorldCoordinates
|
||||
{
|
||||
public:
|
||||
// `viewCenterX_tiles` is the world X at the center of the viewport, matching
|
||||
// how the scroll position is stored and clamped (REQ-GW-SCROLL-LIMIT).
|
||||
WorldCoordinates(QSize widgetSize_px, int worldHeight_tiles,
|
||||
float viewCenterX_tiles);
|
||||
|
||||
// Side length of one tile in pixels. Degenerate world heights yield 1.0 so
|
||||
// callers never divide by zero.
|
||||
float getTilePx() const;
|
||||
float getViewportWidthTiles() const;
|
||||
// World X (tiles) at the left edge of the viewport, derived from the view
|
||||
// center the constructor was given.
|
||||
float getViewLeftTiles() const;
|
||||
|
||||
QPointF worldToWidget(QVector2D worldPos) const;
|
||||
QPointF tileToWidget(QPoint tile) const;
|
||||
QPoint widgetToTile(QPoint widgetPoint) const;
|
||||
QVector2D widgetToWorld(QPoint widgetPoint) const;
|
||||
// Widget-space rect covering the whole of `tile`.
|
||||
QRectF tileRect(QPoint tile) const;
|
||||
|
||||
// Tile-space rect of everything currently on screen, widened by one column on
|
||||
// each side so items straddling an edge are still drawn.
|
||||
QRect getViewportRect() const;
|
||||
|
||||
private:
|
||||
float m_tilePx;
|
||||
float m_viewportWidthTiles;
|
||||
float m_viewLeftTiles;
|
||||
int m_worldHeightTiles;
|
||||
};
|
||||
Reference in New Issue
Block a user