use ifc_model::{Entity, EntityId, Model, Transaction, Value};
use super::release::bind;
use crate::authoring::{invalid, SystemAuthoringResult};
pub(crate) mod distribution_slot {
pub const GLOBAL_ID: usize = 0;
pub const NAME: usize = 2;
pub const DESCRIPTION: usize = 3;
pub const OBJECT_PLACEMENT: usize = 5;
pub const REPRESENTATION: usize = 6;
pub const TAG: usize = 7;
pub const WIDTH: usize = 8;
}
pub(crate) mod zone_slot {
pub const GLOBAL_ID: usize = 0;
pub const NAME: usize = 2;
pub const DESCRIPTION: usize = 3;
pub const LONG_NAME: usize = 5;
pub const WIDTH: usize = 6;
}
pub(crate) mod spatial_zone_slot {
pub const GLOBAL_ID: usize = 0;
pub const NAME: usize = 2;
pub const DESCRIPTION: usize = 3;
pub const OBJECT_PLACEMENT: usize = 5;
pub const REPRESENTATION: usize = 6;
pub const LONG_NAME: usize = 7;
pub const PREDEFINED_TYPE: usize = 8;
pub const WIDTH: usize = 9;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DistributionElementKind {
DistributionElement,
EnergyConversionDevice,
FlowController,
FlowFitting,
FlowMovingDevice,
FlowSegment,
FlowStorageDevice,
FlowTerminal,
FlowTreatmentDevice,
}
impl DistributionElementKind {
pub fn type_name(self) -> &'static str {
match self {
Self::DistributionElement => "IFCDISTRIBUTIONELEMENT",
Self::EnergyConversionDevice => "IFCENERGYCONVERSIONDEVICE",
Self::FlowController => "IFCFLOWCONTROLLER",
Self::FlowFitting => "IFCFLOWFITTING",
Self::FlowMovingDevice => "IFCFLOWMOVINGDEVICE",
Self::FlowSegment => "IFCFLOWSEGMENT",
Self::FlowStorageDevice => "IFCFLOWSTORAGEDEVICE",
Self::FlowTerminal => "IFCFLOWTERMINAL",
Self::FlowTreatmentDevice => "IFCFLOWTREATMENTDEVICE",
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ElementAttributes<'a> {
pub name: Option<&'a str>,
pub description: Option<&'a str>,
pub placement: Option<EntityId>,
pub representation: Option<EntityId>,
pub tag: Option<&'a str>,
}
fn text(value: Option<&str>) -> Value {
value.map_or(Value::Null, |v| Value::Text(v.into()))
}
fn reference(value: Option<EntityId>) -> Value {
value.map_or(Value::Null, Value::Ref)
}
pub fn create_distribution_element(
tx: &mut Transaction,
kind: DistributionElementKind,
global_id: &str,
attributes: ElementAttributes<'_>,
) -> SystemAuthoringResult<EntityId> {
let type_name = kind.type_name();
super::guid(type_name, global_id)?;
let mut attrs = vec![Value::Null; distribution_slot::WIDTH];
attrs[distribution_slot::GLOBAL_ID] = Value::Text(global_id.into());
attrs[distribution_slot::NAME] = text(attributes.name);
attrs[distribution_slot::DESCRIPTION] = text(attributes.description);
attrs[distribution_slot::OBJECT_PLACEMENT] = reference(attributes.placement);
attrs[distribution_slot::REPRESENTATION] = reference(attributes.representation);
attrs[distribution_slot::TAG] = text(attributes.tag);
Ok(tx.create(Entity::new(type_name, attrs)))
}
pub fn create_zone(
tx: &mut Transaction,
global_id: &str,
name: Option<&str>,
description: Option<&str>,
long_name: Option<&str>,
) -> SystemAuthoringResult<EntityId> {
super::guid("IFCZONE", global_id)?;
let mut attrs = vec![Value::Null; zone_slot::WIDTH];
attrs[zone_slot::GLOBAL_ID] = Value::Text(global_id.into());
attrs[zone_slot::NAME] = text(name);
attrs[zone_slot::DESCRIPTION] = text(description);
attrs[zone_slot::LONG_NAME] = text(long_name);
Ok(tx.create(Entity::new("IFCZONE", attrs)))
}
pub fn create_spatial_zone(
tx: &mut Transaction,
global_id: &str,
attributes: ElementAttributes<'_>,
long_name: Option<&str>,
predefined_type: Option<&str>,
object_type: Option<&str>,
) -> SystemAuthoringResult<EntityId> {
check_spatial_zone(global_id, predefined_type, object_type)?;
let mut attrs = vec![Value::Null; spatial_zone_slot::WIDTH];
attrs[spatial_zone_slot::GLOBAL_ID] = Value::Text(global_id.into());
attrs[spatial_zone_slot::NAME] = text(attributes.name);
attrs[spatial_zone_slot::DESCRIPTION] = text(attributes.description);
attrs[4] = text(object_type);
attrs[spatial_zone_slot::OBJECT_PLACEMENT] = reference(attributes.placement);
attrs[spatial_zone_slot::REPRESENTATION] = reference(attributes.representation);
attrs[spatial_zone_slot::LONG_NAME] = text(long_name);
attrs[spatial_zone_slot::PREDEFINED_TYPE] =
predefined_type.map_or(Value::Null, |t| Value::Enum(t.into()));
Ok(tx.create(Entity::new("IFCSPATIALZONE", attrs)))
}
fn check_spatial_zone(
global_id: &str,
predefined_type: Option<&str>,
object_type: Option<&str>,
) -> SystemAuthoringResult<()> {
super::guid("IFCSPATIALZONE", global_id)?;
if predefined_type == Some("USERDEFINED") && object_type.is_none() {
return Err(invalid(
"IFCSPATIALZONE",
"PredefinedType",
"USERDEFINED requires an ObjectType naming the kind",
));
}
Ok(())
}
pub fn create_distribution_element_with_owner_history(
tx: &mut Transaction,
model: &Model,
kind: DistributionElementKind,
global_id: &str,
attributes: ElementAttributes<'_>,
owner_history: EntityId,
) -> SystemAuthoringResult<EntityId> {
let release = bind(model)?;
let type_name = kind.type_name();
super::guid(type_name, global_id)?;
let values = vec![
("GlobalId", Value::Text(global_id.into())),
("Name", text(attributes.name)),
("Description", text(attributes.description)),
("ObjectPlacement", reference(attributes.placement)),
("Representation", reference(attributes.representation)),
("Tag", text(attributes.tag)),
];
release.stage(tx, model, type_name, values, owner_history)
}
pub fn create_zone_with_owner_history(
tx: &mut Transaction,
model: &Model,
global_id: &str,
name: Option<&str>,
description: Option<&str>,
long_name: Option<&str>,
owner_history: EntityId,
) -> SystemAuthoringResult<EntityId> {
let release = bind(model)?;
super::guid("IFCZONE", global_id)?;
let values = vec![
("GlobalId", Value::Text(global_id.into())),
("Name", text(name)),
("Description", text(description)),
("LongName", text(long_name)),
];
release.stage(tx, model, "IFCZONE", values, owner_history)
}
#[allow(clippy::too_many_arguments)]
pub fn create_spatial_zone_with_owner_history(
tx: &mut Transaction,
model: &Model,
global_id: &str,
attributes: ElementAttributes<'_>,
long_name: Option<&str>,
predefined_type: Option<&str>,
object_type: Option<&str>,
owner_history: EntityId,
) -> SystemAuthoringResult<EntityId> {
const ENTITY: &str = "IFCSPATIALZONE";
let release = bind(model)?;
release.require_entity(ENTITY)?;
check_spatial_zone(global_id, predefined_type, object_type)?;
let values = vec![
("GlobalId", Value::Text(global_id.into())),
("Name", text(attributes.name)),
("Description", text(attributes.description)),
("ObjectType", text(object_type)),
("ObjectPlacement", reference(attributes.placement)),
("Representation", reference(attributes.representation)),
("LongName", text(long_name)),
(
"PredefinedType",
predefined_type.map_or(Value::Null, |t| Value::Enum(t.into())),
),
];
release.stage(tx, model, ENTITY, values, owner_history)
}