150 lines
3.2 KiB
Go
150 lines
3.2 KiB
Go
package world
|
|
|
|
import (
|
|
"sort"
|
|
)
|
|
|
|
// drawKind is used only for stable tie-breaking when priorities are equal.
|
|
type drawKind int
|
|
|
|
const (
|
|
drawKindLine drawKind = iota
|
|
drawKindCircle
|
|
drawKindPoint
|
|
)
|
|
|
|
type drawItem struct {
|
|
kind drawKind
|
|
priority int
|
|
id PrimitiveID
|
|
styleID StyleID
|
|
|
|
// Exactly one of these is set.
|
|
p *Point
|
|
c *Circle
|
|
l *Line
|
|
}
|
|
|
|
// drawPlanSinglePass renders a plan using a single ordered pass per tile.
|
|
// Items in each tile are sorted by (Priority asc, Kind asc, ID asc) for determinism.
|
|
//
|
|
// allowWrap controls torus behavior:
|
|
// - true: circles/points produce wrap copies, lines use torus-shortest segments
|
|
// - false: no copies, lines drawn directly as stored
|
|
func (w *World) drawPlanSinglePass(drawer PrimitiveDrawer, plan RenderPlan, allowWrap bool) {
|
|
var lastStyleID StyleID = StyleIDInvalid
|
|
var lastStyle Style
|
|
|
|
applyStyle := func(styleID StyleID) {
|
|
if styleID == lastStyleID {
|
|
return
|
|
}
|
|
s, ok := w.styles.Get(styleID)
|
|
if !ok {
|
|
// Unknown style ID is a programming/config error.
|
|
panic("render: unknown style ID")
|
|
}
|
|
|
|
// Apply style state. Some fields may be nil intentionally.
|
|
if s.FillColor != nil {
|
|
drawer.SetFillColor(s.FillColor)
|
|
}
|
|
if s.StrokeColor != nil {
|
|
drawer.SetStrokeColor(s.StrokeColor)
|
|
}
|
|
drawer.SetLineWidth(s.StrokeWidthPx)
|
|
if len(s.StrokeDashes) > 0 {
|
|
drawer.SetDash(s.StrokeDashes...)
|
|
} else {
|
|
// Ensure solid line when switching from dashed style.
|
|
drawer.SetDash()
|
|
}
|
|
drawer.SetDashOffset(s.StrokeDashOffset)
|
|
|
|
lastStyleID = styleID
|
|
lastStyle = s
|
|
}
|
|
|
|
for _, td := range plan.Tiles {
|
|
if td.ClipW <= 0 || td.ClipH <= 0 {
|
|
continue
|
|
}
|
|
|
|
// Collect items for this tile.
|
|
items := make([]drawItem, 0, len(td.Candidates))
|
|
|
|
for _, it := range td.Candidates {
|
|
switch v := it.(type) {
|
|
case Point:
|
|
vv := v
|
|
items = append(items, drawItem{
|
|
kind: drawKindPoint,
|
|
priority: vv.Priority,
|
|
id: vv.Id,
|
|
styleID: vv.StyleID,
|
|
p: &vv,
|
|
})
|
|
case Circle:
|
|
vv := v
|
|
items = append(items, drawItem{
|
|
kind: drawKindCircle,
|
|
priority: vv.Priority,
|
|
id: vv.Id,
|
|
styleID: vv.StyleID,
|
|
c: &vv,
|
|
})
|
|
case Line:
|
|
vv := v
|
|
items = append(items, drawItem{
|
|
kind: drawKindLine,
|
|
priority: vv.Priority,
|
|
id: vv.Id,
|
|
styleID: vv.StyleID,
|
|
l: &vv,
|
|
})
|
|
default:
|
|
// Unknown map items should not exist.
|
|
panic("render: unknown map item type")
|
|
}
|
|
}
|
|
|
|
if len(items) == 0 {
|
|
continue
|
|
}
|
|
|
|
sort.Slice(items, func(i, j int) bool {
|
|
a, b := items[i], items[j]
|
|
if a.priority != b.priority {
|
|
return a.priority < b.priority
|
|
}
|
|
if a.kind != b.kind {
|
|
return a.kind < b.kind
|
|
}
|
|
return a.id < b.id
|
|
})
|
|
|
|
drawer.Save()
|
|
drawer.ClipRect(float64(td.ClipX), float64(td.ClipY), float64(td.ClipW), float64(td.ClipH))
|
|
|
|
for _, di := range items {
|
|
applyStyle(di.styleID)
|
|
|
|
switch di.kind {
|
|
case drawKindPoint:
|
|
w.drawPointInTile(drawer, plan, td, *di.p, allowWrap, lastStyle)
|
|
|
|
case drawKindCircle:
|
|
w.drawCircleInTile(drawer, plan, td, *di.c, allowWrap, lastStyle)
|
|
|
|
case drawKindLine:
|
|
w.drawLineInTile(drawer, plan, td, *di.l, allowWrap)
|
|
|
|
default:
|
|
panic("render: unknown draw kind")
|
|
}
|
|
}
|
|
|
|
drawer.Restore()
|
|
}
|
|
}
|