use crate::types::{
basic::{Double, OSString},
entities::axles::Axles,
entities::vehicle::{Performance, Properties},
entities::{ScenarioObject, Vehicle},
enums::VehicleCategory,
geometry::{BoundingBox, Center, Dimensions},
};
pub struct VehicleBuilder<'parent> {
parent: &'parent mut crate::builder::scenario::ScenarioBuilder<
crate::builder::scenario::HasEntities,
>,
name: String,
vehicle_data: PartialVehicleData,
}
pub struct DetachedVehicleBuilder {
name: String,
vehicle_data: PartialVehicleData,
}
#[derive(Debug, Default)]
struct PartialVehicleData {
name: Option<String>,
vehicle_category: Option<VehicleCategory>,
properties: Option<Properties>,
bounding_box: Option<BoundingBox>,
performance: Option<Performance>,
axles: Option<Axles>,
}
impl<'parent> VehicleBuilder<'parent> {
pub fn new(
parent: &'parent mut crate::builder::scenario::ScenarioBuilder<
crate::builder::scenario::HasEntities,
>,
name: &str,
) -> Self {
Self {
parent,
name: name.to_string(),
vehicle_data: PartialVehicleData::default(),
}
}
pub fn car(mut self) -> Self {
self.vehicle_data.vehicle_category = Some(VehicleCategory::Car);
self.vehicle_data.name = Some("PassengerCar".to_string());
self.vehicle_data.bounding_box = Some(BoundingBox {
center: Center {
x: Double::literal(1.4),
y: Double::literal(0.0),
z: Double::literal(0.9),
},
dimensions: Dimensions {
width: Double::literal(1.8),
length: Double::literal(4.5),
height: Double::literal(1.4),
},
});
self.vehicle_data.performance = Some(Performance::default());
self.vehicle_data.axles = Some(Axles::car());
self
}
pub fn truck(mut self) -> Self {
self.vehicle_data.vehicle_category = Some(VehicleCategory::Truck);
self.vehicle_data.name = Some("Truck".to_string());
self.vehicle_data.bounding_box = Some(BoundingBox {
center: Center {
x: Double::literal(4.0),
y: Double::literal(0.0),
z: Double::literal(1.5),
},
dimensions: Dimensions {
width: Double::literal(2.5),
length: Double::literal(8.0),
height: Double::literal(3.0),
},
});
self.vehicle_data.performance = Some(Performance {
max_speed: Double::literal(120.0),
max_acceleration: Double::literal(3.0),
max_deceleration: Double::literal(8.0),
});
self.vehicle_data.axles = Some(Axles::truck());
self
}
pub fn with_dimensions(mut self, length: f64, width: f64, height: f64) -> Self {
let existing_bbox = self.vehicle_data.bounding_box.unwrap_or_default();
self.vehicle_data.bounding_box = Some(BoundingBox {
center: existing_bbox.center,
dimensions: Dimensions {
width: Double::literal(width),
length: Double::literal(length),
height: Double::literal(height),
},
});
self
}
pub fn with_performance(
mut self,
max_speed: f64,
max_acceleration: f64,
max_deceleration: f64,
) -> Self {
self.vehicle_data.performance = Some(Performance {
max_speed: Double::literal(max_speed),
max_acceleration: Double::literal(max_acceleration),
max_deceleration: Double::literal(max_deceleration),
});
self
}
pub fn finish(
self,
) -> &'parent mut crate::builder::scenario::ScenarioBuilder<crate::builder::scenario::HasEntities>
{
let vehicle = Vehicle {
name: OSString::literal(
self.vehicle_data
.name
.unwrap_or_else(|| "DefaultVehicle".to_string()),
),
vehicle_category: self
.vehicle_data
.vehicle_category
.unwrap_or(VehicleCategory::Car),
bounding_box: self.vehicle_data.bounding_box.unwrap_or_default(),
performance: self.vehicle_data.performance.unwrap_or_default(),
axles: self.vehicle_data.axles.unwrap_or_else(|| Axles::car()),
properties: self.vehicle_data.properties,
};
let scenario_object = ScenarioObject::new_vehicle(self.name.clone(), vehicle);
if let Some(ref mut entities) = self.parent.data.entities {
entities.add_object(scenario_object);
}
self.parent
}
pub fn detached(self) -> DetachedVehicleBuilder {
DetachedVehicleBuilder {
name: self.name,
vehicle_data: self.vehicle_data,
}
}
}
impl DetachedVehicleBuilder {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
vehicle_data: PartialVehicleData::default(),
}
}
pub fn car(mut self) -> Self {
self.vehicle_data.vehicle_category = Some(VehicleCategory::Car);
self.vehicle_data.name = Some("PassengerCar".to_string());
self.vehicle_data.bounding_box = Some(BoundingBox {
center: Center {
x: Double::literal(1.4),
y: Double::literal(0.0),
z: Double::literal(0.9),
},
dimensions: Dimensions {
width: Double::literal(1.8),
length: Double::literal(4.5),
height: Double::literal(1.4),
},
});
self.vehicle_data.performance = Some(Performance::default());
self.vehicle_data.axles = Some(Axles::car());
self
}
pub fn truck(mut self) -> Self {
self.vehicle_data.vehicle_category = Some(VehicleCategory::Truck);
self.vehicle_data.name = Some("Truck".to_string());
self.vehicle_data.bounding_box = Some(BoundingBox {
center: Center {
x: Double::literal(4.0),
y: Double::literal(0.0),
z: Double::literal(1.5),
},
dimensions: Dimensions {
width: Double::literal(2.5),
length: Double::literal(8.0),
height: Double::literal(3.0),
},
});
self.vehicle_data.performance = Some(Performance {
max_speed: Double::literal(120.0),
max_acceleration: Double::literal(3.0),
max_deceleration: Double::literal(8.0),
});
self.vehicle_data.axles = Some(Axles::truck());
self
}
pub fn with_dimensions(mut self, length: f64, width: f64, height: f64) -> Self {
let existing_bbox = self.vehicle_data.bounding_box.unwrap_or_default();
self.vehicle_data.bounding_box = Some(BoundingBox {
center: existing_bbox.center,
dimensions: Dimensions {
width: Double::literal(width),
length: Double::literal(length),
height: Double::literal(height),
},
});
self
}
pub fn with_performance(
mut self,
max_speed: f64,
max_acceleration: f64,
max_deceleration: f64,
) -> Self {
self.vehicle_data.performance = Some(Performance {
max_speed: Double::literal(max_speed),
max_acceleration: Double::literal(max_acceleration),
max_deceleration: Double::literal(max_deceleration),
});
self
}
pub fn build(self) -> ScenarioObject {
let vehicle = Vehicle {
name: OSString::literal(
self.vehicle_data
.name
.unwrap_or_else(|| "DefaultVehicle".to_string()),
),
vehicle_category: self
.vehicle_data
.vehicle_category
.unwrap_or(VehicleCategory::Car),
bounding_box: self.vehicle_data.bounding_box.unwrap_or_default(),
performance: self.vehicle_data.performance.unwrap_or_default(),
axles: self.vehicle_data.axles.unwrap_or_else(|| Axles::car()),
properties: self.vehicle_data.properties,
};
ScenarioObject::new_vehicle(self.name.clone(), vehicle)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_detached_builder_defaults_when_no_preset_called() {
let obj = DetachedVehicleBuilder::new("ego").build();
let v = obj.vehicle.as_ref().unwrap();
assert_eq!(v.name.as_literal(), Some(&"DefaultVehicle".to_string()));
assert_eq!(v.vehicle_category, VehicleCategory::Car);
}
#[test]
fn test_car_preset_sets_category_and_dimensions() {
let obj = DetachedVehicleBuilder::new("ego").car().build();
let v = obj.vehicle.as_ref().unwrap();
assert_eq!(v.vehicle_category, VehicleCategory::Car);
assert_eq!(v.name.as_literal(), Some(&"PassengerCar".to_string()));
assert_eq!(v.bounding_box.dimensions.length.as_literal(), Some(&4.5));
}
#[test]
fn test_truck_preset_overrides_car_preset() {
let obj = DetachedVehicleBuilder::new("ego").car().truck().build();
let v = obj.vehicle.as_ref().unwrap();
assert_eq!(v.vehicle_category, VehicleCategory::Truck);
assert_eq!(v.bounding_box.dimensions.length.as_literal(), Some(&8.0));
assert_eq!(v.performance.max_speed.as_literal(), Some(&120.0));
}
#[test]
fn test_with_dimensions_overrides_preset_but_preserves_center() {
let obj = DetachedVehicleBuilder::new("ego")
.car()
.with_dimensions(5.0, 2.0, 1.6)
.build();
let v = obj.vehicle.as_ref().unwrap();
assert_eq!(v.bounding_box.dimensions.length.as_literal(), Some(&5.0));
assert_eq!(v.bounding_box.dimensions.width.as_literal(), Some(&2.0));
assert_eq!(v.bounding_box.dimensions.height.as_literal(), Some(&1.6));
assert_eq!(v.bounding_box.center.x.as_literal(), Some(&1.4));
}
#[test]
fn test_with_performance_overrides_preset() {
let obj = DetachedVehicleBuilder::new("ego")
.truck()
.with_performance(200.0, 5.0, 10.0)
.build();
let v = obj.vehicle.as_ref().unwrap();
assert_eq!(v.performance.max_speed.as_literal(), Some(&200.0));
assert_eq!(v.performance.max_acceleration.as_literal(), Some(&5.0));
assert_eq!(v.performance.max_deceleration.as_literal(), Some(&10.0));
}
}