1use axiolid_core::Scalar;
4
5#[derive(Debug, Clone, Copy, PartialEq)]
7#[cfg_attr(
8 feature = "serde",
9 derive(serde::Serialize, serde::Deserialize),
10 serde(deny_unknown_fields)
11)]
12pub struct RectangleProfile {
13 pub x: Scalar,
15 pub y: Scalar,
17 pub thickness: Option<Scalar>,
19 pub outer_radius: Option<Scalar>,
21 pub inner_radius: Option<Scalar>,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq)]
27#[cfg_attr(
28 feature = "serde",
29 derive(serde::Serialize, serde::Deserialize),
30 serde(deny_unknown_fields)
31)]
32pub struct CircleProfile {
33 pub radius: Scalar,
35 pub thickness: Option<Scalar>,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq)]
41#[cfg_attr(
42 feature = "serde",
43 derive(serde::Serialize, serde::Deserialize),
44 serde(deny_unknown_fields)
45)]
46pub struct EllipseProfile {
47 pub semi_axis_x: Scalar,
49 pub semi_axis_y: Scalar,
51}
52
53#[non_exhaustive]
65#[derive(Debug, Clone, PartialEq)]
66#[cfg_attr(
67 feature = "serde",
68 derive(serde::Serialize, serde::Deserialize),
69 serde(deny_unknown_fields)
70)]
71pub enum SectionProfile {
72 I {
74 depth: Scalar,
75 width: Scalar,
76 web_thickness: Scalar,
77 flange_thickness: Scalar,
78 fillet_radius: Option<Scalar>,
80 flange_edge_radius: Option<Scalar>,
82 flange_slope: Option<Scalar>,
84 },
85 AsymmetricI {
91 depth: Scalar,
92 web_thickness: Scalar,
93 bottom_flange_width: Scalar,
94 bottom_flange_thickness: Scalar,
95 bottom_fillet_radius: Option<Scalar>,
96 bottom_flange_edge_radius: Option<Scalar>,
97 bottom_flange_slope: Option<Scalar>,
98 top_flange_width: Scalar,
99 top_flange_thickness: Option<Scalar>,
101 top_fillet_radius: Option<Scalar>,
102 top_flange_edge_radius: Option<Scalar>,
103 top_flange_slope: Option<Scalar>,
104 },
105 L {
107 depth: Scalar,
108 width: Option<Scalar>,
110 thickness: Scalar,
111 fillet_radius: Option<Scalar>,
112 edge_radius: Option<Scalar>,
113 leg_slope: Option<Scalar>,
115 },
116 T {
118 depth: Scalar,
119 flange_width: Scalar,
120 web_thickness: Scalar,
121 flange_thickness: Scalar,
122 fillet_radius: Option<Scalar>,
123 flange_edge_radius: Option<Scalar>,
124 web_edge_radius: Option<Scalar>,
125 web_slope: Option<Scalar>,
126 flange_slope: Option<Scalar>,
127 },
128 U {
130 depth: Scalar,
131 flange_width: Scalar,
132 web_thickness: Scalar,
133 flange_thickness: Scalar,
134 fillet_radius: Option<Scalar>,
135 edge_radius: Option<Scalar>,
136 flange_slope: Option<Scalar>,
137 },
138 C {
140 depth: Scalar,
141 width: Scalar,
142 wall_thickness: Scalar,
143 girth: Scalar,
145 internal_fillet_radius: Option<Scalar>,
146 },
147 Z {
149 depth: Scalar,
150 flange_width: Scalar,
151 web_thickness: Scalar,
152 flange_thickness: Scalar,
153 fillet_radius: Option<Scalar>,
154 edge_radius: Option<Scalar>,
155 },
156 Trapezium {
158 bottom_x: Scalar,
159 top_x: Scalar,
160 y: Scalar,
161 top_offset: Scalar,
163 },
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
177 fn an_asymmetric_i_is_a_distinct_variant_from_a_symmetric_one() {
178 let symmetric = SectionProfile::I {
179 depth: 0.4,
180 width: 0.3,
181 web_thickness: 0.011,
182 flange_thickness: 0.019,
183 fillet_radius: Some(0.021),
184 flange_edge_radius: None,
185 flange_slope: None,
186 };
187 let asymmetric = SectionProfile::AsymmetricI {
188 depth: 0.4,
189 web_thickness: 0.011,
190 bottom_flange_width: 0.3,
191 bottom_flange_thickness: 0.019,
192 bottom_fillet_radius: Some(0.021),
193 bottom_flange_edge_radius: None,
194 bottom_flange_slope: None,
195 top_flange_width: 0.2,
197 top_flange_thickness: Some(0.019),
198 top_fillet_radius: Some(0.021),
199 top_flange_edge_radius: None,
200 top_flange_slope: None,
201 };
202 assert_ne!(symmetric, asymmetric);
203 }
204
205 #[test]
211 fn an_absent_fillet_differs_from_a_declared_zero() {
212 let make = |fillet| SectionProfile::U {
213 depth: 0.3,
214 flange_width: 0.1,
215 web_thickness: 0.0075,
216 flange_thickness: 0.0125,
217 fillet_radius: fillet,
218 edge_radius: None,
219 flange_slope: None,
220 };
221 assert_ne!(make(None), make(Some(0.0)));
222 assert_eq!(make(Some(0.015)), make(Some(0.015)));
223 }
224
225 #[test]
227 fn a_section_wraps_into_a_profile_without_loss() {
228 let section = SectionProfile::L {
229 depth: 0.1,
230 width: None,
231 thickness: 0.008,
232 fillet_radius: Some(0.012),
233 edge_radius: Some(0.006),
234 leg_slope: None,
235 };
236 let profile = crate::Profile::Section(section.clone());
237 match profile {
238 crate::Profile::Section(round_tripped) => assert_eq!(section, round_tripped),
239 other => panic!("expected a section, got {other:?}"),
240 }
241 }
242}