Optimize intersection detection algorithm; use 32-bit floats

This commit is contained in:
Arne Keller 2019-01-27 12:50:25 +01:00
parent 1b9b8ae007
commit fe353e85fc
2 changed files with 71 additions and 35 deletions

View File

@ -25,10 +25,10 @@ fn read_stdin() -> InputData {
let line = lines.next().unwrap(); let line = lines.next().unwrap();
let numbers = line.trim().split(' ').map(|x| x.parse::<usize>().unwrap()).collect::<Vec<_>>(); let numbers = line.trim().split(' ').map(|x| x.parse::<usize>().unwrap()).collect::<Vec<_>>();
let corner_count = numbers[0]; let corner_count = numbers[0];
polygons.push(Polygon::new((0..corner_count).map(|idx| ((numbers[idx*2+1] as f64).into(), (numbers[idx*2+2] as f64).into())).collect::<Vec<_>>().into(), vec![])); polygons.push(Polygon::new((0..corner_count).map(|idx| ((numbers[idx*2+1] as f32).into(), (numbers[idx*2+2] as f32).into())).collect::<Vec<_>>().into(), vec![]));
} }
let home = lines.next().unwrap().trim().split(' ').map(|x| x.parse::<f64>().unwrap().into()).collect::<Vec<_>>(); let home = lines.next().unwrap().trim().split(' ').map(|x| x.parse::<f32>().unwrap().into()).collect::<Vec<_>>();
InputData { InputData {
start: Point::new(home[0], home[1]), start: Point::new(home[0], home[1]),

View File

@ -1,35 +1,33 @@
use geo::*; use geo::*;
use geo::prelude::*; use geo::prelude::*;
use decorum::R64;
use decorum::Real;
use std::cmp; use std::cmp;
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
mod display; mod display;
mod input; mod input;
type Point = geo::Point<R64>; type Point = geo::Point<f32>;
type Line = geo::Line<R64>; type Line = geo::Line<f32>;
type LineString = geo::LineString<R64>; type LineString = geo::LineString<f32>;
type Polygon = geo::Polygon<R64>; type Polygon = geo::Polygon<f32>;
// 30 km/h = 1 units/unit // 30 km/h = 1 units/unit
// 15 km/h = 0.5 units/unit // 15 km/h = 0.5 units/unit
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq)]
struct RunState { struct RunState {
/// Theoretically possible maximum delay /// Theoretically possible maximum delay
delay: R64, delay: f32,
/// Our current location /// Our current location
pos: Point, pos: Point,
/// Current location of the bus, not including any delays /// Current location of the bus, not including any delays
bus: Point bus: Point
} }
impl cmp::Eq for RunState {}
impl cmp::Ord for RunState { impl cmp::Ord for RunState {
fn cmp(&self, other: &Self) -> cmp::Ordering { fn cmp(&self, other: &Self) -> cmp::Ordering {
self.delay.cmp(&other.delay) self.delay.partial_cmp(&other.delay).unwrap()
} }
} }
impl cmp::PartialOrd for RunState { impl cmp::PartialOrd for RunState {
@ -39,7 +37,7 @@ impl cmp::PartialOrd for RunState {
} }
fn main() { fn main() {
let bus = Point::new(0.0.into(), (-1300.0).into()); let bus = Point::new(0.0.into(), (-2000.0).into());
let data = input::read_input(); let data = input::read_input();
let house = data.start; let house = data.start;
@ -65,13 +63,23 @@ fn main() {
let mut states = BinaryHeap::new(); let mut states = BinaryHeap::new();
states.push(vec![start]); states.push(vec![start]);
let mut best_delay = R64::from(0.0); let mut best_delay = f32::from(0.0);
let mut best = vec![]; let mut best = vec![];
while states.peek().map(|x| x.last().unwrap().delay > best_delay) == Some(true) { while states.peek().map(|x| x[0].delay > best_delay) == Some(true) {
eprintln!(". {:?} states left", states.len()); eprintln!(". {:?} states left:", states.len());
/*
if states.len() > 10 {
for s in &states {
eprintln!("{:?}", s);
}
panic!();
}
*/
let s = states.pop().unwrap(); let s = states.pop().unwrap();
let last = s.last().unwrap(); let last = &s[0]; //s.last().unwrap();
eprintln!("{},{}", last.pos.x(), last.pos.y());
//eprintln!("{:?}", states.peek());
// new states // new states
let mut all = vec![]; let mut all = vec![];
@ -81,10 +89,10 @@ fn main() {
let next = Point::from(*next); let next = Point::from(*next);
if next != last.pos && none_intersect(&polys, &Line::new(last.pos, next)) { if next != last.pos && none_intersect(&polys, &Line::new(last.pos, next)) {
// could run to that point // could run to that point
let bus_next = last.bus.translate(0.0.into(), distance(last.pos, next) * R64::from(2.0)); let bus_next = last.bus.translate(0.0, distance(last.pos, next) * 2.0);
let mut route = s.clone(); let mut route = s.clone();
eprintln!("{},{} would go {},{}", last.pos.x(), last.pos.y(), next.x(), next.y()); //eprintln!("{},{} would go {},{}", last.pos.x(), last.pos.y(), next.x(), next.y());
route.push(RunState { route.insert(0, RunState {
pos: next, pos: next,
bus: bus_next, bus: bus_next,
delay: max_possible_delay(bus_next, next) delay: max_possible_delay(bus_next, next)
@ -94,13 +102,13 @@ fn main() {
} }
// attempt to go to the bus, with varying delays // attempt to go to the bus, with varying delays
let mut bus_reached = false; let mut bus_reached = false;
for delay in float_range(last.delay.into(), 0.0).map(R64::from) { for delay in float_range(last.delay.into(), 0.0).map(f32::from) {
let bus = last.bus.translate(0.0.into(), delay); let bus = last.bus.translate(0.0.into(), delay);
let range = to_bus(bus, last.pos); let range = to_bus(bus, last.pos);
if range.len() == 2 { if range.len() == 2 {
bus_reached = true; // TODO: what if effectively unreachable? bus_reached = true; // TODO: what if effectively unreachable?
let range = Line::new(range[0], range[1]); let range = Line::new(range[0], range[1]);
for percent in float_range(0.0, 1.0).map(R64::from) { for percent in float_range(0.0, 1.0).map(f32::from) {
let mut next = range.start; let mut next = range.start;
next.x += range.dx() * percent; next.x += range.dx() * percent;
next.y += range.dy() * percent; next.y += range.dy() * percent;
@ -109,13 +117,14 @@ fn main() {
if delay > best_delay { if delay > best_delay {
// new high score! // new high score!
let mut route = s.clone(); let mut route = s.clone();
route.push(RunState { route.insert(0, RunState {
pos: next, pos: next,
bus: next, bus: next,
delay delay
}); });
eprintln!("# New best delay {:?}", delay); eprintln!("# New best delay {:?}", delay);
best = route; best = route;
best.reverse();
best_delay = delay; best_delay = delay;
} else { } else {
// not worth it // not worth it
@ -125,6 +134,7 @@ fn main() {
} }
} }
if bus_reached { if bus_reached {
//eprintln!("-> adding all to queue");
states.extend(all); states.extend(all);
} }
@ -146,20 +156,33 @@ fn main() {
} }
/// [a; b] /// [a; b]
fn float_range(a: f64, b: f64) -> impl Iterator<Item = f64> { fn float_range(a: f32, b: f32) -> impl Iterator<Item = f32> {
const STEPS: usize = 200; const STEPS: usize = 25;
let d = b - a; let d = b - a;
(0..=STEPS).map(move |s| a + ((s as f64) * d)/STEPS as f64) (0..=STEPS).map(move |s| a + ((s as f32) * d)/STEPS as f32)
} }
fn none_intersect(polys: &[Polygon], line: &Line) -> bool { fn none_intersect(polys: &[Polygon], line: &Line) -> bool {
/*
if line.end.x != 0.0 {
eprintln!("checking {:?}", line);
}
*/
/* /*
if line.end.x > 0.0 { if line.end.x > 0.0 {
eprintln!("{:?}", line); eprintln!("{:?}", line);
} }
*/ */
for p in polys { let mut middle = line.start;
middle.x += line.dx() / 2.0;
middle.y += line.dy() / 2.0;
let middle: Point = middle.into();
'poly: for p in polys {
for l in p.exterior.lines().chain(vec![Line::new(p.exterior.0[0], *p.exterior.0.last().unwrap())]) { for l in p.exterior.lines().chain(vec![Line::new(p.exterior.0[0], *p.exterior.0.last().unwrap())]) {
if (l.start == line.start && l.end == line.end) || (l.start == line.end && l.end == line.start) {
// point is on polygon border
continue 'poly;
}
if l.start == line.start || l.end == line.end || l.start == line.end || l.end == line.start { if l.start == line.start || l.end == line.end || l.start == line.end || l.end == line.start {
/* /*
if line.end.x > 0.0 { if line.end.x > 0.0 {
@ -174,25 +197,38 @@ fn none_intersect(polys: &[Polygon], line: &Line) -> bool {
} }
*/ */
if l.intersects(line) { if l.intersects(line) {
/*
if line.end.x != 0.0 {
eprintln!("{:?} intersects {:?}", l, line);
}
*/
return false; return false;
} }
} }
if p.euclidean_distance(&middle) == 0.0 {
/*
if line.end.x != 0.0 {
eprintln!("{:?} is in polygon {:?}, contains = {}", middle, p.exterior.lines().collect::<Vec<_>>(), p.contains(&middle));
}
*/
return false;
}
} }
true true
} }
fn distance(a: Point, b: Point) -> R64 { fn distance(a: Point, b: Point) -> f32 {
((a.x() - b.x()).powi(2) + (a.y() - b.y()).powi(2)).sqrt() ((a.x() - b.x()).powi(2) + (a.y() - b.y()).powi(2)).sqrt()
} }
fn max_possible_delay(bus: Point, start: Point) -> R64 { fn max_possible_delay(bus: Point, start: Point) -> f32 {
let a = start.x(); let a = start.x();
let b = start.y(); let b = start.y();
let c = bus.x(); let c = bus.x();
assert_eq!(c, 0.0); assert_eq!(c, 0.0);
let d = bus.y(); let d = bus.y();
b - ((a.powi(2)) * R64::from(3.0)).sqrt() - d b - ((a.powi(2)) * f32::from(3.0)).sqrt() - d
} }
// Go straight to the bus. Returns the points where the bus can be reached. // Go straight to the bus. Returns the points where the bus can be reached.
@ -202,15 +238,15 @@ fn to_bus(bus: Point, start: Point) -> Vec<Point> {
let c = bus.x(); let c = bus.x();
let d = bus.y(); 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); let v: f32 = -a.powi(2) * f32::from(3.0) + f32::from(6.0) * a * c + b.powi(2) - f32::from(2.0) * b * d - f32::from(3.0) * c.powi(2) + d.powi(2);
if v >= 0.0 { if v >= 0.0 {
// v = sqrt(-3 A^2 + 6 A C + B^2 - 2 B D - 3 C^2 + D^2) // v = sqrt(-3 A^2 + 6 A C + B^2 - 2 B D - 3 C^2 + D^2)
let v = v.sqrt(); let v = v.sqrt();
// x = 1/3 (+-v + 2 B - 2 D) // 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 x1: f32 = (v + f32::from(2.0) * b - f32::from(2.0) * d) / f32::from(3.0);
let x2: R64 = (-v + R64::from(2.0) * b - R64::from(2.0) * d) / R64::from(3.0); let x2: f32 = (-v + f32::from(2.0) * b - f32::from(2.0) * d) / f32::from(3.0);
if x1 > R64::from(0.0) && x2 > R64::from(0.0) { if x1 > f32::from(0.0) && x2 > f32::from(0.0) {
vec![bus.translate(0.0.into(), R64::from(2.0) * x1), bus.translate(0.0.into(), R64::from(2.0) * x2)] vec![bus.translate(0.0.into(), f32::from(2.0) * x1), bus.translate(0.0.into(), f32::from(2.0) * x2)]
} else { } else {
vec![] vec![]
} }