126 lines
3.7 KiB
Go
126 lines
3.7 KiB
Go
package tins2020
|
|
|
|
import (
|
|
"log"
|
|
|
|
"github.com/veandco/go-sdl2/sdl"
|
|
)
|
|
|
|
func mapToTile(q PointF) Point {
|
|
return Pt(int32(Round32(q.X)), int32(Round32(q.Y)))
|
|
}
|
|
|
|
type projection struct {
|
|
center PointF
|
|
zoom float32
|
|
zoomInv float32
|
|
|
|
windowInteractRect Rectangle
|
|
windowVisibleRect Rectangle
|
|
tileScreenDelta PointF
|
|
tileScreenDeltaInv PointF
|
|
tileScreenOffset Point
|
|
tileScreenSize Point
|
|
tileFitScreenSize Point
|
|
windowCenter Point
|
|
}
|
|
|
|
func newProjection() projection {
|
|
return projection{zoom: 1, tileScreenDelta: PtF(64, 32), tileScreenDeltaInv: PtF(1./64, 1./32)}
|
|
}
|
|
|
|
func (p *projection) mapToScreen(x, y int32) Point {
|
|
return p.mapToScreenF(float32(x), float32(y))
|
|
}
|
|
|
|
func (p *projection) mapToScreenF(x, y float32) Point {
|
|
translated := PtF(x-p.center.X, y-p.center.Y)
|
|
return Pt(p.windowCenter.X+int32((translated.X-translated.Y)*64*p.zoom), p.windowCenter.Y+int32((translated.X+translated.Y)*32*p.zoom))
|
|
}
|
|
|
|
func (p *projection) screenToMap(x, y int32) PointF {
|
|
pos := p.screenToMapRel(x-p.windowCenter.X, y-p.windowCenter.Y)
|
|
return p.center.Add(pos)
|
|
}
|
|
|
|
func (p *projection) screenToMapInt(x, y int32) Point {
|
|
pos := p.screenToMap(x, y)
|
|
return mapToTile(pos)
|
|
}
|
|
|
|
func (p *projection) screenToMapRel(x, y int32) PointF {
|
|
normX := p.zoomInv * float32(x)
|
|
normY := p.zoomInv * float32(y)
|
|
return PtF(.5*(p.tileScreenDeltaInv.X*normX+p.tileScreenDeltaInv.Y*normY), .5*(-p.tileScreenDeltaInv.X*normX+p.tileScreenDeltaInv.Y*normY))
|
|
}
|
|
|
|
func (p *projection) screenToTileFitRect(pos Point) Rectangle {
|
|
return Rect(pos.X-p.tileFitScreenSize.X, pos.Y-p.tileFitScreenSize.Y, 2*p.tileFitScreenSize.X, 2*p.tileFitScreenSize.Y)
|
|
}
|
|
|
|
func (p *projection) screenToTileRect(pos Point) Rectangle {
|
|
return Rect(pos.X-p.tileScreenOffset.X, pos.Y-p.tileScreenOffset.Y, p.tileScreenSize.X, p.tileScreenSize.Y)
|
|
}
|
|
|
|
func (p *projection) update(renderer *sdl.Renderer) {
|
|
p.zoomInv = 1 / p.zoom
|
|
|
|
p.tileScreenOffset = Pt(int32(p.zoom*64), int32(p.zoom*112))
|
|
p.tileScreenSize = Pt(int32(p.zoom*128), int32(p.zoom*160))
|
|
p.tileFitScreenSize = Pt(int32(p.zoom*64), int32(p.zoom*32))
|
|
|
|
windowW, windowH, err := renderer.GetOutputSize()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
p.windowCenter = Pt(windowW/2, windowH/2)
|
|
p.windowInteractRect = RectAbs(buttonBarWidth, 64, windowW-buttonBarWidth, windowH)
|
|
p.windowVisibleRect = RectAbs(buttonBarWidth, 0, windowW-buttonBarWidth, windowH+p.tileScreenSize.Y) // Adding a tile height to the bottom for trees that stick out from the cells below.
|
|
}
|
|
|
|
func (p *projection) visibleTiles(action func(int32, int32, Point)) {
|
|
visible := p.windowVisibleRect
|
|
topLeft := p.screenToMap(visible.X, visible.Y)
|
|
topRight := p.screenToMap(visible.X+visible.W, visible.Y)
|
|
bottomLeft := p.screenToMap(visible.X, visible.Y+visible.H)
|
|
bottomRight := p.screenToMap(visible.X+visible.W, visible.Y+visible.H)
|
|
minY, maxY := int32(Floor32(topRight.Y)), int32(Ceil32(bottomLeft.Y))
|
|
minX, maxX := int32(Floor32(topLeft.X)), int32(Ceil32(bottomRight.X))
|
|
for y := minY; y <= maxY; y++ {
|
|
for x := minX; x <= maxX; x++ {
|
|
pos := p.mapToScreen(x, y)
|
|
rectFit := p.screenToTileFitRect(pos)
|
|
if rectFit.X+rectFit.W < visible.X || rectFit.Y+rectFit.H < visible.Y {
|
|
continue
|
|
}
|
|
if rectFit.X > visible.X+visible.W || rectFit.Y > visible.Y+visible.H {
|
|
break
|
|
}
|
|
action(x, y, pos)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *projection) ZoomOut(ctx *Context, center PointF) {
|
|
if p.zoom <= .25 {
|
|
return
|
|
}
|
|
p.SetZoom(ctx, center, .5*p.zoom)
|
|
}
|
|
|
|
func (p *projection) ZoomIn(ctx *Context, center PointF) {
|
|
if p.zoom >= 2 {
|
|
return
|
|
}
|
|
p.SetZoom(ctx, center, 2*p.zoom)
|
|
}
|
|
|
|
func (p *projection) SetZoom(ctx *Context, center PointF, zoom float32) {
|
|
if p.zoom == zoom {
|
|
return
|
|
}
|
|
p.center = center.Sub(center.Sub(p.center).Mul(p.zoom / zoom))
|
|
p.zoom = zoom
|
|
p.update(ctx.Renderer)
|
|
}
|