use crate::object::*;
use myelin_geometry::*;
#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub struct ObjectBuilderError {
pub missing_shape: bool,
pub missing_location: bool,
pub missing_mobility: bool,
}
#[derive(Default, Debug)]
pub struct ObjectBuilder {
shape: Option<Polygon>,
location: Option<Point>,
rotation: Option<Radians>,
mobility: Option<Mobility>,
passable: bool,
associated_data: Vec<u8>,
}
impl ObjectBuilder {
pub fn shape(&mut self, polygon: Polygon) -> &mut Self {
self.shape = Some(polygon);
self
}
pub fn location(&mut self, x: f64, y: f64) -> &mut Self {
self.location = Some(Point { x, y });
self
}
pub fn mobility(&mut self, mobility: Mobility) -> &mut Self {
self.mobility = Some(mobility);
self
}
pub fn rotation(&mut self, rotation: Radians) -> &mut Self {
self.rotation = Some(rotation);
self
}
pub fn passable(&mut self, passable: bool) -> &mut Self {
self.passable = passable;
self
}
pub fn associated_data(&mut self, associated_data: Vec<u8>) -> &mut Self {
self.associated_data = associated_data;
self
}
pub fn build(&mut self) -> Result<ObjectDescription, ObjectBuilderError> {
let error = ObjectBuilderError {
missing_shape: self.shape.is_none(),
missing_location: self.location.is_none(),
missing_mobility: self.mobility.is_none(),
};
let object = ObjectDescription {
shape: self.shape.take().ok_or_else(|| error.clone())?,
rotation: self.rotation.take().unwrap_or_else(Default::default),
location: self.location.take().ok_or_else(|| error.clone())?,
mobility: self.mobility.take().ok_or_else(|| error.clone())?,
passable: self.passable,
associated_data: self.associated_data.clone(),
};
Ok(object)
}
}
impl From<ObjectDescription> for ObjectBuilder {
fn from(object_description: ObjectDescription) -> Self {
let ObjectDescription {
shape,
location,
rotation,
mobility,
passable,
associated_data,
} = object_description;
ObjectBuilder {
shape: Some(shape),
location: Some(location),
rotation: Some(rotation),
mobility: Some(mobility),
passable,
associated_data,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_object_builder_should_error_for_missing_shape() {
let result = ObjectBuilder::default()
.location(10.0, 10.0)
.rotation(Radians::try_new(0.0).unwrap())
.mobility(Mobility::Immovable)
.build();
assert_eq!(
Err(ObjectBuilderError {
missing_shape: true,
..Default::default()
}),
result
);
}
#[test]
fn test_object_builder_should_error_for_missing_location() {
let result = ObjectBuilder::default()
.shape(
PolygonBuilder::default()
.vertex(0.0, 0.0)
.vertex(0.0, 1.0)
.vertex(1.0, 0.0)
.vertex(1.0, 1.0)
.build()
.unwrap(),
)
.rotation(Radians::try_new(0.0).unwrap())
.mobility(Mobility::Immovable)
.build();
assert_eq!(
Err(ObjectBuilderError {
missing_location: true,
..Default::default()
}),
result
);
}
#[test]
fn test_object_builder_should_error_for_missing_mobility() {
let result = ObjectBuilder::default()
.shape(
PolygonBuilder::default()
.vertex(0.0, 0.0)
.vertex(0.0, 1.0)
.vertex(1.0, 0.0)
.vertex(1.0, 1.0)
.build()
.unwrap(),
)
.rotation(Radians::try_new(0.0).unwrap())
.location(30.0, 40.0)
.build();
assert_eq!(
Err(ObjectBuilderError {
missing_mobility: true,
..Default::default()
}),
result
);
}
#[test]
fn test_object_builder_should_use_default_rotation() {
let result = ObjectBuilder::default()
.shape(
PolygonBuilder::default()
.vertex(0.0, 0.0)
.vertex(0.0, 1.0)
.vertex(1.0, 0.0)
.vertex(1.0, 1.0)
.build()
.unwrap(),
)
.location(30.0, 40.0)
.mobility(Mobility::Immovable)
.build();
let expected = ObjectDescription {
shape: PolygonBuilder::default()
.vertex(0.0, 0.0)
.vertex(0.0, 1.0)
.vertex(1.0, 0.0)
.vertex(1.0, 1.0)
.build()
.unwrap(),
location: Point { x: 30.0, y: 40.0 },
rotation: Radians::try_new(0.0).unwrap(),
mobility: Mobility::Immovable,
passable: false,
associated_data: Vec::new(),
};
assert_eq!(Ok(expected), result);
}
#[test]
fn test_object_builder_uses_passable() {
let result = ObjectBuilder::default()
.shape(
PolygonBuilder::default()
.vertex(0.0, 0.0)
.vertex(0.0, 1.0)
.vertex(1.0, 0.0)
.vertex(1.0, 1.0)
.build()
.unwrap(),
)
.rotation(Radians::try_new(0.0).unwrap())
.location(30.0, 40.0)
.mobility(Mobility::Immovable)
.passable(true)
.build();
let expected = ObjectDescription {
shape: Polygon::try_new(vec![
Point { x: 0.0, y: 0.0 },
Point { x: 0.0, y: 1.0 },
Point { x: 1.0, y: 0.0 },
Point { x: 1.0, y: 1.0 },
])
.unwrap(),
location: Point { x: 30.0, y: 40.0 },
rotation: Radians::try_new(0.0).unwrap(),
mobility: Mobility::Immovable,
passable: true,
associated_data: Vec::new(),
};
assert_eq!(Ok(expected), result);
}
#[test]
fn test_object_builder_uses_name() {
let result = ObjectBuilder::default()
.shape(
PolygonBuilder::default()
.vertex(0.0, 0.0)
.vertex(0.0, 1.0)
.vertex(1.0, 0.0)
.vertex(1.0, 1.0)
.build()
.unwrap(),
)
.rotation(Radians::try_new(0.0).unwrap())
.location(30.0, 40.0)
.mobility(Mobility::Immovable)
.build();
let expected = ObjectDescription {
shape: Polygon::try_new(vec![
Point { x: 0.0, y: 0.0 },
Point { x: 0.0, y: 1.0 },
Point { x: 1.0, y: 0.0 },
Point { x: 1.0, y: 1.0 },
])
.unwrap(),
location: Point { x: 30.0, y: 40.0 },
rotation: Radians::try_new(0.0).unwrap(),
mobility: Mobility::Immovable,
passable: false,
associated_data: Vec::new(),
};
assert_eq!(Ok(expected), result);
}
#[test]
fn test_object_builder_should_error_with_everything_missing() {
let result = ObjectBuilder::default().build();
assert_eq!(
Err(ObjectBuilderError {
missing_shape: true,
missing_location: true,
missing_mobility: true,
}),
result
);
}
#[test]
fn test_object_builder_should_build_object() {
let result = ObjectBuilder::default()
.shape(
PolygonBuilder::default()
.vertex(0.0, 0.0)
.vertex(0.0, 1.0)
.vertex(1.0, 0.0)
.vertex(1.0, 1.0)
.build()
.unwrap(),
)
.mobility(Mobility::Movable(Vector { x: -12.0, y: 5.0 }))
.location(30.0, 40.0)
.rotation(Radians::try_new(1.1).unwrap())
.build();
let expected = ObjectDescription {
location: Point { x: 30.0, y: 40.0 },
rotation: Radians::try_new(1.1).unwrap(),
mobility: Mobility::Movable(Vector { x: -12.0, y: 5.0 }),
shape: Polygon::try_new(vec![
Point { x: 0.0, y: 0.0 },
Point { x: 0.0, y: 1.0 },
Point { x: 1.0, y: 0.0 },
Point { x: 1.0, y: 1.0 },
])
.unwrap(),
passable: false,
associated_data: Vec::new(),
};
assert_eq!(Ok(expected), result);
}
#[test]
fn can_create_object_builder_from_object_description() {
let object_description = ObjectDescription {
location: Point { x: 30.0, y: 40.0 },
rotation: Radians::try_new(1.1).unwrap(),
mobility: Mobility::Movable(Vector { x: -12.0, y: 5.0 }),
shape: Polygon::try_new(vec![
Point { x: 0.0, y: 0.0 },
Point { x: 0.0, y: 1.0 },
Point { x: 1.0, y: 0.0 },
Point { x: 1.0, y: 1.0 },
])
.unwrap(),
passable: true,
associated_data: Vec::new(),
};
assert_eq!(
object_description,
ObjectBuilder::from(object_description.clone())
.build()
.unwrap()
);
}
}