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>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
package calc_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"galaxy/calc"
|
||||
)
|
||||
|
||||
func TestShortestDelta(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
a, b float64
|
||||
size uint32
|
||||
want float64
|
||||
}{
|
||||
{"identity", 5, 5, 100, 0},
|
||||
{"forward small", 10, 30, 100, 20},
|
||||
{"backward small", 30, 10, 100, -20},
|
||||
{"wraps right edge", 95, 5, 100, 10},
|
||||
{"wraps left edge", 5, 95, 100, -10},
|
||||
{"exact half wraps positive", 0, 50, 100, 50},
|
||||
{"slight beyond half goes negative", 0, 51, 100, -49},
|
||||
{"size zero is degenerate identity", 5, 30, 0, 25},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := calc.ShortestDelta(tc.a, tc.b, tc.size)
|
||||
if got != tc.want {
|
||||
t.Fatalf("ShortestDelta(%v, %v, %d) = %v, want %v",
|
||||
tc.a, tc.b, tc.size, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDeltasRectangularGalaxy guards against the pre-Phase-19 bug
|
||||
// where the x-axis wrap used the height instead of the width on
|
||||
// rectangular maps.
|
||||
func TestDeltasRectangularGalaxy(t *testing.T) {
|
||||
w, h := uint32(100), uint32(50)
|
||||
dx, dy := calc.Deltas(w, h, 95, 10, 5, 15)
|
||||
if dx != 10 {
|
||||
t.Errorf("dx = %v, want 10 (wrap distance on width 100)", dx)
|
||||
}
|
||||
if dy != 5 {
|
||||
t.Errorf("dy = %v, want 5 (no wrap on height 50)", dy)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortDistanceTorus(t *testing.T) {
|
||||
got := calc.ShortDistance(100, 100, 95, 95, 5, 5)
|
||||
want := math.Hypot(10, 10)
|
||||
if math.Abs(got-want) > 1e-9 {
|
||||
t.Fatalf("ShortDistance through wrap = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user