package fluvial import ( "math" "testing" ) // TestClampToReposeCutsACone is the smallest possible check on the constraint: a cone far steeper than the // repose angle must come back at or under it. func TestClampToReposeCutsACone(t *testing.T) { const ( w, h = 81, 81 cellM = 10.0 talus = 0.4 // about 22 degrees ) base := make([]bool, w*h) field := make([]float32, w*h) for y := 0; y < h; y++ { for x := 0; x < w; x++ { d := math.Hypot(float64(x-w/2), float64(y-h/2)) * cellM field[y*w+x] = float32(math.Max(0, 800-2.0*d)) // slope 2.0, five times repose } } g := NewGrid(w, h, cellM, base) g.SetElevationRange(-100, 2000) removed := g.ClampToRepose(field, talus) worst := 0.0 for y := 0; y < h; y++ { for x := 0; x < w; x++ { for k := 0; k < 8; k++ { nx, ny := x+dx8[k], y+dy8[k] if nx < 0 || ny < 0 || nx >= w || ny >= h { continue } d := cellM if dx8[k] != 0 && dy8[k] != 0 { d = cellM * math.Sqrt2 } if s := float64(field[y*w+x]-field[ny*w+nx]) / d; s > worst { worst = s } } } } t.Logf("removed %.2f m mean; steepest slope now %.3f (repose %.3f)", removed, worst, talus) if worst > talus*1.02 { t.Errorf("steepest slope %.3f exceeds repose %.3f", worst, talus) } }