use kui_core::{Clip, Color, Core, NodeSpec, Quad, QuadKind, Size, Sizing, Span, TextStyle};
mod wgsl;
use wgsl::{inside, join_alpha};
const R: f32 = 4.0;
const LINES: [&str; 5] = ["fn a() {", " let x = 1;", " ", " y", "}"];
fn sel() -> Color {
Color::rgba8(80, 140, 220, 90)
}
fn selected(scale: f32) -> Vec<(Quad, [f32; 16], Clip)> {
let mut core = Core::new();
let mut ui = core.frame(Size::new(400.0, 200.0), scale);
ui.configure_root(NodeSpec::column().pad(10.3));
let style = TextStyle::new(13.0).mono().nowrap().line_height(20.0);
for line in LINES {
ui.with(NodeSpec::row().height(Sizing::Fixed(20.0)), |ui| {
ui.rich_text(
&[Span::new(line)
.color(Color::rgb8(220, 220, 220))
.bg(sel())
.bg_radius(R)],
style,
)
});
}
ui.finish();
let dl = core.output().0;
dl.quads
.iter()
.filter(|q| q.kind == QuadKind::Fragment)
.map(|q| (*q, dl.fragments[q.uv[0] as usize].params, dl.clip_of(q)))
.collect()
}
fn alpha_at(pieces: &[(Quad, [f32; 16], Clip)], x: f32, y: f32) -> f32 {
let clear = pieces.iter().fold(1.0, |left, (q, p, clip)| {
let r = q.rect;
if !(x >= r.x && x < r.x + r.w && y >= r.y && y < r.y + r.h) {
return left;
}
let a = join_alpha((x - r.x, y - r.y), (r.w, r.h), p);
left * (1.0 - q.color.a * a * inside(clip, x, y))
});
1.0 - clear
}
#[test]
fn a_rounded_selection_over_lines_is_one_shape() {
for scale in [1.0f32, 1.25, 1.5, 1.75, 2.0, 2.175] {
let pieces = selected(scale);
assert_eq!(pieces.len(), LINES.len(), "a piece a line, {scale}×");
let sa = sel().a;
let own = |i: usize| {
let (q, p, _) = &pieces[i];
(q.rect.x + p[0], q.rect.x + p[1])
};
let top = pieces[0].0.rect.y;
let bottom = pieces.last().map(|(q, _, _)| q.rect.y + q.rect.h).unwrap();
let x0 = own(0).0;
let right = (0..pieces.len()).map(|i| own(i).1).fold(0.0, f32::max);
for py in top as i32..bottom as i32 {
for px in x0 as i32 - 2..right as i32 + (R * scale) as i32 + 2 {
let a = alpha_at(&pieces, px as f32 + 0.5, py as f32 + 0.5);
assert!(a < sa + 1e-4, "drawn twice at ({px}, {py}), {scale}×: {a}");
}
}
let r = R * scale;
let cx = x0 + r.ceil() + 1.5;
for py in (top + r).ceil() as i32..(bottom - r).floor() as i32 {
let a = alpha_at(&pieces, cx, py as f32 + 0.5);
assert!(
(a - sa).abs() < 1e-4,
"one surface at ({cx}, {py}), {scale}×: {a}"
);
}
assert!(
alpha_at(&pieces, x0 + 0.5, top + 0.5) < sa * 0.5,
"{scale}×"
);
let join = pieces[0].0.rect.y + pieces[0].0.rect.h;
let fillet = alpha_at(&pieces, own(0).1 + 0.5, join - 0.5);
assert!(fillet > sa * 0.5, "the fillet, {scale}×: {fillet}");
let join1 = pieces[1].0.rect.y + pieces[1].0.rect.h;
let corner = alpha_at(&pieces, own(1).1 - 0.5, join1 - 0.5);
assert!(corner < sa * 0.9, "the convex corner, {scale}×: {corner}");
}
}