1use serde::{Deserialize, Serialize};
2
3use super::AsConstraintData;
4use crate::{
5 bindings::{Slvs_hEntity, Slvs_hGroup, SLVS_C_PT_LINE_DISTANCE},
6 define_element,
7 element::{AsGroup, AsHandle, AsSlvsType, FromSystem},
8 entity::{EntityHandle, LineSegment, Point, Workplane},
9 group::Group,
10 System,
11};
12
13define_element!(
14 SLVS_C_PT_LINE_DISTANCE,
15 struct PtLineDistance {
23 point: EntityHandle<Point>,
24 line: EntityHandle<LineSegment>,
25 distance: f64,
26 workplane: Option<EntityHandle<Workplane>>,
28 }
29);
30
31impl AsConstraintData for PtLineDistance {
32 fn workplane(&self) -> Option<Slvs_hEntity> {
33 self.workplane.map(|workplane| workplane.handle())
34 }
35
36 fn val(&self) -> Option<f64> {
37 Some(self.distance)
38 }
39
40 fn points(&self) -> Option<[Slvs_hEntity; 2]> {
41 Some([self.point.handle(), 0])
42 }
43
44 fn entities(&self) -> Option<[Slvs_hEntity; 4]> {
45 Some([self.line.handle(), 0, 0, 0])
46 }
47}
48
49impl FromSystem for PtLineDistance {
50 fn from_system(sys: &System, element: &impl AsHandle) -> Result<Self, &'static str>
51 where
52 Self: Sized,
53 {
54 let slvs_constraint = sys.slvs_constraint(element.handle())?;
55
56 if SLVS_C_PT_LINE_DISTANCE == slvs_constraint.type_ as _ {
57 Ok(Self {
58 group: Group(slvs_constraint.group),
59 point: EntityHandle::new(slvs_constraint.ptA),
60 line: EntityHandle::new(slvs_constraint.entityA),
61 distance: slvs_constraint.valA,
62 workplane: match slvs_constraint.wrkpl {
63 0 => None,
64 h => Some(EntityHandle::new(h)),
65 },
66 })
67 } else {
68 Err("Expected constraint to have type SLVS_C_PT_LINE_DISTANCE.")
69 }
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use crate::{
76 constraint::PtLineDistance,
77 entity::{LineSegment, Normal, Point, Workplane},
78 len_within_tolerance,
79 utils::{distance, make_quaternion, project_on_line, project_on_plane},
80 System,
81 };
82
83 #[test]
84 fn on_workplane() {
85 let mut sys = System::new();
86
87 let workplane_g = sys.add_group();
88 let origin = sys
89 .sketch(Point::new_in_3d(workplane_g, [4.0, 89.0, 83.0]))
90 .expect("origin created");
91 let normal = sys
92 .sketch(Normal::new_in_3d(
93 workplane_g,
94 make_quaternion([69.0, -98.0, 48.0], [-25.0, -50.0, 57.0]),
95 ))
96 .expect("normal created");
97 let workplane = sys
98 .sketch(Workplane::new(workplane_g, origin, normal))
99 .expect("workplane created");
100
101 let g = sys.add_group();
102 let point = sys
103 .sketch(Point::new_in_3d(g, [-14.0, 67.0, -68.0]))
104 .expect("point created");
105
106 let line_start = sys
107 .sketch(Point::new_in_3d(g, [86.0, -54.0, -38.0]))
108 .expect("point created");
109 let line_end = sys
110 .sketch(Point::new_in_3d(g, [32.0, -92.0, -64.0]))
111 .expect("point created");
112 let line = sys
113 .sketch(LineSegment::new(g, line_start, line_end))
114 .expect("line created");
115
116 let dist = 14.0;
117 sys.constrain(PtLineDistance::new(g, point, line, dist, Some(workplane)))
118 .expect("constraint added");
119
120 dbg!(sys.solve(&g));
121
122 if let (
123 Point::In3d { coords: origin, .. },
124 Normal::In3d { quaternion, .. },
125 Point::In3d {
126 coords: point_coords,
127 ..
128 },
129 Point::In3d {
130 coords: line_start_coords,
131 ..
132 },
133 Point::In3d {
134 coords: line_end_coords,
135 ..
136 },
137 ) = (
138 sys.entity_data(&origin).expect("data found"),
139 sys.entity_data(&normal).expect("data found"),
140 sys.entity_data(&point).expect("data found"),
141 sys.entity_data(&line_start).expect("data found"),
142 sys.entity_data(&line_end).expect("data found"),
143 ) {
144 let proj_pt_coords = project_on_plane(point_coords, origin, quaternion);
145 let proj_line_start = project_on_plane(line_start_coords, origin, quaternion);
146 let proj_line_end = project_on_plane(line_end_coords, origin, quaternion);
147
148 let point_on_line = project_on_line(proj_pt_coords, proj_line_start, proj_line_end);
149
150 len_within_tolerance!(distance(proj_pt_coords, point_on_line), dist);
151 } else {
152 unreachable!()
153 }
154 }
155
156 #[test]
157 fn in_3d() {
158 let mut sys = System::new();
159
160 let g = sys.add_group();
161 let point = sys
162 .sketch(Point::new_in_3d(g, [-70.0, 67.0, -28.0]))
163 .expect("point created");
164
165 let line_start = sys
166 .sketch(Point::new_in_3d(g, [-45.0, 82.0, -29.0]))
167 .expect("point created");
168 let line_end = sys
169 .sketch(Point::new_in_3d(g, [67.0, 43.0, -31.0]))
170 .expect("point created");
171 let line = sys
172 .sketch(LineSegment::new(g, line_start, line_end))
173 .expect("line created");
174
175 let dist = 29.0;
176 sys.constrain(PtLineDistance::new(g, point, line, dist, None))
177 .expect("constraint added");
178
179 dbg!(sys.solve(&g));
180
181 if let (
182 Point::In3d {
183 coords: point_coords,
184 ..
185 },
186 Point::In3d {
187 coords: line_start_coords,
188 ..
189 },
190 Point::In3d {
191 coords: line_end_coords,
192 ..
193 },
194 ) = (
195 sys.entity_data(&point).expect("data found"),
196 sys.entity_data(&line_start).expect("data found"),
197 sys.entity_data(&line_end).expect("data found"),
198 ) {
199 let point_on_line = project_on_line(point_coords, line_start_coords, line_end_coords);
200
201 len_within_tolerance!(distance(point_coords, point_on_line), dist);
202 } else {
203 unreachable!()
204 }
205 }
206}