use std::fmt;
use std::str::FromStr;
use super::error::{Error, Result};
use super::primitive::Matrix3D;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BooleanOperation {
#[default]
Union,
Difference,
Intersection,
}
impl fmt::Display for BooleanOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BooleanOperation::Union => write!(f, "union"),
BooleanOperation::Difference => write!(f, "difference"),
BooleanOperation::Intersection => write!(f, "intersection"),
}
}
}
impl FromStr for BooleanOperation {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_lowercase().as_str() {
"union" => Ok(BooleanOperation::Union),
"difference" => Ok(BooleanOperation::Difference),
"intersection" => Ok(BooleanOperation::Intersection),
_ => Err(Error::InvalidAttribute {
name: "operation".to_string(),
message: format!("unknown boolean operation: {}", s),
}),
}
}
}
#[derive(Debug, Clone)]
pub struct Boolean {
pub object_id: u32,
pub transform: Option<Matrix3D>,
pub path: Option<String>,
}
impl Boolean {
pub fn new(object_id: u32) -> Self {
Self {
object_id,
transform: None,
path: None,
}
}
pub fn with_transform(object_id: u32, transform: Matrix3D) -> Self {
Self {
object_id,
transform: Some(transform),
path: None,
}
}
pub fn with_path(object_id: u32, path: impl Into<String>) -> Self {
Self {
object_id,
transform: None,
path: Some(path.into()),
}
}
pub fn set_transform(&mut self, transform: Matrix3D) -> &mut Self {
self.transform = Some(transform);
self
}
pub fn set_path(&mut self, path: impl Into<String>) -> &mut Self {
self.path = Some(path.into());
self
}
}
#[derive(Debug, Clone)]
pub struct BooleanShape {
pub object_id: u32,
pub operation: BooleanOperation,
pub transform: Option<Matrix3D>,
pub path: Option<String>,
pub booleans: Vec<Boolean>,
}
impl BooleanShape {
pub fn new(object_id: u32) -> Self {
Self {
object_id,
operation: BooleanOperation::Union,
transform: None,
path: None,
booleans: Vec::new(),
}
}
pub fn with_operation(object_id: u32, operation: BooleanOperation) -> Self {
Self {
object_id,
operation,
transform: None,
path: None,
booleans: Vec::new(),
}
}
pub fn with_transform(object_id: u32, transform: Matrix3D) -> Self {
Self {
object_id,
operation: BooleanOperation::Union,
transform: Some(transform),
path: None,
booleans: Vec::new(),
}
}
pub fn with_path(object_id: u32, path: impl Into<String>) -> Self {
Self {
object_id,
operation: BooleanOperation::Union,
transform: None,
path: Some(path.into()),
booleans: Vec::new(),
}
}
pub fn add_boolean(&mut self, boolean: Boolean) -> &mut Self {
self.booleans.push(boolean);
self
}
pub fn set_operation(&mut self, operation: BooleanOperation) -> &mut Self {
self.operation = operation;
self
}
pub fn set_transform(&mut self, transform: Matrix3D) -> &mut Self {
self.transform = Some(transform);
self
}
pub fn set_path(&mut self, path: impl Into<String>) -> &mut Self {
self.path = Some(path.into());
self
}
pub fn boolean_count(&self) -> usize {
self.booleans.len()
}
pub fn validate(&self) -> Result<()> {
if self.object_id == 0 {
return Err(Error::InvalidAttribute {
name: "objectid".to_string(),
message: "base object ID cannot be zero".to_string(),
});
}
for (_, boolean) in self.booleans.iter().enumerate() {
if boolean.object_id == 0 {
return Err(Error::InvalidAttribute {
name: "objectid".to_string(),
message: "boolean operation object ID cannot be zero".to_string(),
});
}
}
Ok(())
}
}
impl Default for BooleanShape {
fn default() -> Self {
Self::new(0)
}
}