Add caching for intersection checks

This commit is contained in:
arnekeller 2019-04-02 17:51:26 +02:00
parent 551a0ba14c
commit c13029c5e5
6 changed files with 67 additions and 23 deletions

31
Cargo.lock generated
View File

@ -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)" = "<none>"
"checksum geo-types 0.4.1 (git+https://github.com/FliegendeWurst/geo)" = "<none>"
"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"

View File

@ -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"

View File

@ -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.

View File

@ -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..] {

View File

@ -85,16 +85,16 @@ fn read_svg(input: &str) -> Result<InputData, Box<Error>> {
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),

View File

@ -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<f64>;
type Line = geo::Line<f64>;
type LineString = geo::LineString<f64>;
type Polygon = geo::Polygon<f64>;
type Point = geo::Point<f32>;
type Line = geo::Line<f32>;
type LineString = geo::LineString<f32>;
type Polygon = geo::Polygon<f32>;
/// 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<RTree<Polygon>> = RwLock::new(RTree::new());
static ref COLLISIONS: RwLock<HashMap<[i16; 4], bool>> = 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