package tins2020 import ( "log" "github.com/veandco/go-sdl2/sdl" ) type projection struct { center PointF zoom float32 zoomInv float32 windowRect 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) 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) *sdl.Rect { return &sdl.Rect{X: pos.X - p.tileFitScreenSize.X, Y: pos.Y - p.tileFitScreenSize.Y, W: 2 * p.tileFitScreenSize.X, H: 2 * p.tileFitScreenSize.Y} } func (p *projection) screenToTileRect(pos Point) *sdl.Rect { return &sdl.Rect{X: pos.X - p.tileScreenOffset.X, Y: pos.Y - p.tileScreenOffset.Y, W: p.tileScreenSize.X, H: 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.windowRect = RectSize(buttonBarWidth, 0, windowW-2*buttonBarWidth, windowH) } func (p *projection) visibleTiles(action func(int32, int32, Point)) { topLeft := p.screenToMap(p.windowRect.X, p.windowRect.Y) topRight := p.screenToMap(p.windowRect.X+p.windowRect.W, p.windowRect.Y) bottomLeft := p.screenToMap(p.windowRect.X, p.windowRect.Y+p.windowRect.H) bottomRight := p.screenToMap(p.windowRect.X+p.windowRect.W, p.windowRect.Y+p.windowRect.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 < p.windowRect.X || rectFit.Y+rectFit.H < p.windowRect.Y { continue } if rectFit.X > p.windowRect.X+p.windowRect.W || rectFit.Y > p.windowRect.Y+p.windowRect.H { break } action(x, y, pos) } } }