commit ce360208145df2532ef6fc776f7ff10c14ce5395 Author: Arne Keller Date: Thu Jan 17 18:37:39 2019 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53eaa21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ac8b877 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,265 @@ +[[package]] +name = "Aufgabe1" +version = "0.1.0" +dependencies = [ + "decorum 0.1.2", + "geo 0.11.0", + "pathfinding 1.1.11-pre", + "svg 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "backtrace" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "decorum" +version = "0.1.2" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "either" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "failure" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fixedbitset" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "geo" +version = "0.11.0" +dependencies = [ + "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "geo-types 0.3.0", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rstar 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "geo-types" +version = "0.3.0" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rstar 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "indexmap" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "itertools" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.45" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-traits" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pathfinding" +version = "1.1.11-pre" +dependencies = [ + "fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pdqselect" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rstar" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "svg" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "0.15.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727" +"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5" +"checksum backtrace-sys 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "3fcce89e5ad5c8949caa9434501f7b55415b3e7ad5270cb88c75a8d35e8f1279" +"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" +"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" +"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" +"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7" +"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596" +"checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" +"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" +"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" +"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" +"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" +"checksum pdqselect 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" +"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" +"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" +"checksum rstar 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64ffe043929ee67d46694af1a4851f6bbe571b52a55677ba1686222dc35fd449" +"checksum rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "01b90379b8664dd83460d59bdc5dd1fd3172b8913788db483ed1325171eab2f7" +"checksum serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "0e732ed5a5592c17d961555e3b552985baf98d50ce418b7b655f31f6ba7eb1b7" +"checksum serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d6115a3ca25c224e409185325afc16a0d5aaaabc15c42b09587d6f1ba39a5b" +"checksum svg 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "7518a18ac78da03c41ca0611128c6d8adf1efae0773c337d6c19793dce860c9b" +"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc" +"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..be27d60 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "Aufgabe1" +version = "0.1.0" +authors = ["arne"] +edition = "2018" + +[dependencies] +geo = { path = "/home/arne/Documents/Code/Github/geo/geo" } +svg = "0.5.11" +pathfinding = { path = "/home/arne/Documents/Code/Github/pathfinding" } +decorum = { path = "/home/arne/Documents/Code/Github/decorum" } diff --git a/src/display.rs b/src/display.rs new file mode 100644 index 0000000..ce91e28 --- /dev/null +++ b/src/display.rs @@ -0,0 +1,65 @@ +use svg::Document; +use svg::Node; +use svg::node::element::Circle; +use svg::node::element::Path; +use svg::node::element::path::Data; + +use std::io; + +use super::*; + +pub(crate) fn dump_svg(points: &[(Point, &'static str)], lines: &[(Line, &'static str)], polys: &[Polygon]) { + let mut document = Document::new() + // view box at (0, -200), w*h (200, 200) + .set("viewBox", (0, -200, 200, 200)); + + for circle in points.iter().map(|(p, color)| { + Circle::new() + .set("cx", f64::from(p.x())) + .set("cy", f64::from(-p.y())) + .set("r", 1.2) + .set("fill", *color) + }) { + document.append(circle); + } + for path in lines.iter().map(|(l, color)| { + let data = Data::new() + .move_to((f64::from(l.start.x), f64::from(-l.start.y))) + .line_to((f64::from(l.end.x), f64::from(-l.end.y))) + .close(); + + Path::new() + .set("fill", "none") + .set("stroke", *color) + .set("stroke-width", 0.5) + .set("d", data) + }) { + document.append(path); + } + + for path in polys.iter().map(|poly| { + let coords = &poly.exterior.0; + let first = coords[0].x_y(); + let mut first: (f64, f64) = (first.0.into(), first.1.into()); + first.1 *= -1.0; + let mut data = Data::new() + .move_to(first); + for c in &coords[1..] { + let c = c.x_y(); + let mut c: (f64, f64) = (c.0.into(), c.1.into()); + c.1 *= -1.0; + data = data.line_to(c); + } + data = data.close(); + + Path::new() + .set("fill", "black") + .set("stroke", "black") + .set("stroke-width", 0) + .set("d", data) + }) { + document.append(path); + } + + svg::write(io::stdout(), &document).unwrap(); +} \ No newline at end of file diff --git a/src/input.rs b/src/input.rs new file mode 100644 index 0000000..93b3f7a --- /dev/null +++ b/src/input.rs @@ -0,0 +1,21 @@ +use super::*; + +pub(crate) struct InputData { + pub start: Point, + pub polys: Vec +} + +pub(crate) fn read_input() -> InputData { + let mut polys = vec![]; + polys.push(Polygon::new(vec![[2.0.into(), 0.0.into()], [6.0.into(), 0.0.into()], [6.0.into(), 6.0.into()], [2.0.into(), 5.0.into()]].into(), vec![])); + polys.push(Polygon::new(vec![[1.0.into(), 20.0.into()], [2.0.into(), 20.0.into()], [2.0.into(), 23.0.into()], [1.0.into(), 23.0.into()]].into(), vec![])); + polys.push(Polygon::new(vec![[1.0.into(), 25.0.into()], [2.0.into(), 25.0.into()], [2.0.into(), 28.0.into()], [1.0.into(), 28.0.into()]].into(), vec![])); + let mut stick = Polygon::new(vec![[1.0.into(), 115.0.into()], [2.0.into(), 115.0.into()], [2.0.into(), 130.0.into()], [1.0.into(), 130.0.into()]].into(), vec![]); + polys.push(stick.clone()); + stick = stick.translate(35.0.into(), (-20.0).into()); + polys.push(stick); + InputData { + start: Point::new(45.0.into(), 95.0.into()), + polys + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..092dd9d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,296 @@ +use geo::*; +use geo::prelude::*; + +use pathfinding::prelude::*; + +use decorum::R64; +use decorum::Real; + +use std::cmp; +use std::collections::BinaryHeap; + +mod display; +mod input; + +type Point = geo::Point; +type Line = geo::Line; +type LineString = geo::LineString; +type Polygon = geo::Polygon; + +// 30 km/h = 1 units/unit +// 15 km/h = 0.5 units/unit + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct RunState { + /// Theoretically possible maximum delay + delay: R64, + /// Our current location + pos: Point, + /// Current location of the bus, not including any delays + bus: Point +} +impl cmp::Ord for RunState { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.delay.cmp(&other.delay) + } +} +impl cmp::PartialOrd for RunState { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +fn main() { + let bus = Point::new(0.0.into(), 0.0.into()); + /* + let house = Point::new(8.0, 20.0); + + let targets = to_bus(bus, house); + + let mut lines = vec![]; + if targets.len() == 2 { + lines.push((Line::new(house, targets[0]), "orange")); + lines.push((Line::new(house, targets[1]), "orange")); + } + display::dump_svg( + &[(bus, "#ffa700"), (house, "green"), (targets[0], "red"), (targets[1], "red")], + &lines + ); + */ + let data = input::read_input(); + let house = data.start; + let polys = data.polys; + let points = polys.iter().map(|x| x.exterior.0.clone()).flatten().collect::>(); + + let start = RunState { + pos: house, + bus: bus, + delay: max_possible_delay(bus, house) + //route: vec![house], + //length: 0.0.into() + }; + /* + eprintln!("# Max delay possible: {:?}", start.delay); + + let mut states = BinaryHeap::new(); + states.push(vec![start]); + + let mut best_delay = R64::from(0.0); + let mut best = vec![]; + + while states.peek().map(|x| x.last().unwrap().delay > best_delay) == Some(true) { + eprintln!(". {:?} states left", states.len()); + let s = states.pop().unwrap(); + let last = s.last().unwrap(); + + // new states + let mut all = vec![]; + + // attempt to go to any other vertex/point + for next in &points { + let next = Point::from(*next); + if next != last.pos && none_intersect(&polys, &Line::new(last.pos, next)) { + // could run to that point + let bus_next = last.bus.translate(0.0.into(), distance(last.pos, next) * R64::from(2.0)); + let mut route = s.clone(); + eprintln!("{},{} would go {},{}", last.pos.x(), last.pos.y(), next.x(), next.y()); + route.push(RunState { + pos: next, + bus: bus_next, + delay: max_possible_delay(bus_next, next) + }); + all.push(route); + } + } + // attempt to go to the bus, with varying delays + let mut bus_reached = false; + for delay in float_range(last.delay.into(), 0.0).map(R64::from) { + let bus = last.bus.translate(0.0.into(), delay); + let range = to_bus(bus, last.pos); + if range.len() == 2 { + bus_reached = true; // TODO: what if effectively unreachable? + let range = Line::new(range[0], range[1]); + for percent in float_range(0.0, 1.0).map(R64::from) { + let mut next = range.start; + next.x += range.dx() * percent; + next.y += range.dy() * percent; + let next = Point::from(next); + if none_intersect(&polys, &Line::new(last.pos, next)) { + if delay > best_delay { + // new high score! + let mut route = s.clone(); + route.push(RunState { + pos: next, + bus: next, + delay + }); + eprintln!("# New best delay {:?}", delay); + best = route; + best_delay = delay; + } else { + // not worth it + } + } + } + } + } + if bus_reached { + states.extend(all); + } + + } + eprintln!("d = {:?}", best_delay); + let route = best; + let first = route.first().unwrap(); + let last = route.last().unwrap(); + let points = vec![(house, "red"), (first.bus.translate(0.0.into(), best_delay), "yellow"), (last.bus, "orange")]; + let lines = route.iter().map(|x| x.pos).collect::().lines().map(|x| { + (x, "gray") + }).collect::>(); + + display::dump_svg(&points, &lines, &polys); + */ + + if let Some(route) = dijkstra::<_, R64, _, _, _>(&start, |node| { + eprintln!("{:?}", node); + if node.pos.x() == 0.0 { + return vec![]; + } + assert!(node.pos.x() > 0.0); + // get successors (next points) + let mut all = Vec::new(); + + // try to go to any points of any polygon + for next in &points { + let next = Point::from(*next); + if none_intersect(&polys, &Line::new(node.pos, next)) { + eprintln!("{},{} would go {},{}", node.pos.x(), node.pos.y(), next.x(), next.y()); + all.push((RunState { + pos: next, + bus: node.bus.translate(0.0.into(), distance(node.pos, next) * R64::from(2.0)), + delay: node.delay, + }, 0.0.into())); + } else { + eprintln!("{},{} can't go {},{}", node.pos.x(), node.pos.y(), next.x(), next.y()); + } + } + + // try to go the bus + for delay in float_range(100.0, 0.0).map(R64::from) { + let bus = node.bus.translate(0.0.into(), delay); + let range = to_bus(bus, node.pos); + if !range.is_empty() { + let range = Line::new(range[0], range[1]); + for percent in float_range(0.0, 1.0).map(R64::from) { + let mut next = range.start; + next.x += range.dx() * percent; + next.y += range.dy() * percent; + let next = Point::from(next); + if none_intersect(&polys, &Line::new(node.pos, next)) { + let cost = distance(node.pos, next); + //eprintln!("(delay {:02}) go from {:?} len {:.2} to {:?}", delay, node.pos, cost, next); + all.push((RunState { + pos: next, + bus: next, + delay, + //route: { let mut route = node.route.clone(); route.push(next); route }, + //length: node.length + cost + }, -delay)); + } + } + } + } + all + /* + }, |node| { + // heuristic + // -> current x coordinate ? + //node.pos.x() + 0.0.into() + */ + }, |node| { + // success condition + node.pos.x() == 0.0 && node.delay > R64::from(5.0) + }) { + eprintln!("route found"); + let first = route.0.first().unwrap(); + let last = route.0.last().unwrap(); + let delay = last.delay; + eprintln!("d = {}", delay); + let points = vec![(house, "red"), (first.bus.translate(0.0.into(), delay), "yellow"), (last.bus, "orange")]; + let lines = route.0.iter().map(|x| x.pos).collect::().lines().map(|x| { + (x, "gray") + }).collect::>(); + + display::dump_svg(&points, &lines, &polys); + } else { + eprintln!("no route found!"); + } + // */ +} + +/// [a; b] +fn float_range(a: f64, b: f64) -> impl Iterator { + const STEPS: usize = 200; + let d = b - a; + (0..=STEPS).map(move |s| a + ((s as f64) * d)/STEPS as f64) +} + +fn none_intersect(polys: &[Polygon], line: &Line) -> bool { + for p in polys { + for l in p.exterior.lines() { + if l.start == line.start || l.end == line.end || l.start == line.end || l.end == line.start { + continue; // would always intersect with itself + } + if l.intersects(line) { + return false; + } + } + } + true +} + +fn distance(a: Point, b: Point) -> R64 { + ((a.x() - b.x()).powi(2) + (a.y() - b.y()).powi(2)).sqrt() +} + +fn max_possible_delay(bus: Point, start: Point) -> R64 { + let a = start.x(); + let b = start.y(); + let c = bus.x(); + assert_eq!(c, 0.0); + let d = bus.y(); + + b - ((a.powi(2)) * R64::from(3.0)).sqrt() - d +} + +// Go straight to the bus. Returns the points where the bus can be reached. +fn to_bus(bus: Point, start: Point) -> Vec { + let a = start.x(); + let b = start.y(); + let c = bus.x(); + let d = bus.y(); + + let v: R64 = -a.powi(2) * R64::from(3.0) + R64::from(6.0) * a * c + b.powi(2) - R64::from(2.0) * b * d - R64::from(3.0) * c.powi(2) + d.powi(2); + if v >= 0.0 { + // v = sqrt(-3 A^2 + 6 A C + B^2 - 2 B D - 3 C^2 + D^2) + let v = v.sqrt(); + // x = 1/3 (+-v + 2 B - 2 D) + let x1: R64 = (v + R64::from(2.0) * b - R64::from(2.0) * d) / R64::from(3.0); + let x2: R64 = (-v + R64::from(2.0) * b - R64::from(2.0) * d) / R64::from(3.0); + if x1 > R64::from(0.0) && x2 > R64::from(0.0) { + vec![bus.translate(0.0.into(), R64::from(2.0) * x1), bus.translate(0.0.into(), R64::from(2.0) * x2)] + } else { + vec![] + } + } else { + vec![] + } +} + +#[cfg(test)] +#[test] +fn test_to_bus() { + assert!(to_bus(Coordinate { x: 0.0.into(), y: 0.0.into() }.into(), Coordinate { x: 1.0.into(), y: 23.0.into() }.into()).len() == 2); + assert_eq!(to_bus(Coordinate { x: 0.0.into(), y: 168.7601848778319.into() }.into(), Coordinate { x: 1.0.into(), y: 23.0.into() }.into()), vec![]); +} \ No newline at end of file