use u_nesting_core::geom::nalgebra_types::NaVector3 as Vector3;
use u_nesting_core::geometry::Boundary;
use u_nesting_core::{Error, Result};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Boundary3D {
dimensions: Vector3<f64>,
max_mass: Option<f64>,
gravity: bool,
stability: bool,
}
impl Boundary3D {
pub fn new(width: f64, depth: f64, height: f64) -> Self {
Self {
dimensions: Vector3::new(width, depth, height),
max_mass: None,
gravity: false,
stability: false,
}
}
pub fn box_shape(width: f64, depth: f64, height: f64) -> Self {
Self::new(width, depth, height)
}
pub fn with_max_mass(mut self, mass: f64) -> Self {
self.max_mass = Some(mass);
self
}
pub fn with_gravity(mut self, enabled: bool) -> Self {
self.gravity = enabled;
self
}
pub fn with_stability(mut self, enabled: bool) -> Self {
self.stability = enabled;
self
}
pub fn dimensions(&self) -> &Vector3<f64> {
&self.dimensions
}
pub fn width(&self) -> f64 {
self.dimensions.x
}
pub fn depth(&self) -> f64 {
self.dimensions.y
}
pub fn height(&self) -> f64 {
self.dimensions.z
}
pub fn max_mass(&self) -> Option<f64> {
self.max_mass
}
pub fn has_gravity(&self) -> bool {
self.gravity
}
pub fn has_stability(&self) -> bool {
self.stability
}
}
impl Boundary for Boundary3D {
type Scalar = f64;
fn measure(&self) -> f64 {
self.dimensions.x * self.dimensions.y * self.dimensions.z
}
fn aabb_vec(&self) -> (Vec<f64>, Vec<f64>) {
(
vec![0.0, 0.0, 0.0],
vec![self.dimensions.x, self.dimensions.y, self.dimensions.z],
)
}
fn validate(&self) -> Result<()> {
if self.dimensions.x <= 0.0 || self.dimensions.y <= 0.0 || self.dimensions.z <= 0.0 {
return Err(Error::InvalidBoundary(
"All dimensions must be positive".into(),
));
}
if let Some(mass) = self.max_mass {
if mass <= 0.0 {
return Err(Error::InvalidBoundary(
"Maximum mass must be positive".into(),
));
}
}
Ok(())
}
fn contains_point(&self, point: &[f64]) -> bool {
if point.len() < 3 {
return false;
}
point[0] >= 0.0
&& point[0] <= self.dimensions.x
&& point[1] >= 0.0
&& point[1] <= self.dimensions.y
&& point[2] >= 0.0
&& point[2] <= self.dimensions.z
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_box_boundary_volume() {
let boundary = Boundary3D::new(100.0, 80.0, 50.0);
assert_relative_eq!(boundary.measure(), 400000.0, epsilon = 0.001);
}
#[test]
fn test_constraints() {
let boundary = Boundary3D::new(100.0, 80.0, 50.0)
.with_max_mass(1000.0)
.with_gravity(true)
.with_stability(true);
assert_eq!(boundary.max_mass(), Some(1000.0));
assert!(boundary.has_gravity());
assert!(boundary.has_stability());
}
#[test]
fn test_validation() {
let valid = Boundary3D::new(100.0, 80.0, 50.0);
assert!(valid.validate().is_ok());
let invalid = Boundary3D::new(-100.0, 80.0, 50.0);
assert!(invalid.validate().is_err());
}
}