Added: Initial world generation tool
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
package check
|
||||
|
||||
import (
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"salty/terrain/internal/fluvial"
|
||||
"salty/terrain/internal/manifest"
|
||||
"salty/terrain/internal/thermal"
|
||||
"salty/terrain/internal/uplift"
|
||||
)
|
||||
|
||||
// TestTalusHoldsInThePipeline is the gap between a unit test and a system. thermal.Apply demonstrably cuts a
|
||||
// cone to the angle of repose, and yet a full run with the angle set to 22 degrees reports a median land slope
|
||||
// of 37. One of those two things is wrong and the difference matters: the angle of repose is the only direct
|
||||
// lever on how steep hill country looks.
|
||||
func TestTalusHoldsInThePipeline(t *testing.T) {
|
||||
const (
|
||||
size = 300
|
||||
deg = 22.0
|
||||
steps = 300
|
||||
)
|
||||
passesUnderTest := 3
|
||||
if v := os.Getenv("TALUS_PASSES"); v != "" {
|
||||
passesUnderTest, _ = strconv.Atoi(v)
|
||||
}
|
||||
m := manifest.Defaults()
|
||||
m.Source.Seed = 7
|
||||
cellM := m.SideM() / float64(size-1)
|
||||
up := uplift.Build(size, cellM, m)
|
||||
h := up.Height.Clone()
|
||||
|
||||
g := fluvial.NewGrid(size, size, cellM, up.Base)
|
||||
g.SetElevationRange(-2000, 4000)
|
||||
talus := thermal.TalusFromDegrees(deg)
|
||||
g.Run(h.Data, up.Rate.Data, up.K.Data, fluvial.Params{
|
||||
K: m.Pipeline.Fluvial.K, M: 0.5, N: 1, DtYr: 1500, Steps: steps,
|
||||
Diffusion: 0.02, FillEvery: 1,
|
||||
TalusSlope: talus, ThermalEvery: 1, ThermalPasses: passesUnderTest,
|
||||
}, nil)
|
||||
|
||||
if os.Getenv("CLAMP_AFTER") != "" {
|
||||
t.Logf("removed %.2f m mean by a final clamp", g.ClampToRepose(h.Data, talus))
|
||||
}
|
||||
|
||||
// The steepest neighbour drop anywhere on land, in the same units the angle of repose is written in.
|
||||
worst, count := 0.0, 0
|
||||
wx, wy := 0, 0
|
||||
worstOther := float32(0)
|
||||
worstOtherBase := false
|
||||
for y := 1; y < size-1; y++ {
|
||||
for x := 1; x < size-1; x++ {
|
||||
i := y*size + x
|
||||
if up.Base[i] {
|
||||
continue
|
||||
}
|
||||
for _, d := range [][2]int{{1, 0}, {0, 1}, {1, 1}, {1, -1}} {
|
||||
ni := (y+d[1])*size + (x + d[0])
|
||||
if up.Base[ni] {
|
||||
continue
|
||||
}
|
||||
dist := cellM
|
||||
if d[0] != 0 && d[1] != 0 {
|
||||
dist = cellM * math.Sqrt2
|
||||
}
|
||||
if s := math.Abs(float64(h.Data[i]-h.Data[ni])) / dist; s > worst {
|
||||
worst = s
|
||||
wx, wy = x, y
|
||||
worstOther = h.Data[ni]
|
||||
worstOtherBase = up.Base[ni]
|
||||
}
|
||||
}
|
||||
if count++; false {
|
||||
_ = count
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Logf("worst pair at (%d,%d): h=%.1f vs h=%.1f, base=%v/%v", wx, wy, h.Data[wy*size+wx], worstOther, up.Base[wy*size+wx], worstOtherBase)
|
||||
gotDeg := math.Atan(worst) * 180 / math.Pi
|
||||
t.Logf("steepest land slope after %d steps: %.1f deg (repose %.1f, cell %.0f m)", steps, gotDeg, deg, cellM)
|
||||
if gotDeg > deg+5 {
|
||||
t.Errorf("steepest land slope is %.1f deg with the angle of repose at %.1f: landsliding is not binding", gotDeg, deg)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user