use ifc_model::{Entity, EntityId, Transaction, Value};
use crate::error::GeometryError;
use crate::slots::profile_slot as base;
use crate::slots::section_slot as slot;
use super::super::require_finite;
use super::{
non_negative, parameterized, positive, put_opt, rule, ProfileHeader, RectangleHollowDims,
RectangleHollowFillets, TrapeziumDims,
};
pub fn ellipse_profile(
tx: &mut Transaction,
header: ProfileHeader<'_>,
semi_axis_1: f64,
semi_axis_2: f64,
) -> Result<EntityId, GeometryError> {
const T: &str = "IFCELLIPSEPROFILEDEF";
positive(T, "SemiAxis1", semi_axis_1)?;
positive(T, "SemiAxis2", semi_axis_2)?;
let mut attrs = parameterized(T, 5, header);
attrs[slot::E_SEMI1] = Value::Real(semi_axis_1);
attrs[slot::E_SEMI2] = Value::Real(semi_axis_2);
Ok(tx.create(Entity::new(T, attrs)))
}
pub fn trapezium_profile(
tx: &mut Transaction,
header: ProfileHeader<'_>,
dims: TrapeziumDims,
) -> Result<EntityId, GeometryError> {
const T: &str = "IFCTRAPEZIUMPROFILEDEF";
let TrapeziumDims {
bottom_x_dim,
top_x_dim,
y_dim,
top_x_offset,
} = dims;
positive(T, "BottomXDim", bottom_x_dim)?;
positive(T, "TopXDim", top_x_dim)?;
positive(T, "YDim", y_dim)?;
require_finite(T, "TopXOffset", &[top_x_offset])?;
let mut attrs = parameterized(T, 7, header);
attrs[slot::TZ_BOTTOM] = Value::Real(bottom_x_dim);
attrs[slot::TZ_TOP] = Value::Real(top_x_dim);
attrs[slot::TZ_Y] = Value::Real(y_dim);
attrs[slot::TZ_OFFSET] = Value::Real(top_x_offset);
Ok(tx.create(Entity::new(T, attrs)))
}
pub fn rectangle_hollow_profile(
tx: &mut Transaction,
header: ProfileHeader<'_>,
dims: RectangleHollowDims,
fillets: RectangleHollowFillets,
) -> Result<EntityId, GeometryError> {
const T: &str = "IFCRECTANGLEHOLLOWPROFILEDEF";
let RectangleHollowDims {
x_dim,
y_dim,
wall_thickness,
} = dims;
let inner_fillet_radius = fillets.inner;
let outer_fillet_radius = fillets.outer;
positive(T, "XDim", x_dim)?;
positive(T, "YDim", y_dim)?;
positive(T, "WallThickness", wall_thickness)?;
non_negative(T, "InnerFilletRadius", inner_fillet_radius)?;
non_negative(T, "OuterFilletRadius", outer_fillet_radius)?;
let half_x = x_dim / 2.0;
let half_y = y_dim / 2.0;
if wall_thickness >= half_x || wall_thickness >= half_y {
return Err(rule(
T,
"WallThickness",
format!("{wall_thickness} is not below half of both dimensions ({half_x}, {half_y})"),
));
}
if let Some(inner) = inner_fillet_radius {
let bore_x = half_x - wall_thickness;
let bore_y = half_y - wall_thickness;
if inner > bore_x || inner > bore_y {
return Err(rule(
T,
"InnerFilletRadius",
format!("{inner} exceeds the bore the wall leaves ({bore_x} by {bore_y})"),
));
}
}
if let Some(outer) = outer_fillet_radius {
if outer > half_x || outer > half_y {
return Err(rule(
T,
"OuterFilletRadius",
format!("{outer} exceeds a half-dimension ({half_x}, {half_y})"),
));
}
}
let mut attrs = parameterized(T, 8, header);
attrs[base::X_DIM] = Value::Real(x_dim);
attrs[base::Y_DIM] = Value::Real(y_dim);
attrs[base::RECT_WALL_THICKNESS] = Value::Real(wall_thickness);
put_opt(&mut attrs, base::RECT_INNER_RADIUS, inner_fillet_radius);
put_opt(&mut attrs, base::RECT_OUTER_RADIUS, outer_fillet_radius);
Ok(tx.create(Entity::new(T, attrs)))
}
pub fn circle_hollow_profile(
tx: &mut Transaction,
header: ProfileHeader<'_>,
radius: f64,
wall_thickness: f64,
) -> Result<EntityId, GeometryError> {
const T: &str = "IFCCIRCLEHOLLOWPROFILEDEF";
positive(T, "Radius", radius)?;
positive(T, "WallThickness", wall_thickness)?;
if wall_thickness >= radius {
return Err(rule(
T,
"WallThickness",
format!("{wall_thickness} is not below the Radius {radius}"),
));
}
let mut attrs = parameterized(T, 5, header);
attrs[base::RADIUS] = Value::Real(radius);
attrs[base::CIRCLE_WALL_THICKNESS] = Value::Real(wall_thickness);
Ok(tx.create(Entity::new(T, attrs)))
}