use ifc_model::{Entity, EntityId, Transaction, Value};
use ifc_schema::Schema;
use super::build_named;
use crate::condition::{AxisValues, BoundaryConditionKind, FailureLimits, StiffnessValue};
use crate::error::{StructuralError, StructuralResult};
#[derive(Debug, Clone, Copy, Default)]
pub struct BoundaryConditionDraft<'a> {
pub name: Option<&'a str>,
pub translational: AxisValues<Option<StiffnessValue>>,
pub rotational: AxisValues<Option<StiffnessValue>>,
pub warping: Option<StiffnessValue>,
}
pub fn stage_boundary_condition(
tx: &mut Transaction,
kind: BoundaryConditionKind,
draft: BoundaryConditionDraft<'_>,
) -> StructuralResult<EntityId> {
let entity_type = entity_name(kind);
if kind == BoundaryConditionKind::Face && has_any(draft.rotational) {
return Err(crate::error::StructuralError::InvalidDraftValue {
entity_type,
attribute: "RotationalStiffness",
expected: "no rotational stiffness: face conditions do not declare one",
});
}
if draft.warping.is_some() && kind != BoundaryConditionKind::NodeWarping {
return Err(crate::error::StructuralError::InvalidDraftValue {
entity_type,
attribute: "WarpingStiffness",
expected: "no warping stiffness outside IfcBoundaryNodeConditionWarping",
});
}
for (axis, value) in axes(draft.translational).chain(axes(draft.rotational)) {
if let Some(StiffnessValue::Measure(measure)) = value {
if !measure.is_finite() {
return Err(crate::error::StructuralError::InvalidDraftValue {
entity_type,
attribute: axis,
expected: "a finite stiffness measure",
});
}
}
}
let mut attributes = vec![optional_text(draft.name)];
attributes.extend(stiffness_triple(draft.translational, kind, true));
if kind != BoundaryConditionKind::Face {
attributes.extend(stiffness_triple(draft.rotational, kind, false));
}
if kind == BoundaryConditionKind::NodeWarping {
attributes.push(stiffness_value(draft.warping, kind, false));
}
Ok(tx.create(Entity::new(entity_type, attributes)))
}
fn entity_name(kind: BoundaryConditionKind) -> &'static str {
match kind {
BoundaryConditionKind::Edge => "IFCBOUNDARYEDGECONDITION",
BoundaryConditionKind::Face => "IFCBOUNDARYFACECONDITION",
BoundaryConditionKind::Node => "IFCBOUNDARYNODECONDITION",
BoundaryConditionKind::NodeWarping => "IFCBOUNDARYNODECONDITIONWARPING",
}
}
fn measure_name(kind: BoundaryConditionKind, translational: bool) -> &'static str {
match (kind, translational) {
(BoundaryConditionKind::Edge, true) => "IFCMODULUSOFTRANSLATIONALSUBGRADEREACTIONMEASURE",
(BoundaryConditionKind::Edge, false) => "IFCMODULUSOFROTATIONALSUBGRADEREACTIONMEASURE",
(BoundaryConditionKind::Face, _) => "IFCMODULUSOFSUBGRADEREACTIONMEASURE",
(_, true) => "IFCLINEARSTIFFNESSMEASURE",
(_, false) => "IFCROTATIONALSTIFFNESSMEASURE",
}
}
fn stiffness_value(
value: Option<StiffnessValue>,
kind: BoundaryConditionKind,
translational: bool,
) -> Value {
match value {
None => Value::Null,
Some(StiffnessValue::Boolean(flag)) => Value::Bool(flag),
Some(StiffnessValue::Measure(measure)) => Value::Typed {
type_name: measure_name(kind, translational).into(),
value: Box::new(Value::Real(measure)),
},
}
}
fn stiffness_triple(
values: AxisValues<Option<StiffnessValue>>,
kind: BoundaryConditionKind,
translational: bool,
) -> [Value; 3] {
[
stiffness_value(values.x, kind, translational),
stiffness_value(values.y, kind, translational),
stiffness_value(values.z, kind, translational),
]
}
fn has_any(values: AxisValues<Option<StiffnessValue>>) -> bool {
values.x.is_some() || values.y.is_some() || values.z.is_some()
}
fn axes(
values: AxisValues<Option<StiffnessValue>>,
) -> impl Iterator<Item = (&'static str, Option<StiffnessValue>)> {
[("X", values.x), ("Y", values.y), ("Z", values.z)].into_iter()
}
fn optional_text(value: Option<&str>) -> Value {
value.map_or(Value::Null, |text| Value::Text(text.into()))
}
#[derive(Debug, Clone, Copy)]
pub enum ConnectionConditionDraft {
Failure(FailureLimits),
Slippage(AxisValues<Option<f64>>),
}
pub fn stage_connection_condition(
tx: &mut Transaction,
schema: &Schema,
name: Option<&str>,
draft: ConnectionConditionDraft,
) -> StructuralResult<EntityId> {
let (entity, values): (&'static str, Vec<(&'static str, Value)>) = match draft {
ConnectionConditionDraft::Failure(limits) => (
"IfcFailureConnectionCondition",
vec![
("TensionFailureX", measure(limits.tension.x)),
("TensionFailureY", measure(limits.tension.y)),
("TensionFailureZ", measure(limits.tension.z)),
("CompressionFailureX", measure(limits.compression.x)),
("CompressionFailureY", measure(limits.compression.y)),
("CompressionFailureZ", measure(limits.compression.z)),
],
),
ConnectionConditionDraft::Slippage(slippage) => (
"IfcSlippageConnectionCondition",
vec![
("SlippageX", measure(slippage.x)),
("SlippageY", measure(slippage.y)),
("SlippageZ", measure(slippage.z)),
],
),
};
for (attribute, value) in &values {
if matches!(value, Value::Real(number) if !number.is_finite()) {
return Err(StructuralError::InvalidDraftValue {
entity_type: entity,
attribute,
expected: "finite measure or null",
});
}
}
let mut fields = vec![(
"Name",
name.map_or(Value::Null, |text| Value::Text(text.into())),
)];
fields.extend(values);
Ok(tx.create(build_named(schema, entity, fields)?))
}
fn measure(value: Option<f64>) -> Value {
value.map_or(Value::Null, Value::Real)
}