This commit is contained in:
Arne Keller 2019-04-29 12:56:45 +02:00
parent 0df1c9f32c
commit ebab3fe944
2 changed files with 107 additions and 112 deletions

View File

@ -1,6 +1,6 @@
use svg::node::element::path::Data; use svg::node::element::path::Data;
use svg::node::element::Path;
use svg::node::element::Circle; use svg::node::element::Circle;
use svg::node::element::Path;
use svg::Document; use svg::Document;
use svg::Node; use svg::Node;
@ -13,29 +13,42 @@ fn idx_to_char(idx: usize) -> char {
0 => 'A', 0 => 'A',
1 => 'B', 1 => 'B',
2 => 'C', 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); let mut text = format!("Gesamtabstand: {:.0}\n", world.width);
for (idx, (_, tri)) in world.tris.iter().enumerate() { for (idx, (_, tri)) in world.tris.iter().enumerate() {
let unrotated_tri = tris[idx]; 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); text += &format!("D{}:", original_idx);
let mut coordinates = vec![tri.0, tri.1, tri.2]; let mut coordinates = vec![tri.0, tri.1, tri.2];
coordinates.sort_by_key(|x| (x.x * 100.0) as u32); coordinates.sort_by_key(|x| (x.x * 100.0) as u32);
for (idx, c) in coordinates.into_iter().enumerate() { 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"; text += "\n";
} }
fs::write(filename, text).unwrap(); 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) { pub(crate) fn save_world(filename: &str, world: &World) {
fs::write( fs::write(
filename, filename,

View File

@ -2,7 +2,6 @@ use geo::prelude::*;
use rand::distributions::{Bernoulli, Distribution}; use rand::distributions::{Bernoulli, Distribution};
use rand::prelude::*; use rand::prelude::*;
use std::cmp;
use std::f32; use std::f32;
use std::f32::consts::PI; use std::f32::consts::PI;
@ -11,6 +10,7 @@ mod input;
type Triangle = geo::Triangle<f32>; type Triangle = geo::Triangle<f32>;
/// Datenstruktur für eine (evtl. unvollständige) Anordnung
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct World { struct World {
/// Dreiecksgrundstücke /// Dreiecksgrundstücke
@ -18,82 +18,35 @@ struct World {
/// Gesamtabstand /// Gesamtabstand
width: f32, 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<cmp::Ordering> {
Some(self.cmp(other))
}
}
fn random_world(rng: &mut SmallRng, tris: &mut Vec<Triangle>, angles: &mut [f32], flips: &mut Vec<bool>) -> 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<Triangle>,
angles: &mut [f32],
flips: &mut Vec<bool>,
) -> World {
tris.shuffle(rng); tris.shuffle(rng);
let mut world = World { tris: Vec::with_capacity(tris.len()), width: 0.0 }; // je ein Dreieck hinzufügen
for (idx, tri) in tris.iter().enumerate() { for idx in 0..tris.len() {
let mut tri = *tri; // zufällige Werte berechnen und anwenden
angles[idx] = rng.gen_range(0.0, 2.0 * PI);
flips[idx] = rng.gen(); 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); construct_world(&tris, angles, flips)
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
} }
/// Welt mit vorgegebener Reihenfolge und Konfiguration (Winkel, Spiegelungen) erstellen.
fn construct_world(tris: &[Triangle], angles: &[f32], flips: &[bool]) -> World { 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() { for (idx, tri) in tris.iter().enumerate() {
// Kopie anlegen
let mut tri = *tri; let mut tri = *tri;
// Werte anwenden
if flips[idx] { if flips[idx] {
tri.0.x *= -1.0; tri.0.x *= -1.0;
tri.1.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.tris.push((0, tri));
world.normalize(); world.normalize();
} else { } else {
//tri.0.x += 1.0; // Dreieck neben den anderen platzieren
//tri.1.x += 1.0; for &delta in &[1.0, 0.6, 0.32, 0.18, 0.1, 0.06, 0.03, 0.01] {
//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 initial_state = world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri));
let dx = if initial_state { delta } else { -delta }; let dx = if initial_state { delta } else { -delta };
while world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri)) == initial_state { while world.tris[0..idx].iter().any(|(_, x)| x.intersects(&tri)) == initial_state {
tri.0.x += dx; tri.0.x += dx;
tri.1.x += dx; tri.1.x += dx;
tri.2.x += dx; tri.2.x += dx;
// "entlaufene" Dreiecke einfangen
if tri.0.x < -1000.0 { if tri.0.x < -1000.0 {
tri.0.x += 1000.0; tri.0.x += 1000.0;
tri.1.x += 1000.0; tri.1.x += 1000.0;
@ -144,6 +92,7 @@ fn construct_world(tris: &[Triangle], angles: &[f32], flips: &[bool]) -> World {
fn main() { fn main() {
let mut tris = input::read_input(); let mut tris = input::read_input();
// Dreiecke zum Ursprung verschieben
for tri in &mut tris { for tri in &mut tris {
tri.1.x -= tri.0.x; tri.1.x -= tri.0.x;
tri.2.x -= tri.0.x; tri.2.x -= tri.0.x;
@ -155,48 +104,74 @@ fn main() {
let original_tris = tris.clone(); let original_tris = tris.clone();
let mut best = f32::MAX; let mut best = f32::MAX;
let mut i = 0u64; let mut i = 0u64;
let save_prefix = format!("rand_sel_{}_", random::<u64>()); let save_prefix = format!("rand_{}_", random::<u64>());
println!("save prefix: {}", save_prefix); println!("Dateipräfix: {}", save_prefix);
loop { loop {
i += 1; i += 1;
let new_best = one_main(&mut tris.clone()); let new_best = find_layout(&mut tris.clone());
if new_best.width < best { if new_best.width < best {
best = new_best.width; best = new_best.width;
eprint!("\r"); eprint!("\r");
println!("[{}] BEST {:.3} ", i, best); println!("[{}] BEST {:.3} ", i, best);
display::save_world(&format!("{}{}.svg", save_prefix, i), &new_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 { } else {
eprint!("\r"); 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<Triangle>) -> World { fn find_layout(tris: &mut Vec<Triangle>) -> World {
let mut angles = vec![0.0; tris.len()]; let mut angles = vec![0.0; tris.len()];
let mut flips = vec![false; tris.len()]; let mut flips = vec![false; tris.len()];
let mut rng = SmallRng::from_entropy(); let mut rng = SmallRng::from_entropy();
let mut best_width = f32::MAX; let mut best_width = f32::MAX;
let mut best_all = f32::MAX;
let _random = random_world(&mut rng, tris, &mut angles, &mut flips); 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(
fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Triangle], best_angles: &mut Vec<f32>, best_flips: &mut Vec<bool>, rng: &mut SmallRng, save_prefix: &'static str, save_counter: usize) -> World { iters: usize,
let mut best = World { tris: vec![], width: f32::MAX }; best_width: &mut f32,
tris: &[Triangle],
best_angles: &mut Vec<f32>,
best_flips: &mut Vec<bool>,
rng: &mut SmallRng,
) -> World {
let mut best = World {
tris: vec![],
width: f32::MAX,
};
let mut found_better = true; let mut found_better = true;
let mut iteration = 0; let mut iteration = 0;
let flip_choice = Bernoulli::from_ratio(1, 11); let flip_choice = Bernoulli::from_ratio(1, 11);
while found_better { while found_better {
found_better = false; found_better = false;
// slightly modify angles // Winkel und Spiegelungen leicht verändern
let start_angles = best_angles.clone(); let start_angles = best_angles.clone();
let start_flips = best_flips.clone(); let start_flips = best_flips.clone();
let mut angles = start_angles.clone(); let mut angles = start_angles.clone();
let mut flips = start_flips.clone(); let mut flips = start_flips.clone();
for i in 0..iters { for i in 0..iters {
if i % 10000 == 0 {
eprint!(".");
}
for a in &mut angles { for a in &mut angles {
*a += rng.gen_range(-0.2, 0.2); *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; *f = !*f;
} }
} }
// construct new world // Breite der neuen Anordnung berechnen
let new = construct_world(tris, &angles, &flips); let new = construct_world(tris, &angles, &flips);
if new.width < *best_width { 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; best = new;
} else { eprint!("\r[O{}o{}] {:.3}", iteration, i, best.width);
//eprint!("[{}O{}o{}] best: {} (min: {})", save_counter, iteration, i, new.width, best_all);
//eprint!(".");
}
*best_width = best.width; *best_width = best.width;
found_better = true; found_better = true;
*best_angles = angles; *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(); angles = start_angles.clone();
flips = start_flips.clone(); flips = start_flips.clone();
} }
// Winkel einzeln leicht verändern
angles = best_angles.clone(); angles = best_angles.clone();
for idx in 0..tris.len() { for idx in 0..tris.len() {
for i in 0..999 { 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; angles[idx] += angle;
let new = construct_world(tris, &angles, &best_flips); let new = construct_world(tris, &angles, &best_flips);
if new.width < *best_width { if new.width < *best_width {
*best_all = new.width; eprint!("\r[O{}I{}E{}] {:.3}", iteration, idx, i, new.width);
eprint!("\r[{}O{}I{}E{}] at {:.3}", save_counter, iteration, idx, i, new.width); best = new;
//display::save_world(&format!("{}{}_{}_{}.svg", save_prefix, save_counter, iteration, i), &new); *best_width = best.width;
//iters = 99999; 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 = new;
*best_width = best.width; *best_width = best.width;
found_better = true; found_better = true;
@ -252,6 +231,7 @@ fn optimize(iters: usize, best_all: &mut f32, best_width: &mut f32, tris: &[Tria
} }
impl World { impl World {
/// rechtes Welt zum Koordinatenursprung verschieben
fn normalize(&mut self) { fn normalize(&mut self) {
let mut maxx = 0.0; let mut maxx = 0.0;
for (_, tri) in &self.tris { for (_, tri) in &self.tris {
@ -276,6 +256,7 @@ impl World {
tri.2.x -= amount; tri.2.x -= amount;
} }
} }
/// Gesamtabstand berechnen
fn calc_width(&mut self) { fn calc_width(&mut self) {
let mut minx = f32::MIN; let mut minx = f32::MIN;
if (self.tris[0].1).0.y > -0.001 && (self.tris[0].1).0.y < 0.001 { 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 { fn rotate(mut tri: Triangle, angle: f32) -> Triangle {
let x1 = tri.1.x; let x1 = tri.1.x;
let y1 = tri.1.y; let y1 = tri.1.y;