2020-05-23 08:19:10 +00:00
package play
import (
"opslag.de/schobers/geom"
)
// IsometricProjection represents an 2D area (view) that contains isometric tiles.
type IsometricProjection struct {
center geom . PointF32 // tile coordinate
zoom float32 // factor a tile is blown up (negative is smaller, possitive is larger)
zoomInverse float32 // 1/zoom; calculated
tileSize geom . PointF32 // size of a single tile (maximum width & height difference of its corners)
viewBounds geom . RectangleF32 // bounds of the view (screen coordinates)
viewCenter geom . PointF32 // center of view; calculated
tileSizeTransformed geom . PointF32 // calculated
tileToViewTransformation geom . PointF32 // calculated
viewToTileTransformation geom . PointF32 // calculated
}
// NewIsometricProjection creates a new isometric projection. By default the tile with the coordinate (0, 0) will be centered in the viewBounds. The tile size is represented with maximum width & height difference of its corners.
func NewIsometricProjection ( tileSize geom . PointF32 , viewBounds geom . RectangleF32 ) * IsometricProjection {
p := & IsometricProjection { zoom : 1 , tileSize : tileSize , viewBounds : viewBounds }
p . update ( )
return p
}
func ( p * IsometricProjection ) update ( ) {
if p . zoom == 0 {
p . zoom = 1
}
p . zoomInverse = 1 / p . zoom
p . viewCenter = p . viewBounds . Center ( )
p . tileSizeTransformed = p . tileSize . Mul ( p . zoom )
p . tileToViewTransformation = p . tileSize . Mul ( .5 * p . zoom )
p . viewToTileTransformation = geom . PtF32 ( 1 / p . tileSizeTransformed . X , 1 / p . tileSizeTransformed . Y )
}
// Center gives back the coordinate of the center tile
func ( p * IsometricProjection ) Center ( ) geom . PointF32 { return p . center }
// Enumerate enumerates all tiles in the set view bounds and calls action for every tile.
func ( p * IsometricProjection ) Enumerate ( action func ( tile geom . PointF32 , view geom . PointF32 ) ) {
p . EnumerateInt ( func ( tile geom . Point , view geom . PointF32 ) {
action ( tile . ToF32 ( ) , view )
} )
}
// EnumerateInt enumerates all tiles in the set view bounds and calls action for every tile.
func ( p * IsometricProjection ) EnumerateInt ( action func ( tile geom . Point , view geom . PointF32 ) ) {
visible := p . viewBounds
visible . Max . Y += p . tileSize . Y * p . zoom
topLeft := p . ViewToTile ( geom . PtF32 ( visible . Min . X , visible . Min . Y ) )
topRight := p . ViewToTile ( geom . PtF32 ( visible . Max . X , visible . Min . Y ) )
bottomLeft := p . ViewToTile ( geom . PtF32 ( visible . Min . X , visible . Max . Y ) )
bottomRight := p . ViewToTile ( geom . PtF32 ( visible . Max . X , visible . Max . Y ) )
minY , maxY := int ( geom . Floor32 ( topRight . Y ) ) , int ( geom . Ceil32 ( bottomLeft . Y ) )
minX , maxX := int ( geom . Floor32 ( topLeft . X ) ) , int ( geom . Ceil32 ( bottomRight . X ) )
tileOffset := p . tileSizeTransformed . Mul ( .5 )
for y := minY ; y <= maxY ; y ++ {
for x := minX ; x <= maxX ; x ++ {
tile := geom . Pt ( x , y )
view := p . TileToView ( tile . ToF32 ( ) )
if view . X + tileOffset . X < visible . Min . X || view . Y + tileOffset . Y < visible . Min . Y {
continue
}
if view . X - tileOffset . X > visible . Max . X || view . Y - tileOffset . Y > visible . Max . Y {
break
}
action ( tile , view )
}
}
}
// MoveCenterTo moves the center of the projection to the given tile.
func ( p * IsometricProjection ) MoveCenterTo ( tile geom . PointF32 ) {
p . center = tile
p . update ( )
}
// Pan translates the center of the projection with the given delta in view coordinates.
func ( p * IsometricProjection ) Pan ( delta geom . PointF32 ) {
2020-05-23 09:02:12 +00:00
p . PanTile ( p . ViewToTileRelative ( delta ) )
}
// PanTile translates the center of the projection with the given delta in tile coordinates.
func ( p * IsometricProjection ) PanTile ( delta geom . PointF32 ) {
p . MoveCenterTo ( p . center . Add ( delta ) )
2020-05-23 08:19:10 +00:00
}
// SetTileSize sets the size of a single tile (maximum width & height difference of its corners).
func ( p * IsometricProjection ) SetTileSize ( size geom . PointF32 ) {
p . tileSize = size
p . update ( )
}
// SetViewBounds sets the bounds of the view coordinates. Used to calculate the center with & for calculating the visible tiles.
func ( p * IsometricProjection ) SetViewBounds ( bounds geom . RectangleF32 ) {
p . viewBounds = bounds
p . update ( )
}
// SetZoom changes the zoom to and keeps the around (tile) coordinate on the same position.
func ( p * IsometricProjection ) SetZoom ( around geom . PointF32 , zoom float32 ) {
if p . zoom == zoom {
return
}
p . center = around . Sub ( around . Sub ( p . center ) . Mul ( p . zoom / zoom ) )
p . zoom = zoom
p . update ( )
}
// TileInt gives the integer tile coordinate.
func ( p * IsometricProjection ) TileInt ( tile geom . PointF32 ) geom . Point {
return geom . Pt ( int ( geom . Round32 ( tile . X ) ) , int ( geom . Round32 ( tile . Y ) ) )
}
// TileToView transforms the tile coordinate to the corresponding view coordinate.
func ( p * IsometricProjection ) TileToView ( tile geom . PointF32 ) geom . PointF32 {
translated := tile . Sub ( p . center )
return p . viewCenter . Add2D ( ( translated . X - translated . Y ) * p . tileToViewTransformation . X , ( translated . X + translated . Y ) * p . tileToViewTransformation . Y )
}
// ViewCenter returns the center of the view (calculated from the set view bounds).
func ( p * IsometricProjection ) ViewCenter ( ) geom . PointF32 { return p . viewCenter }
// ViewToTile transforms the view coordinate to the corresponding tile coordinate.
func ( p * IsometricProjection ) ViewToTile ( view geom . PointF32 ) geom . PointF32 {
return p . ViewToTileRelative ( view . Sub ( p . viewCenter ) ) . Add ( p . center )
}
// ViewToTileInt transforms the view coordinate to the corresponding integer tile coordinate.
func ( p * IsometricProjection ) ViewToTileInt ( view geom . PointF32 ) geom . Point {
tile := p . ViewToTile ( view )
return p . TileInt ( tile )
}
// ViewToTileRelative transforms the relative (to 0,0) view coordinate to the corresponding tile coordinate
func ( p * IsometricProjection ) ViewToTileRelative ( view geom . PointF32 ) geom . PointF32 {
return geom . PtF32 ( view . X * p . viewToTileTransformation . X + view . Y * p . viewToTileTransformation . Y , - view . X * p . viewToTileTransformation . X + view . Y * p . viewToTileTransformation . Y )
}
// Zoom returns the current zoom.
func ( p * IsometricProjection ) Zoom ( ) float32 { return p . zoom }
// ZoomIn zooms in around the given tile coordinate.
func ( p * IsometricProjection ) ZoomIn ( around geom . PointF32 ) {
if p . zoom >= 2 {
return
}
p . SetZoom ( around , 2 * p . zoom )
}
// ZoomOut zooms in around the given tile coordinate.
func ( p * IsometricProjection ) ZoomOut ( around geom . PointF32 ) {
if p . zoom <= .25 {
return
}
p . SetZoom ( around , .5 * p . zoom )
}