use std::{hash::Hash, hash::Hasher, io}; use anyhow::Result; use clap::{app_from_crate, crate_authors, crate_description, crate_name, crate_version, App, Arg}; use io::Write; use lasy::{ euler_graph_to_euler_circuit, interpolate_euler_circuit, point_graph_to_euler_graph, points_to_segments, segments_to_point_graph, Blanked, InterpolationConfig, IsBlank, Lerp, Position, Weight, }; type InputArrayEntry = (f32, f32, u32); #[derive(Clone, Copy)] struct Point { x: f32, y: f32, color: u32, weight: u32, } impl Hash for Point { fn hash(&self, hasher: &mut H) { #[derive(Hash, Eq, PartialEq)] struct HashPoint { x: u32, y: u32, color: u32, } impl From for HashPoint { fn from(p: Point) -> Self { HashPoint { x: (p.x * i16::MAX as f32) as u32, y: (p.y * i16::MAX as f32) as u32, color: p.color, } } } HashPoint::from(*self).hash(hasher); } } impl Position for Point { fn position(&self) -> [f32; 2] { [self.x, self.y] } } impl IsBlank for Point { fn is_blank(&self) -> bool { self.color == 0xffffff } } impl Blanked for Point { fn blanked(&self) -> Self { Self { color: 0xffffff, ..*self } } } impl Weight for Point { fn weight(&self) -> u32 { self.weight } } impl Lerp for Point { type Scalar = f32; fn lerp(&self, other: &Self, amt: f32) -> Self { let v = [self.x, self.y].lerp(&[other.x, other.y], amt); let (b1, g1, r1) = ( ((self.color) & 0xff) as f32, ((self.color >> 8) & 0xff) as f32, ((self.color >> 16) & 0xff) as f32, ); let (b2, g2, r2) = ( ((other.color) & 0xff) as f32, ((other.color >> 8) & 0xff) as f32, ((other.color >> 16) & 0xff) as f32, ); let [r3, g3, b3] = [r1, g1, b1].lerp(&[r2, g2, b2], amt); let color: u32 = ((r3 as u32) << 16) | ((g3 as u32) << 8) | b3 as u32; Point { x: v[0], y: v[1], color, weight: self.weight, } } } fn optimize_line(default_weight: u32, line: &str) -> Result> { let frame: Vec = serde_json::from_str(line)?; let input_points: Vec = frame .into_iter() .map(|(x, y, color)| Point { x, y, color, weight: default_weight, }) .collect(); let segs = points_to_segments(&input_points); let pg = segments_to_point_graph(&input_points, segs); let eg = point_graph_to_euler_graph(&pg); let ec = euler_graph_to_euler_circuit(&input_points, &eg); let output_points: Vec = interpolate_euler_circuit( &input_points, &ec, &eg, input_points.len() as u32, &InterpolationConfig::default(), ); let output_frame: Vec<_> = output_points.iter().map(|p| (p.x, p.y, p.color)).collect(); Ok(output_frame) } fn main() -> Result<()> { let matches = app_from_crate!() .args(&[Arg::with_name("default-weight") .short("w") .takes_value(true) .multiple(false) .default_value("0")]) .get_matches(); let default_weight = matches.value_of("default-weight").unwrap().parse().unwrap(); loop { let mut line = String::new(); io::stdin().read_line(&mut line)?; serde_json::to_writer(io::stdout(), &optimize_line(default_weight, &line)?)?; io::stdout().write(b"\n")?; io::stdout().flush()?; } } #[test] fn optimize_line_test() { optimize_line(0, "[[100.0, 100.0, 65280], [100.0, 500.0, 65280], [500.0, 500.0, 65280], [500.0, 100.0, 65280], [100.0, 100.0, 65280]]").unwrap(); }