pub struct Geodesic {
pub start: StartHint,
}Expand description
Reveal along the “spine” of the art.
The ink forms a graph under 8-connectivity. We take its largest connected
component (so a stray fleck can never hijack the reveal), find the two ends
of that component’s longest geodesic, a double breadth-first sweep, the
standard graph-diameter trick, and rank each cell by geodesic distance from
the chosen start, normalised to 0..=1. A serpent therefore paints from one
tip to the other along its body, around every coil, with no per-art tuning.
Detached ink (shading, flecks, a signature) does not dump at the end. Every cell inherits the rank of the nearest spine cell, a geodesic Voronoi computed by a multi-source flood, so detail reveals in step with the body it sits beside. Both behaviours fall out of one metric; neither is a special case, so imperfect hand-drawn art still reveals gracefully.
Fields§
§start: StartHintWhich tip of the spine the reveal begins from.
Implementations§
Source§impl Geodesic
impl Geodesic
Sourcepub fn diagnose(&self, art: &Art) -> GeodesicReport
pub fn diagnose(&self, art: &Art) -> GeodesicReport
Inspect the art without building a full rank map.
Examples found in repository?
20fn main() {
21 let args: Vec<String> = std::env::args().skip(1).collect();
22 let snapshots = args.iter().any(|a| a == "--snapshots");
23
24 let art = match arg_value(&args, "--art") {
25 Some(path) => match std::fs::read_to_string(&path) {
26 Ok(text) => Art::parse(&text),
27 Err(e) => {
28 eprintln!("inkling: could not read {path}: {e}");
29 std::process::exit(1);
30 }
31 },
32 None => Art::parse(&serpent(64, 13)),
33 };
34
35 let ordering = Geodesic {
36 start: StartHint::TopLeft,
37 };
38 let GeodesicReport {
39 ink_cells,
40 connected_cells,
41 spine_length,
42 } = ordering.diagnose(&art);
43 let ranks = ordering.rank(&art);
44
45 eprintln!(
46 "inkling · {ink_cells} ink cells · {connected_cells} on the spine \
47 ({:.0}% connected) · spine length {spine_length}",
48 100.0 * connected_cells as f32 / ink_cells.max(1) as f32,
49 );
50
51 // Headless / piped / explicit: print staged text frames and exit.
52 if snapshots || !std::io::stdout().is_terminal() {
53 for p in [0.0, 0.2, 0.4, 0.6, 0.8, 1.0] {
54 println!("\n── progress {:>3.0}% {}", p * 100.0, "─".repeat(28));
55 print!("{}", frame::to_string(&art, &ranks, p));
56 }
57 return;
58 }
59
60 #[cfg(feature = "terminal")]
61 {
62 use inkling::{
63 easing::Easing,
64 render::{animate, Style},
65 };
66 use std::time::Duration;
67 if let Err(e) = animate(
68 &art,
69 &ranks,
70 Style::default(),
71 Duration::from_millis(3500),
72 Easing::EaseInOutCubic,
73 ) {
74 eprintln!("inkling: render error: {e}");
75 }
76 }
77}