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::{Handle, Stores},
};
use super::Sweep;
impl Sweep for (Vertex, Handle<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.clone(),
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.clone(),
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 = stores.surfaces.insert(Surface::xz_plane());
let curve = Curve::partial()
.with_surface(surface.clone())
.as_u_axis()
.build(&stores);
let vertex = Vertex::partial()
.with_position([0.])
.with_curve(curve)
.build(&stores);
let half_edge = (vertex, surface.clone()).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);
}
}