use kui_core::{Clip, Color, Core, NodeSpec, Quad, QuadKind, Size, Stroke, Vec2};
mod wgsl;
use wgsl::{AA, inside, sd_segment, smoothstep};
fn coverage(q: &Quad, clip: &Clip, x: f32, y: f32) -> f32 {
let e = q.segment_ends();
let d = sd_segment([x, y], [e[0], e[1]], [e[2], e[3]], q.border_w * 0.5);
q.color.a * (1.0 - smoothstep(-AA, AA, d)) * inside(clip, x, y)
}
fn stroke(points: &[Vec2], stroke: Stroke, scale: f32) -> (Vec<Quad>, Clip) {
let mut core = Core::new();
let mut ui = core.frame(Size::new(200.0, 100.0), scale);
ui.polyline(points, stroke, NodeSpec::column());
ui.finish();
let dl = core.output().0;
let quads = dl.quads.clone();
assert!(quads.iter().all(|q| q.kind == QuadKind::Segment));
let clip = quads.first().map_or(Clip::NONE, |q| dl.clip_of(q));
(quads, clip)
}
fn stroke_one(points: &[Vec2], s: Stroke, scale: f32) -> (Quad, Clip) {
let (quads, clip) = stroke(points, s, scale);
(quads[0], clip)
}
#[test]
fn the_stroke_lands_on_the_segment_the_core_described() {
let (q, clip) = stroke_one(
&[Vec2::new(20.0, 50.0), Vec2::new(120.0, 50.0)],
Stroke::new(6.0, Color::rgba8(0, 0, 0, 255)),
1.0,
);
assert!(
coverage(&q, &clip, 70.0, 50.0) > 0.99,
"opaque on the centre line"
);
assert!(
coverage(&q, &clip, 70.0, 52.0) > 0.99,
"opaque 2px off it, inside a 3px radius"
);
let edge = coverage(&q, &clip, 70.0, 53.0);
assert!(
(edge - 0.5).abs() < 0.05,
"half on the stroke's edge: {edge}"
);
assert!(
coverage(&q, &clip, 70.0, 56.0) < 0.01,
"nothing 3px past it"
);
assert!(coverage(&q, &clip, q.rect.x + 0.5, q.rect.y + 0.5) < 0.01);
let cap = coverage(&q, &clip, 123.0, 50.0);
assert!(
(cap - 0.5).abs() < 0.05,
"half at the end cap's edge: {cap}"
);
assert!(coverage(&q, &clip, 121.0, 50.0) > 0.9, "inside the cap");
assert!(coverage(&q, &clip, 70.0, q.rect.y + 0.5) < 0.01);
assert!(coverage(&q, &clip, 70.0, q.rect.y + q.rect.h - 0.5) < 0.01);
assert!(coverage(&q, &clip, q.rect.x + 0.5, 50.0) < 0.01);
assert!(coverage(&q, &clip, q.rect.x + q.rect.w - 0.5, 50.0) < 0.01);
}
#[test]
fn a_diagonal_is_covered_along_its_length_and_not_in_its_box_corners() {
let (q, clip) = stroke_one(
&[Vec2::new(10.0, 10.0), Vec2::new(90.0, 70.0)],
Stroke::new(4.0, Color::rgba8(0, 0, 0, 255)),
1.0,
);
for t in [0.1f32, 0.5, 0.9] {
let (x, y) = (10.0 + 80.0 * t, 10.0 + 60.0 * t);
assert!(coverage(&q, &clip, x, y) > 0.99, "on the line at {t}");
}
assert!(coverage(&q, &clip, 88.0, 12.0) < 0.01);
assert!(coverage(&q, &clip, 12.0, 68.0) < 0.01);
}
#[test]
fn scale_is_applied_once_on_the_core_side() {
let (q, clip) = stroke_one(
&[Vec2::new(20.0, 50.0), Vec2::new(120.0, 50.0)],
Stroke::new(6.0, Color::rgba8(0, 0, 0, 255)),
2.0,
);
assert_eq!(q.segment_ends(), [40.0, 100.0, 240.0, 100.0]);
assert!(
coverage(&q, &clip, 140.0, 105.0) > 0.99,
"5 physical px off centre is inside a 6px radius"
);
let edge = coverage(&q, &clip, 140.0, 106.0);
assert!((edge - 0.5).abs() < 0.05, "half at 6px: {edge}");
}
#[test]
fn a_polyline_joins_at_its_knot_without_a_gap() {
let (quads, clip) = stroke(
&[
Vec2::new(20.0, 20.0),
Vec2::new(60.0, 20.0),
Vec2::new(60.0, 60.0),
],
Stroke::new(8.0, Color::rgba8(0, 0, 0, 255)),
1.0,
);
assert_eq!(quads.len(), 2);
let at = |x, y| {
quads
.iter()
.map(|q| coverage(q, &clip, x, y))
.fold(0.0f32, f32::max)
};
assert!(at(62.0, 18.0) > 0.99, "the outer corner is inside a cap");
assert!(at(65.0, 15.0) < 0.5, "and the box corner past it is not");
}