Tooling
This commit is contained in:
@@ -22,6 +22,12 @@ type bucketPQ struct {
|
||||
|
||||
const bucketWidthM = 0.01
|
||||
|
||||
// reposeOrderBuckets is how far ClampToRepose scatters a cell from its own bucket, in buckets, either way.
|
||||
// Sixteen buckets is sixteen centimetres: enough that a tie spreads over about thirty of them and the pop
|
||||
// order stops tracking the raster, far below the metres the clamp's ordering could ever depend on. See
|
||||
// pushJittered.
|
||||
const reposeOrderBuckets = 16
|
||||
|
||||
func newBucketPQ(loM, hiM float64) *bucketPQ {
|
||||
if hiM <= loM {
|
||||
hiM = loM + 1
|
||||
@@ -54,6 +60,34 @@ func (q *bucketPQ) push(elev float32, idx int32) {
|
||||
q.count++
|
||||
}
|
||||
|
||||
// pushJittered is push with the bucket chosen from the elevation plus j buckets, so cells at the same
|
||||
// elevation land in different buckets instead of in one and pop in hash order rather than in reverse raster
|
||||
// order.
|
||||
//
|
||||
// Only ClampToRepose wants this. The flood already scatters its own order through the epsilon it adds to the
|
||||
// height, so its cells rarely share a bucket; the clamp reads the raw surface, where flat ground puts every
|
||||
// cell in one bucket and the LIFO below then processes them bottom-right to top-left, every time, everywhere.
|
||||
//
|
||||
// Half a bucket is not enough - it splits a tie across two buckets and halves the correlation instead of
|
||||
// removing it - so the caller scatters over reposeOrderBuckets, and that is safe for a reason worth writing
|
||||
// down. The clamp's order can only matter between two cells whose heights differ by about the talus
|
||||
// allowance, which is metres: a cell popped early is marked closed and never lowered again, but every cell
|
||||
// popping after it stands within the jitter width of it, so its limit is the other cell's height plus the
|
||||
// full allowance and cannot bind. Reordering cells that are centimetres apart therefore cannot break a
|
||||
// constraint that only bites metres apart. The bound is talus*cell/2, which is 2.8 m at 35 degrees on an 8 m
|
||||
// cell; the constant below is two orders of magnitude inside it.
|
||||
func (q *bucketPQ) pushJittered(elev float32, idx int32, j float32) {
|
||||
b := int((float64(elev)-q.lo)/q.width + float64(j))
|
||||
if b < q.cur {
|
||||
b = q.cur
|
||||
}
|
||||
if b >= len(q.buckets) {
|
||||
b = len(q.buckets) - 1
|
||||
}
|
||||
q.buckets[b] = append(q.buckets[b], idx)
|
||||
q.count++
|
||||
}
|
||||
|
||||
// pop returns the lowest cell. The cursor only moves forward, so the total scan cost over a whole flood is
|
||||
// the number of buckets, not the number of pops.
|
||||
func (q *bucketPQ) pop() int32 {
|
||||
|
||||
Reference in New Issue
Block a user