This commit is contained in:
Rainer Leit
2026-09-25 17:02:24 +03:00
parent cc43ed8dc8
commit 9597629951
2149 changed files with 460234 additions and 1770 deletions
@@ -0,0 +1,94 @@
package planet
import (
"math"
"testing"
"salty/terrain/internal/template"
)
// The profile is the whole of what a crater is, so it is worth pinning down: shore at sea level, a crest, an
// inner wall, a floor, and nothing anywhere that is not monotonic on its own segment.
func TestCraterProfileIsRimThenFloor(t *testing.T) {
c := template.Crater{RimM: 340, FloorM: 60, RimAt: 0.30, WallAt: 0.62}
if got := craterProfile(c, 0, 0); got != 0 {
t.Errorf("at the shore = %.1f m, want sea level: the island keeps the outline that was painted", got)
}
if got := craterProfile(c, 0, c.RimAt); math.Abs(got-c.RimM) > 1e-9 {
t.Errorf("at rim_at = %.1f m, want the rim %.1f", got, c.RimM)
}
if got := craterProfile(c, 0, c.WallAt); math.Abs(got-c.FloorM) > 1e-9 {
t.Errorf("at wall_at = %.1f m, want the floor %.1f", got, c.FloorM)
}
if got := craterProfile(c, 0, 1); got != c.FloorM {
t.Errorf("at the centre = %.1f m, want the floor %.1f", got, c.FloorM)
}
// Rising to the crest, falling to the floor, and the floor is dry.
prev := craterProfile(c, 0, 0)
for i := 1; i <= 30; i++ {
v := craterProfile(c, 0, c.RimAt*float64(i)/30)
if v < prev-1e-9 {
t.Fatalf("the outer flank dips at t = %.3f", c.RimAt*float64(i)/30)
}
prev = v
}
prev = craterProfile(c, 0, c.RimAt)
for i := 1; i <= 30; i++ {
v := craterProfile(c, 0, c.RimAt+(c.WallAt-c.RimAt)*float64(i)/30)
if v > prev+1e-9 {
t.Fatalf("the inner wall rises at t = %.3f", c.RimAt+(c.WallAt-c.RimAt)*float64(i)/30)
}
prev = v
}
if c.FloorM <= 0 {
t.Error("this crater's floor is not above sea level, which is what was asked for")
}
}
// A blob is normalised by its own widest point, so the same numbers describe a small crater and a large one.
func TestCraterComponentsMeasureEachBlobsOwnRadius(t *testing.T) {
const w, h = 40, 12
mask := make([]bool, w*h)
dist := make([]float64, w*h)
set := func(x0, y0, wid, hei int, d float64) {
for y := y0; y < y0+hei; y++ {
for x := x0; x < x0+wid; x++ {
mask[y*w+x] = true
dist[y*w+x] = d
}
}
}
set(2, 2, 6, 6, 3) // a blob whose widest point is 3
set(20, 4, 4, 4, 1.5) // and a smaller one at 1.5
wrap := func(x int) int { return ((x % w) + w) % w }
comp, maxDist, n := craterComponents(mask, dist, w, h, wrap)
if n != 2 {
t.Fatalf("found %d blobs, want 2", n)
}
a := maxDist[comp[3*w+3]]
b := maxDist[comp[5*w+21]]
if a != 3 || b != 1.5 {
t.Errorf("radii %.1f and %.1f, want 3 and 1.5", a, b)
}
}
// A crater on the seam is one crater, not two half craters with two different radii.
func TestACraterOnTheSeamIsOneBlob(t *testing.T) {
const w, h = 40, 12
mask := make([]bool, w*h)
dist := make([]float64, w*h)
for y := 4; y < 8; y++ {
for _, x := range []int{38, 39, 0, 1} {
mask[y*w+x] = true
dist[y*w+x] = 2
}
}
wrap := func(x int) int { return ((x % w) + w) % w }
_, _, n := craterComponents(mask, dist, w, h, wrap)
if n != 1 {
t.Fatalf("found %d blobs across the seam, want 1", n)
}
}