138 lines
4.1 KiB
Go
138 lines
4.1 KiB
Go
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) {
|
|
rPx := lastStyle.PointRadiusPx
|
|
if rPx <= 0 {
|
|
// Nothing visible.
|
|
return
|
|
}
|
|
|
|
// Convert screen radius to world-fixed conservatively.
|
|
rWorldFp := PixelSpanToWorldFixed(int(rPx+0.999999), plan.ZoomFp)
|
|
|
|
var shifts []wrapShift
|
|
if allowWrap {
|
|
shifts = pointWrapShifts(p, rWorldFp, w.W, w.H)
|
|
} else {
|
|
shifts = []wrapShift{{dx: 0, dy: 0}}
|
|
}
|
|
|
|
for _, s := range shifts {
|
|
if allowWrap && !pointCopyIntersectsTile(p, rWorldFp, s.dx, s.dy, td.Tile) {
|
|
continue
|
|
}
|
|
|
|
px := worldSpanFixedToCanvasPx((p.X+td.Tile.OffsetX+s.dx)-plan.WorldRect.minX, plan.ZoomFp)
|
|
py := worldSpanFixedToCanvasPx((p.Y+td.Tile.OffsetY+s.dy)-plan.WorldRect.minY, plan.ZoomFp)
|
|
|
|
drawer.AddPoint(float64(px), float64(py), rPx)
|
|
|
|
fill := alphaNonZero(lastStyle.FillColor)
|
|
stroke := alphaNonZero(lastStyle.StrokeColor)
|
|
|
|
if fill {
|
|
drawer.Fill()
|
|
}
|
|
if stroke {
|
|
// Stroke must be last when both are present.
|
|
drawer.Stroke()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *World) drawCircleInTile(drawer PrimitiveDrawer, plan RenderPlan, td TileDrawPlan, c Circle, allowWrap bool, lastStyle Style) {
|
|
var shifts []wrapShift
|
|
effRadius := circleRadiusEffFp(c.Radius, w.circleRadiusScaleFp)
|
|
if allowWrap {
|
|
shifts = circleWrapShiftsInto(w.scratchWrapShifts, c.X, c.Y, effRadius, w.W, w.H)
|
|
} else {
|
|
var one [1]wrapShift
|
|
one[0] = wrapShift{dx: 0, dy: 0}
|
|
shifts = one[:]
|
|
}
|
|
|
|
rPx := worldSpanFixedToCanvasPx(effRadius, plan.ZoomFp)
|
|
|
|
for _, s := range shifts {
|
|
if allowWrap && !circleCopyIntersectsTile(c.X, c.Y, effRadius, s.dx, s.dy, td.Tile, w.W, w.H) {
|
|
continue
|
|
}
|
|
|
|
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)
|
|
|
|
fill := alphaNonZero(lastStyle.FillColor)
|
|
stroke := alphaNonZero(lastStyle.StrokeColor)
|
|
|
|
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()
|
|
|
|
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) 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, tmp = torusShortestLineSegmentsInto(segs, tmp, l, w.W, w.H)
|
|
} else {
|
|
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 {
|
|
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))
|
|
}
|
|
|
|
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()
|
|
}
|