ifc_lite_processing/scan_proposals/
options.rs1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
11pub enum ProposalSchema {
12 #[serde(rename = "IFC2X3")]
13 Ifc2x3,
14 #[serde(rename = "IFC4")]
15 Ifc4,
16 #[serde(rename = "IFC4X3")]
17 Ifc4x3,
18}
19
20#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
21#[serde(rename_all = "camelCase", deny_unknown_fields, default)]
22pub struct ScanProposalOptions {
23 pub scan_to_model: [f64; 16],
27 pub schema: ProposalSchema,
30 pub classification_angle_degrees: f64,
32 pub pairing_angle_degrees: f64,
34 pub min_wall_thickness_metres: f64,
37 pub max_wall_thickness_metres: f64,
39 pub default_wall_thickness_metres: f64,
41 pub min_wall_overlap_fraction: f64,
44 pub min_wall_length_metres: f64,
46 pub min_wall_height_metres: f64,
48 pub min_slab_area_square_metres: f64,
50 pub default_slab_thickness_metres: f64,
52 pub max_slab_thickness_metres: f64,
55 pub level_snap_metres: f64,
59 pub interior_point: Option<[f64; 3]>,
64}
65
66impl Default for ScanProposalOptions {
67 fn default() -> Self {
68 Self {
69 scan_to_model: [1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.],
70 schema: ProposalSchema::Ifc4,
71 classification_angle_degrees: 10.,
72 pairing_angle_degrees: 5.,
73 min_wall_thickness_metres: 0.05,
74 max_wall_thickness_metres: 0.6,
75 default_wall_thickness_metres: 0.2,
76 min_wall_overlap_fraction: 0.5,
77 min_wall_length_metres: 0.5,
78 min_wall_height_metres: 1.,
79 min_slab_area_square_metres: 1.,
80 default_slab_thickness_metres: 0.2,
81 max_slab_thickness_metres: 0.6,
82 level_snap_metres: 0.3,
83 interior_point: None,
84 }
85 }
86}
87
88#[derive(Debug, Clone)]
90pub(crate) struct Params {
91 pub schema: ProposalSchema,
92 pub sin_class: f64,
93 pub cos_class: f64,
94 pub cos_pair: f64,
95 pub min_thickness: f64,
96 pub max_thickness: f64,
97 pub wall_thickness: f64,
98 pub min_overlap: f64,
99 pub min_wall_length: f64,
100 pub min_wall_height: f64,
101 pub min_slab_area: f64,
102 pub slab_thickness: f64,
103 pub max_slab_thickness: f64,
104 pub snap: f64,
105 pub interior: Option<[f64; 3]>,
106}
107
108fn positive(value: f64, hi: f64) -> bool {
109 value > 0. && value <= hi
110}
111
112fn between(value: f64, lo: f64, hi: f64) -> bool {
114 value >= lo && value <= hi
115}
116
117impl ScanProposalOptions {
118 pub(crate) fn validate(&self) -> Result<Params, String> {
119 let o = self;
120 let fail = |what: &str| Err(format!("Scan proposal option {what}"));
121 if !positive(o.classification_angle_degrees, 45.) || !positive(o.pairing_angle_degrees, 45.) {
122 return fail("angles must be within (0, 45] degrees");
123 }
124 if !positive(o.min_wall_thickness_metres, 10.)
125 || !between(o.max_wall_thickness_metres, o.min_wall_thickness_metres, 10.)
126 || !positive(o.default_wall_thickness_metres, 10.)
127 || !positive(o.default_slab_thickness_metres, 10.)
128 || !between(o.max_slab_thickness_metres, o.min_wall_thickness_metres, 10.)
129 {
130 return fail("thicknesses must be within (0, 10] m with min <= max");
131 }
132 if !positive(o.min_wall_overlap_fraction, 1.) {
133 return fail("minWallOverlapFraction must be within (0, 1]");
134 }
135 if !between(o.min_wall_length_metres, 0., 1e3)
136 || !between(o.min_wall_height_metres, 0., 1e3)
137 || !between(o.min_slab_area_square_metres, 0., 1e6)
138 || !between(o.level_snap_metres, 0., 10.)
139 {
140 return fail("minimum sizes and levelSnapMetres must be finite and non-negative");
141 }
142 if o.interior_point.is_some_and(|p| p.iter().any(|v| !v.is_finite() || v.abs() > 1e12)) {
143 return fail("interiorPoint must lie within 1e12 m");
144 }
145 let class = o.classification_angle_degrees.to_radians();
146 Ok(Params {
147 schema: o.schema,
148 sin_class: class.sin(),
149 cos_class: class.cos(),
150 cos_pair: o.pairing_angle_degrees.to_radians().cos(),
151 min_thickness: o.min_wall_thickness_metres,
152 max_thickness: o.max_wall_thickness_metres,
153 wall_thickness: o.default_wall_thickness_metres,
154 min_overlap: o.min_wall_overlap_fraction,
155 min_wall_length: o.min_wall_length_metres,
156 min_wall_height: o.min_wall_height_metres,
157 min_slab_area: o.min_slab_area_square_metres,
158 slab_thickness: o.default_slab_thickness_metres,
159 max_slab_thickness: o.max_slab_thickness_metres,
160 snap: o.level_snap_metres,
161 interior: o.interior_point,
162 })
163 }
164}