79 lines
3.6 KiB
Text
79 lines
3.6 KiB
Text
// Scalar fields on grids: `grid` is a row-major f32 buffer of w columns
|
|
// and h rows (cell units). Isolines are marching squares, one cell per
|
|
// element: a kernel emits `segments(..)` records per cell, deterministic
|
|
// and element-local.
|
|
|
|
// Bilinear sample and normal (the prelude's, under field names).
|
|
fn sample(grid, w, h, x, z) { sample_grid(grid, w, h, x, z) }
|
|
fn normal(grid, w, h, x, z, cell) { grid_normal(grid, w, h, x, z, cell) }
|
|
|
|
// The gradient at (x, z) by central differences, per cell unit.
|
|
fn grad(grid, w, h, x, z) {
|
|
let dx = sample_grid(grid, w, h, x + 1.0, z) - sample_grid(grid, w, h, x - 1.0, z)
|
|
let dz = sample_grid(grid, w, h, x, z + 1.0) - sample_grid(grid, w, h, x, z - 1.0)
|
|
vec2(dx, dz) * 0.5
|
|
}
|
|
|
|
// Marching-squares edges per case: two segments of (edge, edge), -1 for
|
|
// none; edges 0: a-b (bottom), 1: b-c (right), 2: c-d (top), 3: d-a (left),
|
|
// corners a (cx, cz), b (cx+1, cz), c (cx+1, cz+1), d (cx, cz+1); a corner
|
|
// at or above the level sets its bit (a 1, b 2, c 4, d 8). The saddles 5
|
|
// and 10 are resolved by the cell centre.
|
|
let MS_EDGES: int = [
|
|
-1, -1, -1, -1, 3, 0, -1, -1, 0, 1, -1, -1, 3, 1, -1, -1,
|
|
1, 2, -1, -1, 3, 0, 1, 2, 0, 2, -1, -1, 3, 2, -1, -1,
|
|
2, 3, -1, -1, 0, 2, -1, -1, 0, 1, 2, 3, 1, 2, -1, -1,
|
|
1, 3, -1, -1, 0, 1, -1, -1, 3, 0, -1, -1, -1, -1, -1, -1,
|
|
]
|
|
|
|
fn corner(grid, w, h, cx, cz, k) {
|
|
let x = min(cx + (if k == 1 || k == 2 { 1 } else { 0 }), w - 1)
|
|
let z = min(cz + (if k >= 2 { 1 } else { 0 }), h - 1)
|
|
grid[z * w + x]
|
|
}
|
|
|
|
// The case of cell (cx, cz), 0..15. A saddle whose centre is at or above
|
|
// the level joins its two high corners, which is the other saddle's pair of
|
|
// cuts (5 <-> 10).
|
|
fn cell_case(grid, w, h, level, cx, cz) {
|
|
let a = corner(grid, w, h, cx, cz, 0)
|
|
let b = corner(grid, w, h, cx, cz, 1)
|
|
let c = corner(grid, w, h, cx, cz, 2)
|
|
let d = corner(grid, w, h, cx, cz, 3)
|
|
let m = (if a >= level { 1 } else { 0 }) + (if b >= level { 2 } else { 0 }) + (if c >= level { 4 } else { 0 }) + (if d >= level { 8 } else { 0 })
|
|
let centre = (a + b + c + d) * 0.25
|
|
if m == 5 && centre >= level { 10 } elif m == 10 && centre >= level { 5 } else { m }
|
|
}
|
|
|
|
// Isoline segments crossing cell (cx, cz) at `level`: 0, 1 or 2.
|
|
fn segments(grid, w, h, level, cx, cz) {
|
|
let m = cell_case(grid, w, h, level, cx, cz)
|
|
if m == 0 || m == 15 { 0 } elif m == 5 || m == 10 { 2 } else { 1 }
|
|
}
|
|
|
|
// Where the level crosses edge e of the cell (cell units, x and z).
|
|
fn edge_point(grid, w, h, level, cx, cz, e) {
|
|
let p = corner(grid, w, h, cx, cz, e)
|
|
let q = corner(grid, w, h, cx, cz, (e + 1) % 4)
|
|
let t = if q != p { clamp((level - p) / (q - p), 0.0, 1.0) } else { 0.5 }
|
|
let x0 = float(cx) + (if e == 1 || e == 2 { 1.0 } else { 0.0 })
|
|
let z0 = float(cz) + (if e >= 2 { 1.0 } else { 0.0 })
|
|
let x1 = float(cx) + (if e == 0 || e == 1 { 1.0 } else { 0.0 })
|
|
let z1 = float(cz) + (if e == 1 || e == 2 { 1.0 } else { 0.0 })
|
|
vec2(mix(x0, x1, t), mix(z0, z1, t))
|
|
}
|
|
|
|
// Segment k (0 or 1) of the cell: vec4(x0, z0, x1, z1) in cell units.
|
|
fn segment(grid, w, h, level, cx, cz, k) {
|
|
let m = cell_case(grid, w, h, level, cx, cz)
|
|
let e0 = MS_EDGES[m * 4 + k * 2]
|
|
let e1 = MS_EDGES[m * 4 + k * 2 + 1]
|
|
let p = edge_point(grid, w, h, level, cx, cz, max(e0, 0))
|
|
let q = edge_point(grid, w, h, level, cx, cz, max(e1, 0))
|
|
vec4(p.x, p.y, q.x, q.y)
|
|
}
|
|
|
|
// The unit direction of a segment (a label's baseline) and its midpoint
|
|
// (the label's anchor).
|
|
fn segment_tangent(s) { normalize(vec2(s.z - s.x, s.w - s.y)) }
|
|
fn segment_mid(s) { vec2(s.x + s.z, s.y + s.w) * 0.5 }
|