1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*!
This crate is a wrapper around the [SolveSpace](https://solvespace.com/index.pl)
geometic constraint solver library.
Sketch geometries by creating [entities][crate::entity] within the system,
then add [constraints][crate::constraint] to define relationships between multiple
entities.
Entities and constraints are referenced by their handles ([`EntityHandle`][crate::entity::EntityHandle]
and [`ConstraintHandle`][crate::constraint::ConstraintHandle], respectively). These are wrappers around
`u32` values with a phantom type used to ensure that entity and constraint definitions
are correctly referencing the expected type of entity.
# Examples
An example of a constraint in 2d. In our first group, we create a workplane
along the reference frame's xy plane. In a second group, we create some
entities in that group and dimension them.
```
use slvs::{
constraint::{Diameter, EqualRadius, PtLineDistance, PtPtDistance, Vertical},
entity::{ArcOfCircle, Circle, Distance, LineSegment, Normal, Point, Workplane},
utils::make_quaternion,
system::{FailReason, SolveResult},
System,
};
// Initialize the system and create a group for the canvas.
let mut sys = System::new();
let g1 = sys.add_group();
// First, we create our workplane. Its origin corresponds to the origin
// of our base frame (x y z) = (0 0 0)
let origin = sys
.sketch(Point::new_in_3d(g1, [0.0, 0.0, 0.0]))
.expect("origin point created");
// and it is parallel to the xy plane, so it has basis vectors (1 0 0)
// and (0 1 0).
let normal = sys
.sketch(Normal::new_in_3d(
g1,
make_quaternion([1.0, 0.0, 0.0], [0.0, 1.0, 0.0]),
))
.expect("normal created");
let workplane = sys
.sketch(Workplane::new(g1, origin, normal))
.expect("workplane created");
// Now create a second group. We'll solve group 2, while leaving group 1
// constant; so the workplane that we've created will be locked down,
// and the solver can't move it.
let g2 = sys.add_group();
// These points are represented by their coordinates (u v) within the
// workplane, so they need only two parameters each.
let p1 = sys
.sketch(Point::new_on_workplane(g2, workplane, [10.0, 20.0]))
.expect("point in 2d created");
let p2 = sys
.sketch(Point::new_on_workplane(g2, workplane, [20.0, 10.0]))
.expect("point in 2d created");
// And we create a line segment with those endpoints.
let line = sys
.sketch(LineSegment::new(g2, p1, p2))
.expect("line segment created");
// Now three more points.
let arc_center = sys
.sketch(Point::new_on_workplane(g2, workplane, [100.0, 120.0]))
.expect("point in 2d created");
let arc_start = sys
.sketch(Point::new_on_workplane(g2, workplane, [120.0, 110.0]))
.expect("point in 2d created");
let arc_finish = sys
.sketch(Point::new_on_workplane(g2, workplane, [115.0, 115.0]))
.expect("point in 2d created");
// And arc, centered at point arc_center, starting at point arc_start, ending at
// point arc_finish.
let arc = sys
.sketch(ArcOfCircle::new(
g2, workplane, arc_center, arc_start, arc_finish,
))
.expect("arc created");
// Now one more point, and a distance
let circle_center = sys
.sketch(Point::new_on_workplane(g2, workplane, [200.0, 200.0]))
.expect("point in 2d created");
let circle_radius = sys
.sketch(Distance::new(g2, 30.0))
.expect("distance created");
// And a complete circle, centered at point circle_center with radius equal to
// distance circle_radius. Creating a normal on the workplane lets the system
// know that the cirle lies on the workplane, not just parallel to it.
let workplane_normal = sys
.sketch(Normal::new_on_workplane(g2, workplane))
.expect("2d normal created");
let circle = sys
.sketch(Circle::new(
g2,
workplane_normal,
circle_center,
circle_radius,
))
.expect("circle created");
// The length of our line segment is 30.0 units.
sys.constrain(PtPtDistance::new(g2, p1, p2, 30.0, Some(workplane)))
.expect("constrain line segment to 30.0 units");
// And the distance from our line segment to the origin is 10.0 units.
sys.constrain(PtLineDistance::new(g2, origin, line, 10.0, Some(workplane)))
.expect("distance from line to origin is 10.0");
// And the line segment is vertical.
sys.constrain(Vertical::from_line(g2, workplane, line))
.expect("line segment is vertical");
// And the distance from one endpoint to the origin is 15.0 units.
sys.constrain(PtPtDistance::new(g2, p1, origin, 15.0, Some(workplane)))
.expect("distance from p1 to origin is 15.0 units");
// And same for the other endpoint; so if you add this constraint then
// the sketch is overconstrained and will signal an error.
// sys.constrain(PtPtDistance::new(g2, p2, origin, 18.0, Some(workplane)))
// .expect("distance from p2 to origin is 18.0 units");
// The arc and the circle have equal radius.
sys.constrain(EqualRadius::new(g2, arc, circle))
.expect("arc and circle have equal radius");
// The arc has radius 17.0 units.
sys.constrain(Diameter::new(g2, arc, 17.0 * 2.0))
.expect("arc has diameter of 17.0 units");
// And solve.
let result = sys.solve(&g2);
match result {
SolveResult::Ok { dof } => {
println!("solved okay");
if let (
Point::OnWorkplane {
coords: [u1, v1], ..
},
Point::OnWorkplane {
coords: [u2, v2], ..
},
) = (
sys.entity_data(&p1).expect("data for p1 found"),
sys.entity_data(&p2).expect("data for p1 found"),
) {
println!("line from ({:.3} {:.3}) to ({:.3} {:.3})", u1, v1, u2, v2);
}
if let (
Point::OnWorkplane {
coords: [arc_center_u, arc_center_v],
..
},
Point::OnWorkplane {
coords: [arc_start_u, arc_start_v],
..
},
Point::OnWorkplane {
coords: [arc_finish_u, arc_finish_v],
..
},
) = (
sys.entity_data(&arc_center)
.expect("data for arc_center found"),
sys.entity_data(&arc_start)
.expect("data for arc_start found"),
sys.entity_data(&arc_finish)
.expect("data for arc_finish found"),
) {
println!(
"arc center ({:.3} {:.3}) start ({:.3} {:.3}) finish ({:.3} {:.3})",
arc_center_u,
arc_center_v,
arc_start_u,
arc_start_v,
arc_finish_u,
arc_finish_v
);
}
if let Point::OnWorkplane {
coords: [center_u, center_v],
..
} = sys
.entity_data(&circle_center)
.expect("data for circle_center found")
{
let radius = sys
.entity_data(&circle_radius)
.expect("data for circle_radius found")
.val;
println!(
"circle center ({:.3} {:.3}) radius {:.3}",
center_u, center_v, radius
);
}
println!("{} DOF", dof);
}
SolveResult::Fail {
reason,
failed_constraints,
..
} => {
println!(
"solve failed: problematic constraints are: {:#?}",
failed_constraints
);
match reason {
FailReason::Inconsistent => println!("system inconsistent"),
_ => println!("system nonconvergent"),
}
}
}
```
*/
pub use System;