use crate::components::{BoundingBox, Space, SpatialEntity};
use specs::prelude::*;
#[derive(Debug)]
pub struct SpatialRelation {
changes: ReaderId<ComponentEvent>,
to_insert: BitSet,
to_update: BitSet,
}
impl SpatialRelation {
pub const NAME: &'static str = module_path!();
pub fn new(world: &World) -> Self {
SpatialRelation {
changes: world.write_storage::<BoundingBox>().register_reader(),
to_insert: BitSet::new(),
to_update: BitSet::new(),
}
}
}
impl<'world> System<'world> for SpatialRelation {
type SystemData = (
Write<'world, Space>,
ReadStorage<'world, BoundingBox>,
Entities<'world>,
);
fn run(&mut self, data: Self::SystemData) {
self.to_insert.clear();
self.to_update.clear();
let (mut space, bounds, entities) = data;
for event in bounds.channel().read(&mut self.changes) {
match *event {
ComponentEvent::Inserted(id) => {
self.to_insert.add(id);
},
ComponentEvent::Modified(id) => {
self.to_update.add(id);
},
ComponentEvent::Removed(id) => {
space.remove_by_id(id);
},
}
}
for (ent, bounding_box, _) in
(&entities, &bounds, &self.to_insert).join()
{
space.modify(SpatialEntity::new(*bounding_box, ent));
}
for (ent, bounding_box, _) in
(&entities, &bounds, &self.to_update).join()
{
space.modify(SpatialEntity::new(*bounding_box, ent));
}
}
fn setup(&mut self, world: &mut World) {
<Self::SystemData as shred::DynamicSystemData>::setup(
&self.accessor(),
world,
);
let bounding_storage = world.read_storage::<BoundingBox>();
let mut space = world.write_resource::<Space>();
space.clear();
for (entity, bounding_box) in
(&world.entities(), &bounding_storage).join()
{
space.modify(SpatialEntity::new(*bounding_box, entity));
}
}
}
#[cfg(test)]
mod tests {
use crate::{
algorithms::{Bounded, Translate},
components::{
register, Dimension, DrawingObject, Geometry, Layer, LineStyle,
Name, Space,
},
systems::{SpatialRelation, SyncBounds},
Line, Point, Vector,
};
use euclid::Length;
use piet::Color;
use specs::prelude::*;
#[test]
fn setup_creates_all_outstanding_spatial_entities() {
let mut world = World::new();
register(&mut world);
let layer = Layer::create(
world.create_entity(),
Name::new("default"),
Layer {
z_level: 0,
visible: true,
},
);
let line = Line::new(Point::new(2.0, 1.0), Point::new(5.0, -1.0));
let first = world
.create_entity()
.with(DrawingObject {
geometry: Geometry::Line(line),
layer,
})
.with(LineStyle {
width: Dimension::DrawingUnits(Length::new(5.0)),
stroke: Color::rgb8(0xff, 0, 0),
})
.with(line.bounding_box())
.build();
let mut system = SpatialRelation::new(&world);
System::setup(&mut system, &mut world);
let space = world.read_resource::<Space>();
assert_eq!(space.len(), 1);
assert_eq!(space.iter().next().unwrap().entity, first);
}
#[test]
fn run_will_keep_spatial_updated() {
let mut world = World::new();
register(&mut world);
let layer = Layer::create(
world.create_entity(),
Name::new("default"),
Layer {
z_level: 0,
visible: true,
},
);
let line = Line::new(Point::new(2.0, 1.0), Point::new(5.0, -1.0));
let first = world
.create_entity()
.with(DrawingObject {
geometry: Geometry::Line(line),
layer,
})
.with(LineStyle {
width: Dimension::DrawingUnits(Length::new(5.0)),
stroke: Color::rgb8(0xff, 0, 0),
})
.with(line.bounding_box())
.build();
let mut system = SpatialRelation::new(&world);
System::setup(&mut system, &mut world);
let line = Line::new(Point::new(3.0, 0.0), Point::new(-1.0, 2.0));
let second = world
.create_entity()
.with(DrawingObject {
geometry: Geometry::Line(line),
layer,
})
.with(LineStyle {
width: Dimension::DrawingUnits(Length::new(5.0)),
stroke: Color::rgb8(0xff, 0, 0),
})
.with(line.bounding_box())
.build();
system.run_now(&world);
let query: Vec<_> = world
.read_resource::<Space>()
.query_point(Point::new(3.0, -0.5), 1.0)
.collect();
assert!(!query.is_empty());
assert_eq!(query.len(), 1);
assert_eq!(query[0].entity, first);
let query: Vec<_> = world
.read_resource::<Space>()
.query_point(Point::new(2.5, 0.5), 1.0)
.collect();
assert!(!query.is_empty());
assert_eq!(query.len(), 2);
assert!(
(query[0].entity == first && query[1].entity == second)
| (query[0].entity == second && query[1].entity == first)
);
}
#[test]
fn spatial_will_update_on_modified() {
let mut world = World::new();
register(&mut world);
let layer = Layer::create(
world.create_entity(),
Name::new("default"),
Layer {
z_level: 0,
visible: true,
},
);
let line = Line::new(Point::new(2.0, 1.0), Point::new(5.0, -1.0));
let first = world
.create_entity()
.with(DrawingObject {
geometry: Geometry::Line(line),
layer,
})
.with(LineStyle {
width: Dimension::DrawingUnits(Length::new(5.0)),
stroke: Color::rgb8(0xff, 0, 0),
})
.with(line.bounding_box())
.build();
let mut spatial_system = SpatialRelation::new(&world);
System::setup(&mut spatial_system, &mut world);
let mut syncbounds_system = SyncBounds::new(&world);
System::setup(&mut syncbounds_system, &mut world);
let query: Vec<_> = world
.read_resource::<Space>()
.query_point(Point::new(3.0, -0.5), 1.0)
.collect();
assert!(!query.is_empty());
assert_eq!(query.len(), 1);
assert_eq!(query[0].entity, first);
let mut temp_line =
Line::new(Point::new(3.0, 0.0), Point::new(-1.0, 2.0));
temp_line.translate(Vector::new(100.0, 0.0));
world
.write_storage::<DrawingObject>()
.get_mut(first)
.unwrap()
.geometry = Geometry::Line(temp_line);
syncbounds_system.run_now(&world);
spatial_system.run_now(&world);
let query: Vec<_> = world
.read_resource::<Space>()
.query_point(Point::new(3.0, -0.5), 1.0)
.collect();
assert!(query.is_empty());
}
#[test]
fn spatial_will_update_on_removed() {
let mut world = World::new();
register(&mut world);
let layer = Layer::create(
world.create_entity(),
Name::new("default"),
Layer {
z_level: 0,
visible: true,
},
);
let line = Line::new(Point::new(2.0, 1.0), Point::new(5.0, -1.0));
let first = world
.create_entity()
.with(DrawingObject {
geometry: Geometry::Line(line),
layer,
})
.with(LineStyle {
width: Dimension::DrawingUnits(Length::new(5.0)),
stroke: Color::rgb8(0xff, 0, 0),
})
.with(line.bounding_box())
.build();
let mut spatial_system = SpatialRelation::new(&world);
System::setup(&mut spatial_system, &mut world);
let mut syncbounds_system = SyncBounds::new(&world);
System::setup(&mut syncbounds_system, &mut world);
let query: Vec<_> = world
.read_resource::<Space>()
.query_point(Point::new(3.0, -0.5), 1.0)
.collect();
assert!(!query.is_empty());
assert_eq!(query.len(), 1);
assert_eq!(query[0].entity, first);
world.delete_entity(first).unwrap();
world.maintain();
syncbounds_system.run_now(&world);
spatial_system.run_now(&world);
let query: Vec<_> = world
.read_resource::<Space>()
.query_point(Point::new(3.0, -0.5), 1.0)
.collect();
assert!(query.is_empty());
}
}