-
-
Notifications
You must be signed in to change notification settings - Fork 119
/
Copy pathvertex.rs
179 lines (155 loc) · 6.84 KB
/
vertex.rs
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
use fj_math::{Line, Point, Scalar, Vector};
use crate::{
objects::{
Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface,
SurfaceVertex, Vertex,
},
path::SurfacePath,
stores::Stores,
};
use super::Sweep;
impl Sweep for (Vertex, Surface) {
type Swept = HalfEdge;
fn sweep(self, path: impl Into<Vector<3>>, stores: &Stores) -> Self::Swept {
let (vertex, surface) = self;
let path = path.into();
// The result of sweeping a `Vertex` is an `Edge`. Seems
// straight-forward at first, but there are some subtleties we need to
// understand:
//
// 1. To create an `Edge`, we need the `Curve` that defines it. A
// `Curve` is defined in a `Surface`, and we're going to need that to
// create the `Curve`. Which is why this `Sweep` implementation is
// for `(Vertex, Surface)`, and not just for `Vertex`.
// 2. Please note that, while the output `Edge` has two vertices, our
// input `Vertex` is not one of them! It can't be, unless the `Curve`
// of the output `Edge` happens to be the same `Curve` that the input
// `Vertex` is defined on. That would be an edge case that probably
// can't result in anything valid, and we're going to ignore it for
// now.
// 3. This means, we have to compute everything that defines the
// output `Edge`: The `Curve`, the vertices, and the `GlobalCurve`.
//
// Before we get to that though, let's make sure that whoever called
// this didn't give us bad input.
// So, we're supposed to create the `Edge` by sweeping a `Vertex` using
// `path`. Unless `path` is identical to the path that created the
// `Surface`, this doesn't make any sense. Let's make sure this
// requirement is met.
//
// Further, the `Curve` that was swept to create the `Surface` needs to
// be the same `Curve` that the input `Vertex` is defined on. If it's
// not, we have no way of knowing the surface coordinates of the input
// `Vertex` on the `Surface`, and we're going to need to do that further
// down. There's no way to check for that, unfortunately.
assert_eq!(path, surface.v());
// With that out of the way, let's start by creating the `GlobalEdge`,
// as that is the most straight-forward part of this operations, and
// we're going to need it soon anyway.
let (edge_global, vertices_global) =
vertex.global_form().sweep(path, stores);
// Next, let's compute the surface coordinates of the two vertices of
// the output `Edge`, as we're going to need these for the rest of this
// operation.
//
// They both share a u-coordinate, which is the t-coordinate of our
// input `Vertex`. Remember, we validated above, that the `Curve` of the
// `Surface` and the curve of the input `Vertex` are the same, so we can
// do that.
//
// Now remember what we also validated above: That `path`, which we're
// using to create the output `Edge`, also created the `Surface`, and
// thereby defined its coordinate system. That makes the v-coordinates
// straight-forward: The start of the edge is at zero, the end is at
// one.
let points_surface = [
Point::from([vertex.position().t, Scalar::ZERO]),
Point::from([vertex.position().t, Scalar::ONE]),
];
// Armed with those coordinates, creating the `Curve` of the output
// `Edge` is straight-forward.
let curve = {
let line = Line::from_points(points_surface);
Curve::new(
surface,
SurfacePath::Line(line),
edge_global.curve().clone(),
)
};
// And now the vertices. Again, nothing wild here.
let vertices = {
// Can be cleaned up, once `zip` is stable:
// https://doc.rust-lang.org/std/primitive.array.html#method.zip
let [a_surface, b_surface] = points_surface;
let [a_global, b_global] = vertices_global;
let vertices_surface =
[(a_surface, a_global), (b_surface, b_global)].map(
|(point_surface, vertex_global)| {
SurfaceVertex::new(
point_surface,
surface,
vertex_global,
)
},
);
// Can be cleaned up, once `zip` is stable:
// https://doc.rust-lang.org/std/primitive.array.html#method.zip
let [a_surface, b_surface] = vertices_surface;
let [a_global, b_global] = vertices_global;
let vertices = [(a_surface, a_global), (b_surface, b_global)];
vertices.map(|(vertex_surface, vertex_global)| {
Vertex::new(
[vertex_surface.position().v],
curve.clone(),
vertex_surface,
vertex_global,
)
})
};
// And finally, creating the output `Edge` is just a matter of
// assembling the pieces we've already created.
HalfEdge::new(curve, vertices, edge_global)
}
}
impl Sweep for GlobalVertex {
type Swept = (GlobalEdge, [GlobalVertex; 2]);
fn sweep(self, path: impl Into<Vector<3>>, stores: &Stores) -> Self::Swept {
let curve = GlobalCurve::new(stores);
let a = self;
let b = GlobalVertex::from_position(self.position() + path.into());
let vertices = [a, b];
let global_edge = GlobalEdge::new(curve, vertices);
// The vertices of the returned `GlobalEdge` are in normalized order,
// which means the order can't be relied upon by the caller. Return the
// ordered vertices in addition.
(global_edge, vertices)
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use crate::{
algorithms::sweep::Sweep,
objects::{Curve, HalfEdge, Surface, Vertex},
partial::HasPartial,
stores::Stores,
};
#[test]
fn vertex_surface() {
let stores = Stores::new();
let surface = Surface::xz_plane();
let curve = Curve::partial()
.with_surface(surface)
.as_u_axis()
.build(&stores);
let vertex = Vertex::partial()
.with_position([0.])
.with_curve(curve)
.build(&stores);
let half_edge = (vertex, surface).sweep([0., 0., 1.], &stores);
let expected_half_edge = HalfEdge::partial()
.as_line_segment_from_points(surface, [[0., 0.], [0., 1.]])
.build(&stores);
assert_eq!(half_edge, expected_half_edge);
}
}