From ebab3fe94477aedc5e794b26e5892b1942fda21c Mon Sep 17 00:00:00 2001 From: Arne Keller Date: Mon, 29 Apr 2019 12:56:45 +0200 Subject: [PATCH] Cleanup --- src/display.rs | 29 +++++--- src/main.rs | 190 ++++++++++++++++++++++--------------------------- 2 files changed, 107 insertions(+), 112 deletions(-) diff --git a/src/display.rs b/src/display.rs index 050f639..9e7c56e 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,6 +1,6 @@ use svg::node::element::path::Data; -use svg::node::element::Path; use svg::node::element::Circle; +use svg::node::element::Path; use svg::Document; use svg::Node; @@ -13,29 +13,42 @@ fn idx_to_char(idx: usize) -> char { 0 => 'A', 1 => 'B', 2 => 'C', - _ => unreachable!() + _ => unreachable!(), } } -pub(crate) fn save_info(filename: &str, world: &World, tris: &[Triangle], original_tris: &[Triangle]) { +pub(crate) fn save_info( + filename: &str, + world: &World, + tris: &[Triangle], + original_tris: &[Triangle], +) { let mut text = format!("Gesamtabstand: {:.0}\n", world.width); for (idx, (_, tri)) in world.tris.iter().enumerate() { let unrotated_tri = tris[idx]; - let original_idx = original_tris.iter().position(|x| x.0 == unrotated_tri.0 && x.1 == unrotated_tri.1 && x.2 == unrotated_tri.2).unwrap(); + let original_idx = original_tris + .iter() + .position(|x| { + x.0 == unrotated_tri.0 && x.1 == unrotated_tri.1 && x.2 == unrotated_tri.2 + }) + .unwrap(); text += &format!("D{}:", original_idx); let mut coordinates = vec![tri.0, tri.1, tri.2]; coordinates.sort_by_key(|x| (x.x * 100.0) as u32); for (idx, c) in coordinates.into_iter().enumerate() { - text += &format!(" D{}{}[{:.0} {:.0}]", original_idx, idx_to_char(idx), c.x, c.y); + text += &format!( + " D{}{}[{:.0} {:.0}]", + original_idx, + idx_to_char(idx), + c.x, + c.y + ); } text += "\n"; } fs::write(filename, text).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, diff --git a/src/main.rs b/src/main.rs index 7192d1d..1092631 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ use geo::prelude::*; use rand::distributions::{Bernoulli, Distribution}; use rand::prelude::*; -use std::cmp; use std::f32; use std::f32::consts::PI; @@ -11,6 +10,7 @@ mod input; type Triangle = geo::Triangle; +/// Datenstruktur für eine (evtl. unvollständige) Anordnung #[derive(Debug, Clone)] struct World { /// Dreiecksgrundstücke @@ -18,82 +18,35 @@ struct World { /// Gesamtabstand width: f32, } -impl cmp::PartialEq for World { - fn eq(&self, _other: &Self) -> bool { - unimplemented!() - } -} -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() - } -} -impl cmp::PartialOrd for World { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -fn random_world(rng: &mut SmallRng, tris: &mut Vec, angles: &mut [f32], flips: &mut Vec) -> World { +/// Erzeugt eine zufällig konfigurierte Welt. Die Reihenfolge der Grundstücke als auch Winkel und Spiegelungen werden beliebig festgelegt. +fn random_world( + rng: &mut SmallRng, + tris: &mut Vec, + angles: &mut [f32], + flips: &mut Vec, +) -> World { tris.shuffle(rng); - let mut world = World { tris: Vec::with_capacity(tris.len()), width: 0.0 }; - for (idx, tri) in tris.iter().enumerate() { - let mut tri = *tri; + // je ein Dreieck hinzufügen + for idx in 0..tris.len() { + // zufällige Werte berechnen und anwenden + angles[idx] = rng.gen_range(0.0, 2.0 * PI); flips[idx] = rng.gen(); - if flips[idx] { - tri.0.x *= -1.0; - tri.1.x *= -1.0; - tri.2.x *= -1.0; - } - angles[idx] = rng.gen_range(0.0, 2.0*PI); - tri = rotate(tri, angles[idx]); - let minx = min(min(tri.0.x, tri.1.x), tri.2.x); - tri.0.x -= minx; - tri.1.x -= minx; - tri.2.x -= minx; - let miny = min(min(tri.0.y, tri.1.y), tri.2.y); - tri.0.y -= miny; - tri.1.y -= miny; - tri.2.y -= miny; - if world.tris.is_empty() { - world.tris.push((0, tri)); - world.normalize(); - } else { - //tri.0.x += 1.0; - //tri.1.x += 1.0; - //tri.2.x += 1.0; - for &delta in &[ - /*10.0, 5.0,*/ 1.0, 0.6, 0.32, 0.18, 0.1, 0.06, 0.03, 0.01 - ] { - //eprintln!("moving {:?}", tri); - //eprintln!("world {:?}", world); - let initial_state = world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri)); - let dx = if initial_state { delta } else { -delta }; - while world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri)) == initial_state { - tri.0.x += dx; - tri.1.x += dx; - tri.2.x += dx; - if tri.0.x < -1000.0 { - tri.0.x += 1000.0; - tri.1.x += 1000.0; - tri.2.x += 1000.0; - break; - } - } - } - world.tris.push((0, tri)); - world.normalize(); - } } - world.calc_width(); - world + construct_world(&tris, angles, flips) } +/// Welt mit vorgegebener Reihenfolge und Konfiguration (Winkel, Spiegelungen) erstellen. fn construct_world(tris: &[Triangle], angles: &[f32], flips: &[bool]) -> World { - let mut world = World { tris: Vec::with_capacity(tris.len()), width: 0.0 }; + let mut world = World { + tris: Vec::with_capacity(tris.len()), + width: 0.0, + }; + // jedes Dreieck einzeln hinzufügen for (idx, tri) in tris.iter().enumerate() { + // Kopie anlegen let mut tri = *tri; + // Werte anwenden if flips[idx] { tri.0.x *= -1.0; tri.1.x *= -1.0; @@ -112,20 +65,15 @@ fn construct_world(tris: &[Triangle], angles: &[f32], flips: &[bool]) -> World { world.tris.push((0, tri)); world.normalize(); } else { - //tri.0.x += 1.0; - //tri.1.x += 1.0; - //tri.2.x += 1.0; - for &delta in &[ - /*10.0, 5.0,*/ 1.0, 0.6, 0.32, 0.18, 0.1, 0.06, 0.03, 0.01 - ] { - //eprintln!("moving {:?}", tri); - //eprintln!("world {:?}", world); + // Dreieck neben den anderen platzieren + for &delta in &[1.0, 0.6, 0.32, 0.18, 0.1, 0.06, 0.03, 0.01] { let initial_state = world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri)); let dx = if initial_state { delta } else { -delta }; while world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri)) == initial_state { tri.0.x += dx; tri.1.x += dx; tri.2.x += dx; + // "entlaufene" Dreiecke einfangen if tri.0.x < -1000.0 { tri.0.x += 1000.0; tri.1.x += 1000.0; @@ -144,6 +92,7 @@ fn construct_world(tris: &[Triangle], angles: &[f32], flips: &[bool]) -> World { fn main() { let mut tris = input::read_input(); + // Dreiecke zum Ursprung verschieben for tri in &mut tris { tri.1.x -= tri.0.x; tri.2.x -= tri.0.x; @@ -155,48 +104,74 @@ fn main() { let original_tris = tris.clone(); let mut best = f32::MAX; let mut i = 0u64; - let save_prefix = format!("rand_sel_{}_", random::()); - println!("save prefix: {}", save_prefix); + let save_prefix = format!("rand_{}_", random::()); + println!("Dateipräfix: {}", save_prefix); loop { i += 1; - let new_best = one_main(&mut tris.clone()); + let new_best = find_layout(&mut tris.clone()); if new_best.width < best { best = new_best.width; eprint!("\r"); - println!("[{}] BEST {:.3} ", i, best); + println!("[{}] BEST {:.3} ", i, best); display::save_world(&format!("{}{}.svg", save_prefix, i), &new_best); - display::save_info(&format!("{}{}.txt", save_prefix, i), &new_best, &tris, &original_tris); + display::save_info( + &format!("{}{}.txt", save_prefix, i), + &new_best, + &tris, + &original_tris, + ); } else { eprint!("\r"); - println!("[{}] best {:.3} / prev. {:.3} ", i, new_best.width, best); + println!( + "[{}] best {:.3} ({:.3}) ", + i, new_best.width, best + ); } } } -fn one_main(tris: &mut Vec) -> World { +fn find_layout(tris: &mut Vec) -> World { let mut angles = vec![0.0; tris.len()]; let mut flips = vec![false; tris.len()]; let mut rng = SmallRng::from_entropy(); let mut best_width = f32::MAX; - let mut best_all = f32::MAX; let _random = random_world(&mut rng, tris, &mut angles, &mut flips); - optimize(69999, &mut best_all, &mut best_width, &tris, &mut angles, &mut flips, &mut rng, "rand_", 0) + optimize( + 99999, + &mut best_width, + &tris, + &mut angles, + &mut flips, + &mut rng, + ) } -#[allow(clippy::too_many_arguments)] -fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Triangle], best_angles: &mut Vec, best_flips: &mut Vec, rng: &mut SmallRng, save_prefix: &'static str, save_counter: usize) -> World { - let mut best = World { tris: vec![], width: f32::MAX }; +fn optimize( + iters: usize, + best_width: &mut f32, + tris: &[Triangle], + best_angles: &mut Vec, + best_flips: &mut Vec, + rng: &mut SmallRng, +) -> World { + let mut best = World { + tris: vec![], + width: f32::MAX, + }; let mut found_better = true; let mut iteration = 0; let flip_choice = Bernoulli::from_ratio(1, 11); while found_better { found_better = false; - // slightly modify angles + // Winkel und Spiegelungen leicht verändern let start_angles = best_angles.clone(); let start_flips = best_flips.clone(); let mut angles = start_angles.clone(); let mut flips = start_flips.clone(); for i in 0..iters { + if i % 10000 == 0 { + eprint!("."); + } for a in &mut angles { *a += rng.gen_range(-0.2, 0.2); } @@ -205,19 +180,11 @@ fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Tria *f = !*f; } } - // construct new world + // Breite der neuen Anordnung berechnen let new = construct_world(tris, &angles, &flips); if new.width < *best_width { - if new.width < *best_all { - *best_all = new.width; - eprint!("\r[{}O{}o{}] at {:.3}", save_counter, iteration, i, new.width); - //display::save_world(&format!("{}{}_{}_{}.svg", save_prefix, save_counter, iteration, i), &new); - //iters = 99999; - best = new; - } else { - //eprint!("[{}O{}o{}] best: {} (min: {})", save_counter, iteration, i, new.width, best_all); - //eprint!("."); - } + best = new; + eprint!("\r[O{}o{}] {:.3}", iteration, i, best.width); *best_width = best.width; found_better = true; *best_angles = angles; @@ -226,6 +193,7 @@ fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Tria angles = start_angles.clone(); flips = start_flips.clone(); } + // Winkel einzeln leicht verändern angles = best_angles.clone(); for idx in 0..tris.len() { for i in 0..999 { @@ -233,10 +201,21 @@ fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Tria angles[idx] += angle; let new = construct_world(tris, &angles, &best_flips); if new.width < *best_width { - *best_all = new.width; - eprint!("\r[{}O{}I{}E{}] at {:.3}", save_counter, iteration, idx, i, new.width); - //display::save_world(&format!("{}{}_{}_{}.svg", save_prefix, save_counter, iteration, i), &new); - //iters = 99999; + eprint!("\r[O{}I{}E{}] {:.3}", iteration, idx, i, new.width); + best = new; + *best_width = best.width; + found_better = true; + *best_angles = angles.clone(); + } else { + angles[idx] -= angle; + } + } + for i in 1000..2499 { + let angle = rng.gen_range(-PI, PI); + angles[idx] += angle; + let new = construct_world(tris, &angles, &best_flips); + if new.width < *best_width { + eprint!("\r[O{}I{}EE{}] {:.3}", iteration, idx, i, new.width); best = new; *best_width = best.width; found_better = true; @@ -252,6 +231,7 @@ fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Tria } impl World { + /// rechtes Welt zum Koordinatenursprung verschieben fn normalize(&mut self) { let mut maxx = 0.0; for (_, tri) in &self.tris { @@ -276,6 +256,7 @@ impl World { tri.2.x -= amount; } } + /// Gesamtabstand berechnen fn calc_width(&mut self) { let mut minx = f32::MIN; if (self.tris[0].1).0.y > -0.001 && (self.tris[0].1).0.y < 0.001 { @@ -307,6 +288,7 @@ impl World { } } +/// Dreieck drehen (Winkel im Bogenmaß) fn rotate(mut tri: Triangle, angle: f32) -> Triangle { let x1 = tri.1.x; let y1 = tri.1.y;