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
use fj_math::Point;
use crate::{
geometry::curve::Curve,
objects::Vertex,
storage::{Handle, HandleWrapper},
};
/// A directed edge, defined in a surface's 2D space
///
/// The concept of an "edge" in Fornjot is represented by two structs,
/// `HalfEdge` and [`GlobalEdge`]. `HalfEdge` has two attributes that make it
/// distinct from [`GlobalEdge`]:
///
/// - `HalfEdge` is directed, meaning it has a defined start and end vertex.
/// - `HalfEdge` is defined in the 2-dimensional space of a surface.
///
/// When multiple faces, which are bound by edges, are combined to form a solid,
/// the `HalfEdge`s that bound the face on the surface are then coincident with
/// the `HalfEdge`s of other faces, where those faces touch. Those coincident
/// `HalfEdge`s are different representations of the same edge. This edge is
/// represented by an instance of [`GlobalEdge`].
///
/// There are some requirements that a `HalfEdge` needs to uphold to be valid:
///
/// 1. Coincident `HalfEdge`s *must* refer to the same [`GlobalEdge`].
/// 2. `HalfEdge`s that are coincident, i.e. located in the same space, must
/// always be congruent. This means they must coincide *exactly*. The overlap
/// must be complete. None of the coincident `HalfEdge`s must overlap with
/// just a section of another.
///
/// That second requirement means that a `HalfEdge` might need to be split into
/// multiple smaller `HalfEdge`s that are each coincident with a `HalfEdge` in
/// another face.
///
/// # Implementation Note
///
/// There is currently no validation code to verify that coincident `HalfEdge`s
/// are congruent:
/// <https://github.com/hannobraun/Fornjot/issues/1608>
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct HalfEdge {
curve: Curve,
boundary: [Point<1>; 2],
start_vertex: HandleWrapper<Vertex>,
global_form: HandleWrapper<GlobalEdge>,
}
impl HalfEdge {
/// Create an instance of `HalfEdge`
pub fn new(
curve: Curve,
boundary: [Point<1>; 2],
start_vertex: Handle<Vertex>,
global_form: Handle<GlobalEdge>,
) -> Self {
Self {
curve,
boundary,
start_vertex: start_vertex.into(),
global_form: global_form.into(),
}
}
/// Access the curve that defines the half-edge's geometry
pub fn curve(&self) -> Curve {
self.curve
}
/// Access the boundary points of the half-edge on the curve
pub fn boundary(&self) -> [Point<1>; 2] {
self.boundary
}
/// Compute the surface position where the half-edge starts
pub fn start_position(&self) -> Point<2> {
// Computing the surface position from the curve position is fine.
// `HalfEdge` "owns" its start position. There is no competing code that
// could compute the surface position from slightly different data.
let [start, _] = self.boundary;
self.curve.point_from_path_coords(start)
}
/// Access the vertex from where this half-edge starts
pub fn start_vertex(&self) -> &Handle<Vertex> {
&self.start_vertex
}
/// Access the global form of the half-edge
pub fn global_form(&self) -> &Handle<GlobalEdge> {
&self.global_form
}
}
/// An undirected edge, defined in global (3D) coordinates
///
/// In contrast to [`HalfEdge`], `GlobalEdge` is undirected, meaning it has no
/// defined direction. This means it can be used to determine whether two
/// [`HalfEdge`]s map to the same `GlobalEdge`, regardless of their direction.
///
/// See [`HalfEdge`]'s documentation for more information on the relationship
/// between [`HalfEdge`] and `GlobalEdge`.
///
/// # Equality
///
/// `GlobalEdge` contains no data and exists purely to be used within a
/// `Handle`, where `Handle::id` can be used to compare different instances of
/// `GlobalEdge`.
///
/// If `GlobalEdge` had `Eq`/`PartialEq` implementations, it containing no data
/// would mean that all instances of `GlobalEdge` would be considered equal.
/// This would be very error-prone.
///
/// If you need to reference a `GlobalEdge` from a struct that needs to derive
/// `Eq`/`Ord`/..., you can use `HandleWrapper<GlobalEdge>` to do that. It will
/// use `Handle::id` to provide those `Eq`/`Ord`/... implementations.
#[derive(Clone, Debug, Default, Hash)]
pub struct GlobalEdge {}
impl GlobalEdge {
/// Create a new instance
///
/// The order of `vertices` is irrelevant. Two `GlobalEdge`s with the same
/// `curve` and `vertices` will end up being equal, regardless of the order
/// of `vertices` here.
pub fn new() -> Self {
Self::default()
}
}