use kui_core::{Clip, Color, Core, NodeSpec, Quad, Size};
mod wgsl;
use wgsl::{inside, inside_inner, turned};
fn solids(build: impl FnOnce(&mut kui_core::Ui<'_>), scale: f32) -> Vec<(Quad, Clip)> {
let mut core = Core::new();
let mut ui = core.frame(Size::new(200.0, 200.0), scale);
ui.configure_root(NodeSpec::column().pad(20.0));
build(&mut ui);
ui.finish();
let dl = core.output().0;
dl.quads
.iter()
.filter(|q| q.kind == kui_core::QuadKind::Solid)
.map(|q| (*q, dl.clip_of(q)))
.collect()
}
fn near(a: f32, b: f32) -> bool {
(a - b).abs() < 1e-3
}
#[test]
fn the_corners_land_where_the_core_says_at_any_scale() {
for scale in [1.0, 2.0] {
let qs = solids(
|ui| {
ui.leaf_keyed(
"card",
NodeSpec::column()
.size(100.0, 50.0)
.bg(Color::WHITE)
.rotate(0.25),
);
},
scale,
);
let (q, clip) = qs[0];
let (x, y) = turned(&clip, q.rect.x, q.rect.y);
assert!(
near(x, 95.0 * scale) && near(y, -5.0 * scale),
"{x} {y} at {scale}"
);
let (cx, cy) = turned(&clip, q.rect.x + q.rect.w / 2.0, q.rect.y + q.rect.h / 2.0);
assert!(
near(cx, 70.0 * scale) && near(cy, 45.0 * scale),
"{cx} {cy}"
);
let (ix, iy) = turned(&Clip::NONE, 12.0, 34.0);
assert!(near(ix, 12.0) && near(iy, 34.0));
}
}
#[test]
fn a_child_of_a_turned_rounded_card_is_cut_by_the_cards_corners_in_its_own_space() {
let qs = solids(
|ui| {
ui.with_keyed(
"card",
NodeSpec::column()
.size(100.0, 100.0)
.radius(16.0)
.clip()
.bg(Color::WHITE)
.rotate(0.1),
|ui| {
ui.leaf_keyed(
"child",
NodeSpec::column().fill().bg(Color::rgb8(20, 20, 30)),
);
},
);
},
1.0,
);
let (_, child_clip) = qs[1];
assert!(child_clip.turned());
assert_eq!(child_clip.inner_radius, [16.0; 4]);
assert_eq!(inside_inner(&child_clip, 70.0, 70.0), 1.0);
assert_eq!(inside_inner(&child_clip, 70.0, 21.0), 1.0, "mid top edge");
assert_eq!(inside_inner(&child_clip, 21.0, 21.0), 0.0, "the corner");
assert!(
inside_inner(&child_clip, 24.5, 24.5) > 0.1,
"just inside the arc"
);
let (x, y) = turned(&child_clip, 21.0, 21.0);
assert_eq!(inside(&child_clip, x, y), 1.0);
}
#[test]
fn a_clipper_outside_the_turn_cuts_in_framebuffer_space() {
let qs = solids(
|ui| {
ui.with_keyed(
"scroller",
NodeSpec::column().size(100.0, 100.0).clip(),
|ui| {
ui.leaf_keyed(
"card",
NodeSpec::column()
.size(100.0, 50.0)
.bg(Color::WHITE)
.rotate(0.25),
);
},
);
},
1.0,
);
let (q, clip) = qs[0];
assert_eq!(clip.rect, kui_core::Rect::new(20.0, 20.0, 100.0, 100.0));
assert_eq!(clip.inner, kui_core::NO_CLIP);
let (x, y) = turned(&clip, q.rect.x, q.rect.y);
assert_eq!(inside(&clip, x, y), 0.0);
let (cx, cy) = turned(&clip, 70.0, 45.0);
assert_eq!(inside(&clip, cx, cy), 1.0);
assert_eq!(
inside_inner(&clip, q.rect.x, q.rect.y),
1.0,
"no inner clip"
);
}
#[test]
fn a_square_clip_inside_a_turn_ramps_its_edge() {
let qs = solids(
|ui| {
ui.with_keyed(
"card",
NodeSpec::column().size(100.0, 100.0).rotate(0.1),
|ui| {
ui.with(NodeSpec::column().size(60.0, 60.0).clip(), |ui| {
ui.leaf(NodeSpec::column().size(90.0, 90.0).bg(Color::WHITE));
});
},
);
},
1.0,
);
let (_, clip) = qs.last().copied().expect("the child");
let r = clip.inner;
assert!(r != kui_core::NO_CLIP, "the clipper is inside the turn");
let mid_y = r.y + r.h * 0.5;
assert_eq!(
inside_inner(&clip, r.x + r.w * 0.5, mid_y),
1.0,
"well inside"
);
let edge = inside_inner(&clip, r.x + r.w, mid_y);
assert!(edge > 0.4 && edge < 0.6, "half covered on the edge: {edge}");
assert_eq!(inside_inner(&clip, r.x + r.w + 1.0, mid_y), 0.0, "past it");
let mut none = clip;
none.inner = kui_core::NO_CLIP;
assert_eq!(inside_inner(&none, 37.0, -512.0), 1.0, "no inner clip");
}