2020-05-09 14:48:39 +00:00
package tins2020
import (
2020-05-17 08:56:56 +00:00
"opslag.de/schobers/geom"
"opslag.de/schobers/zntg/ui"
2020-05-09 14:48:39 +00:00
)
2020-05-17 08:56:56 +00:00
func mapToTile ( q geom . PointF32 ) geom . Point {
return geom . Pt ( int ( geom . Round32 ( q . X ) ) , int ( geom . Round32 ( q . Y ) ) )
2020-05-11 01:09:01 +00:00
}
2020-05-09 14:48:39 +00:00
type projection struct {
2020-05-17 08:56:56 +00:00
center geom . PointF32
2020-05-09 14:48:39 +00:00
zoom float32
zoomInv float32
2020-05-17 08:56:56 +00:00
windowInteractRect geom . Rectangle
windowVisibleRect geom . Rectangle
tileScreenDelta geom . PointF32
tileScreenDeltaInv geom . PointF32
tileScreenOffset geom . Point
tileScreenSize geom . Point
tileFitScreenSize geom . Point
windowCenter geom . Point
2020-05-09 14:48:39 +00:00
}
func newProjection ( ) projection {
2020-05-17 08:56:56 +00:00
return projection { zoom : 1 , tileScreenDelta : geom . PtF32 ( 64 , 32 ) , tileScreenDeltaInv : geom . PtF32 ( 1. / 64 , 1. / 32 ) }
2020-05-09 14:48:39 +00:00
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) mapToScreen ( x , y int ) geom . Point {
2020-05-09 14:48:39 +00:00
return p . mapToScreenF ( float32 ( x ) , float32 ( y ) )
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) mapToScreenF ( x , y float32 ) geom . Point {
translated := geom . PtF32 ( x - p . center . X , y - p . center . Y )
return geom . Pt ( p . windowCenter . X + int ( ( translated . X - translated . Y ) * 64 * p . zoom ) , p . windowCenter . Y + int ( ( translated . X + translated . Y ) * 32 * p . zoom ) )
2020-05-09 14:48:39 +00:00
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) screenToMap ( x , y int ) geom . PointF32 {
2020-05-09 14:48:39 +00:00
pos := p . screenToMapRel ( x - p . windowCenter . X , y - p . windowCenter . Y )
return p . center . Add ( pos )
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) screenToMapInt ( x , y int ) geom . Point {
2020-05-10 18:44:20 +00:00
pos := p . screenToMap ( x , y )
2020-05-11 01:09:01 +00:00
return mapToTile ( pos )
2020-05-10 18:44:20 +00:00
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) screenToMapRel ( x , y int ) geom . PointF32 {
2020-05-10 10:40:44 +00:00
normX := p . zoomInv * float32 ( x )
normY := p . zoomInv * float32 ( y )
2020-05-17 08:56:56 +00:00
return geom . PtF32 ( .5 * ( p . tileScreenDeltaInv . X * normX + p . tileScreenDeltaInv . Y * normY ) , .5 * ( - p . tileScreenDeltaInv . X * normX + p . tileScreenDeltaInv . Y * normY ) )
2020-05-09 14:48:39 +00:00
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) screenToTileFitRect ( pos geom . Point ) geom . Rectangle {
return geom . RectRel ( pos . X - p . tileFitScreenSize . X , pos . Y - p . tileFitScreenSize . Y , 2 * p . tileFitScreenSize . X , 2 * p . tileFitScreenSize . Y )
2020-05-09 14:48:39 +00:00
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) screenToTileRect ( pos geom . Point ) geom . Rectangle {
return geom . RectRel ( pos . X - p . tileScreenOffset . X , pos . Y - p . tileScreenOffset . Y , p . tileScreenSize . X , p . tileScreenSize . Y )
2020-05-09 14:48:39 +00:00
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) update ( renderer ui . Renderer ) {
2020-05-09 14:48:39 +00:00
p . zoomInv = 1 / p . zoom
2020-05-17 08:56:56 +00:00
p . tileScreenOffset = geom . Pt ( int ( p . zoom * 64 ) , int ( p . zoom * 112 ) )
p . tileScreenSize = geom . Pt ( int ( p . zoom * 128 ) , int ( p . zoom * 160 ) )
p . tileFitScreenSize = geom . Pt ( int ( p . zoom * 64 ) , int ( p . zoom * 32 ) )
2020-05-09 14:48:39 +00:00
2020-05-17 08:56:56 +00:00
windowF32 := renderer . Size ( )
window := geom . Pt ( int ( windowF32 . X ) , int ( windowF32 . Y ) )
p . windowCenter = geom . Pt ( window . X / 2 , window . Y / 2 )
p . windowInteractRect = geom . Rect ( buttonBarWidth , 64 , window . X - buttonBarWidth , window . Y )
p . windowVisibleRect = geom . Rect ( buttonBarWidth , 0 , window . X - buttonBarWidth , window . Y + p . tileScreenSize . Y ) // Adding a tile height to the bottom for trees that stick out from the cells below.
2020-05-09 14:48:39 +00:00
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) visibleTiles ( action func ( int , int , geom . Point ) ) {
2020-05-10 15:16:18 +00:00
visible := p . windowVisibleRect
2020-05-17 08:56:56 +00:00
topLeft := p . screenToMap ( visible . Min . X , visible . Min . Y )
topRight := p . screenToMap ( visible . Max . X , visible . Min . Y )
bottomLeft := p . screenToMap ( visible . Min . X , visible . Max . Y )
bottomRight := p . screenToMap ( visible . Max . Y , visible . Max . Y )
minY , maxY := int ( Floor32 ( topRight . Y ) ) , int ( Ceil32 ( bottomLeft . Y ) )
minX , maxX := int ( Floor32 ( topLeft . X ) ) , int ( Ceil32 ( bottomRight . X ) )
2020-05-09 14:48:39 +00:00
for y := minY ; y <= maxY ; y ++ {
for x := minX ; x <= maxX ; x ++ {
pos := p . mapToScreen ( x , y )
rectFit := p . screenToTileFitRect ( pos )
2020-05-17 08:56:56 +00:00
if rectFit . Max . X < visible . Min . X || rectFit . Max . Y < visible . Min . Y {
2020-05-09 14:48:39 +00:00
continue
}
2020-05-17 08:56:56 +00:00
if rectFit . Min . X > visible . Max . X || rectFit . Min . Y > visible . Max . Y {
2020-05-09 14:48:39 +00:00
break
}
action ( x , y , pos )
}
}
}
2020-05-11 10:05:54 +00:00
2020-05-17 08:56:56 +00:00
func ( p * projection ) Pan ( ctx ui . Context , delta geom . PointF32 ) {
2020-05-14 05:47:29 +00:00
p . center = p . center . Add ( delta . Mul ( p . zoomInv ) )
2020-05-17 08:56:56 +00:00
p . update ( ctx . Renderer ( ) )
2020-05-14 05:47:29 +00:00
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) SetZoom ( ctx ui . Context , center geom . PointF32 , zoom float32 ) {
2020-05-14 05:47:29 +00:00
if p . zoom == zoom {
return
}
p . center = center . Sub ( center . Sub ( p . center ) . Mul ( p . zoom / zoom ) )
p . zoom = zoom
2020-05-17 08:56:56 +00:00
p . update ( ctx . Renderer ( ) )
2020-05-14 05:47:29 +00:00
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) ZoomOut ( ctx ui . Context , center geom . PointF32 ) {
2020-05-11 10:05:54 +00:00
if p . zoom <= .25 {
return
}
p . SetZoom ( ctx , center , .5 * p . zoom )
}
2020-05-17 08:56:56 +00:00
func ( p * projection ) ZoomIn ( ctx ui . Context , center geom . PointF32 ) {
2020-05-11 10:05:54 +00:00
if p . zoom >= 2 {
return
}
p . SetZoom ( ctx , center , 2 * p . zoom )
}