draw optimizations

This commit is contained in:
IliaDenisov
2026-03-08 23:30:11 +02:00
parent fdcbb5d6f4
commit ac35360d60
18 changed files with 875 additions and 566 deletions
+54 -14
View File
@@ -1,5 +1,12 @@
package world
// lineSeg is one canonical segment (endpoints in [0..W) x [0..H)) to be drawn.
// It represents part of the torus-shortest polyline for a Line primitive after wrap splitting.
type lineSeg struct {
x1, y1 int
x2, y2 int
}
// drawPointInTile draws point marker copies that intersect the tile.
// lastStyle is already applied; it provides PointRadiusPx.
func (w *World) drawPointInTile(drawer PrimitiveDrawer, plan RenderPlan, td TileDrawPlan, p Point, allowWrap bool, lastStyle Style) {
@@ -46,9 +53,11 @@ func (w *World) drawCircleInTile(drawer PrimitiveDrawer, plan RenderPlan, td Til
var shifts []wrapShift
effRadius := circleRadiusEffFp(c.Radius, w.circleRadiusScaleFp)
if allowWrap {
shifts = circleWrapShifts(c.X, c.Y, effRadius, w.W, w.H)
shifts = circleWrapShiftsInto(w.scratchWrapShifts, c.X, c.Y, effRadius, w.W, w.H)
} else {
shifts = []wrapShift{{dx: 0, dy: 0}}
var one [1]wrapShift
one[0] = wrapShift{dx: 0, dy: 0}
shifts = one[:]
}
rPx := worldSpanFixedToCanvasPx(effRadius, plan.ZoomFp)
@@ -61,37 +70,68 @@ func (w *World) drawCircleInTile(drawer PrimitiveDrawer, plan RenderPlan, td Til
cxPx := worldSpanFixedToCanvasPx((c.X+td.Tile.OffsetX+s.dx)-plan.WorldRect.minX, plan.ZoomFp)
cyPx := worldSpanFixedToCanvasPx((c.Y+td.Tile.OffsetY+s.dy)-plan.WorldRect.minY, plan.ZoomFp)
drawer.AddCircle(float64(cxPx), float64(cyPx), float64(rPx))
fill := alphaNonZero(lastStyle.FillColor)
stroke := alphaNonZero(lastStyle.StrokeColor)
if fill {
switch {
case fill && stroke:
// gg consumes the current path on Fill/Stroke, so we must draw twice:
// once for fill, then again for stroke.
drawer.AddCircle(float64(cxPx), float64(cyPx), float64(rPx))
drawer.Fill()
}
if stroke {
// Stroke must be last when both are present.
drawer.AddCircle(float64(cxPx), float64(cyPx), float64(rPx))
drawer.Stroke()
case fill:
drawer.AddCircle(float64(cxPx), float64(cyPx), float64(rPx))
drawer.Fill()
case stroke:
drawer.AddCircle(float64(cxPx), float64(cyPx), float64(rPx))
drawer.Stroke()
default:
// neither visible => nothing
}
}
w.scratchWrapShifts = shifts[:0]
}
func (w *World) drawLineInTile(drawer PrimitiveDrawer, plan RenderPlan, td TileDrawPlan, l Line, allowWrap bool) {
var segs []lineSeg
func (w *World) drawLineInTilePath(drawer PrimitiveDrawer, plan RenderPlan, td TileDrawPlan, l Line, allowWrap bool) int {
segs := w.scratchLineSegs[:0]
tmp := w.scratchLineSegsTmp[:0]
if cap(segs) < 4 {
segs = make([]lineSeg, 0, 4)
}
if cap(tmp) < 4 {
tmp = make([]lineSeg, 0, 4)
}
if allowWrap {
segs = torusShortestLineSegments(l, w.W, w.H)
segs, tmp = torusShortestLineSegmentsInto(segs, tmp, l, w.W, w.H)
} else {
segs = []lineSeg{{x1: l.X1, y1: l.Y1, x2: l.X2, y2: l.Y2}}
var one [1]lineSeg
one[0] = lineSeg{x1: l.X1, y1: l.Y1, x2: l.X2, y2: l.Y2}
segs = one[:]
}
for _, s := range segs {
// Project into tile/canvas.
x1 := worldSpanFixedToCanvasPx((s.x1+td.Tile.OffsetX)-plan.WorldRect.minX, plan.ZoomFp)
y1 := worldSpanFixedToCanvasPx((s.y1+td.Tile.OffsetY)-plan.WorldRect.minY, plan.ZoomFp)
x2 := worldSpanFixedToCanvasPx((s.x2+td.Tile.OffsetX)-plan.WorldRect.minX, plan.ZoomFp)
y2 := worldSpanFixedToCanvasPx((s.y2+td.Tile.OffsetY)-plan.WorldRect.minY, plan.ZoomFp)
drawer.AddLine(float64(x1), float64(y1), float64(x2), float64(y2))
drawer.Stroke()
}
w.scratchLineSegs = segs[:0]
w.scratchLineSegsTmp = tmp[:0]
return len(segs)
}
func (w *World) drawLineInTile(drawer PrimitiveDrawer, plan RenderPlan, td TileDrawPlan, l Line, allowWrap bool) {
w.drawLineInTilePath(drawer, plan, td, l, allowWrap)
drawer.Stroke()
}