use ifc_model::{Entity, EntityId, Transaction, Value};
use crate::PropertyResult;
use super::authoring::optional_text;
use super::predefined::{invalid, measure, root_slots, Measure};
const BAR_SURFACE: &[&str] = &["PLAIN", "TEXTURED"];
#[derive(Debug, Clone, Copy)]
pub struct ReinforcementBarDraft<'a> {
pub total_cross_section_area: f64,
pub steel_grade: &'a str,
pub bar_surface: Option<&'a str>,
pub effective_depth: Option<f64>,
pub nominal_bar_diameter: Option<f64>,
pub bar_count: Option<i64>,
}
pub fn add_reinforcement_bar_properties(
tx: &mut Transaction,
draft: ReinforcementBarDraft<'_>,
) -> PropertyResult<EntityId> {
const ENTITY: &str = "IFCREINFORCEMENTBARPROPERTIES";
if draft.steel_grade.trim().is_empty() {
return Err(invalid(ENTITY, "SteelGrade", "blank"));
}
if !draft.total_cross_section_area.is_finite() || draft.total_cross_section_area < 0.0 {
return Err(invalid(
ENTITY,
"TotalCrossSectionArea",
format!("{}", draft.total_cross_section_area),
));
}
if let Some(surface) = draft.bar_surface {
if !BAR_SURFACE.contains(&surface) {
return Err(invalid(ENTITY, "BarSurface", surface));
}
}
let mut attributes = vec![Value::Null; 6];
attributes[0] = Value::Real(draft.total_cross_section_area);
attributes[1] = Value::Text(draft.steel_grade.into());
attributes[2] = draft
.bar_surface
.map_or(Value::Null, |s| Value::Enum(s.into()));
attributes[3] = measure(
ENTITY,
"EffectiveDepth",
draft.effective_depth,
Measure::Length,
)?;
attributes[4] = measure(
ENTITY,
"NominalBarDiameter",
draft.nominal_bar_diameter,
Measure::Positive,
)?;
attributes[5] = draft.bar_count.map_or(Value::Null, Value::Integer);
Ok(tx.create(Entity::new(ENTITY, attributes)))
}
const SECTION_TYPE: &[&str] = &["TAPERED", "UNIFORM"];
pub fn add_section_properties(
tx: &mut Transaction,
section_type: &str,
start_profile: EntityId,
end_profile: Option<EntityId>,
) -> PropertyResult<EntityId> {
const ENTITY: &str = "IFCSECTIONPROPERTIES";
if !SECTION_TYPE.contains(§ion_type) {
return Err(invalid(ENTITY, "SectionType", section_type));
}
let mut attributes = vec![Value::Null; 3];
attributes[0] = Value::Enum(section_type.into());
attributes[1] = Value::Ref(start_profile);
attributes[2] = end_profile.map_or(Value::Null, Value::Ref);
Ok(tx.create(Entity::new(ENTITY, attributes)))
}
const BAR_ROLE: &[&str] = &[
"ANCHORING",
"EDGE",
"LIGATURE",
"MAIN",
"PUNCHING",
"RING",
"SHEAR",
"STUD",
"USERDEFINED",
"NOTDEFINED",
];
#[derive(Debug, Clone, Copy)]
pub struct SectionReinforcementDraft<'a> {
pub longitudinal_start_position: f64,
pub longitudinal_end_position: f64,
pub transverse_position: Option<f64>,
pub reinforcement_role: &'a str,
pub section_definition: EntityId,
pub cross_section_reinforcement_definitions: &'a [EntityId],
}
pub fn add_section_reinforcement_properties(
tx: &mut Transaction,
draft: SectionReinforcementDraft<'_>,
) -> PropertyResult<EntityId> {
const ENTITY: &str = "IFCSECTIONREINFORCEMENTPROPERTIES";
if !BAR_ROLE.contains(&draft.reinforcement_role) {
return Err(invalid(
ENTITY,
"ReinforcementRole",
draft.reinforcement_role,
));
}
if draft.cross_section_reinforcement_definitions.is_empty() {
return Err(invalid(
ENTITY,
"CrossSectionReinforcementDefinitions",
"empty",
));
}
let mut attributes = vec![Value::Null; 6];
attributes[0] = measure(
ENTITY,
"LongitudinalStartPosition",
Some(draft.longitudinal_start_position),
Measure::Length,
)?;
attributes[1] = measure(
ENTITY,
"LongitudinalEndPosition",
Some(draft.longitudinal_end_position),
Measure::Length,
)?;
attributes[2] = measure(
ENTITY,
"TransversePosition",
draft.transverse_position,
Measure::Length,
)?;
attributes[3] = Value::Enum(draft.reinforcement_role.into());
attributes[4] = Value::Ref(draft.section_definition);
attributes[5] = Value::List(
draft
.cross_section_reinforcement_definitions
.iter()
.copied()
.map(Value::Ref)
.collect(),
);
Ok(tx.create(Entity::new(ENTITY, attributes)))
}
pub fn add_reinforcement_definition_properties(
tx: &mut Transaction,
global_id: &str,
name: Option<&str>,
description: Option<&str>,
definition_type: Option<&str>,
reinforcement_section_definitions: &[EntityId],
) -> PropertyResult<EntityId> {
const ENTITY: &str = "IFCREINFORCEMENTDEFINITIONPROPERTIES";
if reinforcement_section_definitions.is_empty() {
return Err(invalid(ENTITY, "ReinforcementSectionDefinitions", "empty"));
}
let mut attributes = root_slots(ENTITY, global_id, name, description, 6)?;
attributes[4] = optional_text(definition_type);
attributes[5] = Value::List(
reinforcement_section_definitions
.iter()
.copied()
.map(Value::Ref)
.collect(),
);
Ok(tx.create(Entity::new(ENTITY, attributes)))
}
pub fn add_profile_properties(
tx: &mut Transaction,
name: Option<&str>,
description: Option<&str>,
properties: &[EntityId],
profile_definition: EntityId,
) -> PropertyResult<EntityId> {
const ENTITY: &str = "IFCPROFILEPROPERTIES";
if properties.is_empty() {
return Err(invalid(ENTITY, "Properties", "empty"));
}
let mut attributes = vec![Value::Null; 4];
attributes[0] = optional_text(name);
attributes[1] = optional_text(description);
attributes[2] = Value::List(properties.iter().copied().map(Value::Ref).collect());
attributes[3] = Value::Ref(profile_definition);
Ok(tx.create(Entity::new(ENTITY, attributes)))
}