1use ifc_model::{Entity, EntityId, Transaction, Value};
7
8use super::{finite, invalid};
9use crate::error::AlignmentError;
10use crate::slot;
11
12#[derive(Debug, Clone, Copy)]
17pub struct HorizontalSegmentDraft {
18 pub start_point: EntityId,
20 pub start_direction: f64,
22 pub start_radius: f64,
24 pub end_radius: f64,
26 pub segment_length: f64,
28 pub gravity_center_line_height: Option<f64>,
30 pub predefined_type: &'static str,
32}
33
34const HSEG: &str = "IFCALIGNMENTHORIZONTALSEGMENT";
35
36pub fn horizontal_segment(
44 tx: &mut Transaction,
45 draft: &HorizontalSegmentDraft,
46) -> Result<EntityId, AlignmentError> {
47 finite(HSEG, "StartDirection", draft.start_direction)?;
48 finite(HSEG, "StartRadiusOfCurvature", draft.start_radius)?;
49 finite(HSEG, "EndRadiusOfCurvature", draft.end_radius)?;
50 finite(HSEG, "SegmentLength", draft.segment_length)?;
51 if let Some(height) = draft.gravity_center_line_height {
52 finite(HSEG, "GravityCenterLineHeight", height)?;
53 }
54 if draft.segment_length < 0.0 {
55 return Err(invalid(
56 HSEG,
57 "SegmentLength",
58 "a segment cannot be shorter than nothing",
59 ));
60 }
61 if draft.predefined_type == "LINE" && (draft.start_radius != 0.0 || draft.end_radius != 0.0) {
62 return Err(invalid(
63 HSEG,
64 "PredefinedType",
65 "LINE requires zero start and end radii",
66 ));
67 }
68 let mut attrs = vec![Value::Null; slot::horizontal::PREDEFINED_TYPE + 1];
69 attrs[slot::horizontal::START_POINT] = Value::Ref(draft.start_point);
70 attrs[slot::horizontal::START_DIRECTION] = Value::Real(draft.start_direction);
71 attrs[slot::horizontal::START_RADIUS] = Value::Real(draft.start_radius);
72 attrs[slot::horizontal::END_RADIUS] = Value::Real(draft.end_radius);
73 attrs[slot::horizontal::SEGMENT_LENGTH] = Value::Real(draft.segment_length);
74 if let Some(height) = draft.gravity_center_line_height {
75 attrs[slot::horizontal::GRAVITY_CENTER_LINE_HEIGHT] = Value::Real(height);
76 }
77 attrs[slot::horizontal::PREDEFINED_TYPE] = Value::Enum(draft.predefined_type.into());
78 Ok(tx.create(Entity::new(HSEG, attrs)))
79}
80
81#[derive(Debug, Clone, Copy)]
83pub struct VerticalSegmentDraft {
84 pub start_dist_along: f64,
86 pub horizontal_length: f64,
88 pub start_height: f64,
90 pub start_gradient: f64,
92 pub end_gradient: f64,
94 pub radius_of_curvature: Option<f64>,
96 pub predefined_type: &'static str,
98}
99
100const VSEG: &str = "IFCALIGNMENTVERTICALSEGMENT";
101
102pub fn vertical_segment(
111 tx: &mut Transaction,
112 draft: &VerticalSegmentDraft,
113) -> Result<EntityId, AlignmentError> {
114 finite(VSEG, "StartDistAlong", draft.start_dist_along)?;
115 finite(VSEG, "HorizontalLength", draft.horizontal_length)?;
116 finite(VSEG, "StartHeight", draft.start_height)?;
117 finite(VSEG, "StartGradient", draft.start_gradient)?;
118 finite(VSEG, "EndGradient", draft.end_gradient)?;
119 if let Some(radius) = draft.radius_of_curvature {
120 finite(VSEG, "RadiusOfCurvature", radius)?;
121 }
122 if draft.horizontal_length < 0.0 {
123 return Err(invalid(
124 VSEG,
125 "HorizontalLength",
126 "a segment cannot be shorter than nothing",
127 ));
128 }
129 let needs_radius = matches!(draft.predefined_type, "CIRCULARARC" | "PARABOLICARC");
130 if needs_radius != draft.radius_of_curvature.is_some() {
131 return Err(invalid(
132 VSEG,
133 "RadiusOfCurvature",
134 "radius is required exactly for circular and parabolic vertical segments",
135 ));
136 }
137 let mut attrs = vec![Value::Null; slot::vertical::PREDEFINED_TYPE + 1];
138 attrs[slot::vertical::START_DIST_ALONG] = Value::Real(draft.start_dist_along);
139 attrs[slot::vertical::HORIZONTAL_LENGTH] = Value::Real(draft.horizontal_length);
140 attrs[slot::vertical::START_HEIGHT] = Value::Real(draft.start_height);
141 attrs[slot::vertical::START_GRADIENT] = Value::Real(draft.start_gradient);
142 attrs[slot::vertical::END_GRADIENT] = Value::Real(draft.end_gradient);
143 if let Some(radius) = draft.radius_of_curvature {
144 attrs[slot::vertical::RADIUS_OF_CURVATURE] = Value::Real(radius);
145 }
146 attrs[slot::vertical::PREDEFINED_TYPE] = Value::Enum(draft.predefined_type.into());
147 Ok(tx.create(Entity::new(VSEG, attrs)))
148}
149
150#[derive(Debug, Clone, Copy)]
152pub struct CantSegmentDraft {
153 pub start_dist_along: f64,
155 pub horizontal_length: f64,
157 pub start_cant_left: f64,
159 pub end_cant_left: Option<f64>,
161 pub start_cant_right: f64,
163 pub end_cant_right: Option<f64>,
165 pub predefined_type: &'static str,
167}
168
169const CSEG: &str = "IFCALIGNMENTCANTSEGMENT";
170
171pub fn cant_segment(
179 tx: &mut Transaction,
180 draft: &CantSegmentDraft,
181) -> Result<EntityId, AlignmentError> {
182 finite(CSEG, "StartDistAlong", draft.start_dist_along)?;
183 finite(CSEG, "HorizontalLength", draft.horizontal_length)?;
184 finite(CSEG, "StartCantLeft", draft.start_cant_left)?;
185 finite(CSEG, "StartCantRight", draft.start_cant_right)?;
186 if let Some(value) = draft.end_cant_left {
187 finite(CSEG, "EndCantLeft", value)?;
188 }
189 if let Some(value) = draft.end_cant_right {
190 finite(CSEG, "EndCantRight", value)?;
191 }
192 if draft.horizontal_length < 0.0 {
193 return Err(invalid(
194 CSEG,
195 "HorizontalLength",
196 "a segment cannot be shorter than nothing",
197 ));
198 }
199 if draft.end_cant_left.is_some() != draft.end_cant_right.is_some() {
200 return Err(invalid(
201 CSEG,
202 "EndCantLeft",
203 "left and right end cant must both be supplied or both omitted",
204 ));
205 }
206 let mut attrs = vec![Value::Null; slot::cant::PREDEFINED_TYPE + 1];
207 attrs[slot::cant::START_DIST_ALONG] = Value::Real(draft.start_dist_along);
208 attrs[slot::cant::HORIZONTAL_LENGTH] = Value::Real(draft.horizontal_length);
209 attrs[slot::cant::START_CANT_LEFT] = Value::Real(draft.start_cant_left);
210 if let Some(value) = draft.end_cant_left {
211 attrs[slot::cant::END_CANT_LEFT] = Value::Real(value);
212 }
213 attrs[slot::cant::START_CANT_RIGHT] = Value::Real(draft.start_cant_right);
214 if let Some(value) = draft.end_cant_right {
215 attrs[slot::cant::END_CANT_RIGHT] = Value::Real(value);
216 }
217 attrs[slot::cant::PREDEFINED_TYPE] = Value::Enum(draft.predefined_type.into());
218 Ok(tx.create(Entity::new(CSEG, attrs)))
219}