Allow walking through open doors
This commit is contained in:
+82
-4
@@ -24,11 +24,20 @@ extern "C" {
|
||||
pub type JsWall;
|
||||
pub type JsWallData;
|
||||
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn id(this: &JsWall) -> String;
|
||||
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn data(this: &JsWall) -> JsWallData;
|
||||
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn c(this: &JsWallData) -> Vec<f64>;
|
||||
|
||||
#[wasm_bindgen(method, getter, js_name = "door")]
|
||||
fn door_type(this: &JsWallData) -> DoorType;
|
||||
|
||||
#[wasm_bindgen(method, getter, js_name = "ds")]
|
||||
fn door_state(this: &JsWallData) -> DoorState;
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
@@ -51,15 +60,78 @@ impl From<JsPoint> for Point {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum DoorState {
|
||||
CLOSED = 0,
|
||||
OPEN = 1,
|
||||
LOCKED = 2,
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for DoorState {
|
||||
type Error = ();
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
x if x == Self::CLOSED as usize => Ok(Self::CLOSED),
|
||||
x if x == Self::OPEN as usize => Ok(Self::OPEN),
|
||||
x if x == Self::LOCKED as usize => Ok(Self::LOCKED),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum DoorType {
|
||||
NONE = 0,
|
||||
DOOR = 1,
|
||||
SECRET = 2,
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for DoorType {
|
||||
type Error = ();
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
x if x == Self::NONE as usize => Ok(Self::NONE),
|
||||
x if x == Self::DOOR as usize => Ok(Self::DOOR),
|
||||
x if x == Self::SECRET as usize => Ok(Self::SECRET),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Wall {
|
||||
pub id: String,
|
||||
pub p1: Point,
|
||||
pub p2: Point,
|
||||
pub door_type: DoorType,
|
||||
pub door_state: DoorState,
|
||||
}
|
||||
|
||||
impl Wall {
|
||||
pub fn new(p1: Point, p2: Point) -> Self {
|
||||
Self { p1, p2 }
|
||||
pub fn new(
|
||||
id: String,
|
||||
p1: Point,
|
||||
p2: Point,
|
||||
door_type: DoorType,
|
||||
door_state: DoorState,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
p1,
|
||||
p2,
|
||||
door_type,
|
||||
door_state,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_door(&self) -> bool {
|
||||
self.door_type != DoorType::NONE
|
||||
}
|
||||
|
||||
pub fn is_open(&self) -> bool {
|
||||
self.door_state == DoorState::OPEN
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +140,13 @@ impl Wall {
|
||||
let data = wall.data();
|
||||
let mut c = data.c();
|
||||
c.iter_mut().for_each(|val| *val = val.round());
|
||||
Self::new(Point::new(c[0], c[1]), Point::new(c[2], c[3]))
|
||||
Self::new(
|
||||
wall.id(),
|
||||
Point::new(c[0], c[1]),
|
||||
Point::new(c[2], c[3]),
|
||||
data.door_type(),
|
||||
data.door_state(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+108
-26
@@ -18,6 +18,8 @@ pub struct Edge {
|
||||
pub struct Node {
|
||||
pub point: Point,
|
||||
edges: Option<Vec<Edge>>,
|
||||
dynamic_neighbors: Option<Vec<NodePtr>>,
|
||||
dynamic_edges: Option<Vec<Edge>>,
|
||||
final_edge: Option<Option<Edge>>,
|
||||
}
|
||||
|
||||
@@ -26,18 +28,17 @@ impl Node {
|
||||
Self {
|
||||
point,
|
||||
edges: None,
|
||||
dynamic_neighbors: None,
|
||||
dynamic_edges: None,
|
||||
final_edge: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn iter_edges(
|
||||
&self,
|
||||
) -> std::iter::Chain<std::slice::Iter<'_, Edge>, std::option::Iter<'_, Edge>> {
|
||||
self.edges
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.chain(self.final_edge.as_ref().unwrap().iter())
|
||||
fn iter_edges(&self) -> impl Iterator<Item = &Edge> {
|
||||
let edge_iter = self.edges.as_ref().unwrap().iter();
|
||||
let dynamic_edges_iter = self.dynamic_edges.as_ref().unwrap().iter();
|
||||
let final_edge_iter = self.final_edge.as_ref().unwrap().iter();
|
||||
edge_iter.chain(dynamic_edges_iter).chain(final_edge_iter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +85,7 @@ impl NodeStorage {
|
||||
self.regular_nodes.push(node);
|
||||
}
|
||||
|
||||
fn initialize_edges(&mut self, node: &NodePtr, walls: &[LineSegment]) {
|
||||
fn initialize_edges(&mut self, node: &NodePtr, walls: &WallStorage) {
|
||||
if node.borrow().final_edge.is_none() {
|
||||
let final_edge = self
|
||||
.final_node
|
||||
@@ -92,7 +93,7 @@ impl NodeStorage {
|
||||
.filter(|neighbor| {
|
||||
!self.collides_with_wall(
|
||||
&LineSegment::new(node.borrow().point, neighbor.borrow().point),
|
||||
walls,
|
||||
walls.iter_all(),
|
||||
)
|
||||
})
|
||||
.map(|neighbor| Edge {
|
||||
@@ -102,29 +103,63 @@ impl NodeStorage {
|
||||
node.borrow_mut().final_edge = Some(final_edge);
|
||||
}
|
||||
|
||||
if node.borrow().edges.is_some() {
|
||||
let persistent_initialized = node.borrow().edges.is_some();
|
||||
if persistent_initialized && node.borrow().dynamic_edges.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let point = node.borrow().point;
|
||||
let mut dynamic_edges = Vec::new();
|
||||
if persistent_initialized {
|
||||
for neighbor in node.borrow().dynamic_neighbors.as_ref().unwrap() {
|
||||
let neighbor_point = neighbor.borrow().point;
|
||||
let line = LineSegment::new(point, neighbor_point);
|
||||
if !self.collides_with_wall(&line, walls.iter_dynamic_active()) {
|
||||
let cost = point.distance_to(neighbor_point);
|
||||
let edge = Edge {
|
||||
target: neighbor.clone(),
|
||||
cost,
|
||||
};
|
||||
dynamic_edges.push(edge);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut edges = Vec::new();
|
||||
let mut dynamic_neighbors = Vec::new();
|
||||
for neighbor in &self.regular_nodes {
|
||||
if Rc::ptr_eq(neighbor, node) {
|
||||
continue;
|
||||
}
|
||||
let neighbor_point = neighbor.borrow().point;
|
||||
if !self.collides_with_wall(&LineSegment::new(point, neighbor_point), walls) {
|
||||
let line = LineSegment::new(point, neighbor_point);
|
||||
if !self.collides_with_wall(&line, &walls.persistent) {
|
||||
let cost = point.distance_to(neighbor_point);
|
||||
edges.push(Edge {
|
||||
let edge = Edge {
|
||||
target: neighbor.clone(),
|
||||
cost,
|
||||
});
|
||||
};
|
||||
if self.collides_with_wall(&line, walls.iter_dynamic_active()) {
|
||||
dynamic_neighbors.push(neighbor.clone());
|
||||
} else if self.collides_with_wall(&line, walls.iter_dynamic_inactive()) {
|
||||
dynamic_neighbors.push(neighbor.clone());
|
||||
dynamic_edges.push(edge);
|
||||
} else {
|
||||
edges.push(edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
node.borrow_mut().edges = Some(edges);
|
||||
}
|
||||
node.borrow_mut().dynamic_edges = Some(dynamic_edges);
|
||||
}
|
||||
|
||||
fn collides_with_wall(&self, line: &LineSegment, walls: &[LineSegment]) -> bool {
|
||||
walls.iter().any(|wall| line.intersection(wall).is_some())
|
||||
fn collides_with_wall<'a, I>(&self, line: &LineSegment, walls: I) -> bool
|
||||
where
|
||||
I: IntoIterator<Item = &'a LineSegment>,
|
||||
{
|
||||
walls
|
||||
.into_iter()
|
||||
.any(|wall| line.intersection(wall).is_some())
|
||||
}
|
||||
|
||||
pub fn cleanup_final_edges(&mut self) {
|
||||
@@ -138,12 +173,50 @@ impl NodeStorage {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct DynamicWall {
|
||||
pub line: LineSegment,
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
impl DynamicWall {
|
||||
fn new(line: LineSegment, active: bool) -> Self {
|
||||
Self { line, active }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct WallStorage {
|
||||
pub persistent: Vec<LineSegment>,
|
||||
pub dynamic: FxHashMap<String, DynamicWall>,
|
||||
}
|
||||
|
||||
impl WallStorage {
|
||||
pub fn iter_dynamic_inactive(&self) -> impl Iterator<Item = &LineSegment> {
|
||||
self.dynamic
|
||||
.values()
|
||||
.filter(|wall| !wall.active)
|
||||
.map(|wall| &wall.line)
|
||||
}
|
||||
|
||||
pub fn iter_dynamic_active(&self) -> impl Iterator<Item = &LineSegment> {
|
||||
self.dynamic
|
||||
.values()
|
||||
.filter(|wall| wall.active)
|
||||
.map(|wall| &wall.line)
|
||||
}
|
||||
|
||||
pub fn iter_all(&self) -> impl Iterator<Item = &LineSegment> {
|
||||
self.persistent.iter().chain(self.iter_dynamic_active())
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Pathfinder {
|
||||
#[wasm_bindgen(skip)]
|
||||
pub nodes: NodeStorage,
|
||||
#[wasm_bindgen(skip)]
|
||||
pub walls: Vec<LineSegment>,
|
||||
pub walls: WallStorage,
|
||||
}
|
||||
|
||||
impl Pathfinder {
|
||||
@@ -153,7 +226,8 @@ impl Pathfinder {
|
||||
{
|
||||
let distance_from_walls = token_size / 2.0;
|
||||
let mut endpoints = FxHashMap::<Point, Vec<f64>>::default();
|
||||
let mut line_segments = Vec::new();
|
||||
let mut wall_storage = WallStorage::default();
|
||||
|
||||
for wall in walls {
|
||||
let x_diff = wall.p2.x - wall.p1.x;
|
||||
let y_diff = wall.p2.y - wall.p1.y;
|
||||
@@ -163,7 +237,15 @@ impl Pathfinder {
|
||||
let angles = endpoints.entry(point).or_insert_with(Vec::new);
|
||||
angles.push(angle);
|
||||
}
|
||||
line_segments.push(LineSegment::new(wall.p1, wall.p2));
|
||||
let line = LineSegment::new(wall.p1, wall.p2);
|
||||
if !wall.is_door() {
|
||||
wall_storage.persistent.push(line);
|
||||
} else {
|
||||
let is_open = wall.is_open();
|
||||
wall_storage
|
||||
.dynamic
|
||||
.insert(wall.id, DynamicWall::new(line, !is_open));
|
||||
}
|
||||
}
|
||||
endpoints
|
||||
.values_mut()
|
||||
@@ -187,20 +269,20 @@ impl Pathfinder {
|
||||
point,
|
||||
angle_between,
|
||||
distance_from_walls,
|
||||
&mut line_segments,
|
||||
&mut wall_storage.persistent,
|
||||
));
|
||||
}
|
||||
nodes.push(calc_pathfinding_node(
|
||||
point,
|
||||
angle1 + 0.5 * PI,
|
||||
distance_from_walls,
|
||||
&mut line_segments,
|
||||
&mut wall_storage.persistent,
|
||||
));
|
||||
nodes.push(calc_pathfinding_node(
|
||||
point,
|
||||
angle2 - 0.5 * PI,
|
||||
distance_from_walls,
|
||||
&mut line_segments,
|
||||
&mut wall_storage.persistent,
|
||||
));
|
||||
}
|
||||
let angle1 = angles.last().unwrap();
|
||||
@@ -215,26 +297,26 @@ impl Pathfinder {
|
||||
point,
|
||||
angle_between,
|
||||
distance_from_walls,
|
||||
&mut line_segments,
|
||||
&mut wall_storage.persistent,
|
||||
));
|
||||
}
|
||||
nodes.push(calc_pathfinding_node(
|
||||
point,
|
||||
angle1 + 0.5 * PI,
|
||||
distance_from_walls,
|
||||
&mut line_segments,
|
||||
&mut wall_storage.persistent,
|
||||
));
|
||||
nodes.push(calc_pathfinding_node(
|
||||
point,
|
||||
angle2 - 0.5 * PI,
|
||||
distance_from_walls,
|
||||
&mut line_segments,
|
||||
&mut wall_storage.persistent,
|
||||
));
|
||||
}
|
||||
// TODO Eliminating nodes close to each other may improve performance
|
||||
Self {
|
||||
nodes,
|
||||
walls: line_segments,
|
||||
walls: wall_storage,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user