Skip to main content

SDF_WGSL

Constant SDF_WGSL 

Source
pub const SDF_WGSL: &str = r#"
const FLAT_RECT: u32 = 0u;
const FLAT_CIRCLE: u32 = 1u;
const FLAT_RING: u32 = 2u;
const FLAT_ARC: u32 = 3u;
const FLAT_ICON_FILL: u32 = 4u;
const FLAT_ICON_LINE: u32 = 5u;
const FLAT_TAU: f32 = 6.283185307179586;

fn flat_sign(value: f32) -> f32 {
    return select(-1.0, 1.0, value >= 0.0);
}

fn flat_direction(p: vec2f) -> vec2f {
    let length_p = length(p);
    return select(vec2f(1.0, 0.0), p / length_p, length_p > 0.0);
}

fn flat_corner(p: vec2f, radii: vec4f) -> f32 {
    let right = p.x >= 0.0;
    if (p.y >= 0.0) {
        return select(radii.w, radii.z, right);
    }
    return select(radii.x, radii.y, right);
}

fn flat_rect(p: vec2f, half: vec2f, radii: vec4f) -> vec3f {
    let radius = flat_corner(p, radii);
    let s = vec2f(flat_sign(p.x), flat_sign(p.y));
    let q = abs(p) - half + vec2f(radius);
    if (q.x > 0.0 && q.y > 0.0) {
        let l = length(q);
        return vec3f(l - radius, s * q / l);
    }
    if (q.x > q.y) {
        return vec3f(q.x - radius, s.x, 0.0);
    }
    return vec3f(q.y - radius, 0.0, s.y);
}

fn flat_circle(p: vec2f, radius: f32) -> vec3f {
    return vec3f(length(p) - radius, flat_direction(p));
}

fn flat_ring(p: vec2f, radius: f32, width: f32) -> vec3f {
    let offset = length(p) - radius;
    return vec3f(abs(offset) - width * 0.5, flat_sign(offset) * flat_direction(p));
}

fn flat_turn(p: vec2f, angle: f32) -> vec2f {
    let c = cos(angle);
    let s = sin(angle);
    return vec2f(c * p.x - s * p.y, s * p.x + c * p.y);
}

fn flat_arc(p: vec2f, radius: f32, width: f32, start: f32, sweep: f32) -> vec3f {
    if (sweep >= FLAT_TAU) {
        return flat_ring(p, radius, width);
    }
    let half = sweep * 0.5;
    let middle = start + half;
    let q = flat_turn(p, -middle);
    let angle = atan2(q.y, q.x);
    if (abs(angle) <= half) {
        return flat_ring(p, radius, width);
    }
    let end = radius * vec2f(cos(half), flat_sign(q.y) * sin(half));
    let away = q - end;
    return vec3f(length(away) - width * 0.5, flat_turn(flat_direction(away), middle));
}

struct FlatAlong {
    s: f32,
    tangent: vec2f,
}

fn flat_rect_along(p: vec2f, half: vec2f, radii: vec4f) -> FlatAlong {
    let arc = radii * (FLAT_TAU * 0.25);
    let top = (half.x - radii.x) + (half.x - radii.y);
    let right = (half.y - radii.y) + (half.y - radii.z);
    let bottom = (half.x - radii.w) + (half.x - radii.z);
    let left = (half.y - radii.x) + (half.y - radii.w);
    let at_right = top + arc.y;
    let at_bottom_arc = at_right + right;
    let at_bottom = at_bottom_arc + arc.z;
    let at_left_arc = at_bottom + bottom;
    let at_left = at_left_arc + arc.w;
    let at_top_arc = at_left + left;
    let radius = flat_corner(p, radii);
    let inner = half - vec2f(radius);
    let q = abs(p) - inner;
    if (q.x > 0.0 && q.y > 0.0) {
        let s = vec2f(flat_sign(p.x), flat_sign(p.y));
        let centre = s * inner;
        let offset = p - centre;
        let rho = max(length(offset), 1e-6);
        let tangent = vec2f(-offset.y, offset.x) / rho * (radius / rho);
        var start = top;
        var angle = atan2(offset.y, offset.x) + FLAT_TAU * 0.25;
        if (s.x > 0.0 && s.y > 0.0) {
            start = at_bottom_arc;
            angle = atan2(offset.y, offset.x);
        } else if (s.x < 0.0 && s.y > 0.0) {
            start = at_left_arc;
            angle = atan2(offset.y, offset.x) - FLAT_TAU * 0.25;
        } else if (s.x < 0.0 && s.y < 0.0) {
            start = at_top_arc;
            angle = atan2(-offset.y, -offset.x);
        }
        return FlatAlong(start + radius * clamp(angle, 0.0, FLAT_TAU * 0.25), tangent);
    }
    if (q.x > q.y) {
        if (p.x > 0.0) {
            return FlatAlong(at_right + p.y + (half.y - radii.y), vec2f(0.0, 1.0));
        }
        return FlatAlong(at_left + (half.y - radii.w) - p.y, vec2f(0.0, -1.0));
    }
    if (p.y < 0.0) {
        return FlatAlong(p.x + (half.x - radii.x), vec2f(1.0, 0.0));
    }
    return FlatAlong(at_bottom + (half.x - radii.z) - p.x, vec2f(-1.0, 0.0));
}

fn flat_circle_along(p: vec2f, radius: f32) -> FlatAlong {
    let rho = max(length(p), 1e-6);
    var angle = atan2(p.y, p.x);
    if (angle < 0.0) {
        angle += FLAT_TAU;
    }
    return FlatAlong(radius * angle, vec2f(-p.y, p.x) / rho * (radius / rho));
}

fn flat_dash(along: FlatAlong, on: f32, off: f32, phase: f32) -> vec3f {
    let period = on + off;
    let u = along.s + phase - on * 0.5;
    let centred = u - period * floor(u / period + 0.5);
    return vec3f(abs(centred) - on * 0.5, flat_sign(centred) * along.tangent);
}

fn flat_coverage(field: vec3f, inverse: mat2x2f) -> f32 {
    let screen = vec2f(dot(inverse[0], field.yz), dot(inverse[1], field.yz));
    return clamp(0.5 - field.x / max(length(screen), 1e-6), 0.0, 1.0);
}

fn flat_coverage_even(distance: f32, inverse: mat2x2f) -> f32 {
    let unit = sqrt(abs(determinant(inverse)));
    return clamp(0.5 - distance / max(unit, 1e-6), 0.0, 1.0);
}

const FLAT_SHADOW: u32 = 6u;
const FLAT_SHADOW_STEPS: u32 = 8u;
const FLAT_SHADOW_REACH: f32 = 4.0;

fn flat_erf(x: f32) -> f32 {
    let t = 1.0 / (1.0 + 0.3275911 * abs(x));
    let y = 1.0 - (((((1.0614054 * t - 1.4531521) * t) + 1.4214138) * t - 0.28449674) * t + 0.2548296) * t * exp(-x * x);
    return select(-y, y, x >= 0.0);
}

fn flat_shadow_reach(half: vec2f, radius: f32, y: f32) -> f32 {
    let delta = min(half.y - radius - abs(y), 0.0);
    return half.x - radius + sqrt(max(radius * radius - delta * delta, 0.0));
}

fn flat_shadow_row(x: f32, y: f32, half: vec2f, radii: vec4f, sigma: f32) -> f32 {
    var left = radii.w;
    var right = radii.z;
    if (y < 0.0) {
        left = radii.x;
        right = radii.y;
    }
    let k = 0.70710678 / sigma;
    return 0.5 * (flat_erf((x + flat_shadow_reach(half, left, y)) * k) - flat_erf((x - flat_shadow_reach(half, right, y)) * k));
}

fn flat_rounded_shadow(p: vec2f, half: vec2f, radii: vec4f, sigma: vec2f) -> f32 {
    let k = 0.70710678 / sigma.y;
    let low = max(-half.y, p.y - FLAT_SHADOW_REACH * sigma.y);
    let high = min(half.y, p.y + FLAT_SHADOW_REACH * sigma.y);
    if (high <= low) {
        return 0.0;
    }
    let first = asin(clamp(low / half.y, -1.0, 1.0));
    let last = asin(clamp(high / half.y, -1.0, 1.0));
    let step = (last - first) / f32(FLAT_SHADOW_STEPS);
    var total = 0.0;
    var edge = flat_erf((p.y - low) * k);
    for (var index = 0u; index < FLAT_SHADOW_STEPS; index += 1u) {
        let a = first + step * f32(index);
        let b = a + step;
        let next = flat_erf((p.y - half.y * sin(b)) * k);
        let weight = 0.5 * (edge - next);
        edge = next;
        total += weight * flat_shadow_row(p.x, half.y * sin((a + b) * 0.5), half, radii, sigma.x);
    }
    return total;
}

fn flat_stripe(u: f32, width: f32) -> vec2f {
    let c = u - floor(u + 0.5);
    return vec2f(abs(c) - width * 0.5, flat_sign(c));
}

fn flat_pattern_field(kind: u32, params: vec4f, p: vec2f) -> vec3f {
    let scale = params.x;
    let width = params.z;
    let q = flat_turn(p, -params.y) / scale;
    var d = 0.0;
    var g = vec2f(1.0, 0.0);
    if (kind == 1u) {
        let s = flat_stripe(q.x, width);
        d = s.x;
        g = vec2f(s.y, 0.0);
    } else if (kind == 2u) {
        let f = q - floor(q + vec2f(0.5));
        d = length(f) - width * 0.5;
        g = flat_direction(f);
    } else if (kind == 3u) {
        let a = flat_stripe(q.x, width);
        let b = flat_stripe(q.y, width);
        if (a.x <= b.x) {
            d = a.x;
            g = vec2f(a.y, 0.0);
        } else {
            d = b.x;
            g = vec2f(0.0, b.y);
        }
    } else {
        let t = q.y - floor(q.y + 0.5);
        let s = flat_stripe(q.x + abs(t), width);
        d = s.x * 0.70710678;
        g = vec2f(s.y, s.y * flat_sign(t)) * 0.70710678;
    }
    return vec3f(d * scale, flat_turn(g, params.y));
}

fn flat_pattern_over(base: vec4f, kind: u32, params: vec4f, packed: u32, p: vec2f, inverse: mat2x2f) -> vec4f {
    let colour = unpack4x8unorm(packed);
    let cover = flat_coverage(flat_pattern_field(kind, params, p), inverse);
    let paint = vec4f(colour.rgb * colour.a, colour.a) * cover;
    return paint + base * (1.0 - paint.a);
}
"#;