use ifc_model::{EntityId, Transaction, Value};
use ifc_schema::Schema;
use super::{build_named, stiffness};
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> {
stage_boundary_condition_in(tx, ifc_schema::ifc4(), kind, draft)
}
pub fn stage_boundary_condition_in(
tx: &mut Transaction,
schema: &Schema,
kind: BoundaryConditionKind,
draft: BoundaryConditionDraft<'_>,
) -> StructuralResult<EntityId> {
let entity_type = stiffness::entity_name(kind);
if kind == BoundaryConditionKind::Face && has_any(draft.rotational) {
return Err(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(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(StructuralError::InvalidDraftValue {
entity_type,
attribute: axis,
expected: "a finite stiffness measure",
});
}
}
}
let mut fields = vec![("Name", optional_text(draft.name))];
fields.extend(stiffness::triple(schema, kind, true, draft.translational)?);
if kind != BoundaryConditionKind::Face {
fields.extend(stiffness::triple(schema, kind, false, draft.rotational)?);
}
if kind == BoundaryConditionKind::NodeWarping {
fields.push(stiffness::warping(schema, draft.warping)?);
}
Ok(tx.create(build_named(schema, entity_type, fields)?))
}
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)
}