108 lines
3.1 KiB
Go
108 lines
3.1 KiB
Go
package world
|
|
|
|
// RenderPlan describes the full expanded-canvas redraw plan for one RenderParams.
|
|
// It is a pure description: it does not execute any drawing.
|
|
type RenderPlan struct {
|
|
CanvasWidthPx int
|
|
CanvasHeightPx int
|
|
|
|
ZoomFp int
|
|
|
|
// WorldRect is the unwrapped world-space rect covered by the expanded canvas.
|
|
WorldRect Rect
|
|
|
|
// Tiles are ordered in the same order as produced by tileWorldRect:
|
|
// increasing tile X index, then increasing tile Y index.
|
|
Tiles []TileDrawPlan
|
|
}
|
|
|
|
// TileDrawPlan describes how to draw one torus tile contribution.
|
|
type TileDrawPlan struct {
|
|
Tile WorldTile
|
|
|
|
// Clip rect on the expanded canvas in pixel coordinates.
|
|
// It is half-open in spirit: [ClipX, ClipX+ClipW) x [ClipY, ClipY+ClipH).
|
|
ClipX int
|
|
ClipY int
|
|
ClipW int
|
|
ClipH int
|
|
|
|
// Candidates are unique per tile (deduped by ID).
|
|
Candidates []MapItem
|
|
}
|
|
|
|
// worldSpanFixedToCanvasPx converts a world fixed-point span into a canvas pixel span
|
|
// for the given fixed-point zoom. The conversion is truncating (floor).
|
|
func worldSpanFixedToCanvasPx(spanWorldFp, zoomFp int) int {
|
|
// spanWorldFp can be negative in some internal cases, but for clip computations
|
|
// we always pass non-negative spans.
|
|
return (spanWorldFp * zoomFp) / (SCALE * SCALE)
|
|
}
|
|
|
|
// buildRenderPlanStageA builds a full expanded-canvas redraw plan (Stage A).
|
|
//
|
|
// It assumes the world grid is already built (IndexOnViewportChange called).
|
|
// The plan contains per-tile clip rectangles and per-tile candidate lists
|
|
// from the spatial index.
|
|
func (w *World) buildRenderPlanStageA(params RenderParams) (RenderPlan, error) {
|
|
if err := params.Validate(); err != nil {
|
|
return RenderPlan{}, err
|
|
}
|
|
|
|
zoomFp, err := params.CameraZoomFp()
|
|
if err != nil {
|
|
return RenderPlan{}, err
|
|
}
|
|
|
|
worldRect, err := params.ExpandedCanvasWorldRect()
|
|
if err != nil {
|
|
return RenderPlan{}, err
|
|
}
|
|
|
|
tiles := tileWorldRect(worldRect, w.W, w.H)
|
|
|
|
// Query candidates per tile.
|
|
batches, err := w.collectCandidatesForTiles(tiles)
|
|
if err != nil {
|
|
return RenderPlan{}, err
|
|
}
|
|
|
|
planTiles := make([]TileDrawPlan, 0, len(batches))
|
|
|
|
for _, batch := range batches {
|
|
tile := batch.Tile
|
|
|
|
// Convert the tile's canonical rect + offsets into the unwrapped segment.
|
|
segMinX := tile.Rect.minX + tile.OffsetX
|
|
segMaxX := tile.Rect.maxX + tile.OffsetX
|
|
segMinY := tile.Rect.minY + tile.OffsetY
|
|
segMaxY := tile.Rect.maxY + tile.OffsetY
|
|
|
|
// Map that segment into expanded canvas pixel coordinates relative to worldRect.minX/minY.
|
|
clipX := worldSpanFixedToCanvasPx(segMinX-worldRect.minX, zoomFp)
|
|
clipY := worldSpanFixedToCanvasPx(segMinY-worldRect.minY, zoomFp)
|
|
clipX2 := worldSpanFixedToCanvasPx(segMaxX-worldRect.minX, zoomFp)
|
|
clipY2 := worldSpanFixedToCanvasPx(segMaxY-worldRect.minY, zoomFp)
|
|
|
|
clipW := clipX2 - clipX
|
|
clipH := clipY2 - clipY
|
|
|
|
planTiles = append(planTiles, TileDrawPlan{
|
|
Tile: tile,
|
|
ClipX: clipX,
|
|
ClipY: clipY,
|
|
ClipW: clipW,
|
|
ClipH: clipH,
|
|
Candidates: batch.Items,
|
|
})
|
|
}
|
|
|
|
return RenderPlan{
|
|
CanvasWidthPx: params.CanvasWidthPx(),
|
|
CanvasHeightPx: params.CanvasHeightPx(),
|
|
ZoomFp: zoomFp,
|
|
WorldRect: worldRect,
|
|
Tiles: planTiles,
|
|
}, nil
|
|
}
|