Use an internal colission cecker for better performance
This commit is contained in:
@@ -25,6 +25,11 @@ impl Point {
|
|||||||
Self { x, y }
|
Self { x, y }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_line_x(line: &Line, x: f64) -> Self {
|
||||||
|
let y = line.calc_y(x);
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn distance_to(&self, to: Point) -> f64 {
|
pub fn distance_to(&self, to: Point) -> f64 {
|
||||||
(self.y - to.y).hypot(self.x - to.x)
|
(self.y - to.y).hypot(self.x - to.x)
|
||||||
}
|
}
|
||||||
@@ -55,3 +60,128 @@ impl From<&JsPoint> for Point {
|
|||||||
Self::new(point.x(), point.y())
|
Self::new(point.x(), point.y())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Line {
|
||||||
|
pub m: f64,
|
||||||
|
pub b: f64,
|
||||||
|
pub p1: Point,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Line {
|
||||||
|
pub fn new(m: f64, b: f64, p1: Point) -> Self {
|
||||||
|
Self { m, b, p1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_points(p1: Point, p2: Point) -> Self {
|
||||||
|
let m = (p1.y - p2.y) / (p1.x - p2.x);
|
||||||
|
let b = p1.y - m * p1.x;
|
||||||
|
Self { m, b, p1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_point_and_angle(p1: Point, angle: f64) -> Self {
|
||||||
|
let p2 = Point {
|
||||||
|
x: p1.x - angle.cos(),
|
||||||
|
y: p1.y - angle.sin(),
|
||||||
|
};
|
||||||
|
Line::from_points(p1, p2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_vertical(&self) -> bool {
|
||||||
|
self.m.is_infinite()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_horizontal(&self) -> bool {
|
||||||
|
self.m == 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calc_x(&self, y: f64) -> f64 {
|
||||||
|
(y - self.b) / self.m
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calc_y(&self, x: f64) -> f64 {
|
||||||
|
self.m * x + self.b
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersection(&self, other: &Line) -> Option<Point> {
|
||||||
|
// Are both lines vertical?
|
||||||
|
if self.is_vertical() && other.is_vertical() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Are the lines paralell?
|
||||||
|
if (self.m - other.m).abs() < 0.00000005 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is one of the lines vertical?
|
||||||
|
if self.is_vertical() || other.is_vertical() {
|
||||||
|
let vertical;
|
||||||
|
let regular;
|
||||||
|
if self.is_vertical() {
|
||||||
|
vertical = self;
|
||||||
|
regular = other;
|
||||||
|
} else {
|
||||||
|
vertical = other;
|
||||||
|
regular = self;
|
||||||
|
}
|
||||||
|
return Some(Point::from_line_x(®ular, vertical.p1.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate x coordinate of intersection point between both lines
|
||||||
|
// Find intersection point: x * m1 + b1 = x * m2 + b2
|
||||||
|
// Solve for x: x = (b1 - b2) / (m2 - m1)
|
||||||
|
let x = (self.b - other.b) / (other.m - self.m);
|
||||||
|
if self.m.abs() < other.m.abs() {
|
||||||
|
Some(Point::from_line_x(&self, x))
|
||||||
|
} else {
|
||||||
|
Some(Point::from_line_x(&other, x))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_perpendicular_through_point(&self, p: Point) -> Self {
|
||||||
|
let m = -1.0 / self.m;
|
||||||
|
let b = p.y - m * p.x;
|
||||||
|
Self { m, b, p1: p }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct LineSegment {
|
||||||
|
pub p1: Point,
|
||||||
|
pub p2: Point,
|
||||||
|
pub line: Line,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineSegment {
|
||||||
|
pub fn new(p1: Point, p2: Point) -> Self {
|
||||||
|
Self {
|
||||||
|
p1,
|
||||||
|
p2,
|
||||||
|
line: Line::from_points(p1, p2),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersection(&self, other: &LineSegment) -> Option<Point> {
|
||||||
|
let intersection = self.line.intersection(&other.line);
|
||||||
|
intersection.filter(|intersection| {
|
||||||
|
self.is_intersection_on_segment(*intersection)
|
||||||
|
&& other.is_intersection_on_segment(*intersection)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_intersection_on_segment(&self, intersection: Point) -> bool {
|
||||||
|
if intersection.is_same_as(&self.p1) || intersection.is_same_as(&self.p2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if self.line.is_vertical() || self.line.m.abs() > 1.0 {
|
||||||
|
return between(intersection.y, self.p1.y, self.p2.y);
|
||||||
|
}
|
||||||
|
between(intersection.x, self.p1.x, self.p2.x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn between<T: Copy + PartialOrd>(num: T, a: T, b: T) -> bool {
|
||||||
|
let (min, max) = if a < b { (a, b) } else { (b, a) };
|
||||||
|
num >= min && num <= max
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,14 +19,6 @@ extern "C" {
|
|||||||
pub fn log(s: &str);
|
pub fn log(s: &str);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen(
|
|
||||||
inline_js = "export function collidesWithWall(p1, p2) { return canvas.walls.checkCollision(new Ray(p1, p2));}"
|
|
||||||
)]
|
|
||||||
extern "C" {
|
|
||||||
#[wasm_bindgen(js_name=collidesWithWall)]
|
|
||||||
pub fn collides_with_wall(p1: Point, p2: Point) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub type JsWall;
|
pub type JsWall;
|
||||||
|
|||||||
+22
-8
@@ -5,8 +5,8 @@ use wasm_bindgen::prelude::*;
|
|||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::Point,
|
geometry::{LineSegment, Point},
|
||||||
js_api::{collides_with_wall, Wall},
|
js_api::Wall,
|
||||||
ptr_indexed_hash_set::PtrIndexedHashSet,
|
ptr_indexed_hash_set::PtrIndexedHashSet,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -79,13 +79,16 @@ impl NodeStorage {
|
|||||||
self.regular_nodes.push(node);
|
self.regular_nodes.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_edges(&mut self, node: &NodePtr) {
|
fn initialize_edges(&mut self, node: &NodePtr, walls: &Vec<LineSegment>) {
|
||||||
if node.borrow().final_edge.is_none() {
|
if node.borrow().final_edge.is_none() {
|
||||||
let final_edge = self
|
let final_edge = self
|
||||||
.final_node
|
.final_node
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.filter(|neighbor| {
|
.filter(|neighbor| {
|
||||||
!collides_with_wall(node.borrow().point, neighbor.borrow().point)
|
!self.collides_with_wall(
|
||||||
|
&LineSegment::new(node.borrow().point, neighbor.borrow().point),
|
||||||
|
walls,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.map(|neighbor| Edge {
|
.map(|neighbor| Edge {
|
||||||
target: neighbor.clone(),
|
target: neighbor.clone(),
|
||||||
@@ -104,7 +107,7 @@ impl NodeStorage {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let neighbor_point = neighbor.borrow().point;
|
let neighbor_point = neighbor.borrow().point;
|
||||||
if !collides_with_wall(point, neighbor_point) {
|
if !self.collides_with_wall(&LineSegment::new(point, neighbor_point), walls) {
|
||||||
let cost = point.distance_to(neighbor_point);
|
let cost = point.distance_to(neighbor_point);
|
||||||
edges.push(Edge {
|
edges.push(Edge {
|
||||||
target: neighbor.clone(),
|
target: neighbor.clone(),
|
||||||
@@ -115,6 +118,10 @@ impl NodeStorage {
|
|||||||
node.borrow_mut().edges = Some(edges);
|
node.borrow_mut().edges = Some(edges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collides_with_wall(&self, line: &LineSegment, walls: &Vec<LineSegment>) -> bool {
|
||||||
|
walls.iter().any(|wall| line.intersection(wall).is_some())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cleanup_final_edges(&mut self) {
|
pub fn cleanup_final_edges(&mut self) {
|
||||||
for node in &self.regular_nodes {
|
for node in &self.regular_nodes {
|
||||||
node.borrow_mut().final_edge = None;
|
node.borrow_mut().final_edge = None;
|
||||||
@@ -135,6 +142,8 @@ impl NodeStorage {
|
|||||||
pub struct Pathfinder {
|
pub struct Pathfinder {
|
||||||
#[wasm_bindgen(skip)]
|
#[wasm_bindgen(skip)]
|
||||||
pub nodes: NodeStorage,
|
pub nodes: NodeStorage,
|
||||||
|
#[wasm_bindgen(skip)]
|
||||||
|
pub walls: Vec<LineSegment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pathfinder {
|
impl Pathfinder {
|
||||||
@@ -143,6 +152,7 @@ impl Pathfinder {
|
|||||||
I: IntoIterator<Item = Wall>,
|
I: IntoIterator<Item = Wall>,
|
||||||
{
|
{
|
||||||
let mut endpoints = FxHashMap::<Point, Vec<f64>>::default();
|
let mut endpoints = FxHashMap::<Point, Vec<f64>>::default();
|
||||||
|
let mut line_segments = Vec::new();
|
||||||
for wall in walls {
|
for wall in walls {
|
||||||
let x_diff = wall.p2.x - wall.p1.x;
|
let x_diff = wall.p2.x - wall.p1.x;
|
||||||
let y_diff = wall.p2.y - wall.p1.y;
|
let y_diff = wall.p2.y - wall.p1.y;
|
||||||
@@ -152,6 +162,7 @@ impl Pathfinder {
|
|||||||
let angles = endpoints.entry(point).or_insert_with(Vec::new);
|
let angles = endpoints.entry(point).or_insert_with(Vec::new);
|
||||||
angles.push(angle);
|
angles.push(angle);
|
||||||
}
|
}
|
||||||
|
line_segments.push(LineSegment::new(wall.p1, wall.p2));
|
||||||
}
|
}
|
||||||
endpoints
|
endpoints
|
||||||
.values_mut()
|
.values_mut()
|
||||||
@@ -175,7 +186,10 @@ impl Pathfinder {
|
|||||||
nodes.push(calc_pathfinding_node(point, angle_between));
|
nodes.push(calc_pathfinding_node(point, angle_between));
|
||||||
}
|
}
|
||||||
// TODO Eliminating nodes close to each other may improve performance
|
// TODO Eliminating nodes close to each other may improve performance
|
||||||
Self { nodes }
|
Self {
|
||||||
|
nodes,
|
||||||
|
walls: line_segments,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_path(&mut self, from: Point, to: Point) -> Option<DiscoveredNodePtr> {
|
pub fn find_path(&mut self, from: Point, to: Point) -> Option<DiscoveredNodePtr> {
|
||||||
@@ -183,7 +197,7 @@ impl Pathfinder {
|
|||||||
let mut nodes = self.nodes.clone();
|
let mut nodes = self.nodes.clone();
|
||||||
nodes.final_node = Some(NodePtr::from(Node::new(from)));
|
nodes.final_node = Some(NodePtr::from(Node::new(from)));
|
||||||
let to_node = NodePtr::from(Node::new(to));
|
let to_node = NodePtr::from(Node::new(to));
|
||||||
nodes.initialize_edges(&to_node);
|
nodes.initialize_edges(&to_node, &self.walls);
|
||||||
let to = DiscoveredNode {
|
let to = DiscoveredNode {
|
||||||
node: to_node,
|
node: to_node,
|
||||||
cost: 0.0,
|
cost: 0.0,
|
||||||
@@ -216,7 +230,7 @@ impl Pathfinder {
|
|||||||
if previous_nodes.contains(neighbor) {
|
if previous_nodes.contains(neighbor) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
nodes.initialize_edges(neighbor);
|
nodes.initialize_edges(neighbor, &self.walls);
|
||||||
let cost = current_node.borrow().cost + edge.cost;
|
let cost = current_node.borrow().cost + edge.cost;
|
||||||
let discovered_neighbor = DiscoveredNode {
|
let discovered_neighbor = DiscoveredNode {
|
||||||
node: neighbor.clone(),
|
node: neighbor.clone(),
|
||||||
|
|||||||
Reference in New Issue
Block a user