diff --git a/Cargo.lock b/Cargo.lock index 7b3f80d..bcc67ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,7 +5,8 @@ name = "aufgabe1" version = "0.1.0" dependencies = [ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "geo 0.12.0", + "geo 0.12.0 (git+https://github.com/FliegendeWurst/geo)", + "hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", @@ -45,6 +46,11 @@ name = "bitflags" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byteorder" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cc" version = "1.0.32" @@ -106,9 +112,10 @@ dependencies = [ [[package]] name = "geo" version = "0.12.0" +source = "git+https://github.com/FliegendeWurst/geo#2b702bce790d918e09dbc6cb4f244312636a87b5" dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "geo-types 0.4.1", + "geo-types 0.4.1 (git+https://github.com/FliegendeWurst/geo)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -116,11 +123,21 @@ dependencies = [ [[package]] name = "geo-types" version = "0.4.1" +source = "git+https://github.com/FliegendeWurst/geo#2b702bce790d918e09dbc6cb4f244312636a87b5" dependencies = [ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hashbrown" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "heck" version = "0.3.1" @@ -233,6 +250,11 @@ name = "rustc-demangle" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scopeguard" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "simplecss" version = "0.1.0" @@ -385,6 +407,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum backtrace 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5a90e2b463010cd0e0ce9a11d4a9d5d58d9f41d4a6ba3dcaf9e68b466e88b4" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum cc 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ad0daef304fa0b4238f5f7ed7178774b43b06f6a9b6509f6642bef4ff1f7b9b2" "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" @@ -392,6 +415,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600" +"checksum geo 0.12.0 (git+https://github.com/FliegendeWurst/geo)" = "" +"checksum geo-types 0.4.1 (git+https://github.com/FliegendeWurst/geo)" = "" +"checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" @@ -408,6 +434,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum roxmltree 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "53b0200cbfa8b3f6cfd6076592717d697a1ddc57cb2a8fbfd3d133c06011b579" "checksum rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "120bfe4837befb82c5a637a5a8c490a27d25524ac19fffec5b4e555ca6e36ee8" "checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum simplecss 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "135685097a85a64067df36e28a243e94a94f76d829087ce0be34eeb014260c0e" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" diff --git a/Cargo.toml b/Cargo.toml index e1fce39..4ca8209 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,14 @@ edition = "2018" codegen-units = 1 opt-level = 3 panic = "abort" +debug = true [dependencies] -geo = { path = "/home/arne/Documents/Code/Github/geo/geo" } +geo = { git = "https://github.com/FliegendeWurst/geo" } svg = "0.5.11" chrono = "0.4.6" structopt = { version = "0.2", default-features = false } svgdom = "0.16.1" rstar = "0.2.0" lazy_static = "1.3.0" +hashbrown = "0.1.8" diff --git a/DokumentationAufgabe1.tex b/DokumentationAufgabe1.tex index 1683d9a..1c450d9 100644 --- a/DokumentationAufgabe1.tex +++ b/DokumentationAufgabe1.tex @@ -239,7 +239,7 @@ Der Weg von Lisa und dem Bus ist animiert. Zusätzlich zu den unbedingt benötig \subsection{Optimierungen} Wie bereits in der Laufzeitanalyse kurz angesprochen, benutzt das Programm bestimmte Heuristiken, um die Suche zu beschleunigen. Eine davon ist die maximal mögliche Wartezeit für eine bestimmte Route. Dafür berechnet das Programm die höchstmögliche Position des Busses, was proportional zur gesuchten Größe ist. -Um zu überprüfen, ob ein Routenabschnitt die Hindernisse schneidet, wird unter anderem ein R*-Baum benutzt. Mit ca. 40 Polygonen ist diese Methode ca. 5-6x schneller als alle Polygone zu überprüfen. Die Geschwindigkeitsgewinne werden größer, je mehr Polygone vorhanden sind. +Um zu überprüfen, ob ein Routenabschnitt die Hindernisse schneidet, wird unter anderem ein R*-Baum benutzt. Fûr ca. 40 Polygonen ist diese Methode ca. 5-6x schneller als alle Polygone zu überprüfen. Die Geschwindigkeitsgewinne werden größer, je mehr Polygone vorhanden sind. Zudem wird das Ergebnis in einer Hashtabelle gespeichert, was die insgesamte Performanz ca. 6-10x steigert ($\frac{n*(n-1)}{2}$ Berechnungen statt $n!$). \section{Beispiele} Alle Beispiele sind im Maßstab 1:70m. diff --git a/src/display.rs b/src/display.rs index c2a9e26..44d4a9b 100644 --- a/src/display.rs +++ b/src/display.rs @@ -9,7 +9,7 @@ use std::fs; use super::*; pub(crate) fn dump_latex_code(route: &[RunState], polys: &[Polygon]) { - const SCALE: f64 = 70.0; + const SCALE: f32 = 70.0; let mut xmax = 0.0; let mut ymax = 0.0; for poly in polys { @@ -220,7 +220,7 @@ pub(crate) fn generate_svg( document.append(path); } - let mut first: (f64, f64) = (route1[0].x(), route1[0].y()); + let mut first: (f32, f32) = (route1[0].x(), route1[0].y()); first.1 *= -1.0; let mut data = Data::new().move_to(first); for point in &route1[1..] { diff --git a/src/input.rs b/src/input.rs index 5d10802..4aefe5b 100644 --- a/src/input.rs +++ b/src/input.rs @@ -85,16 +85,16 @@ fn read_svg(input: &str) -> Result> { for p in &path.0 { match p { PathSegment::MoveTo { x, y, .. } => { - points.push(Point::new(*x, -*y)); + points.push(Point::new(*x as f32, -*y as f32)); } PathSegment::LineTo { x, y, .. } => { - points.push(Point::new(*x, -*y)); + points.push(Point::new(*x as f32, -*y as f32)); } PathSegment::VerticalLineTo { y, .. } => { - points.push(Point::new(points.last().unwrap().x(), -*y)); + points.push(Point::new(points.last().unwrap().x(), -*y as f32)); } PathSegment::HorizontalLineTo { x, .. } => { - points.push(Point::new(*x, points.last().unwrap().y())); + points.push(Point::new(*x as f32, points.last().unwrap().y())); } PathSegment::ClosePath { .. } => break, _ => unimplemented!("nicht unterstütztes SVG-Element: {:?}", p), diff --git a/src/main.rs b/src/main.rs index 47eee09..688a52f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,13 @@ use chrono::{Duration, NaiveTime}; use geo::prelude::*; +use hashbrown::HashMap; use lazy_static::lazy_static; use rstar::{RTree, RTreeObject}; use structopt::StructOpt; use std::cmp; use std::collections::BinaryHeap; -use std::f64; +use std::f32; use std::process::exit; use std::sync::RwLock; use std::time::{self, Instant}; @@ -14,10 +15,10 @@ use std::time::{self, Instant}; mod display; mod input; -type Point = geo::Point; -type Line = geo::Line; -type LineString = geo::LineString; -type Polygon = geo::Polygon; +type Point = geo::Point; +type Line = geo::Line; +type LineString = geo::LineString; +type Polygon = geo::Polygon; /// Datenstruktur für Kommandozeilen-Argumente #[derive(StructOpt, Clone, Copy)] @@ -34,21 +35,21 @@ struct Opt { tkz: bool, /// Geschwindigkeit von Lisa (in km/h) #[structopt(short = "l", long = "lisa", default_value = "15")] - lisa: f64, + lisa: f32, /// Geschwindigkeit vom Bus (in km/h) #[structopt(short = "b", long = "bus", default_value = "30")] - bus: f64, + bus: f32, } /// Möglicher Zustand von Lisas Rennen #[derive(Debug, Clone, Copy, PartialEq)] struct RunState { /// Theoretisch mögliche Startposition des Busses - bus: f64, + bus: f32, /// Lisas Position pos: Point, // Bisher zurückgelegte Strecke - total_distance: f64, + total_distance: f32, } /// Trait-Implementierung, damit die Struktur in BinaryHeaps passt impl cmp::Eq for RunState {} @@ -134,7 +135,7 @@ fn main() { states.push(vec![start]); // beste Lösung speichern - let mut best_bus = f64::NEG_INFINITY; + let mut best_bus = f32::NEG_INFINITY; let mut best = vec![]; // Zwischenlösungen speichern let save_prefix = "route_"; @@ -274,11 +275,22 @@ fn main() { lazy_static! { static ref RTREE: RwLock> = RwLock::new(RTree::new()); + static ref COLLISIONS: RwLock> = RwLock::new(HashMap::new()); } /// Schneidet die Linie keines der Polygone? /// (Berührungen erlaubt) -fn none_intersect(line: Line) -> bool { +fn none_intersect(mut line: Line) -> bool { + if line.start.y > line.end.y || (line.start.y == line.end.y && line.start.x > line.end.x) { + let end = line.start; + line.start = line.end; + line.end = end; + } + let mut collisions = COLLISIONS.write().unwrap(); + let id = [line.start.x as i16, line.start.y as i16, line.end.x as i16, line.end.y as i16]; + if let Some(&r) = collisions.get(&id) { + return r; + } let rtree = RTREE.read().unwrap(); let polys = rtree.locate_in_envelope_intersecting(&line.envelope()); let mut middle = line.start; @@ -305,29 +317,32 @@ fn none_intersect(line: Line) -> bool { continue; // Linie schneidet sich immer selbst } if l.intersects(&line) { + collisions.insert(id, false); return false; } } // falls Mittelpunkt in Polygon: if p.euclidean_distance(&middle) == 0.0 { // schneidet Polygon + collisions.insert(id, false); return false; } // (dieser Fall tritt auf, wenn die Linie von einer Polygon-Ecke zu einer anderen Ecke verläuft) } // keine Überschneidung gefunden: der Weg ist frei! + collisions.insert(id, true); true } /// Euklidische Distanz zweier Punkte -fn distance(a: Point, b: Point) -> f64 { +fn distance(a: Point, b: Point) -> f32 { ((a.x() - b.x()).powi(2) + (a.y() - b.y()).powi(2)).sqrt() } /// Mit dieser Funktion kann man bestimmen, wo der Bus zu Lisas Startzeit war. /// Die Funktion nimmt an, dass Lisa, von ihrer aktuellen Position aus, den Bus ohne Umwege erreichen kann. /// Sehr hilfreich als Heuristik, da die theoretisch beste Lösung nie schlechter als das eigentliche Ergebnis sein kann. -fn best_bus_pos(opt: &Opt, total_distance: f64, lisa: Point) -> f64 { +fn best_bus_pos(opt: &Opt, total_distance: f32, lisa: Point) -> f32 { let x_l = lisa.x(); // a let y_l = lisa.y(); // b