diff --git a/.gitignore b/.gitignore index ea61568..ac49798 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /target **/*.rs.bk -tri*.svg -world*.svg \ No newline at end of file +*.svg diff --git a/src/display.rs b/src/display.rs index 6a7bb39..00333f4 100644 --- a/src/display.rs +++ b/src/display.rs @@ -8,47 +8,50 @@ use std::fs; use super::*; -pub(crate) fn save_tri(filename: &str, tri: Triangle) { - fs::write( - filename, - generate_svg(&[tri]), - ) - .unwrap(); +pub(crate) fn save_tri(filename: &str, tri: Triangle) { + fs::write(filename, generate_svg(&[tri])).unwrap(); } pub(crate) fn save_world(filename: &str, world: &World) { - fs::write( - filename, - generate_svg(&world.tris.iter().map(|(_idx, tri)| *tri).collect::>()), - ) - .unwrap(); + fs::write( + filename, + generate_svg( + &world + .tris + .iter() + .map(|(_idx, tri)| *tri) + .collect::>(), + ), + ) + .unwrap(); } -pub(crate) fn generate_svg(tris: &[Triangle]) -> String { - let mut document = Document::new() - // view box at (x,y), (w,h) - .set( - "viewBox", - (-30.0, -30.0, 60.0, 30.0), - ) - .set("xmlns:xlink", "http://www.w3.org/1999/xlink"); +pub(crate) fn generate_svg(tris: &[Triangle]) -> String { + let mut document = Document::new() + // view box at (x,y), (w,h) + .set("viewBox", (-30.0, -30.0, 60.0, 30.0)) + .set("xmlns:xlink", "http://www.w3.org/1999/xlink"); - for tri in tris { - let data = Data::new() - .move_to((tri.0.x, -tri.0.y)) - .line_to((tri.1.x, -tri.1.y)) - .line_to((tri.2.x, -tri.2.y)) - .close(); - document.append(Path::new() - .set("fill", "none") - .set("stroke", "black") - .set("stroke-width", 0.4) - .set("d", data)); - document.append(Circle::new() - .set("fill", "red") - .set("cx", tri.0.x) - .set("cy", -tri.0.y) - .set("r", 0.3)); - } + for tri in tris { + let data = Data::new() + .move_to((tri.0.x, -tri.0.y)) + .line_to((tri.1.x, -tri.1.y)) + .line_to((tri.2.x, -tri.2.y)) + .close(); + document.append( + Path::new() + .set("fill", "none") + .set("stroke", "black") + .set("stroke-width", 0.4) + .set("d", data), + ); + document.append( + Circle::new() + .set("fill", "red") + .set("cx", tri.0.x) + .set("cy", -tri.0.y) + .set("r", 0.3), + ); + } - document.to_string() + document.to_string() } diff --git a/src/input.rs b/src/input.rs index a38b234..45ec5ae 100644 --- a/src/input.rs +++ b/src/input.rs @@ -3,28 +3,28 @@ use super::*; use std::io; use std::io::prelude::*; -pub(crate) fn read_input() -> Vec> { - read_stdin() +pub(crate) fn read_input() -> Vec> { + read_stdin() } -fn read_stdin() -> Vec> { - let stdin = io::stdin(); - let stdin = stdin.lock(); +fn read_stdin() -> Vec> { + let stdin = io::stdin(); + let stdin = stdin.lock(); - let mut lines = stdin.lines().map(Result::unwrap); - let tri_count = lines.next().unwrap().parse().unwrap(); - let mut tris = Vec::with_capacity(tri_count); + let mut lines = stdin.lines().map(Result::unwrap); + let tri_count = lines.next().unwrap().parse().unwrap(); + let mut tris = Vec::with_capacity(tri_count); - for _ in 0..tri_count { - let line = lines.next().unwrap(); - let n = line - .trim() - .split(' ') - .skip(1) - .map(|x| x.parse::().unwrap()) - .collect::>(); - tris.push([[n[0],n[1]],[n[2],n[3]],[n[4],n[5]]].into()); - } + for _ in 0..tri_count { + let line = lines.next().unwrap(); + let n = line + .trim() + .split(' ') + .skip(1) + .map(|x| x.parse::().unwrap()) + .collect::>(); + tris.push([[n[0], n[1]], [n[2], n[3]], [n[4], n[5]]].into()); + } - tris -} \ No newline at end of file + tris +} diff --git a/src/main.rs b/src/main.rs index 048d797..3bb8668 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,20 @@ -use geo::{Coordinate, Triangle}; use geo::prelude::*; +use geo::{Coordinate, Triangle}; use std::cmp; use std::collections::BinaryHeap; -use std::f64; -use std::f64::consts::PI; +use std::f32; +use std::f32::consts::PI; mod display; mod input; #[derive(Debug, Clone)] struct World { - /// Dreiecksgrundstücke - tris: Vec<(usize, Triangle)>, + /// Dreiecksgrundstücke + tris: Vec<(usize, Triangle)>, /// Gesamtabstand - width: f64, + width: f32, } impl cmp::PartialEq for World { fn eq(&self, _other: &Self) -> bool { @@ -23,14 +23,14 @@ impl cmp::PartialEq for World { } impl cmp::Eq for World {} impl cmp::Ord for World { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.width.partial_cmp(&other.width).unwrap().reverse() - } + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.width.partial_cmp(&other.width).unwrap().reverse() + } } impl cmp::PartialOrd for World { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } } fn main() { @@ -46,67 +46,97 @@ fn main() { idx /= 6; let prev_angle = angle_of(t, t.1); let mut added = false; - let rotated = rotate(t, 0.5*PI - prev_angle); - if rotated.1.y >= -0.001 && rotated.2.y >= -0.001 && (rotated.1.y < 0.1 || rotated.2.y < 0.1) { + let rotated = rotate(t, 0.5 * PI - prev_angle); + if rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { normalized_tris.push((idx, rotated)); - if rotated.1.x > 0.0 && rotated.2.x > 0.0 { + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { left_tris.push((idx, rotated)); } added = true; } let rotated = rotate(rotated, PI); - if !added && rotated.1.y >= -0.001 && rotated.2.y >= -0.001 && (rotated.1.y < 0.1 || rotated.2.y < 0.1) { + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { normalized_tris.push((idx, rotated)); - if rotated.1.x > 0.0 && rotated.2.x > 0.0 { + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { left_tris.push((idx, rotated)); } added = true; } - let rotated = rotate(t, -0.5*PI + prev_angle); - if !added && rotated.1.y >= -0.001 && rotated.2.y >= -0.001 && (rotated.1.y < 0.1 || rotated.2.y < 0.1) { + let rotated = rotate(t, -0.5 * PI + prev_angle); + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { normalized_tris.push((idx, rotated)); - if rotated.1.x > 0.0 && rotated.2.x > 0.0 { + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { left_tris.push((idx, rotated)); } added = true; } let rotated = rotate(rotated, PI); - if !added && rotated.1.y >= -0.001 && rotated.2.y >= -0.001 && (rotated.1.y < 0.1 || rotated.2.y < 0.1) { + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { normalized_tris.push((idx, rotated)); - if rotated.1.x > 0.0 && rotated.2.x > 0.0 { + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { left_tris.push((idx, rotated)); } } let prev_angle = angle_of(t, t.2); added = false; - let rotated = rotate(t, 0.5*PI - prev_angle); - if rotated.1.y >= -0.001 && rotated.2.y >= -0.001 && (rotated.1.y < 0.1 || rotated.2.y < 0.1) { + let rotated = rotate(t, 0.5 * PI - prev_angle); + if rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { normalized_tris.push((idx, rotated)); - if rotated.1.x > 0.0 && rotated.2.x > 0.0 { + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { left_tris.push((idx, rotated)); } added = true; } let rotated = rotate(rotated, PI); - if !added && rotated.1.y >= -0.001 && rotated.2.y >= -0.001 && (rotated.1.y < 0.1 || rotated.2.y < 0.1) { + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { normalized_tris.push((idx, rotated)); - if rotated.1.x > 0.0 && rotated.2.x > 0.0 { + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { left_tris.push((idx, rotated)); } added = true; } - let rotated = rotate(t, -0.5*PI + prev_angle); - if !added && rotated.1.y >= -0.001 && rotated.2.y >= -0.001 && (rotated.1.y < 0.1 || rotated.2.y < 0.1) { + let rotated = rotate(t, -0.5 * PI + prev_angle); + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { normalized_tris.push((idx, rotated)); - if rotated.1.x > 0.0 && rotated.2.x > 0.0 { + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { left_tris.push((idx, rotated)); } added = true; } let rotated = rotate(rotated, PI); - if !added && rotated.1.y >= -0.001 && rotated.2.y >= -0.001 && (rotated.1.y < 0.1 || rotated.2.y < 0.1) { + if !added + && rotated.1.y >= -0.001 + && rotated.2.y >= -0.001 + && (rotated.1.y < 0.1 || rotated.2.y < 0.1) + { normalized_tris.push((idx, rotated)); - if rotated.1.x > 0.0 && rotated.2.x > 0.0 { + if rotated.1.x > -0.001 && rotated.2.x > -0.001 { left_tris.push((idx, rotated)); } } @@ -115,58 +145,80 @@ fn main() { display::save_tri(&format!("{}{}.svg", save_prefix, save_counter), *t); save_counter += 1; } - + let save_prefix = "start_"; + save_counter = 0; let mut worlds = BinaryHeap::new(); for t in &left_tris { let mut world = World { tris: vec![*t], - width: 0.0 + width: 0.0, }; world.normalize(); + display::save_world(&format!("{}{}.svg", save_prefix, save_counter), &world); + save_counter += 1; worlds.push(world); } - let mut best = World { tris: vec![], width: f64::MAX }; + let mut best = World { + tris: vec![], + width: f32::MAX, + }; let save_prefix = "world_"; - let mut save_counter = 0; + save_counter = 0; while worlds.peek().map(|x| x.width <= best.width) == Some(true) { let w = worlds.pop().unwrap(); //println!("s: {:?}", w); let mut new = vec![]; - for (next_idx, next_tri) in left_tris.iter() - .filter(|(idx1, _tri)| w.tris.iter().all(|(idx2, _tri)| idx1 != idx2)) { + for (next_idx, next_tri) in left_tris + .iter() + .filter(|(idx1, _tri)| w.tris.iter().all(|(idx2, _tri)| idx1 != idx2)) + { //println!("trying {:?}", next_idx); let (_, last_tri) = *w.tris.last().unwrap(); - let last_vertex = if last_tri.1.y > 0.01 && !(last_tri.2.y > 0.01 && last_tri.2.x > last_tri.1.x) { - last_tri.1 - } else { - last_tri.2 - }; + let last_vertex = + if last_tri.1.y > 0.01 && !(last_tri.2.y > 0.01 && last_tri.2.x > last_tri.1.x) { + last_tri.1 + } else { + last_tri.2 + }; + let mut free_angle = angle_of(last_tri, last_vertex); //println!("angle of {:?}: {:?}", last_vertex, free_angle); if last_vertex.x > 0.0 { - free_angle = 0.5*PI - free_angle; + free_angle = 0.5 * PI - free_angle; } else { - free_angle = 0.5*PI + free_angle; + free_angle = 0.5 * PI + free_angle; } - let next_vertex = if next_tri.1.y > 0.01 { + let next_vertex = if next_tri.1.y < 0.001 && next_tri.1.y > -0.001 { + next_tri.2 + } else if next_tri.2.y < 0.001 && next_tri.2.y > -0.001 { + next_tri.1 + } else if next_tri.1.x < next_tri.2.x { next_tri.1 } else { next_tri.2 }; - let mut next_angle = angle_of(*next_tri, next_vertex); - if next_vertex.x > 0.0 { - next_angle = 0.5*PI - next_angle; - } else { - next_angle = 0.5*PI + next_angle; - } - if next_angle <= free_angle { - // easy: just rotate to fit and put it below the last triangle + let next_angle = PI - angle_of(*next_tri, next_vertex); + + for angle in 0..=150 { + let angle = (1.0 * angle as f32).to_radians(); + let next_angle = next_angle - angle; + let mut tri = rotate(*next_tri, angle); + + let dx = if last_tri.intersects(&tri) { + 0.005 + } else { + -0.005 + }; + while last_tri.intersects(&tri) { + tri.0.x += dx; + tri.1.x += dx; + tri.2.x += dx; + } let mut w = w.clone(); - w.tris.push((*next_idx, rotate(*next_tri, free_angle - next_angle))); - //println!("new {:?} {:?} {:?}: {:?}", save_counter, free_angle, next_angle, w); + w.tris.push((*next_idx, tri)); w.normalize(); w.calc_width(); if save_counter < 100 { @@ -175,36 +227,15 @@ fn main() { if w.tris.len() == count_tris { if w.width < best.width { println!("new best: {}", w.width); + println!("{:?}", w.tris); + display::save_world( + &format!("{}best_{}.svg", save_prefix, save_counter), + &w, + ); best = w; } } else { - new.push(w); - } - save_counter += 1; - } else { - // "hard": move it right till it fits - print!("error: doesn't fit [S{} {}]", w.tris.len(), save_counter); - assert!(last_tri.intersects(next_tri)); - let mut next_tri = *next_tri; - while last_tri.intersects(&next_tri) { - next_tri.0.x += 0.005; - next_tri.1.x += 0.005; - next_tri.2.x += 0.005; - } - let mut w = w.clone(); - w.tris.push((*next_idx, next_tri)); - w.normalize(); - w.calc_width(); - println!(" -> width = {}", w.width); - if save_counter < 100 { - display::save_world(&format!("{}{}.svg", save_prefix, save_counter), &w); - } - if w.tris.len() == count_tris { - if w.width < best.width { - println!("new best: {}", w.width); - best = w; - } - } else { + w.width -= (2.0 * PI - free_angle - next_angle) * 0.003 * w.tris.len() as f32; new.push(w); } save_counter += 1; @@ -234,7 +265,7 @@ impl World { self.move_left(maxx); } } - fn move_left(&mut self, amount: f64) { + fn move_left(&mut self, amount: f32) { for (_, tri) in &mut self.tris { tri.0.x -= amount; tri.1.x -= amount; @@ -242,55 +273,96 @@ impl World { } } fn calc_width(&mut self) { - let mut x = vec![]; - for (_, tri) in &self.tris { - if tri.0.y < 0.001 && tri.0.y > -0.001 { - x.push(tri.0.x); - } - if tri.1.y < 0.001 && tri.1.y > -0.001 { - x.push(tri.1.x); - } - if tri.2.y < 0.001 && tri.2.y > -0.001 { - x.push(tri.2.x); - } + let mut minx = f32::MIN; + if (self.tris[0].1).0.y > -0.001 && (self.tris[0].1).0.y < 0.001 { + minx = max((self.tris[0].1).0.x, minx); } - x.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); - self.width = x[x.len()-2] - x[1]; + if (self.tris[0].1).1.y > -0.001 && (self.tris[0].1).1.y < 0.001 { + minx = max((self.tris[0].1).1.x, minx); + } + if (self.tris[0].1).2.y > -0.001 && (self.tris[0].1).2.y < 0.001 { + minx = max((self.tris[0].1).2.x, minx); + } + let mut maxx = f32::MAX; + if (self.tris[self.tris.len() - 1].1).0.y > -0.001 + && (self.tris[self.tris.len() - 1].1).0.y < 0.001 + { + maxx = min((self.tris[self.tris.len() - 1].1).0.x, maxx); + } + if (self.tris[self.tris.len() - 1].1).1.y > -0.001 + && (self.tris[self.tris.len() - 1].1).1.y < 0.001 + { + maxx = min((self.tris[self.tris.len() - 1].1).1.x, maxx); + } + if (self.tris[self.tris.len() - 1].1).2.y > -0.001 + && (self.tris[self.tris.len() - 1].1).2.y < 0.001 + { + maxx = min((self.tris[self.tris.len() - 1].1).2.x, maxx); + } + self.width = maxx - minx; } } -fn rotate(mut tri: Triangle, angle: f64) -> Triangle { +fn rotate(mut tri: Triangle, angle: f32) -> Triangle { let x1 = tri.1.x; let y1 = tri.1.y; - tri.1.x = x1*angle.cos() - y1*angle.sin(); - tri.1.y = x1*angle.sin() + y1*angle.cos(); + tri.1.x = x1 * angle.cos() - y1 * angle.sin(); + tri.1.y = x1 * angle.sin() + y1 * angle.cos(); let x2 = tri.2.x; let y2 = tri.2.y; - tri.2.x = x2*angle.cos() - y2*angle.sin(); - tri.2.y = x2*angle.sin() + y2*angle.cos(); + tri.2.x = x2 * angle.cos() - y2 * angle.sin(); + tri.2.y = x2 * angle.sin() + y2 * angle.cos(); tri } -fn angle_of(_tri: Triangle, point: Coordinate) -> f64 { +fn angle_of(_tri: Triangle, point: Coordinate) -> f32 { let d = (point.x.powi(2) + point.y.powi(2)).sqrt(); (point.y / d).acos() } -fn transformations(tris: &[Triangle]) -> Vec> { +fn transformations(tris: &[Triangle]) -> Vec> { let mut new = Vec::with_capacity(tris.len() * 6); for t in tris { - let n = Triangle((0.0, 0.0).into(), (t.1.x - t.0.x, t.1.y - t.0.y).into(), (t.2.x - t.0.x, t.2.y - t.0.y).into()); + let n = Triangle( + (0.0, 0.0).into(), + (t.1.x - t.0.x, t.1.y - t.0.y).into(), + (t.2.x - t.0.x, t.2.y - t.0.y).into(), + ); new.push(n); let n = [[-n.0.x, n.0.y], [-n.1.x, n.1.y], [-n.2.x, n.2.y]].into(); new.push(n); - let n = Triangle((0.0, 0.0).into(), (t.2.x - t.1.x, t.2.y - t.1.y).into(), (t.0.x - t.1.x, t.0.y - t.1.y).into()); + let n = Triangle( + (0.0, 0.0).into(), + (t.2.x - t.1.x, t.2.y - t.1.y).into(), + (t.0.x - t.1.x, t.0.y - t.1.y).into(), + ); new.push(n); let n = [[-n.0.x, n.0.y], [-n.1.x, n.1.y], [-n.2.x, n.2.y]].into(); new.push(n); - let n = Triangle((0.0, 0.0).into(), (t.0.x - t.2.x, t.0.y - t.2.y).into(), (t.1.x - t.2.x, t.1.y - t.2.y).into()); + let n = Triangle( + (0.0, 0.0).into(), + (t.0.x - t.2.x, t.0.y - t.2.y).into(), + (t.1.x - t.2.x, t.1.y - t.2.y).into(), + ); new.push(n); let n = [[-n.0.x, n.0.y], [-n.1.x, n.1.y], [-n.2.x, n.2.y]].into(); new.push(n); } new } + +fn min(a: f32, b: f32) -> f32 { + if a < b { + a + } else { + b + } +} + +fn max(a: f32, b: f32) -> f32 { + if a > b { + a + } else { + b + } +}