Files
Ilia Denisov d63fe44618 pkg/calc: fix Deltas wrap on rectangular maps + add signed ShortestDelta
The pre-existing `Deltas` helper used the height to wrap the x-axis,
which silently produced wrong values on any rectangular galaxy
(`w != h`). Square galaxies — the only configuration the engine
ships today — masked the bug, so it stayed in tree.

`Deltas` is now a thin wrapper around the new `ShortestDelta(a, b,
size)`, which returns the signed per-axis shortest delta on a 1-D
circle (range `(-size/2, size/2]`). The signed flavour is what the
Phase 19 ship-group renderer needs to draw an IncomingGroup
trajectory across the torus seam; `Deltas` continues to return the
pair of absolute deltas for distance computation.

Adds `pkg/calc/map_test.go` with table-driven coverage for both
helpers, including a regression that exercises the rectangular
case the bug was hiding behind, and the half-circumference
tie-break.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 15:08:16 +02:00

40 lines
1.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package calc
import "math"
// ShortDistance returns the shortest Euclidean distance between two
// points on a torus of size w×h.
func ShortDistance(w, h uint32, x1, y1, x2, y2 float64) float64 {
return math.Hypot(Deltas(w, h, x1, y1, x2, y2))
}
// Deltas returns the per-axis absolute distance between two points on
// a torus of size w×h. Each axis wraps independently: the x-axis
// against width w, the y-axis against height h. The returned values
// are always non-negative; combine via math.Hypot for the Euclidean
// torus distance, or use [ShortestDelta] when the signed direction
// matters (for example when drawing a wrap-aware line).
func Deltas(w, h uint32, x1, y1, x2, y2 float64) (float64, float64) {
return math.Abs(ShortestDelta(x1, x2, w)), math.Abs(ShortestDelta(y1, y2, h))
}
// ShortestDelta returns the signed delta (b - a) on a 1-D circle of
// circumference size, picking whichever direction has the shorter
// absolute distance. The result lies in (-size/2, size/2]: at exactly
// half the circumference the function returns +size/2 so the tie-case
// is deterministic regardless of input order.
func ShortestDelta(a, b float64, size uint32) float64 {
if size == 0 {
return b - a
}
s := float64(size)
d := math.Mod(b-a, s)
half := s / 2
if d > half {
d -= s
} else if d <= -half {
d += s
}
return d
}