use kui_core::{Clip, Color, Core, NodeSpec, Quad, Size};
mod wgsl;
use wgsl::inside;
const CARD: f32 = 100.0;
const X0: f32 = 20.0;
const Y0: f32 = 20.0;
const R: f32 = 16.0;
fn child_of_rounded_card(radius: NodeSpec) -> (Quad, Clip) {
let mut core = Core::new();
let mut ui = core.frame(Size::new(200.0, 200.0), 1.0);
ui.configure_root(NodeSpec::column().pad(20.0));
ui.with_keyed("card", radius.size(CARD, CARD).clip(), |ui| {
ui.leaf_keyed(
"child",
NodeSpec::column().fill().bg(Color::rgb8(20, 20, 30)),
);
});
ui.finish();
let dl = core.output().0;
let q = dl.quads[0];
assert_eq!(
q.color,
Color::rgb8(20, 20, 30),
"the child is the only fill"
);
(q, dl.clip_of(&q))
}
#[test]
fn the_clip_cuts_the_corner_the_child_used_to_poke_out_of() {
let (_q, clip) = child_of_rounded_card(NodeSpec::column().radius(R));
assert_eq!(clip.radius, [R; 4]);
assert_eq!(inside(&clip, X0 + CARD / 2.0, Y0 + CARD / 2.0), 1.0);
assert_eq!(
inside(&clip, X0 + CARD / 2.0, Y0 + 1.0),
1.0,
"mid top edge"
);
assert_eq!(
inside(&clip, X0 + 1.0, Y0 + CARD / 2.0),
1.0,
"mid left edge"
);
let straight = inside(&clip, X0 + CARD / 2.0, Y0);
assert!(
(straight - 0.5).abs() < 0.05,
"half on the edge: {straight}"
);
assert_eq!(
inside(&clip, X0 + CARD / 2.0, Y0 - 1.0),
0.0,
"and out past it"
);
for (cx, cy) in [
(X0 + 0.5, Y0 + 0.5),
(X0 + CARD - 0.5, Y0 + 0.5),
(X0 + CARD - 0.5, Y0 + CARD - 0.5),
(X0 + 0.5, Y0 + CARD - 0.5),
] {
assert_eq!(
inside(&clip, cx, cy),
0.0,
"corner ({cx}, {cy}) still painted"
);
}
let d = R - R / 2f32.sqrt(); let edge = inside(&clip, X0 + d, Y0 + d);
assert!((edge - 0.5).abs() < 0.2, "soft on the arc: {edge}");
}
#[test]
fn a_square_clip_is_the_rect_it_always_was() {
let (_q, clip) = child_of_rounded_card(NodeSpec::column());
assert_eq!(clip.radius, [0.0; 4]);
assert_eq!(
inside(&clip, X0 + 0.5, Y0 + 0.5),
1.0,
"corner still painted"
);
assert_eq!(inside(&clip, X0 - 0.5, Y0 - 0.5), 0.0);
}
#[test]
fn per_corner_radii_cut_only_their_own_corners() {
let (_q, clip) = child_of_rounded_card(NodeSpec::column().radius_top(R));
assert_eq!(clip.radius, [R, R, 0.0, 0.0]);
assert_eq!(inside(&clip, X0 + 0.5, Y0 + 0.5), 0.0, "top-left is cut");
assert_eq!(
inside(&clip, X0 + CARD - 0.5, Y0 + 0.5),
0.0,
"top-right is cut"
);
assert_eq!(
inside(&clip, X0 + 1.0, Y0 + CARD - 1.0),
1.0,
"bottom-left is square"
);
assert_eq!(
inside(&clip, X0 + CARD - 1.0, Y0 + CARD - 1.0),
1.0,
"bottom-right is square"
);
}
#[test]
fn the_clip_shape_is_the_clippers_box_not_the_quad_being_clipped() {
let mut core = Core::new();
let mut ui = core.frame(Size::new(200.0, 200.0), 1.0);
ui.configure_root(NodeSpec::column().pad(20.0));
ui.with_keyed(
"card",
NodeSpec::column()
.size(CARD, CARD)
.radius(R)
.clip()
.pad(30.0),
|ui| {
ui.leaf_keyed(
"inner",
NodeSpec::column()
.size(20.0, 20.0)
.bg(Color::rgb8(200, 30, 30)),
);
},
);
ui.finish();
let dl = core.output().0;
let clip = dl.clip_of(&dl.quads[0]);
assert_eq!(inside(&clip, X0 + 50.0, Y0 + 50.0), 1.0);
assert_eq!(inside(&clip, X0 + 51.0, Y0 + 51.0), 1.0);
assert_eq!(inside(&clip, X0 + 0.5, Y0 + 0.5), 0.0);
}
#[test]
fn an_oversized_clip_radius_degrades_to_a_pill() {
let (_q, clip) = child_of_rounded_card(NodeSpec::column().radius(500.0));
assert_eq!(clip.radius, [500.0; 4]);
assert_eq!(
inside(&clip, X0 + CARD / 2.0, Y0 + CARD / 2.0),
1.0,
"centre"
);
assert_eq!(inside(&clip, X0 + 0.5, Y0 + 0.5), 0.0, "corner");
let edge = inside(&clip, X0 + CARD / 2.0, Y0 + 1.0);
assert!(edge > 0.9, "the middle of an edge survives: {edge}");
}