use kui_core::{Color, Core, NodeSpec, Quad, QuadKind, Shadow, Size};
mod wgsl;
use wgsl::{AA, sd_rounded_box, smoothstep};
fn coverage(q: &Quad, x: f32, y: f32) -> f32 {
let local = [x - q.rect.x, y - q.rect.y];
let half = [q.rect.w * 0.5, q.rect.h * 0.5];
let shape = [(half[0] - q.blur).max(0.0), (half[1] - q.blur).max(0.0)];
let d = sd_rounded_box([local[0] - half[0], local[1] - half[1]], shape, q.radius);
let ramp = q.blur.max(AA);
q.color.a * (1.0 - smoothstep(-ramp, ramp, d))
}
fn card_shadow(spec: NodeSpec) -> Quad {
let mut core = Core::new();
let mut ui = core.frame(Size::new(200.0, 100.0), 1.0);
ui.configure_root(NodeSpec::column().pad(20.0));
ui.leaf_keyed("card", spec.size(80.0, 40.0));
ui.finish();
let q = core.output().0.quads[0];
assert_eq!(q.kind, QuadKind::Shadow);
q
}
fn blurred() -> Quad {
card_shadow(
NodeSpec::column()
.bg(Color::WHITE)
.radius(6.0)
.shadow(Shadow {
color: Color::rgba8(0, 0, 0, 255),
dx: 0.0,
dy: 4.0,
blur: 8.0,
spread: 2.0,
}),
)
}
#[test]
fn the_shadow_lands_on_the_shape_the_core_described() {
let q = blurred();
assert!(coverage(&q, 60.0, 42.0) > 0.99, "opaque in the middle");
assert!(coverage(&q, 30.0, 30.0) > 0.99, "opaque inside a corner");
let edge = coverage(&q, 60.0, 22.0);
assert!(
(edge - 0.5).abs() < 0.05,
"half at the shape's edge: {edge}"
);
let out = coverage(&q, 60.0, q.rect.y + 0.5);
assert!(out < 0.01, "faded out by the quad's edge: {out}");
assert!(coverage(&q, q.rect.x + 0.5, 42.0) < 0.01);
}
#[test]
fn the_offset_casts_the_shadow_downward() {
let q = blurred();
let above = coverage(&q, 60.0, 17.0);
let below = coverage(&q, 60.0, 63.0);
assert!(above < 0.35, "little above the card: {above}");
assert!(below > 0.65, "much below it: {below}");
}
#[test]
fn a_hard_shadow_is_the_shape_itself() {
let q = card_shadow(NodeSpec::column().shadow_color(Color::rgba8(0, 0, 0, 255)));
assert_eq!(
(q.rect.x, q.rect.y, q.rect.w, q.rect.h),
(20.0, 20.0, 80.0, 40.0)
);
assert!(coverage(&q, 60.0, 40.0) > 0.99);
assert!(coverage(&q, 60.0, 21.0) > 0.7, "its top edge is covered");
assert!(coverage(&q, 60.0, 19.0) == 0.0, "and nothing above it");
}