#![cfg(all(feature = "geometry-select", feature = "properties"))]
mod geometry;
mod layout;
mod read;
#[cfg(test)]
mod tests;
use std::{fmt, sync::Arc};
use ifc_geometry::{GeometryError, Transform};
use ifc_model::{EntityId, Model};
use ifc_properties::{ExactPropertyError, ExactSource, ExactUnitError};
use crate::operation::{ReadError, Sector, Side};
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct DoorOperation {
pub door: EntityId,
pub operation: DoorOperationType,
pub operation_source: ExactSource,
pub panel_source: ExactSource,
pub overall_width: f64,
pub leaves: Vec<Leaf>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum DoorOperationType {
SingleSwingLeft,
SingleSwingRight,
DoubleDoorSingleSwing,
DoubleSwingLeft,
DoubleSwingRight,
DoubleDoorDoubleSwing,
SlidingToLeft,
SlidingToRight,
DoubleDoorSliding,
RollingUp,
SwingFixedLeft,
SwingFixedRight,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PanelPosition {
Left,
Middle,
Right,
NotDefined,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum LeafMotion {
Swing,
DoubleSwing,
Slide {
direction: [f64; 3],
},
RollUp,
Fixed,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Leaf {
pub panel_set: EntityId,
pub position: PanelPosition,
pub motion: LeafMotion,
pub frame_world: Transform,
pub width: f64,
pub hinge_side: Option<Side>,
pub swing: Option<Sector>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum RefusedOperation {
NotDefined,
UserDefined,
Revolving,
Folding,
AmbiguousSwingDirection,
Lifting,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum DoorOperationError {
Property(ExactPropertyError),
Unit(ExactUnitError),
Placement(GeometryError),
NotADoor {
entity: EntityId,
type_name: Arc<str>,
},
UnsupportedTypeObject {
type_object: EntityId,
type_name: Arc<str>,
},
MalformedAttribute {
entity: EntityId,
attribute: &'static str,
},
MissingOperationType {
door: EntityId,
},
ConflictingOperationType {
occurrence: Arc<str>,
type_object: EntityId,
type_value: Arc<str>,
},
RefusedOperation {
operation: Arc<str>,
reason: RefusedOperation,
},
MissingOverallWidth {
door: EntityId,
},
NoPanelProperties {
door: EntityId,
},
PanelCount {
expected: usize,
found: usize,
},
PanelMismatch {
set: EntityId,
attribute: &'static str,
found: Arc<str>,
},
MissingPanelWidth {
set: EntityId,
},
InvalidPanelWidth {
set: EntityId,
width: f64,
},
PanelWidthsDoNotPartition {
sum: f64,
},
UnplacedPanel {
set: EntityId,
},
NonRigidPlacement {
door: EntityId,
},
NoPlanHandedness {
door: EntityId,
},
}
impl fmt::Display for DoorOperationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "door operation cannot be derived exactly: {self:?}")
}
}
impl std::error::Error for DoorOperationError {}
impl From<ExactPropertyError> for DoorOperationError {
fn from(error: ExactPropertyError) -> Self {
Self::Property(error)
}
}
impl From<ExactUnitError> for DoorOperationError {
fn from(error: ExactUnitError) -> Self {
Self::Unit(error)
}
}
impl DoorOperationError {
fn read(error: ReadError) -> Self {
match error {
ReadError::Malformed { entity, attribute } => {
Self::MalformedAttribute { entity, attribute }
}
ReadError::Unit(error) => Self::Unit(error),
ReadError::Placement(error) => Self::Placement(error),
}
}
}
pub fn door_operation(model: &Model, door: EntityId) -> Result<DoorOperation, DoorOperationError> {
let inputs = read::read_door(model, door)?;
let leaves = layout::place_leaves(inputs.operation, &inputs.panels)?;
let placement = geometry::DoorFrame::new(door, inputs.world)?;
let leaves = leaves
.into_iter()
.map(|placed| placement.leaf(&placed, inputs.overall_width))
.collect::<Result<_, _>>()?;
Ok(DoorOperation {
door,
operation: inputs.operation,
operation_source: inputs.operation_source,
panel_source: inputs.panel_source,
overall_width: inputs.overall_width,
leaves,
})
}