use super::super::{NODE_SEATS, Tints};
use super::paint;
use crate::theme::{HYDRA, ICHOR, SPECTRE};
type Circle = (egui::Pos2, f32, egui::Color32);
fn circles_of(shape: &egui::Shape, out: &mut Vec<Circle>) {
match shape {
egui::Shape::Circle(circle) => out.push((circle.center, circle.radius, circle.fill)),
egui::Shape::Vec(shapes) => shapes.iter().for_each(|s| circles_of(s, out)),
_ => {}
}
}
fn circles(tints: &Tints, w: f32, h: f32) -> Vec<Circle> {
let ctx = egui::Context::default();
let output = ctx.run(crate::paint_probe::screen(), |ctx| {
egui::CentralPanel::default().show(ctx, |ui| {
let rect = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(w, h));
paint(ui.painter(), rect, tints);
});
});
let mut out = Vec::new();
for clipped in &output.shapes {
circles_of(&clipped.shape, &mut out);
}
out
}
fn paths_of(shape: &egui::Shape, out: &mut Vec<(usize, f32)>) {
match shape {
egui::Shape::Path(path) if path.stroke.width > 0.0 => {
out.push((path.points.len(), path.stroke.width));
}
egui::Shape::Vec(shapes) => shapes.iter().for_each(|s| paths_of(s, out)),
_ => {}
}
}
fn traces(side: f32) -> Vec<(usize, f32)> {
let ctx = egui::Context::default();
let output = ctx.run(crate::paint_probe::screen(), |ctx| {
egui::CentralPanel::default().show(ctx, |ui| {
let rect = egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(side, side));
paint(ui.painter(), rect, &Tints::rest());
});
});
let mut out = Vec::new();
for clipped in &output.shapes {
paths_of(&clipped.shape, &mut out);
}
out
}
#[test]
fn a_trace_goes_out_as_its_centreline_and_its_width() {
let laid = traces(100.0);
assert_eq!(laid.len(), 6, "three casings and three conductors");
assert!(laid.iter().all(|(points, _)| *points == 49), "{laid:?}");
let widths: Vec<f32> = laid.iter().map(|(_, w)| *w).collect();
assert!(
widths.iter().take(3).all(|w| (w - 5.4).abs() < 0.01),
"{widths:?}"
);
assert!(
widths.iter().skip(3).all(|w| (w - 1.6).abs() < 0.01),
"{widths:?}"
);
}
fn hues(painted: &[Circle]) -> std::collections::BTreeSet<[u8; 4]> {
painted.iter().map(|(_, _, fill)| fill.to_array()).collect()
}
#[test]
fn the_painter_lays_down_one_circle_per_seat_plus_the_eye() {
assert_eq!(circles(&Tints::rest(), 100.0, 100.0).len(), NODE_SEATS + 1);
}
#[test]
fn a_rest_tint_paints_one_hue_on_every_circle() {
assert_eq!(hues(&circles(&Tints::rest(), 100.0, 100.0)).len(), 1);
}
#[test]
fn each_seat_takes_its_own_hue_and_the_eye_takes_the_largest_circle() {
let mut tints = Tints::rest();
tints.eye = ICHOR;
if let Some(first) = tints.nodes.first_mut() {
*first = SPECTRE;
}
let painted = circles(&tints, 100.0, 100.0);
assert_eq!(hues(&painted).len(), 3);
let eye = painted
.iter()
.copied()
.reduce(|best, one| if one.1 > best.1 { one } else { best })
.expect("the mark paints circles");
assert!((eye.0.x - 50.0).abs() < 1.0 && (eye.0.y - 50.0).abs() < 1.0);
assert_ne!(eye.2, ICHOR, "the hue is driven, never pasted in");
assert_ne!(eye.2, HYDRA, "the eye did not take its tint");
}
#[test]
fn a_wide_rect_centres_the_mark_instead_of_stretching_it() {
let painted = circles(&Tints::rest(), 200.0, 40.0);
let eye = painted
.iter()
.copied()
.reduce(|best, one| if one.1 > best.1 { one } else { best })
.expect("the mark paints circles");
assert!((eye.1 - 0.132 * 40.0).abs() < 0.5, "eye radius {}", eye.1);
assert!((eye.0.x - 100.0).abs() < 1.0 && (eye.0.y - 20.0).abs() < 1.0);
}