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 // } // allowWrap := params.Options == nil || !params.Options.DisableWrapScroll // applyPointStyle(drawer, style) // drawPointsFromPlanWithRadius(drawer, plan, w.W, w.H, style.PointRadiusPx, allowWrap) // return nil // } // drawPointsFromPlan keeps backward compatibility for older tests/helpers. func drawPointsFromPlan(drawer PrimitiveDrawer, plan RenderPlan, allowWrap bool) { // 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, allowWrap) } // 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, allowWrap bool) { // 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 { var shifts []wrapShift if allowWrap { shifts = pointWrapShifts(p, rWorldFp, worldW, worldH) } else { shifts = []wrapShift{{dx: 0, dy: 0}} } 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 }