use std::fmt;
use std::str::FromStr;
use super::error::{Error, Result};
use super::primitive::*;
use super::booleanoperations::BooleanShape;
use super::displacement::{DisplacementResources, DisplacementMesh};
use super::material::MaterialResources;
use super::production::{ProductionItem, ProductionComponent, ProductionObject, ProductionBuild, Alternatives};
use super::slice::{SliceResources, SliceObject};
use super::volumetric::{VolumetricResources, LevelSet};
use super::implicit::ImplicitResources;
#[derive(Debug, Clone, Default)]
pub struct Model {
pub unit: Unit,
pub language: Option<String>,
pub required_extensions: Vec<String>,
pub recommended_extensions: Vec<String>,
pub metadatas: Vec<Metadata>,
pub resources: Resources,
pub build: Build,
}
impl Model {
pub fn new() -> Self {
Self::default()
}
pub fn with_unit(unit: Unit) -> Self {
Self {
unit,
..Default::default()
}
}
pub fn add_metadata(&mut self, name: impl Into<String>, value: impl Into<String>) -> &mut Self {
self.metadatas.push(Metadata {
name: name.into(),
value: value.into(),
preserve: false,
r#type: None,
});
self
}
pub fn set_title(&mut self, title: impl Into<String>) -> &mut Self {
self.add_metadata("Title", title)
}
pub fn set_designer(&mut self, designer: impl Into<String>) -> &mut Self {
self.add_metadata("Designer", designer)
}
pub fn set_description(&mut self, description: impl Into<String>) -> &mut Self {
self.add_metadata("Description", description)
}
pub fn set_application(&mut self, application: impl Into<String>) -> &mut Self {
self.add_metadata("Application", application)
}
pub fn add_base_materials(&mut self, materials: Vec<BaseMaterial>) -> u32 {
let id = self.resources.next_id();
self.resources.base_materials.push(BaseMaterials {
id,
materials,
display_properties_id: None,
});
id
}
pub fn add_object(&mut self, object: Object) -> u32 {
let id = object.id;
self.resources.objects.push(object);
self.resources.set_next_id(id + 1);
id
}
pub fn gen_object(&mut self) -> Object {
Object::new(self.resources.next_id())
}
pub fn add_build_item(&mut self, object_id: u32, transform: Option<Matrix3D>) -> &mut Self {
self.build.items.push(Item::with_transform(object_id, transform));
self
}
pub fn get_object(&self, id: u32) -> Option<&Object> {
self.resources.objects.iter().find(|o| o.id == id)
}
pub fn get_object_mut(&mut self, id: u32) -> Option<&mut Object> {
self.resources.objects.iter_mut().find(|o| o.id == id)
}
pub fn get_objects(&self, objt: ObjectType) -> Vec<&Object> {
self.resources.objects.iter().filter(|o| o.r#type == objt).collect()
}
pub fn get_objects_mut(&mut self, objt: ObjectType) -> Vec<&mut Object> {
self.resources.objects.iter_mut().filter(|o| o.r#type == objt).collect()
}
pub fn get_base_material(&self, id: u32) -> Option<&BaseMaterials> {
self.resources.base_materials.iter().find(|m| m.id == id)
}
pub fn get_base_material_mut(&mut self, id: u32) -> Option<&mut BaseMaterials> {
self.resources.base_materials.iter_mut().find(|m| m.id == id)
}
pub fn get_displacement(&self) -> Option<&DisplacementResources> {
self.resources.displacements.as_ref()
}
pub fn get_displacements_mut(&mut self) -> Option<&mut DisplacementResources> {
self.resources.displacements.as_mut()
}
pub fn get_materials(&self) -> Option<&MaterialResources> {
self.resources.materials.as_ref()
}
pub fn get_materials_mut(&mut self) -> Option<&mut MaterialResources> {
self.resources.materials.as_mut()
}
pub fn get_volumetric(&self) -> Option<&VolumetricResources> {
self.resources.volumetric.as_ref()
}
pub fn get_volumetric_mut(&mut self) -> Option<&mut VolumetricResources> {
self.resources.volumetric.as_mut()
}
pub fn get_implicit(&self) -> Option<&ImplicitResources> {
self.resources.implicit.as_ref()
}
pub fn get_implicit_mut(&mut self) -> Option<&mut ImplicitResources> {
self.resources.implicit.as_mut()
}
pub fn validate(&self) -> Result<()> {
let mut ids = std::collections::HashSet::new();
for obj in &self.resources.objects {
if !ids.insert(obj.id) {
return Err(Error::DuplicateResourceId(obj.id));
}
}
for mat in &self.resources.base_materials {
if !ids.insert(mat.id) {
return Err(Error::DuplicateResourceId(mat.id));
}
}
for item in &self.build.items {
if self.get_object(item.object_id).is_none() {
return Err(Error::InvalidReference(format!(
"build item references non-existent object: {}",
item.object_id
)));
}
}
for item in &self.build.items {
if let Some(obj) = self.get_object(item.object_id) {
if obj.r#type == ObjectType::Other {
return Err(Error::InvalidReference(format!(
"build item cannot reference 'other' type object: {}",
item.object_id
)));
}
}
}
for obj in &self.resources.objects {
if obj.is_boolean_shape() {
if obj.pid.is_some() || obj.pindex.is_some() {
return Err(Error::InvalidAttribute {
name: "pid/pindex".to_string(),
message: format!(
"boolean shape object {} cannot have material properties",
obj.id
),
});
}
if let Some(boolean_shape) = obj.get_boolean_shape() {
if self.get_object(boolean_shape.object_id).is_none() {
return Err(Error::InvalidReference(format!(
"boolean shape {} references non-existent base object: {}",
obj.id, boolean_shape.object_id
)));
}
for (i, boolean) in boolean_shape.booleans.iter().enumerate() {
if self.get_object(boolean.object_id).is_none() {
return Err(Error::InvalidReference(format!(
"boolean shape {} boolean[{}] references non-existent object: {}",
obj.id, i, boolean.object_id
)));
}
}
if let Err(e) = boolean_shape.validate() {
return Err(Error::InvalidStructure(format!(
"boolean shape {} validation failed: {}",
obj.id, e
)));
}
}
}
}
Ok(())
}
}
#[derive(Debug, Clone, Default)]
pub struct MetadataGroup {
pub metadatas: Vec<Metadata>,
}
impl MetadataGroup {
pub fn new() -> Self {
Self { metadatas: Vec::new() }
}
}
#[derive(Debug, Clone, Default)]
pub struct Metadata {
pub name: String,
pub value: String,
pub preserve: bool,
pub r#type: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct Resources {
pub base_materials: Vec<BaseMaterials>,
pub displacements: Option<DisplacementResources>,
pub materials: Option<MaterialResources>,
pub slices: Option<SliceResources>,
pub volumetric: Option<VolumetricResources>,
pub implicit: Option<ImplicitResources>,
pub objects: Vec<Object>,
next_id: u32,
}
impl Resources {
pub fn next_id(&mut self) -> u32 {
self.next_id += 1;
self.next_id
}
pub fn set_next_id(&mut self, id: u32) {
if id >= self.next_id {
self.next_id = id;
}
}
}
#[derive(Debug, Clone)]
pub struct BaseMaterials {
pub id: u32,
pub materials: Vec<BaseMaterial>,
pub display_properties_id: Option<u32>,
}
impl BaseMaterials {
pub fn new(id: u32) -> Self {
Self {
id,
materials: Vec::new(),
display_properties_id: None,
}
}
pub fn add(&mut self, name: impl Into<String>, display_color: Color) -> usize {
let index = self.materials.len();
self.materials.push(BaseMaterial {
name: name.into(),
display_color,
});
index
}
}
#[derive(Debug, Clone)]
pub struct BaseMaterial {
pub name: String,
pub display_color: Color,
}
impl BaseMaterial {
pub fn new(name: impl Into<String>, display_color: Color) -> Self {
Self {
name: name.into(),
display_color,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct Build {
pub production: Option<ProductionBuild>,
pub items: Vec<Item>,
}
impl Build {
pub fn new() -> Self {
Self::default()
}
pub fn add_item(&mut self, item: Item) -> &mut Self {
self.items.push(item);
self
}
pub fn add_object(&mut self, object_id: u32) -> &mut Self {
self.items.push(Item::new(object_id));
self
}
pub fn add_object_with_transform(&mut self, object_id: u32, transform: Matrix3D) -> &mut Self {
self.items.push(Item {
object_id,
transform: Some(transform),
part_number: None,
production: None,
metadata_group: MetadataGroup::new(),
});
self
}
pub fn get_transform(&self, object_id: u32) -> Option<&Matrix3D> {
let mut trans = None;
for item in &self.items {
if item.object_id == object_id {
trans = item.transform.as_ref();
break;
}
}
trans
}
}
#[derive(Debug, Clone)]
pub struct Item {
pub object_id: u32,
pub transform: Option<Matrix3D>,
pub part_number: Option<String>,
pub production: Option<ProductionItem>,
pub metadata_group: MetadataGroup,
}
impl Item {
pub fn new(object_id: u32) -> Self {
Self {
object_id,
transform: None,
part_number: None,
production: None,
metadata_group: MetadataGroup::new(),
}
}
pub fn with_transform(object_id: u32, transform: Option<Matrix3D>) -> Self {
Self {
object_id,
transform,
part_number: None,
production: None,
metadata_group: MetadataGroup::new(),
}
}
pub fn set_transform(&mut self, transform: Matrix3D) -> &mut Self {
self.transform = Some(transform);
self
}
pub fn set_part_number(&mut self, part_number: impl Into<String>) -> &mut Self {
self.part_number = Some(part_number.into());
self
}
}
#[derive(Debug, Clone)]
pub struct Object {
pub id: u32,
pub r#type: ObjectType,
pub thumbnail: Option<String>,
pub part_number: Option<String>,
pub name: Option<String>,
pub pid: Option<u32>,
pub pindex: Option<u32>,
pub did: Option<u32>,
pub production: Option<ProductionObject>,
pub slice: Option<SliceObject>,
pub metadata_group: MetadataGroup,
pub content: ObjectContent,
}
impl Object {
pub fn new(id: u32) -> Self {
Self {
id,
r#type: ObjectType::Model,
thumbnail: None,
part_number: None,
name: None,
pid: None,
pindex: None,
did: None,
production: None,
slice: None,
metadata_group: MetadataGroup::new(),
content: ObjectContent::Mesh(Mesh::new()),
}
}
pub fn with_mesh(id: u32, mesh: Mesh) -> Self {
Self {
id,
r#type: ObjectType::Model,
thumbnail: None,
part_number: None,
name: None,
pid: None,
pindex: None,
did: None,
production: None,
slice: None,
metadata_group: MetadataGroup::new(),
content: ObjectContent::Mesh(mesh),
}
}
pub fn with_components(id: u32, components: Components) -> Self {
Self {
id,
r#type: ObjectType::Model,
thumbnail: None,
part_number: None,
name: None,
pid: None,
pindex: None,
did: None,
production: None,
slice: None,
metadata_group: MetadataGroup::new(),
content: ObjectContent::Components(components),
}
}
pub fn with_boolean_shape(id: u32, boolean_shape: BooleanShape) -> Self {
Self {
id,
r#type: ObjectType::Model,
thumbnail: None,
part_number: None,
name: None,
pid: None,
pindex: None,
did: None,
production: None,
slice: None,
metadata_group: MetadataGroup::new(),
content: ObjectContent::BooleanShape(boolean_shape),
}
}
pub fn with_displacement_mesh(id: u32, displacement_mesh: DisplacementMesh) -> Self {
Self {
id,
r#type: ObjectType::Model,
thumbnail: None,
part_number: None,
name: None,
pid: None,
pindex: None,
did: None,
production: None,
slice: None,
metadata_group: MetadataGroup::new(),
content: ObjectContent::DisplacementMesh(displacement_mesh),
}
}
pub fn is_mesh(&self) -> bool {
matches!(self.content, ObjectContent::Mesh(_))
}
pub fn is_components(&self) -> bool {
matches!(self.content, ObjectContent::Components(_))
}
pub fn is_boolean_shape(&self) -> bool {
matches!(self.content, ObjectContent::BooleanShape(_))
}
pub fn is_displacement_mesh(&self) -> bool {
matches!(self.content, ObjectContent::DisplacementMesh(_))
}
pub fn is_alternatives(&self) -> bool {
matches!(self.content, ObjectContent::Alternatives(_))
}
pub fn get_mesh(&self) -> Option<&Mesh> {
match &self.content {
ObjectContent::Mesh(mesh) => Some(mesh),
_ => None,
}
}
pub fn get_mesh_mut(&mut self) -> Option<&mut Mesh> {
match &mut self.content {
ObjectContent::Mesh(mesh) => Some(mesh),
_ => None,
}
}
pub fn get_components(&self) -> Option<&Components> {
match &self.content {
ObjectContent::Components(components) => Some(components),
_ => None,
}
}
pub fn get_boolean_shape(&self) -> Option<&BooleanShape> {
match &self.content {
ObjectContent::BooleanShape(boolean_shape) => Some(boolean_shape),
_ => None,
}
}
pub fn get_boolean_shape_mut(&mut self) -> Option<&mut BooleanShape> {
match &mut self.content {
ObjectContent::BooleanShape(boolean_shape) => Some(boolean_shape),
_ => None,
}
}
pub fn get_displacement_mesh(&self) -> Option<&DisplacementMesh> {
match &self.content {
ObjectContent::DisplacementMesh(displacement_mesh) => Some(displacement_mesh),
_ => None,
}
}
pub fn get_displacement_mesh_mut(&mut self) -> Option<&mut DisplacementMesh> {
match &mut self.content {
ObjectContent::DisplacementMesh(displacement_mesh) => Some(displacement_mesh),
_ => None,
}
}
pub fn get_alternatives(&self) -> Option<&Alternatives> {
match &self.content {
ObjectContent::Alternatives(alternatives) => Some(alternatives),
_ => None,
}
}
pub fn get_alternatives_mut(&mut self) -> Option<&mut Alternatives> {
match &mut self.content {
ObjectContent::Alternatives(alternatives) => Some(alternatives),
_ => None,
}
}
pub fn set_mesh(&mut self, mesh: Mesh) -> &mut Self {
self.content = ObjectContent::Mesh(mesh);
self
}
pub fn set_components(&mut self, components: Components) -> &mut Self {
self.content = ObjectContent::Components(components);
self
}
pub fn set_boolean_shape(&mut self, boolean_shape: BooleanShape) -> &mut Self {
self.content = ObjectContent::BooleanShape(boolean_shape);
self
}
pub fn set_displacement_mesh(&mut self, displacement_mesh: DisplacementMesh) -> &mut Self {
self.content = ObjectContent::DisplacementMesh(displacement_mesh);
self
}
pub fn set_alternatives(&mut self, alternatives: Alternatives) -> &mut Self {
self.content = ObjectContent::Alternatives(alternatives);
self
}
pub fn set_name(&mut self, name: impl Into<String>) -> &mut Self {
self.name = Some(name.into());
self
}
pub fn set_type(&mut self, object_type: ObjectType) -> &mut Self {
self.r#type = object_type;
self
}
pub fn set_material(&mut self, pid: u32, pindex: u32) -> &mut Self {
self.pid = Some(pid);
self.pindex = Some(pindex);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ObjectType {
#[default]
Model,
Solidsupport,
Support,
Surface,
Other,
}
impl fmt::Display for ObjectType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ObjectType::Model => write!(f, "model"),
ObjectType::Solidsupport => write!(f, "solidsupport"),
ObjectType::Support => write!(f, "support"),
ObjectType::Surface => write!(f, "surface"),
ObjectType::Other => write!(f, "other"),
}
}
}
impl FromStr for ObjectType {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_lowercase().as_str() {
"model" => Ok(ObjectType::Model),
"solidsupport" => Ok(ObjectType::Solidsupport),
"support" => Ok(ObjectType::Support),
"surface" => Ok(ObjectType::Surface),
"other" => Ok(ObjectType::Other),
_ => Err(Error::InvalidAttribute {
name: "type".to_string(),
message: format!("unknown object type: {}", s),
}),
}
}
}
#[derive(Debug, Clone)]
pub enum ObjectContent {
Mesh(Mesh),
Components(Components),
BooleanShape(BooleanShape),
DisplacementMesh(DisplacementMesh),
Alternatives(Alternatives),
LevelSet(LevelSet),
}
impl Default for ObjectContent {
fn default() -> Self {
ObjectContent::Mesh(Mesh::default())
}
}
#[derive(Debug, Clone)]
pub struct Components {
pub components: Vec<Component>,
}
#[derive(Debug, Clone)]
pub struct Component {
pub object_id: u32,
pub transform: Option<Matrix3D>,
pub production: Option<ProductionComponent>,
}
impl Component {
pub fn new(object_id: u32) -> Self {
Self {
object_id,
transform: None,
production: None,
}
}
pub fn with_transform(object_id: u32, transform: Matrix3D) -> Self {
Self {
object_id,
transform: Some(transform),
production: None,
}
}
}