Allow walking through open doors
This commit is contained in:
+82
-4
@@ -24,11 +24,20 @@ extern "C" {
|
|||||||
pub type JsWall;
|
pub type JsWall;
|
||||||
pub type JsWallData;
|
pub type JsWallData;
|
||||||
|
|
||||||
|
#[wasm_bindgen(method, getter)]
|
||||||
|
fn id(this: &JsWall) -> String;
|
||||||
|
|
||||||
#[wasm_bindgen(method, getter)]
|
#[wasm_bindgen(method, getter)]
|
||||||
fn data(this: &JsWall) -> JsWallData;
|
fn data(this: &JsWall) -> JsWallData;
|
||||||
|
|
||||||
#[wasm_bindgen(method, getter)]
|
#[wasm_bindgen(method, getter)]
|
||||||
fn c(this: &JsWallData) -> Vec<f64>;
|
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]
|
#[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 struct Wall {
|
||||||
|
pub id: String,
|
||||||
pub p1: Point,
|
pub p1: Point,
|
||||||
pub p2: Point,
|
pub p2: Point,
|
||||||
|
pub door_type: DoorType,
|
||||||
|
pub door_state: DoorState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wall {
|
impl Wall {
|
||||||
pub fn new(p1: Point, p2: Point) -> Self {
|
pub fn new(
|
||||||
Self { p1, p2 }
|
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 data = wall.data();
|
||||||
let mut c = data.c();
|
let mut c = data.c();
|
||||||
c.iter_mut().for_each(|val| *val = val.round());
|
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(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+117
-35
@@ -18,6 +18,8 @@ pub struct Edge {
|
|||||||
pub struct Node {
|
pub struct Node {
|
||||||
pub point: Point,
|
pub point: Point,
|
||||||
edges: Option<Vec<Edge>>,
|
edges: Option<Vec<Edge>>,
|
||||||
|
dynamic_neighbors: Option<Vec<NodePtr>>,
|
||||||
|
dynamic_edges: Option<Vec<Edge>>,
|
||||||
final_edge: Option<Option<Edge>>,
|
final_edge: Option<Option<Edge>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,18 +28,17 @@ impl Node {
|
|||||||
Self {
|
Self {
|
||||||
point,
|
point,
|
||||||
edges: None,
|
edges: None,
|
||||||
|
dynamic_neighbors: None,
|
||||||
|
dynamic_edges: None,
|
||||||
final_edge: None,
|
final_edge: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_edges(
|
fn iter_edges(&self) -> impl Iterator<Item = &Edge> {
|
||||||
&self,
|
let edge_iter = self.edges.as_ref().unwrap().iter();
|
||||||
) -> std::iter::Chain<std::slice::Iter<'_, Edge>, std::option::Iter<'_, Edge>> {
|
let dynamic_edges_iter = self.dynamic_edges.as_ref().unwrap().iter();
|
||||||
self.edges
|
let final_edge_iter = self.final_edge.as_ref().unwrap().iter();
|
||||||
.as_ref()
|
edge_iter.chain(dynamic_edges_iter).chain(final_edge_iter)
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.chain(self.final_edge.as_ref().unwrap().iter())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +85,7 @@ impl NodeStorage {
|
|||||||
self.regular_nodes.push(node);
|
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() {
|
if node.borrow().final_edge.is_none() {
|
||||||
let final_edge = self
|
let final_edge = self
|
||||||
.final_node
|
.final_node
|
||||||
@@ -92,7 +93,7 @@ impl NodeStorage {
|
|||||||
.filter(|neighbor| {
|
.filter(|neighbor| {
|
||||||
!self.collides_with_wall(
|
!self.collides_with_wall(
|
||||||
&LineSegment::new(node.borrow().point, neighbor.borrow().point),
|
&LineSegment::new(node.borrow().point, neighbor.borrow().point),
|
||||||
walls,
|
walls.iter_all(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map(|neighbor| Edge {
|
.map(|neighbor| Edge {
|
||||||
@@ -102,29 +103,63 @@ impl NodeStorage {
|
|||||||
node.borrow_mut().final_edge = Some(final_edge);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let point = node.borrow().point;
|
let point = node.borrow().point;
|
||||||
let mut edges = Vec::new();
|
let mut dynamic_edges = Vec::new();
|
||||||
for neighbor in &self.regular_nodes {
|
if persistent_initialized {
|
||||||
if Rc::ptr_eq(neighbor, node) {
|
for neighbor in node.borrow().dynamic_neighbors.as_ref().unwrap() {
|
||||||
continue;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let neighbor_point = neighbor.borrow().point;
|
} else {
|
||||||
if !self.collides_with_wall(&LineSegment::new(point, neighbor_point), walls) {
|
let mut edges = Vec::new();
|
||||||
let cost = point.distance_to(neighbor_point);
|
let mut dynamic_neighbors = Vec::new();
|
||||||
edges.push(Edge {
|
for neighbor in &self.regular_nodes {
|
||||||
target: neighbor.clone(),
|
if Rc::ptr_eq(neighbor, node) {
|
||||||
cost,
|
continue;
|
||||||
});
|
}
|
||||||
|
let neighbor_point = neighbor.borrow().point;
|
||||||
|
let line = LineSegment::new(point, neighbor_point);
|
||||||
|
if !self.collides_with_wall(&line, &walls.persistent) {
|
||||||
|
let cost = point.distance_to(neighbor_point);
|
||||||
|
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().edges = Some(edges);
|
node.borrow_mut().dynamic_edges = Some(dynamic_edges);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collides_with_wall(&self, line: &LineSegment, walls: &[LineSegment]) -> bool {
|
fn collides_with_wall<'a, I>(&self, line: &LineSegment, walls: I) -> bool
|
||||||
walls.iter().any(|wall| line.intersection(wall).is_some())
|
where
|
||||||
|
I: IntoIterator<Item = &'a LineSegment>,
|
||||||
|
{
|
||||||
|
walls
|
||||||
|
.into_iter()
|
||||||
|
.any(|wall| line.intersection(wall).is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cleanup_final_edges(&mut self) {
|
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]
|
#[wasm_bindgen]
|
||||||
pub struct Pathfinder {
|
pub struct Pathfinder {
|
||||||
#[wasm_bindgen(skip)]
|
#[wasm_bindgen(skip)]
|
||||||
pub nodes: NodeStorage,
|
pub nodes: NodeStorage,
|
||||||
#[wasm_bindgen(skip)]
|
#[wasm_bindgen(skip)]
|
||||||
pub walls: Vec<LineSegment>,
|
pub walls: WallStorage,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pathfinder {
|
impl Pathfinder {
|
||||||
@@ -153,7 +226,8 @@ impl Pathfinder {
|
|||||||
{
|
{
|
||||||
let distance_from_walls = token_size / 2.0;
|
let distance_from_walls = token_size / 2.0;
|
||||||
let mut endpoints = FxHashMap::<Point, Vec<f64>>::default();
|
let mut endpoints = FxHashMap::<Point, Vec<f64>>::default();
|
||||||
let mut line_segments = Vec::new();
|
let mut wall_storage = WallStorage::default();
|
||||||
|
|
||||||
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;
|
||||||
@@ -163,7 +237,15 @@ 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));
|
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
|
endpoints
|
||||||
.values_mut()
|
.values_mut()
|
||||||
@@ -187,20 +269,20 @@ impl Pathfinder {
|
|||||||
point,
|
point,
|
||||||
angle_between,
|
angle_between,
|
||||||
distance_from_walls,
|
distance_from_walls,
|
||||||
&mut line_segments,
|
&mut wall_storage.persistent,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
nodes.push(calc_pathfinding_node(
|
nodes.push(calc_pathfinding_node(
|
||||||
point,
|
point,
|
||||||
angle1 + 0.5 * PI,
|
angle1 + 0.5 * PI,
|
||||||
distance_from_walls,
|
distance_from_walls,
|
||||||
&mut line_segments,
|
&mut wall_storage.persistent,
|
||||||
));
|
));
|
||||||
nodes.push(calc_pathfinding_node(
|
nodes.push(calc_pathfinding_node(
|
||||||
point,
|
point,
|
||||||
angle2 - 0.5 * PI,
|
angle2 - 0.5 * PI,
|
||||||
distance_from_walls,
|
distance_from_walls,
|
||||||
&mut line_segments,
|
&mut wall_storage.persistent,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let angle1 = angles.last().unwrap();
|
let angle1 = angles.last().unwrap();
|
||||||
@@ -215,26 +297,26 @@ impl Pathfinder {
|
|||||||
point,
|
point,
|
||||||
angle_between,
|
angle_between,
|
||||||
distance_from_walls,
|
distance_from_walls,
|
||||||
&mut line_segments,
|
&mut wall_storage.persistent,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
nodes.push(calc_pathfinding_node(
|
nodes.push(calc_pathfinding_node(
|
||||||
point,
|
point,
|
||||||
angle1 + 0.5 * PI,
|
angle1 + 0.5 * PI,
|
||||||
distance_from_walls,
|
distance_from_walls,
|
||||||
&mut line_segments,
|
&mut wall_storage.persistent,
|
||||||
));
|
));
|
||||||
nodes.push(calc_pathfinding_node(
|
nodes.push(calc_pathfinding_node(
|
||||||
point,
|
point,
|
||||||
angle2 - 0.5 * PI,
|
angle2 - 0.5 * PI,
|
||||||
distance_from_walls,
|
distance_from_walls,
|
||||||
&mut line_segments,
|
&mut wall_storage.persistent,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// TODO Eliminating nodes close to each other may improve performance
|
// TODO Eliminating nodes close to each other may improve performance
|
||||||
Self {
|
Self {
|
||||||
nodes,
|
nodes,
|
||||||
walls: line_segments,
|
walls: wall_storage,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user