brep_kernel/feature_pipeline/assembly/
constraints.rs1use crate::feature_pipeline::SelectionProbe;
14
15pub(crate) mod angle;
16pub(crate) mod coincident;
17pub(crate) mod concentric;
18pub(crate) mod distance;
19pub(crate) mod fixed;
20pub(crate) mod parallel;
21pub(crate) mod perpendicular;
22pub(crate) mod tangent;
23pub(crate) mod touch_align;
24
25pub struct ConstraintTypeDef {
27 pub type_id: &'static str,
29 pub short_name: &'static str,
31 pub long_name: &'static str,
33 pub label: &'static str,
36 pub element_count: usize,
38 pub duplicate_family: bool,
41 pub applicable: fn(&SelectionProbe) -> bool,
49}
50
51pub const CONSTRAINT_TYPES: [ConstraintTypeDef; 9] = [
53 fixed::DEF,
54 coincident::DEF,
55 touch_align::DEF,
56 parallel::DEF,
57 distance::DEF,
58 angle::DEF,
59 concentric::DEF,
60 perpendicular::DEF,
61 tangent::DEF,
62];
63
64pub fn constraint_type(type_id: &str) -> Option<&'static ConstraintTypeDef> {
66 CONSTRAINT_TYPES.iter().find(|def| def.type_id == type_id)
67}
68
69pub fn constraint_schema_catalogue() -> serde_json::Value {
73 serde_json::Value::Array(vec![
74 fixed::schema(),
75 coincident::schema(),
76 touch_align::schema(),
77 parallel::schema(),
78 distance::schema(),
79 angle::schema(),
80 concentric::schema(),
81 perpendicular::schema(),
82 tangent::schema(),
83 ])
84}
85
86pub(super) fn pair_applicable(probe: &SelectionProbe, seedable: usize) -> bool {
90 probe.all_component && probe.components == 2 && seedable == 2
91}
92
93pub(super) fn id_field() -> serde_json::Value {
96 serde_json::json!({
97 "type": "string",
98 "default_value": null,
99 "hint": "Unique identifier for the constraint"
100 })
101}
102
103pub(super) fn elements_field(filter: &[&str], count: usize, hint: &str) -> serde_json::Value {
104 serde_json::json!({
105 "type": "reference_selection",
106 "selectionFilter": filter,
107 "multiple": count > 1,
108 "minSelections": count,
109 "maxSelections": count,
110 "default_value": null,
111 "hint": hint
112 })
113}
114
115pub(super) fn schema_entry(
116 def: &ConstraintTypeDef,
117 params: serde_json::Value,
118) -> serde_json::Value {
119 serde_json::json!({
120 "type": def.type_id,
121 "shortName": def.short_name,
122 "longName": def.long_name,
123 "inputParamsSchema": params,
124 })
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 fn probe(
132 solids: usize,
133 faces: usize,
134 edges: usize,
135 components: usize,
136 all_component: bool,
137 ) -> SelectionProbe {
138 SelectionProbe {
139 solids,
140 faces,
141 edges,
142 components,
143 all_component,
144 ..Default::default()
145 }
146 }
147
148 #[test]
151 fn constraint_applicability_shapes() {
152 let one_component = probe(1, 0, 0, 1, true);
153 let two_faces_two_components = probe(0, 2, 0, 2, true);
154 let face_edge_two_components = probe(0, 1, 1, 2, true);
155 let two_faces_one_component = probe(0, 2, 0, 1, true);
156 let non_component = probe(0, 2, 0, 0, false);
157
158 let applicable = |type_id: &str, p: &SelectionProbe| {
159 (constraint_type(type_id).expect(type_id).applicable)(p)
160 };
161
162 assert!(applicable("fixed", &one_component));
163 assert!(!applicable("fixed", &two_faces_two_components));
164
165 for pair in [
166 "coincident",
167 "touch_align",
168 "parallel",
169 "distance",
170 "angle",
171 "concentric",
172 "perpendicular",
173 ] {
174 assert!(applicable(pair, &two_faces_two_components), "{pair}: 2 faces / 2 comps");
175 assert!(applicable(pair, &face_edge_two_components), "{pair}: face+edge / 2 comps");
176 assert!(!applicable(pair, &two_faces_one_component), "{pair}: same component");
178 assert!(!applicable(pair, &non_component), "{pair}: non-component");
180 }
181
182 assert!(applicable("tangent", &two_faces_two_components));
184 assert!(!applicable("tangent", &face_edge_two_components));
185
186 assert!(applicable("coincident", &probe(2, 0, 0, 2, true)));
188 }
189}