use ifc_model::{Entity, EntityId, Transaction, Value};
use crate::curve::polyline::polyline_slot;
use crate::error::GeometryError;
use crate::slots::profile_slot as slot;
use super::{invalid, refs, require_finite};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProfileType {
Area,
Curve,
}
impl ProfileType {
pub(super) fn token(self) -> &'static str {
match self {
Self::Area => "AREA",
Self::Curve => "CURVE",
}
}
}
pub fn polyline(tx: &mut Transaction, points: &[EntityId]) -> Result<EntityId, GeometryError> {
if points.len() < 2 {
return Err(invalid(
"IFCPOLYLINE",
"Points",
format!("expected at least 2 points, got {}", points.len()),
));
}
let mut attrs = vec![Value::Null];
attrs[polyline_slot::POINTS] = refs(points);
Ok(tx.create(Entity::new("IFCPOLYLINE", attrs)))
}
pub fn rectangle_profile(
tx: &mut Transaction,
name: Option<&str>,
position: Option<EntityId>,
x_dim: f64,
y_dim: f64,
) -> Result<EntityId, GeometryError> {
positive("IFCRECTANGLEPROFILEDEF", "XDim", x_dim)?;
positive("IFCRECTANGLEPROFILEDEF", "YDim", y_dim)?;
let mut attrs = vec![Value::Null; 5];
attrs[slot::PROFILE_TYPE] = Value::Enum(ProfileType::Area.token().into());
attrs[slot::PROFILE_NAME] = name.map_or(Value::Null, |n| Value::Text(n.into()));
attrs[slot::POSITION] = position.map_or(Value::Null, Value::Ref);
attrs[slot::X_DIM] = Value::Real(x_dim);
attrs[slot::Y_DIM] = Value::Real(y_dim);
Ok(tx.create(Entity::new("IFCRECTANGLEPROFILEDEF", attrs)))
}
pub fn circle_profile(
tx: &mut Transaction,
name: Option<&str>,
position: Option<EntityId>,
radius: f64,
) -> Result<EntityId, GeometryError> {
positive("IFCCIRCLEPROFILEDEF", "Radius", radius)?;
let mut attrs = vec![Value::Null; 4];
attrs[slot::PROFILE_TYPE] = Value::Enum(ProfileType::Area.token().into());
attrs[slot::PROFILE_NAME] = name.map_or(Value::Null, |n| Value::Text(n.into()));
attrs[slot::POSITION] = position.map_or(Value::Null, Value::Ref);
attrs[slot::RADIUS] = Value::Real(radius);
Ok(tx.create(Entity::new("IFCCIRCLEPROFILEDEF", attrs)))
}
pub fn arbitrary_closed_profile(
tx: &mut Transaction,
name: Option<&str>,
outer_curve: EntityId,
) -> EntityId {
let mut attrs = vec![Value::Null; 3];
attrs[slot::PROFILE_TYPE] = Value::Enum(ProfileType::Area.token().into());
attrs[slot::PROFILE_NAME] = name.map_or(Value::Null, |n| Value::Text(n.into()));
attrs[slot::OUTER_CURVE] = Value::Ref(outer_curve);
tx.create(Entity::new("IFCARBITRARYCLOSEDPROFILEDEF", attrs))
}
fn positive(
type_name: &'static str,
attribute: &'static str,
value: f64,
) -> Result<(), GeometryError> {
require_finite(type_name, attribute, &[value])?;
if value <= 0.0 {
return Err(invalid(
type_name,
attribute,
format!("expected a positive length, got {value}"),
));
}
Ok(())
}