use ifc_model::{Entity, EntityId, Model};
use crate::error::{GeometryError, GeometryResult};
use crate::slots::Slots;
pub mod product_shape_slot {
pub const REPRESENTATIONS: usize = 2;
}
pub mod representation_slot {
pub const CONTEXT_OF_ITEMS: usize = 0;
pub const REPRESENTATION_IDENTIFIER: usize = 1;
pub const REPRESENTATION_TYPE: usize = 2;
pub const ITEMS: usize = 3;
}
#[derive(Debug, Clone, Copy)]
pub struct Representation<'m> {
slots: Slots<'m>,
}
impl<'m> Representation<'m> {
pub fn new(id: EntityId, entity: &'m Entity) -> Self {
Self {
slots: Slots::new(id, entity),
}
}
pub fn identifier(&self) -> Option<String> {
self.slots
.opt_text(representation_slot::REPRESENTATION_IDENTIFIER)
}
pub fn representation_type(&self) -> Option<String> {
self.slots
.opt_text(representation_slot::REPRESENTATION_TYPE)
}
pub fn context(&self) -> Option<EntityId> {
match self.slots.opt(representation_slot::CONTEXT_OF_ITEMS)? {
ifc_model::Value::Ref(id) => Some(*id),
_ => None,
}
}
pub fn items(&self) -> GeometryResult<Vec<EntityId>> {
self.slots.req_ref_list(representation_slot::ITEMS, "Items")
}
}
#[derive(Debug, Clone, Copy)]
pub struct ProductShape<'m> {
slots: Slots<'m>,
}
impl<'m> ProductShape<'m> {
pub fn new(id: EntityId, entity: &'m Entity) -> Self {
Self {
slots: Slots::new(id, entity),
}
}
pub fn representations(&self) -> GeometryResult<Vec<EntityId>> {
self.slots
.req_ref_list(product_shape_slot::REPRESENTATIONS, "Representations")
}
}
pub const SOLID_IDENTIFIERS: &[&str] = &["Body", "Facetation"];
pub fn select_shape_representation(
model: &Model,
product: EntityId,
) -> GeometryResult<Option<EntityId>> {
let entity = model.get(product).ok_or(GeometryError::MissingEntity {
referrer: product,
missing: product,
})?;
let Some(shape_id) = super::product::Product::new(product, entity).representation() else {
return Ok(None);
};
let shape_entity = model.get(shape_id).ok_or(GeometryError::MissingEntity {
referrer: product,
missing: shape_id,
})?;
let candidates = ProductShape::new(shape_id, shape_entity).representations()?;
for wanted in SOLID_IDENTIFIERS {
for &candidate in &candidates {
let Some(entity) = model.get(candidate) else {
continue;
};
let identifier = Representation::new(candidate, entity).identifier();
if identifier.as_deref() == Some(*wanted) {
return Ok(Some(candidate));
}
}
}
for &candidate in &candidates {
let Some(entity) = model.get(candidate) else {
continue;
};
if Representation::new(candidate, entity)
.identifier()
.is_none()
{
return Ok(Some(candidate));
}
}
Ok(None)
}
pub const PLAN_IDENTIFIERS: &[&str] = &["Plan", "Annotation", "FootPrint", "Axis"];
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RepresentationPurpose {
Body,
Plan,
}
pub fn select_product_representation(
model: &Model,
product: EntityId,
purpose: RepresentationPurpose,
) -> GeometryResult<Option<EntityId>> {
match purpose {
RepresentationPurpose::Body => select_shape_representation(model, product),
RepresentationPurpose::Plan => select_plan_representation(model, product),
}
}
pub fn select_plan_representation(
model: &Model,
product: EntityId,
) -> GeometryResult<Option<EntityId>> {
let entity = model.get(product).ok_or(GeometryError::MissingEntity {
referrer: product,
missing: product,
})?;
let Some(shape_id) = super::product::Product::new(product, entity).representation() else {
return Ok(None);
};
let shape_entity = model.get(shape_id).ok_or(GeometryError::MissingEntity {
referrer: product,
missing: shape_id,
})?;
let candidates = ProductShape::new(shape_id, shape_entity).representations()?;
for wanted in PLAN_IDENTIFIERS {
for &candidate in &candidates {
let Some(entity) = model.get(candidate) else {
continue;
};
if Representation::new(candidate, entity)
.identifier()
.as_deref()
!= Some(*wanted)
{
continue;
}
let Some(context) = super::context::context_of(model, candidate) else {
continue;
};
if context.is_plan_view() {
return Ok(Some(candidate));
}
}
}
for wanted in PLAN_IDENTIFIERS {
for &candidate in &candidates {
let Some(entity) = model.get(candidate) else {
continue;
};
if Representation::new(candidate, entity)
.identifier()
.as_deref()
== Some(*wanted)
{
return Ok(Some(candidate));
}
}
}
Ok(None)
}