use super::types::ProfileParameters;
use crate::error::GeometryResult;
use crate::slots::{profile_slot as slot, section_slot, Slots};
use crate::units::UnitScale;
fn len(
slots: &Slots<'_>,
index: usize,
name: &'static str,
units: &UnitScale,
) -> GeometryResult<f64> {
Ok(units.length(slots.req_f64(index, name)?))
}
fn opt_len(slots: &Slots<'_>, index: usize, units: &UnitScale) -> Option<f64> {
slots.opt_f64(index).map(|value| units.length(value))
}
fn opt_angle(slots: &Slots<'_>, index: usize, units: &UnitScale) -> Option<f64> {
slots.opt_f64(index).map(|value| units.angle(value))
}
pub(super) fn rectangle(
slots: &Slots<'_>,
units: &UnitScale,
rounded: bool,
) -> GeometryResult<ProfileParameters> {
let x_dim = len(slots, slot::X_DIM, "XDim", units)?;
let y_dim = len(slots, slot::Y_DIM, "YDim", units)?;
let rounding_radius = if rounded {
Some(len(
slots,
slot::ROUNDED_RECT_RADIUS,
"RoundingRadius",
units,
)?)
} else {
None
};
if x_dim <= 0.0 || y_dim <= 0.0 || rounding_radius.is_some_and(|radius| radius < 0.0) {
return Err(slots.degenerate("rectangle dimensions and radius must be non-negative"));
}
Ok(match rounding_radius {
Some(rounding_radius) => ProfileParameters::RoundedRectangle {
x_dim,
y_dim,
rounding_radius,
},
None => ProfileParameters::Rectangle { x_dim, y_dim },
})
}
pub(super) fn rectangle_hollow(
slots: &Slots<'_>,
units: &UnitScale,
) -> GeometryResult<ProfileParameters> {
let x_dim = len(slots, slot::X_DIM, "XDim", units)?;
let y_dim = len(slots, slot::Y_DIM, "YDim", units)?;
let wall_thickness = len(slots, slot::RECT_WALL_THICKNESS, "WallThickness", units)?;
if x_dim <= 0.0
|| y_dim <= 0.0
|| wall_thickness <= 0.0
|| 2.0 * wall_thickness >= x_dim.min(y_dim)
{
return Err(slots.degenerate("wall thickness consumes the rectangular section"));
}
Ok(ProfileParameters::RectangleHollow {
x_dim,
y_dim,
wall_thickness,
inner_fillet_radius: opt_len(slots, slot::RECT_INNER_RADIUS, units),
outer_fillet_radius: opt_len(slots, slot::RECT_OUTER_RADIUS, units),
})
}
pub(super) fn circle(
slots: &Slots<'_>,
units: &UnitScale,
hollow: bool,
) -> GeometryResult<ProfileParameters> {
let radius = len(slots, slot::RADIUS, "Radius", units)?;
let wall_thickness = if hollow {
Some(len(
slots,
slot::CIRCLE_WALL_THICKNESS,
"WallThickness",
units,
)?)
} else {
None
};
if radius <= 0.0 || wall_thickness.is_some_and(|wall| wall <= 0.0 || wall >= radius) {
return Err(slots.degenerate("circle radius or wall thickness is non-physical"));
}
Ok(match wall_thickness {
Some(wall_thickness) => ProfileParameters::CircleHollow {
radius,
wall_thickness,
},
None => ProfileParameters::Circle { radius },
})
}
pub(super) fn ellipse(slots: &Slots<'_>, units: &UnitScale) -> GeometryResult<ProfileParameters> {
Ok(ProfileParameters::Ellipse {
semi_axis_1: len(slots, section_slot::E_SEMI1, "SemiAxis1", units)?,
semi_axis_2: len(slots, section_slot::E_SEMI2, "SemiAxis2", units)?,
})
}
pub(super) fn i_shape(slots: &Slots<'_>, units: &UnitScale) -> GeometryResult<ProfileParameters> {
Ok(ProfileParameters::IShape {
overall_width: len(slots, section_slot::I_WIDTH, "OverallWidth", units)?,
overall_depth: len(slots, section_slot::I_DEPTH, "OverallDepth", units)?,
web_thickness: len(slots, section_slot::I_WEB, "WebThickness", units)?,
flange_thickness: len(slots, section_slot::I_FLANGE, "FlangeThickness", units)?,
fillet_radius: opt_len(slots, section_slot::I_FILLET, units),
flange_edge_radius: opt_len(slots, section_slot::I_EDGE, units),
flange_slope: opt_angle(slots, section_slot::I_SLOPE, units),
})
}
pub(super) fn asymmetric_i(
slots: &Slots<'_>,
units: &UnitScale,
ifc2x3: bool,
) -> GeometryResult<ProfileParameters> {
let (bottom_edge, bottom_slope, top_edge, top_slope) = if ifc2x3 {
(None, None, None, None)
} else {
(
opt_len(slots, section_slot::AI_BOTTOM_EDGE, units),
opt_angle(slots, section_slot::AI_BOTTOM_SLOPE, units),
opt_len(slots, section_slot::AI_TOP_EDGE, units),
opt_angle(slots, section_slot::AI_TOP_SLOPE, units),
)
};
Ok(ProfileParameters::AsymmetricIShape {
bottom_flange_width: len(
slots,
section_slot::AI_BOTTOM_WIDTH,
"BottomFlangeWidth",
units,
)?,
overall_depth: len(slots, section_slot::AI_DEPTH, "OverallDepth", units)?,
web_thickness: len(slots, section_slot::AI_WEB, "WebThickness", units)?,
bottom_flange_thickness: len(
slots,
section_slot::AI_BOTTOM_FLANGE,
"BottomFlangeThickness",
units,
)?,
bottom_flange_fillet_radius: opt_len(slots, section_slot::AI_BOTTOM_FILLET, units),
top_flange_width: len(slots, section_slot::AI_TOP_WIDTH, "TopFlangeWidth", units)?,
top_flange_thickness: opt_len(slots, section_slot::AI_TOP_FLANGE, units),
top_flange_fillet_radius: opt_len(slots, section_slot::AI_TOP_FILLET, units),
bottom_flange_edge_radius: bottom_edge,
bottom_flange_slope: bottom_slope,
top_flange_edge_radius: top_edge,
top_flange_slope: top_slope,
})
}
pub(super) fn l_shape(slots: &Slots<'_>, units: &UnitScale) -> GeometryResult<ProfileParameters> {
Ok(ProfileParameters::LShape {
depth: len(slots, section_slot::L_DEPTH, "Depth", units)?,
width: opt_len(slots, section_slot::L_WIDTH, units),
thickness: len(slots, section_slot::L_THICKNESS, "Thickness", units)?,
fillet_radius: opt_len(slots, section_slot::L_FILLET, units),
edge_radius: opt_len(slots, section_slot::L_EDGE, units),
leg_slope: opt_angle(slots, section_slot::L_SLOPE, units),
})
}
pub(super) fn t_shape(slots: &Slots<'_>, units: &UnitScale) -> GeometryResult<ProfileParameters> {
Ok(ProfileParameters::TShape {
depth: len(slots, section_slot::T_DEPTH, "Depth", units)?,
flange_width: len(slots, section_slot::T_WIDTH, "FlangeWidth", units)?,
web_thickness: len(slots, section_slot::T_WEB, "WebThickness", units)?,
flange_thickness: len(slots, section_slot::T_FLANGE, "FlangeThickness", units)?,
fillet_radius: opt_len(slots, section_slot::T_FILLET, units),
flange_edge_radius: opt_len(slots, section_slot::T_FLANGE_EDGE, units),
web_edge_radius: opt_len(slots, section_slot::T_WEB_EDGE, units),
web_slope: opt_angle(slots, section_slot::T_WEB_SLOPE, units),
flange_slope: opt_angle(slots, section_slot::T_FLANGE_SLOPE, units),
})
}
pub(super) fn u_shape(slots: &Slots<'_>, units: &UnitScale) -> GeometryResult<ProfileParameters> {
Ok(ProfileParameters::UShape {
depth: len(slots, section_slot::U_DEPTH, "Depth", units)?,
flange_width: len(slots, section_slot::U_WIDTH, "FlangeWidth", units)?,
web_thickness: len(slots, section_slot::U_WEB, "WebThickness", units)?,
flange_thickness: len(slots, section_slot::U_FLANGE, "FlangeThickness", units)?,
fillet_radius: opt_len(slots, section_slot::U_FILLET, units),
edge_radius: opt_len(slots, section_slot::U_EDGE, units),
flange_slope: opt_angle(slots, section_slot::U_SLOPE, units),
})
}
pub(super) fn c_shape(slots: &Slots<'_>, units: &UnitScale) -> GeometryResult<ProfileParameters> {
Ok(ProfileParameters::CShape {
depth: len(slots, section_slot::C_DEPTH, "Depth", units)?,
width: len(slots, section_slot::C_WIDTH, "Width", units)?,
wall_thickness: len(slots, section_slot::C_WALL, "WallThickness", units)?,
girth: len(slots, section_slot::C_GIRTH, "Girth", units)?,
internal_fillet_radius: opt_len(slots, section_slot::C_FILLET, units),
})
}
pub(super) fn z_shape(slots: &Slots<'_>, units: &UnitScale) -> GeometryResult<ProfileParameters> {
Ok(ProfileParameters::ZShape {
depth: len(slots, section_slot::Z_DEPTH, "Depth", units)?,
flange_width: len(slots, section_slot::Z_FLANGE_WIDTH, "FlangeWidth", units)?,
web_thickness: len(slots, section_slot::Z_WEB, "WebThickness", units)?,
flange_thickness: len(slots, section_slot::Z_FLANGE, "FlangeThickness", units)?,
fillet_radius: opt_len(slots, section_slot::Z_FILLET, units),
edge_radius: opt_len(slots, section_slot::Z_EDGE, units),
})
}
pub(super) fn trapezium(slots: &Slots<'_>, units: &UnitScale) -> GeometryResult<ProfileParameters> {
Ok(ProfileParameters::Trapezium {
bottom_x_dim: len(slots, section_slot::TZ_BOTTOM, "BottomXDim", units)?,
top_x_dim: len(slots, section_slot::TZ_TOP, "TopXDim", units)?,
y_dim: len(slots, section_slot::TZ_Y, "YDim", units)?,
top_x_offset: len(slots, section_slot::TZ_OFFSET, "TopXOffset", units)?,
})
}