use ifc_model::{Entity, EntityId, Transaction, Value};
use super::{finite, guid, invalid};
use crate::error::AlignmentError;
use crate::slot;
pub fn cartesian_point(
tx: &mut Transaction,
coordinates: &[f64],
) -> Result<EntityId, AlignmentError> {
if coordinates.is_empty() || coordinates.len() > 3 {
return Err(invalid(
"IFCCARTESIANPOINT",
"Coordinates",
format!("{} coordinates", coordinates.len()),
));
}
for value in coordinates {
finite("IFCCARTESIANPOINT", "Coordinates", *value)?;
}
Ok(tx.create(Entity::new(
"IFCCARTESIANPOINT",
vec![Value::List(
coordinates.iter().copied().map(Value::Real).collect(),
)],
)))
}
pub fn point_by_distance(
tx: &mut Transaction,
distance_along: f64,
offsets: (Option<f64>, Option<f64>, Option<f64>),
basis_curve: EntityId,
) -> Result<EntityId, AlignmentError> {
finite(
"IFCPOINTBYDISTANCEEXPRESSION",
"DistanceAlong",
distance_along,
)?;
let (lateral, vertical, longitudinal) = offsets;
for (name, value) in [
("OffsetLateral", lateral),
("OffsetVertical", vertical),
("OffsetLongitudinal", longitudinal),
] {
if let Some(value) = value {
finite("IFCPOINTBYDISTANCEEXPRESSION", name, value)?;
}
}
let mut attrs = vec![Value::Null; slot::point_by_distance::ARITY];
attrs[slot::point_by_distance::DISTANCE_ALONG] = Value::Real(distance_along);
attrs[slot::point_by_distance::OFFSET_LATERAL] = lateral.map_or(Value::Null, Value::Real);
attrs[slot::point_by_distance::OFFSET_VERTICAL] = vertical.map_or(Value::Null, Value::Real);
attrs[slot::point_by_distance::OFFSET_LONGITUDINAL] =
longitudinal.map_or(Value::Null, Value::Real);
attrs[slot::point_by_distance::BASIS_CURVE] = Value::Ref(basis_curve);
Ok(tx.create(Entity::new("IFCPOINTBYDISTANCEEXPRESSION", attrs)))
}
pub fn axis2_placement_linear(
tx: &mut Transaction,
location: EntityId,
axis: Option<EntityId>,
ref_direction: Option<EntityId>,
) -> Result<EntityId, AlignmentError> {
let mut attrs = vec![Value::Null; slot::axis2_placement_linear::ARITY];
attrs[slot::axis2_placement_linear::LOCATION] = Value::Ref(location);
attrs[slot::axis2_placement_linear::AXIS] = axis.map_or(Value::Null, Value::Ref);
attrs[slot::axis2_placement_linear::REF_DIRECTION] =
ref_direction.map_or(Value::Null, Value::Ref);
Ok(tx.create(Entity::new("IFCAXIS2PLACEMENTLINEAR", attrs)))
}
pub fn linear_placement(
tx: &mut Transaction,
relative_placement: EntityId,
placement_rel_to: Option<EntityId>,
cartesian_position: Option<EntityId>,
) -> Result<EntityId, AlignmentError> {
let mut attrs = vec![Value::Null; slot::linear_placement::ARITY];
attrs[slot::linear_placement::PLACEMENT_REL_TO] =
placement_rel_to.map_or(Value::Null, Value::Ref);
attrs[slot::linear_placement::RELATIVE_PLACEMENT] = Value::Ref(relative_placement);
attrs[slot::linear_placement::CARTESIAN_POSITION] =
cartesian_position.map_or(Value::Null, Value::Ref);
Ok(tx.create(Entity::new("IFCLINEARPLACEMENT", attrs)))
}
pub fn referent(
tx: &mut Transaction,
global_id: &str,
name: Option<&str>,
predefined_type: Option<&str>,
placement: Option<EntityId>,
) -> Result<EntityId, AlignmentError> {
let mut attrs = vec![Value::Null; slot::referent::ARITY];
attrs[slot::product::GLOBAL_ID] = guid("IFCREFERENT", global_id)?;
if let Some(name) = name {
attrs[slot::product::NAME] = Value::Text(name.into());
}
attrs[slot::product::OBJECT_PLACEMENT] = placement.map_or(Value::Null, Value::Ref);
attrs[slot::referent::PREDEFINED_TYPE] =
predefined_type.map_or(Value::Null, |token| Value::Enum(token.into()));
Ok(tx.create(Entity::new("IFCREFERENT", attrs)))
}
pub fn stationing(
tx: &mut Transaction,
pset_global_id: &str,
rel_global_id: &str,
referent: EntityId,
station: f64,
incoming_station: Option<f64>,
has_increasing_station: Option<bool>,
) -> Result<EntityId, AlignmentError> {
finite("IFCPROPERTYSET", "Station", station)?;
if let Some(value) = incoming_station {
finite("IFCPROPERTYSET", "IncomingStation", value)?;
}
let pset_guid = guid("IFCPROPERTYSET", pset_global_id)?;
let rel_guid = guid("IFCRELDEFINESBYPROPERTIES", rel_global_id)?;
let mut properties = vec![single_value(tx, "Station", Value::Real(station))];
if let Some(value) = incoming_station {
properties.push(single_value(tx, "IncomingStation", Value::Real(value)));
}
if let Some(value) = has_increasing_station {
properties.push(single_value(tx, "HasIncreasingStation", Value::Bool(value)));
}
let mut pset_attrs = vec![Value::Null; 5];
pset_attrs[0] = pset_guid;
pset_attrs[2] = Value::Text("Pset_Stationing".into());
pset_attrs[4] = Value::List(properties.into_iter().map(Value::Ref).collect());
let pset = tx.create(Entity::new("IFCPROPERTYSET", pset_attrs));
let mut rel_attrs = vec![Value::Null; 6];
rel_attrs[0] = rel_guid;
rel_attrs[4] = Value::List(vec![Value::Ref(referent)]);
rel_attrs[5] = Value::Ref(pset);
Ok(tx.create(Entity::new("IFCRELDEFINESBYPROPERTIES", rel_attrs)))
}
fn single_value(tx: &mut Transaction, name: &str, value: Value) -> EntityId {
let mut attrs = vec![Value::Null; 4];
attrs[0] = Value::Text(name.into());
attrs[2] = value;
tx.create(Entity::new("IFCPROPERTYSINGLEVALUE", attrs))
}