ui: basic map scroller
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
package world
|
||||
|
||||
import "math"
|
||||
|
||||
// renderPointsStageA performs a full expanded-canvas redraw but renders ONLY Point primitives.
|
||||
func (w *World) renderPointsStageA(drawer PrimitiveDrawer, params RenderParams) error {
|
||||
plan, err := w.buildRenderPlanStageA(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
style := DefaultRenderStyle()
|
||||
if params.Options != nil && params.Options.Style != nil {
|
||||
style = *params.Options.Style
|
||||
}
|
||||
|
||||
applyPointStyle(drawer, style)
|
||||
drawPointsFromPlanWithRadius(drawer, plan, w.W, w.H, style.PointRadiusPx)
|
||||
return nil
|
||||
}
|
||||
|
||||
// drawPointsFromPlan keeps backward compatibility for older tests/helpers.
|
||||
func drawPointsFromPlan(drawer PrimitiveDrawer, plan RenderPlan) {
|
||||
// Default world sizes are unknown here, so this wrapper is no longer suitable for wrap-aware points.
|
||||
// Keep it for historical call sites only if they pass through Render().
|
||||
// Prefer calling drawPointsFromPlanWithRadius with world sizes.
|
||||
drawPointsFromPlanWithRadius(drawer, plan, 0, 0, DefaultRenderStyle().PointRadiusPx)
|
||||
}
|
||||
|
||||
// drawPointsFromPlanWithRadius executes a points-only draw from an already built render plan,
|
||||
// using the provided screen-space radius. If worldW/worldH are zero, wrap copies are disabled.
|
||||
func drawPointsFromPlanWithRadius(drawer PrimitiveDrawer, plan RenderPlan, worldW, worldH int, radiusPx float64) {
|
||||
// Convert screen radius to world-fixed conservatively (ceil), so wrap copies are not missed.
|
||||
rPxInt := int(math.Ceil(radiusPx))
|
||||
if rPxInt < 0 {
|
||||
rPxInt = 0
|
||||
}
|
||||
rWorldFp := 0
|
||||
if rPxInt > 0 {
|
||||
rWorldFp = PixelSpanToWorldFixed(rPxInt, plan.ZoomFp)
|
||||
}
|
||||
|
||||
for _, td := range plan.Tiles {
|
||||
if td.ClipW <= 0 || td.ClipH <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
points := make([]Point, 0, len(td.Candidates))
|
||||
for _, it := range td.Candidates {
|
||||
p, ok := it.(Point)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
points = append(points, p)
|
||||
}
|
||||
if len(points) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
type pointCopy struct {
|
||||
p Point
|
||||
dx int
|
||||
dy int
|
||||
}
|
||||
copiesToDraw := make([]pointCopy, 0, len(points))
|
||||
|
||||
for _, p := range points {
|
||||
shifts := pointWrapShifts(p, rWorldFp, worldW, worldH)
|
||||
for _, s := range shifts {
|
||||
if pointCopyIntersectsTile(p, rWorldFp, s.dx, s.dy, td.Tile) {
|
||||
copiesToDraw = append(copiesToDraw, pointCopy{p: p, dx: s.dx, dy: s.dy})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(copiesToDraw) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
drawer.Save()
|
||||
drawer.ClipRect(float64(td.ClipX), float64(td.ClipY), float64(td.ClipW), float64(td.ClipH))
|
||||
|
||||
for _, pc := range copiesToDraw {
|
||||
p := pc.p
|
||||
|
||||
px := worldSpanFixedToCanvasPx((p.X+td.Tile.OffsetX+pc.dx)-plan.WorldRect.minX, plan.ZoomFp)
|
||||
py := worldSpanFixedToCanvasPx((p.Y+td.Tile.OffsetY+pc.dy)-plan.WorldRect.minY, plan.ZoomFp)
|
||||
|
||||
drawer.AddPoint(float64(px), float64(py), radiusPx)
|
||||
}
|
||||
|
||||
drawer.Fill()
|
||||
drawer.Restore()
|
||||
}
|
||||
}
|
||||
|
||||
func pointWrapShifts(p Point, rWorldFp, worldW, worldH int) []wrapShift {
|
||||
// If world sizes are unknown, do not generate wrap copies.
|
||||
if worldW <= 0 || worldH <= 0 {
|
||||
return []wrapShift{{dx: 0, dy: 0}}
|
||||
}
|
||||
|
||||
xShifts := []int{0}
|
||||
yShifts := []int{0}
|
||||
|
||||
if p.X+rWorldFp >= worldW {
|
||||
xShifts = append(xShifts, -worldW)
|
||||
}
|
||||
if p.X-rWorldFp < 0 {
|
||||
xShifts = append(xShifts, worldW)
|
||||
}
|
||||
|
||||
if p.Y+rWorldFp >= worldH {
|
||||
yShifts = append(yShifts, -worldH)
|
||||
}
|
||||
if p.Y-rWorldFp < 0 {
|
||||
yShifts = append(yShifts, worldH)
|
||||
}
|
||||
|
||||
out := make([]wrapShift, 0, len(xShifts)*len(yShifts))
|
||||
for _, dx := range xShifts {
|
||||
for _, dy := range yShifts {
|
||||
out = append(out, wrapShift{dx: dx, dy: dy})
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func pointCopyIntersectsTile(p Point, rWorldFp, dx, dy int, tile WorldTile) bool {
|
||||
segMinX := tile.OffsetX + tile.Rect.minX
|
||||
segMaxX := tile.OffsetX + tile.Rect.maxX
|
||||
segMinY := tile.OffsetY + tile.Rect.minY
|
||||
segMaxY := tile.OffsetY + tile.Rect.maxY
|
||||
|
||||
px := p.X + tile.OffsetX + dx
|
||||
py := p.Y + tile.OffsetY + dy
|
||||
|
||||
minX := px - rWorldFp
|
||||
maxX := px + rWorldFp
|
||||
minY := py - rWorldFp
|
||||
maxY := py + rWorldFp
|
||||
|
||||
if maxX <= segMinX || minX >= segMaxX || maxY <= segMinY || minY >= segMaxY {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user