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,104 @@
package coast
import (
"testing"
"salty/terrain/internal/field"
)
// paintedSeaFixture is a straight coast whose whole sea is painted at one depth, the way a planet's ocean
// class is. The sea is made wide enough to hold the derived margin and a stretch of open water past it, so
// the test can ask the question that matters: how much of what the author painted survives.
func paintedSeaFixture(w, h, split int, cellM, backshoreM, paintedM float64) (*field.Field, []bool, []float32) {
f, sea := coastFixture(w, h, split, cellM, backshoreM)
abyss := make([]float32, w*h)
for i := range abyss {
if sea[i] {
abyss[i] = float32(paintedM)
}
}
return f, sea, abyss
}
// TestTheDerivedMarginDoesNotSwallowThePaintedOcean is the D-64 regression, stated as the property rather
// than as the number that was wrong.
//
// The sea floor near a shore is derived and the sea floor away from it is the painting; the break depth is
// what joins them. Set the break far shallower than the paint and the join stops being a join: the derived
// profile is then a shallow bench that runs from the waterline out to the full reach of the margin, and on a
// planet whose straits are narrower than twice that reach it *is* the ocean. That is not visible in a profile
// test - the shape is monotone and correct at any break depth - so this measures the volume instead.
//
// 512 m is the first template's `ocean` class. 30 m was the inherited square-canvas break, 130 m is the
// planet default.
func TestTheDerivedMarginDoesNotSwallowThePaintedOcean(t *testing.T) {
const w, h, split = 1400, 20, 1200
const cellM, painted = 8.0, 512.0
cfg := shelfOnlyCfg()
measure := func(breakM float64) (shallow float64, atBreak, far float64) {
f, sea, abyss := paintedSeaFixture(w, h, split, cellM, 5, painted)
Build(Input{Height: f, Sea: sea, SeaLevelM: 0, BreakM: breakM, AbyssM: painted,
Abyss: abyss, Seed: 7, Cfg: cfg})
y := h / 2
n, under50 := 0, 0
for x := 0; x < split; x++ {
n++
if -float64(f.Data[y*w+x]) < 50 {
under50++
}
}
// Just inside the widest shelf, and well past the shelf and the slope together.
shelfCells := int(cfg.ShelfKm.Hi()*1000/cellM) - 2
reachCells := int((cfg.ShelfKm.Hi() + cfg.SlopeKm) * 1000 / cellM)
return float64(under50) / float64(n),
-float64(f.Data[y*w+split-1-shelfCells]),
-float64(f.Data[y*w+split-1-reachCells-20])
}
oldShallow, oldBreak, oldFar := measure(30)
newShallow, newBreak, newFar := measure(130)
t.Logf("break 30 m: %.0f%% of this sea shallower than 50 m, %.0f m at the break, %.0f m offshore",
oldShallow*100, oldBreak, oldFar)
t.Logf("break 130 m: %.0f%% of this sea shallower than 50 m, %.0f m at the break, %.0f m offshore",
newShallow*100, newBreak, newFar)
// Both must reach the painting in open water: the margin is a join, never a replacement.
for _, c := range []struct {
name string
far float64
}{{"30 m", oldFar}, {"130 m", newFar}} {
if c.far < painted-2 {
t.Errorf("break %s: open water is %.0f m, want the painted %.0f m", c.name, c.far, painted)
}
}
// The break is where it was asked for, which is what makes it the knob worth having.
if newBreak < 110 || newBreak > 140 {
t.Errorf("the shelf break is at %.0f m, want about 130 m", newBreak)
}
// And the shallow bench shrinks. This is the whole defect: at a 30 m break every cell of the derived
// margin is shallower than 50 m by construction, so the bench is as wide as the margin reaches.
if !(newShallow < oldShallow*0.75) {
t.Errorf("shallow water is %.0f%% of the sea at a 130 m break against %.0f%% at 30 m; deepening the "+
"break has to shrink the bench or it is not doing anything", newShallow*100, oldShallow*100)
}
}
// TestAPaintedShallowStraitIsStillShallow is the other half, and it is what stops the fix above from being a
// blunt instrument: the break can never be deeper than the water it is a break in. An author who paints a
// 20 m surf class gets 20 m of water, not a 130 m trench dug through it.
func TestAPaintedShallowStraitIsStillShallow(t *testing.T) {
const w, h, split = 1400, 20, 1200
const cellM, painted = 8.0, 20.0
f, sea, abyss := paintedSeaFixture(w, h, split, cellM, 5, painted)
Build(Input{Height: f, Sea: sea, SeaLevelM: 0, BreakM: 130, AbyssM: painted,
Abyss: abyss, Seed: 7, Cfg: shelfOnlyCfg()})
y := h / 2
for x := 0; x < split; x++ {
if d := -float64(f.Data[y*w+x]); d > painted+1 {
t.Fatalf("%.0f m offshore: %.1f m of water over a sea painted at %.0f m",
float64(split-x)*cellM, d, painted)
}
}
}