148 lines
3.9 KiB
Go
148 lines
3.9 KiB
Go
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
|
|
}
|