mirror of
https://github.com/hannobraun/Fornjot
synced 2025-02-12 10:15:51 +00:00
Merge pull request #2000 from hannobraun/edge
Rename `HalfEdge` to `Edge`
This commit is contained in:
commit
6c41170f58
@ -9,7 +9,7 @@ use fj_math::Segment;
|
|||||||
use crate::objects::{Cycle, Surface};
|
use crate::objects::{Cycle, Surface};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
edge::{EdgeCache, HalfEdgeApprox},
|
edge::{EdgeApprox, EdgeCache},
|
||||||
Approx, ApproxPoint, Tolerance,
|
Approx, ApproxPoint, Tolerance,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -25,14 +25,14 @@ impl Approx for (&Cycle, &Surface) {
|
|||||||
let (cycle, surface) = self;
|
let (cycle, surface) = self;
|
||||||
let tolerance = tolerance.into();
|
let tolerance = tolerance.into();
|
||||||
|
|
||||||
let half_edges = cycle
|
let edges = cycle
|
||||||
.half_edges()
|
.edges()
|
||||||
.map(|half_edge| {
|
.map(|edge| {
|
||||||
(half_edge.deref(), surface).approx_with_cache(tolerance, cache)
|
(edge.deref(), surface).approx_with_cache(tolerance, cache)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
CycleApprox { half_edges }
|
CycleApprox { edges }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ impl Approx for (&Cycle, &Surface) {
|
|||||||
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct CycleApprox {
|
pub struct CycleApprox {
|
||||||
/// The approximated edges that make up the approximated cycle
|
/// The approximated edges that make up the approximated cycle
|
||||||
pub half_edges: Vec<HalfEdgeApprox>,
|
pub edges: Vec<EdgeApprox>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CycleApprox {
|
impl CycleApprox {
|
||||||
@ -48,7 +48,7 @@ impl CycleApprox {
|
|||||||
pub fn points(&self) -> Vec<ApproxPoint<2>> {
|
pub fn points(&self) -> Vec<ApproxPoint<2>> {
|
||||||
let mut points = Vec::new();
|
let mut points = Vec::new();
|
||||||
|
|
||||||
for approx in &self.half_edges {
|
for approx in &self.edges {
|
||||||
points.extend(approx.points());
|
points.extend(approx.points());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,14 +11,14 @@ use fj_math::Point;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{CurveBoundary, GlobalPath, SurfacePath},
|
geometry::{CurveBoundary, GlobalPath, SurfacePath},
|
||||||
objects::{Curve, HalfEdge, Surface, Vertex},
|
objects::{Curve, Edge, Surface, Vertex},
|
||||||
storage::{Handle, HandleWrapper},
|
storage::{Handle, HandleWrapper},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{curve::CurveApproxSegment, Approx, ApproxPoint, Tolerance};
|
use super::{curve::CurveApproxSegment, Approx, ApproxPoint, Tolerance};
|
||||||
|
|
||||||
impl Approx for (&HalfEdge, &Surface) {
|
impl Approx for (&Edge, &Surface) {
|
||||||
type Approximation = HalfEdgeApprox;
|
type Approximation = EdgeApprox;
|
||||||
type Cache = EdgeCache;
|
type Cache = EdgeCache;
|
||||||
|
|
||||||
fn approx_with_cache(
|
fn approx_with_cache(
|
||||||
@ -26,50 +26,48 @@ impl Approx for (&HalfEdge, &Surface) {
|
|||||||
tolerance: impl Into<Tolerance>,
|
tolerance: impl Into<Tolerance>,
|
||||||
cache: &mut Self::Cache,
|
cache: &mut Self::Cache,
|
||||||
) -> Self::Approximation {
|
) -> Self::Approximation {
|
||||||
let (half_edge, surface) = self;
|
let (edge, surface) = self;
|
||||||
|
|
||||||
let position_surface = half_edge.start_position();
|
let position_surface = edge.start_position();
|
||||||
let position_global = match cache.get_position(half_edge.start_vertex())
|
let position_global = match cache.get_position(edge.start_vertex()) {
|
||||||
{
|
|
||||||
Some(position) => position,
|
Some(position) => position,
|
||||||
None => {
|
None => {
|
||||||
let position_global = surface
|
let position_global = surface
|
||||||
.geometry()
|
.geometry()
|
||||||
.point_from_surface_coords(position_surface);
|
.point_from_surface_coords(position_surface);
|
||||||
cache.insert_position(half_edge.start_vertex(), position_global)
|
cache.insert_position(edge.start_vertex(), position_global)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let first = ApproxPoint::new(position_surface, position_global);
|
let first = ApproxPoint::new(position_surface, position_global);
|
||||||
|
|
||||||
let points = {
|
let points = {
|
||||||
// We cache approximated `HalfEdge`s using the `Curve`s they
|
// We cache approximated `Edge`s using the `Curve`s they reference
|
||||||
// reference and their boundary on that curve as the key. That bakes
|
// and their boundary on that curve as the key. That bakes in the
|
||||||
// in the undesirable assumption that all coincident `HalfEdge`s are
|
// undesirable assumption that all coincident `Edge`s are also
|
||||||
// also congruent. Let me explain.
|
// congruent. Let me explain.
|
||||||
//
|
//
|
||||||
// When two `HalfEdge`s are coincident, we need to make sure their
|
// When two `Edge`s are coincident, we need to make sure their
|
||||||
// approximations are identical where they overlap. Otherwise, we'll
|
// approximations are identical where they overlap. Otherwise, we'll
|
||||||
// get an invalid triangle mesh in the end. Hence, we cache
|
// get an invalid triangle mesh in the end. Hence, we cache
|
||||||
// approximations.
|
// approximations.
|
||||||
//
|
//
|
||||||
// Caching works like this: We check whether there already is a
|
// Caching works like this: We check whether there already is a
|
||||||
// cache entry for the curve/boundary. If there isn't, we create the
|
// cache entry for the curve/boundary. If there isn't, we create the
|
||||||
// 3D approximation from the 2D `HalfEdge`. Next time we check for a
|
// 3D approximation from the 2D `Edge`. Next time we check for a
|
||||||
// coincident `HalfEdge`, we'll find the cache and use that, getting
|
// coincident `Edge`, we'll find the cache and use that, getting
|
||||||
// the exact same 3D approximation, instead of generating a slightly
|
// the exact same 3D approximation, instead of generating a slightly
|
||||||
// different one from the different 2D `HalfEdge`.
|
// different one from the different 2D `Edge`.
|
||||||
//
|
//
|
||||||
// So what if we had two coincident `HalfEdge`s that aren't
|
// So what if we had two coincident `fEdge`s that aren't congruent?
|
||||||
// congruent? Meaning, they overlap partially, but not fully. Then
|
// Meaning, they overlap partially, but not fully. Then obviously,
|
||||||
// obviously, they wouldn't refer to the same combination of curve
|
// they wouldn't refer to the same combination of curve and
|
||||||
// and boundary. And since those are the key in our cache, those
|
// boundary. And since those are the key in our cache, those `Edge`s
|
||||||
// `HalfEdge`s would not share an approximation where they overlap,
|
// would not share an approximation where they overlap, leading to
|
||||||
// leading to exactly the problems that the cache is supposed to
|
// exactly the problems that the cache is supposed to prevent.
|
||||||
// prevent.
|
|
||||||
//
|
//
|
||||||
// As of this writing, it is a documented (but not validated)
|
// As of this writing, it is a documented (but not validated)
|
||||||
// limitation, that coincident `HalfEdge`s must always be congruent.
|
// limitation, that coincident `Edge`s must always be congruent.
|
||||||
// However, we're going to need to lift this limitation going
|
// However, we're going to need to lift this limitation going
|
||||||
// forward, as it is, well, too limiting. This means things here
|
// forward, as it is, well, too limiting. This means things here
|
||||||
// will need to change.
|
// will need to change.
|
||||||
@ -80,19 +78,19 @@ impl Approx for (&HalfEdge, &Surface) {
|
|||||||
// able to deliver partial results for a given boundary, then
|
// able to deliver partial results for a given boundary, then
|
||||||
// generating (and caching) the rest of it on the fly.
|
// generating (and caching) the rest of it on the fly.
|
||||||
let cached_approx =
|
let cached_approx =
|
||||||
cache.get_edge(half_edge.curve().clone(), half_edge.boundary());
|
cache.get_edge(edge.curve().clone(), edge.boundary());
|
||||||
let approx = match cached_approx {
|
let approx = match cached_approx {
|
||||||
Some(approx) => approx,
|
Some(approx) => approx,
|
||||||
None => {
|
None => {
|
||||||
let approx = approx_edge(
|
let approx = approx_edge(
|
||||||
&half_edge.path(),
|
&edge.path(),
|
||||||
surface,
|
surface,
|
||||||
half_edge.boundary(),
|
edge.boundary(),
|
||||||
tolerance,
|
tolerance,
|
||||||
);
|
);
|
||||||
cache.insert_edge(
|
cache.insert_edge(
|
||||||
half_edge.curve().clone(),
|
edge.curve().clone(),
|
||||||
half_edge.boundary(),
|
edge.boundary(),
|
||||||
approx,
|
approx,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -102,22 +100,21 @@ impl Approx for (&HalfEdge, &Surface) {
|
|||||||
.points
|
.points
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|point| {
|
.map(|point| {
|
||||||
let point_surface = half_edge
|
let point_surface =
|
||||||
.path()
|
edge.path().point_from_path_coords(point.local_form);
|
||||||
.point_from_path_coords(point.local_form);
|
|
||||||
|
|
||||||
ApproxPoint::new(point_surface, point.global_form)
|
ApproxPoint::new(point_surface, point.global_form)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
HalfEdgeApprox { first, points }
|
EdgeApprox { first, points }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An approximation of an [`HalfEdge`]
|
/// An approximation of an [`Edge`]
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct HalfEdgeApprox {
|
pub struct EdgeApprox {
|
||||||
/// The point that approximates the first vertex of the edge
|
/// The point that approximates the first vertex of the edge
|
||||||
pub first: ApproxPoint<2>,
|
pub first: ApproxPoint<2>,
|
||||||
|
|
||||||
@ -125,7 +122,7 @@ pub struct HalfEdgeApprox {
|
|||||||
pub points: Vec<ApproxPoint<2>>,
|
pub points: Vec<ApproxPoint<2>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HalfEdgeApprox {
|
impl EdgeApprox {
|
||||||
/// Compute the points that approximate the edge
|
/// Compute the points that approximate the edge
|
||||||
pub fn points(&self) -> Vec<ApproxPoint<2>> {
|
pub fn points(&self) -> Vec<ApproxPoint<2>> {
|
||||||
let mut points = Vec::new();
|
let mut points = Vec::new();
|
||||||
@ -287,8 +284,8 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
algorithms::approx::{Approx, ApproxPoint},
|
algorithms::approx::{Approx, ApproxPoint},
|
||||||
geometry::{CurveBoundary, GlobalPath, SurfaceGeometry},
|
geometry::{CurveBoundary, GlobalPath, SurfaceGeometry},
|
||||||
objects::{HalfEdge, Surface},
|
objects::{Edge, Surface},
|
||||||
operations::BuildHalfEdge,
|
operations::BuildEdge,
|
||||||
services::Services,
|
services::Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -297,11 +294,11 @@ mod tests {
|
|||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let surface = services.objects.surfaces.xz_plane();
|
let surface = services.objects.surfaces.xz_plane();
|
||||||
let half_edge =
|
let edge =
|
||||||
HalfEdge::line_segment([[1., 1.], [2., 1.]], None, &mut services);
|
Edge::line_segment([[1., 1.], [2., 1.]], None, &mut services);
|
||||||
|
|
||||||
let tolerance = 1.;
|
let tolerance = 1.;
|
||||||
let approx = (&half_edge, surface.deref()).approx(tolerance);
|
let approx = (&edge, surface.deref()).approx(tolerance);
|
||||||
|
|
||||||
assert_eq!(approx.points, Vec::new());
|
assert_eq!(approx.points, Vec::new());
|
||||||
}
|
}
|
||||||
@ -314,11 +311,11 @@ mod tests {
|
|||||||
u: GlobalPath::circle_from_radius(1.),
|
u: GlobalPath::circle_from_radius(1.),
|
||||||
v: [0., 0., 1.].into(),
|
v: [0., 0., 1.].into(),
|
||||||
});
|
});
|
||||||
let half_edge =
|
let edge =
|
||||||
HalfEdge::line_segment([[1., 1.], [2., 1.]], None, &mut services);
|
Edge::line_segment([[1., 1.], [2., 1.]], None, &mut services);
|
||||||
|
|
||||||
let tolerance = 1.;
|
let tolerance = 1.;
|
||||||
let approx = (&half_edge, &surface).approx(tolerance);
|
let approx = (&edge, &surface).approx(tolerance);
|
||||||
|
|
||||||
assert_eq!(approx.points, Vec::new());
|
assert_eq!(approx.points, Vec::new());
|
||||||
}
|
}
|
||||||
@ -334,21 +331,21 @@ mod tests {
|
|||||||
u: path,
|
u: path,
|
||||||
v: [0., 0., 1.].into(),
|
v: [0., 0., 1.].into(),
|
||||||
});
|
});
|
||||||
let half_edge = HalfEdge::line_segment(
|
let edge = Edge::line_segment(
|
||||||
[[0., 1.], [TAU, 1.]],
|
[[0., 1.], [TAU, 1.]],
|
||||||
Some(boundary.inner),
|
Some(boundary.inner),
|
||||||
&mut services,
|
&mut services,
|
||||||
);
|
);
|
||||||
|
|
||||||
let tolerance = 1.;
|
let tolerance = 1.;
|
||||||
let approx = (&half_edge, &surface).approx(tolerance);
|
let approx = (&edge, &surface).approx(tolerance);
|
||||||
|
|
||||||
let expected_approx = (path, boundary)
|
let expected_approx = (path, boundary)
|
||||||
.approx(tolerance)
|
.approx(tolerance)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(point_local, _)| {
|
.map(|(point_local, _)| {
|
||||||
let point_surface =
|
let point_surface =
|
||||||
half_edge.path().point_from_path_coords(point_local);
|
edge.path().point_from_path_coords(point_local);
|
||||||
let point_global =
|
let point_global =
|
||||||
surface.geometry().point_from_surface_coords(point_surface);
|
surface.geometry().point_from_surface_coords(point_surface);
|
||||||
ApproxPoint::new(point_surface, point_global)
|
ApproxPoint::new(point_surface, point_global)
|
||||||
@ -362,13 +359,13 @@ mod tests {
|
|||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let surface = services.objects.surfaces.xz_plane();
|
let surface = services.objects.surfaces.xz_plane();
|
||||||
let half_edge = HalfEdge::circle([0., 0.], 1., &mut services);
|
let edge = Edge::circle([0., 0.], 1., &mut services);
|
||||||
|
|
||||||
let tolerance = 1.;
|
let tolerance = 1.;
|
||||||
let approx = (&half_edge, surface.deref()).approx(tolerance);
|
let approx = (&edge, surface.deref()).approx(tolerance);
|
||||||
|
|
||||||
let expected_approx =
|
let expected_approx =
|
||||||
(&half_edge.path(), CurveBoundary::from([[0.], [TAU]]))
|
(&edge.path(), CurveBoundary::from([[0.], [TAU]]))
|
||||||
.approx(tolerance)
|
.approx(tolerance)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(_, point_surface)| {
|
.map(|(_, point_surface)| {
|
||||||
|
@ -6,10 +6,8 @@ impl super::BoundingVolume<2> for Cycle {
|
|||||||
fn aabb(&self) -> Option<Aabb<2>> {
|
fn aabb(&self) -> Option<Aabb<2>> {
|
||||||
let mut aabb: Option<Aabb<2>> = None;
|
let mut aabb: Option<Aabb<2>> = None;
|
||||||
|
|
||||||
for half_edge in self.half_edges() {
|
for edge in self.edges() {
|
||||||
let new_aabb = half_edge
|
let new_aabb = edge.aabb().expect("`Edge` can always compute AABB");
|
||||||
.aabb()
|
|
||||||
.expect("`HalfEdge` can always compute AABB");
|
|
||||||
aabb = Some(aabb.map_or(new_aabb, |aabb| aabb.merged(&new_aabb)));
|
aabb = Some(aabb.map_or(new_aabb, |aabb| aabb.merged(&new_aabb)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use fj_math::{Aabb, Vector};
|
use fj_math::{Aabb, Vector};
|
||||||
|
|
||||||
use crate::{geometry::SurfacePath, objects::HalfEdge};
|
use crate::{geometry::SurfacePath, objects::Edge};
|
||||||
|
|
||||||
impl super::BoundingVolume<2> for HalfEdge {
|
impl super::BoundingVolume<2> for Edge {
|
||||||
fn aabb(&self) -> Option<Aabb<2>> {
|
fn aabb(&self) -> Option<Aabb<2>> {
|
||||||
match self.path() {
|
match self.path() {
|
||||||
SurfacePath::Circle(circle) => {
|
SurfacePath::Circle(circle) => {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use fj_math::{Point, Segment};
|
use fj_math::{Point, Segment};
|
||||||
|
|
||||||
use crate::{geometry::SurfacePath, objects::HalfEdge};
|
use crate::{geometry::SurfacePath, objects::Edge};
|
||||||
|
|
||||||
use super::LineSegmentIntersection;
|
use super::LineSegmentIntersection;
|
||||||
|
|
||||||
/// The intersection between a curve and a [`HalfEdge`]
|
/// The intersection between a curve and an [`Edge`]
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub enum CurveEdgeIntersection {
|
pub enum CurveEdgeIntersection {
|
||||||
/// The curve and edge intersect at a point
|
/// The curve and edge intersect at a point
|
||||||
@ -26,23 +26,22 @@ impl CurveEdgeIntersection {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Currently, only intersections between lines and line segments can be
|
/// Currently, only intersections between lines and line segments can be
|
||||||
/// computed. Panics, if a different type of curve or [`HalfEdge`] is
|
/// computed. Panics, if a different type of curve or [`Edge`] is passed.
|
||||||
/// passed.
|
pub fn compute(path: &SurfacePath, edge: &Edge) -> Option<Self> {
|
||||||
pub fn compute(path: &SurfacePath, half_edge: &HalfEdge) -> Option<Self> {
|
|
||||||
let path_as_line = match path {
|
let path_as_line = match path {
|
||||||
SurfacePath::Line(line) => line,
|
SurfacePath::Line(line) => line,
|
||||||
_ => todo!("Curve-edge intersection only supports lines"),
|
_ => todo!("Curve-edge intersection only supports lines"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let edge_as_segment = {
|
let edge_as_segment = {
|
||||||
let edge_path_as_line = match half_edge.path() {
|
let edge_path_as_line = match edge.path() {
|
||||||
SurfacePath::Line(line) => line,
|
SurfacePath::Line(line) => line,
|
||||||
_ => {
|
_ => {
|
||||||
todo!("Curve-edge intersection only supports line segments")
|
todo!("Curve-edge intersection only supports line segments")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let edge_vertices = half_edge
|
let edge_vertices = edge
|
||||||
.boundary()
|
.boundary()
|
||||||
.inner
|
.inner
|
||||||
.map(|point| edge_path_as_line.point_from_line_coords(point));
|
.map(|point| edge_path_as_line.point_from_line_coords(point));
|
||||||
@ -73,7 +72,7 @@ mod tests {
|
|||||||
use fj_math::Point;
|
use fj_math::Point;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::SurfacePath, objects::HalfEdge, operations::BuildHalfEdge,
|
geometry::SurfacePath, objects::Edge, operations::BuildEdge,
|
||||||
services::Services,
|
services::Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -84,10 +83,10 @@ mod tests {
|
|||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let path = SurfacePath::u_axis();
|
let path = SurfacePath::u_axis();
|
||||||
let half_edge =
|
let edge =
|
||||||
HalfEdge::line_segment([[1., -1.], [1., 1.]], None, &mut services);
|
Edge::line_segment([[1., -1.], [1., 1.]], None, &mut services);
|
||||||
|
|
||||||
let intersection = CurveEdgeIntersection::compute(&path, &half_edge);
|
let intersection = CurveEdgeIntersection::compute(&path, &edge);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
intersection,
|
intersection,
|
||||||
@ -102,13 +101,10 @@ mod tests {
|
|||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let path = SurfacePath::u_axis();
|
let path = SurfacePath::u_axis();
|
||||||
let half_edge = HalfEdge::line_segment(
|
let edge =
|
||||||
[[-1., -1.], [-1., 1.]],
|
Edge::line_segment([[-1., -1.], [-1., 1.]], None, &mut services);
|
||||||
None,
|
|
||||||
&mut services,
|
|
||||||
);
|
|
||||||
|
|
||||||
let intersection = CurveEdgeIntersection::compute(&path, &half_edge);
|
let intersection = CurveEdgeIntersection::compute(&path, &edge);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
intersection,
|
intersection,
|
||||||
@ -123,13 +119,10 @@ mod tests {
|
|||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let path = SurfacePath::u_axis();
|
let path = SurfacePath::u_axis();
|
||||||
let half_edge = HalfEdge::line_segment(
|
let edge =
|
||||||
[[-1., -1.], [1., -1.]],
|
Edge::line_segment([[-1., -1.], [1., -1.]], None, &mut services);
|
||||||
None,
|
|
||||||
&mut services,
|
|
||||||
);
|
|
||||||
|
|
||||||
let intersection = CurveEdgeIntersection::compute(&path, &half_edge);
|
let intersection = CurveEdgeIntersection::compute(&path, &edge);
|
||||||
|
|
||||||
assert!(intersection.is_none());
|
assert!(intersection.is_none());
|
||||||
}
|
}
|
||||||
@ -139,10 +132,10 @@ mod tests {
|
|||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let path = SurfacePath::u_axis();
|
let path = SurfacePath::u_axis();
|
||||||
let half_edge =
|
let edge =
|
||||||
HalfEdge::line_segment([[-1., 0.], [1., 0.]], None, &mut services);
|
Edge::line_segment([[-1., 0.], [1., 0.]], None, &mut services);
|
||||||
|
|
||||||
let intersection = CurveEdgeIntersection::compute(&path, &half_edge);
|
let intersection = CurveEdgeIntersection::compute(&path, &edge);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
intersection,
|
intersection,
|
||||||
|
@ -29,15 +29,12 @@ impl CurveFaceIntersection {
|
|||||||
|
|
||||||
/// Compute the intersection
|
/// Compute the intersection
|
||||||
pub fn compute(path: &SurfacePath, face: &Face) -> Self {
|
pub fn compute(path: &SurfacePath, face: &Face) -> Self {
|
||||||
let half_edges = face
|
let edges = face.region().all_cycles().flat_map(|cycle| cycle.edges());
|
||||||
.region()
|
|
||||||
.all_cycles()
|
|
||||||
.flat_map(|cycle| cycle.half_edges());
|
|
||||||
|
|
||||||
let mut intersections = Vec::new();
|
let mut intersections = Vec::new();
|
||||||
|
|
||||||
for half_edge in half_edges {
|
for edge in edges {
|
||||||
let intersection = CurveEdgeIntersection::compute(path, half_edge);
|
let intersection = CurveEdgeIntersection::compute(path, edge);
|
||||||
|
|
||||||
if let Some(intersection) = intersection {
|
if let Some(intersection) = intersection {
|
||||||
match intersection {
|
match intersection {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use fj_math::Point;
|
use fj_math::Point;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{Face, HalfEdge},
|
objects::{Edge, Face},
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -28,13 +28,13 @@ impl Intersect for (&Face, &Point<2>) {
|
|||||||
// as long as we initialize the `previous_hit` variable with the
|
// as long as we initialize the `previous_hit` variable with the
|
||||||
// result of the last segment.
|
// result of the last segment.
|
||||||
let mut previous_hit = cycle
|
let mut previous_hit = cycle
|
||||||
.half_edges()
|
.edges()
|
||||||
.last()
|
.last()
|
||||||
.cloned()
|
.cloned()
|
||||||
.and_then(|edge| (&ray, &edge).intersect());
|
.and_then(|edge| (&ray, &edge).intersect());
|
||||||
|
|
||||||
for (half_edge, next_half_edge) in cycle.half_edge_pairs() {
|
for (edge, next_edge) in cycle.edge_pairs() {
|
||||||
let hit = (&ray, half_edge).intersect();
|
let hit = (&ray, edge).intersect();
|
||||||
|
|
||||||
let count_hit = match (hit, previous_hit) {
|
let count_hit = match (hit, previous_hit) {
|
||||||
(
|
(
|
||||||
@ -44,17 +44,17 @@ impl Intersect for (&Face, &Point<2>) {
|
|||||||
// If the ray starts on the boundary of the face,
|
// If the ray starts on the boundary of the face,
|
||||||
// there's nothing to else check.
|
// there's nothing to else check.
|
||||||
return Some(FacePointIntersection::PointIsOnEdge(
|
return Some(FacePointIntersection::PointIsOnEdge(
|
||||||
half_edge.clone()
|
edge.clone()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
(Some(RaySegmentIntersection::RayStartsOnOnFirstVertex), _) => {
|
(Some(RaySegmentIntersection::RayStartsOnOnFirstVertex), _) => {
|
||||||
let vertex = half_edge.start_position();
|
let vertex = edge.start_position();
|
||||||
return Some(
|
return Some(
|
||||||
FacePointIntersection::PointIsOnVertex(vertex)
|
FacePointIntersection::PointIsOnVertex(vertex)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(Some(RaySegmentIntersection::RayStartsOnSecondVertex), _) => {
|
(Some(RaySegmentIntersection::RayStartsOnSecondVertex), _) => {
|
||||||
let vertex = next_half_edge.start_position();
|
let vertex = next_edge.start_position();
|
||||||
return Some(
|
return Some(
|
||||||
FacePointIntersection::PointIsOnVertex(vertex)
|
FacePointIntersection::PointIsOnVertex(vertex)
|
||||||
);
|
);
|
||||||
@ -122,7 +122,7 @@ pub enum FacePointIntersection {
|
|||||||
PointIsInsideFace,
|
PointIsInsideFace,
|
||||||
|
|
||||||
/// The point is coincident with an edge
|
/// The point is coincident with an edge
|
||||||
PointIsOnEdge(Handle<HalfEdge>),
|
PointIsOnEdge(Handle<Edge>),
|
||||||
|
|
||||||
/// The point is coincident with a vertex
|
/// The point is coincident with a vertex
|
||||||
PointIsOnVertex(Point<2>),
|
PointIsOnVertex(Point<2>),
|
||||||
@ -335,7 +335,7 @@ mod tests {
|
|||||||
let edge = face
|
let edge = face
|
||||||
.region()
|
.region()
|
||||||
.exterior()
|
.exterior()
|
||||||
.half_edges()
|
.edges()
|
||||||
.find(|edge| edge.start_position() == Point::from([0., 0.]))
|
.find(|edge| edge.start_position() == Point::from([0., 0.]))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -370,11 +370,9 @@ mod tests {
|
|||||||
let vertex = face
|
let vertex = face
|
||||||
.region()
|
.region()
|
||||||
.exterior()
|
.exterior()
|
||||||
.half_edges()
|
.edges()
|
||||||
.find(|half_edge| {
|
.find(|edge| edge.start_position() == Point::from([1., 0.]))
|
||||||
half_edge.start_position() == Point::from([1., 0.])
|
.map(|edge| edge.start_position())
|
||||||
})
|
|
||||||
.map(|half_edge| half_edge.start_position())
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
intersection,
|
intersection,
|
||||||
|
@ -5,13 +5,13 @@ use fj_math::Segment;
|
|||||||
use crate::{
|
use crate::{
|
||||||
algorithms::intersect::{HorizontalRayToTheRight, Intersect},
|
algorithms::intersect::{HorizontalRayToTheRight, Intersect},
|
||||||
geometry::SurfacePath,
|
geometry::SurfacePath,
|
||||||
objects::HalfEdge,
|
objects::Edge,
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ray_segment::RaySegmentIntersection;
|
use super::ray_segment::RaySegmentIntersection;
|
||||||
|
|
||||||
impl Intersect for (&HorizontalRayToTheRight<2>, &Handle<HalfEdge>) {
|
impl Intersect for (&HorizontalRayToTheRight<2>, &Handle<Edge>) {
|
||||||
type Intersection = RaySegmentIntersection;
|
type Intersection = RaySegmentIntersection;
|
||||||
|
|
||||||
fn intersect(self) -> Option<Self::Intersection> {
|
fn intersect(self) -> Option<Self::Intersection> {
|
||||||
|
@ -5,7 +5,7 @@ use fj_math::{Plane, Point, Scalar};
|
|||||||
use crate::{
|
use crate::{
|
||||||
algorithms::intersect::face_point::FacePointIntersection,
|
algorithms::intersect::face_point::FacePointIntersection,
|
||||||
geometry::GlobalPath,
|
geometry::GlobalPath,
|
||||||
objects::{Face, HalfEdge},
|
objects::{Edge, Face},
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ pub enum RayFaceIntersection {
|
|||||||
RayHitsFaceAndAreParallel,
|
RayHitsFaceAndAreParallel,
|
||||||
|
|
||||||
/// The ray hits an edge
|
/// The ray hits an edge
|
||||||
RayHitsEdge(Handle<HalfEdge>),
|
RayHitsEdge(Handle<Edge>),
|
||||||
|
|
||||||
/// The ray hits a vertex
|
/// The ray hits a vertex
|
||||||
RayHitsVertex(Point<2>),
|
RayHitsVertex(Point<2>),
|
||||||
@ -262,7 +262,7 @@ mod tests {
|
|||||||
let edge = face
|
let edge = face
|
||||||
.region()
|
.region()
|
||||||
.exterior()
|
.exterior()
|
||||||
.half_edges()
|
.edges()
|
||||||
.find(|edge| edge.start_position() == Point::from([-1., 1.]))
|
.find(|edge| edge.start_position() == Point::from([-1., 1.]))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -297,11 +297,9 @@ mod tests {
|
|||||||
let vertex = face
|
let vertex = face
|
||||||
.region()
|
.region()
|
||||||
.exterior()
|
.exterior()
|
||||||
.half_edges()
|
.edges()
|
||||||
.find(|half_edge| {
|
.find(|edge| edge.start_position() == Point::from([-1., -1.]))
|
||||||
half_edge.start_position() == Point::from([-1., -1.])
|
.map(|edge| edge.start_position())
|
||||||
})
|
|
||||||
.map(|half_edge| half_edge.start_position())
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(&ray, &face).intersect(),
|
(&ray, &face).intersect(),
|
||||||
|
@ -2,16 +2,16 @@ use fj_interop::{ext::ArrayExt, mesh::Color};
|
|||||||
use fj_math::{Point, Scalar, Vector};
|
use fj_math::{Point, Scalar, Vector};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{Cycle, Face, HalfEdge, Region, Surface, Vertex},
|
objects::{Cycle, Edge, Face, Region, Surface, Vertex},
|
||||||
operations::{BuildHalfEdge, Insert, UpdateCycle, UpdateHalfEdge},
|
operations::{BuildEdge, Insert, UpdateCycle, UpdateEdge},
|
||||||
services::Services,
|
services::Services,
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Sweep, SweepCache};
|
use super::{Sweep, SweepCache};
|
||||||
|
|
||||||
impl Sweep for (&HalfEdge, &Handle<Vertex>, &Surface, Option<Color>) {
|
impl Sweep for (&Edge, &Handle<Vertex>, &Surface, Option<Color>) {
|
||||||
type Swept = (Handle<Face>, Handle<HalfEdge>);
|
type Swept = (Handle<Face>, Handle<Edge>);
|
||||||
|
|
||||||
fn sweep_with_cache(
|
fn sweep_with_cache(
|
||||||
self,
|
self,
|
||||||
@ -80,31 +80,27 @@ impl Sweep for (&HalfEdge, &Handle<Vertex>, &Surface, Option<Color>) {
|
|||||||
.zip_ext(vertices)
|
.zip_ext(vertices)
|
||||||
.zip_ext(curves)
|
.zip_ext(curves)
|
||||||
.map(|((((boundary, start), end), start_vertex), curve)| {
|
.map(|((((boundary, start), end), start_vertex), curve)| {
|
||||||
let half_edge = {
|
let edge = {
|
||||||
let half_edge = HalfEdge::line_segment(
|
let edge = Edge::line_segment(
|
||||||
[start, end],
|
[start, end],
|
||||||
Some(boundary),
|
Some(boundary),
|
||||||
services,
|
services,
|
||||||
)
|
)
|
||||||
.replace_start_vertex(start_vertex);
|
.replace_start_vertex(start_vertex);
|
||||||
|
|
||||||
let half_edge = if let Some(curve) = curve {
|
let edge = if let Some(curve) = curve {
|
||||||
half_edge.replace_curve(curve)
|
edge.replace_curve(curve)
|
||||||
} else {
|
} else {
|
||||||
half_edge
|
edge
|
||||||
};
|
};
|
||||||
|
|
||||||
half_edge.insert(services)
|
edge.insert(services)
|
||||||
};
|
};
|
||||||
|
|
||||||
exterior = Some(
|
exterior =
|
||||||
exterior
|
Some(exterior.take().unwrap().add_edges([edge.clone()]));
|
||||||
.take()
|
|
||||||
.unwrap()
|
|
||||||
.add_half_edges([half_edge.clone()]),
|
|
||||||
);
|
|
||||||
|
|
||||||
half_edge
|
edge
|
||||||
});
|
});
|
||||||
|
|
||||||
let region = Region::new(exterior.unwrap().insert(services), [], color)
|
let region = Region::new(exterior.unwrap().insert(services), [], color)
|
||||||
|
@ -61,9 +61,9 @@ impl Sweep for Handle<Face> {
|
|||||||
let cycle = cycle.reverse(services);
|
let cycle = cycle.reverse(services);
|
||||||
|
|
||||||
let mut top_edges = Vec::new();
|
let mut top_edges = Vec::new();
|
||||||
for (half_edge, next) in cycle.half_edge_pairs() {
|
for (edge, next) in cycle.edge_pairs() {
|
||||||
let (face, top_edge) = (
|
let (face, top_edge) = (
|
||||||
half_edge.deref(),
|
edge.deref(),
|
||||||
next.start_vertex(),
|
next.start_vertex(),
|
||||||
self.surface().deref(),
|
self.surface().deref(),
|
||||||
self.region().color(),
|
self.region().color(),
|
||||||
@ -72,11 +72,7 @@ impl Sweep for Handle<Face> {
|
|||||||
|
|
||||||
faces.push(face);
|
faces.push(face);
|
||||||
|
|
||||||
top_edges.push((
|
top_edges.push((top_edge, edge.path(), edge.boundary()));
|
||||||
top_edge,
|
|
||||||
half_edge.path(),
|
|
||||||
half_edge.boundary(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let top_cycle = Cycle::empty()
|
let top_cycle = Cycle::empty()
|
||||||
|
@ -11,12 +11,11 @@ impl TransformObject for Cycle {
|
|||||||
services: &mut Services,
|
services: &mut Services,
|
||||||
cache: &mut TransformCache,
|
cache: &mut TransformCache,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let half_edges = self.half_edges().map(|half_edge| {
|
let edges = self.edges().map(|edge| {
|
||||||
half_edge
|
edge.clone()
|
||||||
.clone()
|
|
||||||
.transform_with_cache(transform, services, cache)
|
.transform_with_cache(transform, services, cache)
|
||||||
});
|
});
|
||||||
|
|
||||||
Self::new(half_edges)
|
Self::new(edges)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use fj_math::Transform;
|
use fj_math::Transform;
|
||||||
|
|
||||||
use crate::{objects::HalfEdge, services::Services};
|
use crate::{objects::Edge, services::Services};
|
||||||
|
|
||||||
use super::{TransformCache, TransformObject};
|
use super::{TransformCache, TransformObject};
|
||||||
|
|
||||||
impl TransformObject for HalfEdge {
|
impl TransformObject for Edge {
|
||||||
fn transform_with_cache(
|
fn transform_with_cache(
|
||||||
self,
|
self,
|
||||||
transform: &Transform,
|
transform: &Transform,
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
/// A curve
|
/// A curve
|
||||||
///
|
///
|
||||||
/// `Curve` represents a curve in space, but holds no data to define that curve.
|
/// `Curve` represents a curve in space, but holds no data to define that curve.
|
||||||
/// It is referenced by [`HalfEdge`], which defines the curve in the coordinates
|
/// It is referenced by [`Edge`], which defines the curve in the coordinates of
|
||||||
/// of its surface.
|
/// its surface.
|
||||||
///
|
///
|
||||||
/// `Curve` exists to allow identifying which [`HalfEdge`]s are supposed to be
|
/// `Curve` exists to allow identifying which [`Edge`]s are supposed to be
|
||||||
/// coincident in global space.
|
/// coincident in global space.
|
||||||
///
|
///
|
||||||
/// # Equality
|
/// # Equality
|
||||||
@ -20,7 +20,7 @@
|
|||||||
/// `Eq`/`Ord`/..., you can use `HandleWrapper<Curve>` to do that. It will use
|
/// `Eq`/`Ord`/..., you can use `HandleWrapper<Curve>` to do that. It will use
|
||||||
/// `Handle::id` to provide those `Eq`/`Ord`/... implementations.
|
/// `Handle::id` to provide those `Eq`/`Ord`/... implementations.
|
||||||
///
|
///
|
||||||
/// [`HalfEdge`]: crate::objects::HalfEdge
|
/// [`Edge`]: crate::objects::Edge
|
||||||
#[derive(Clone, Debug, Default, Hash)]
|
#[derive(Clone, Debug, Default, Hash)]
|
||||||
pub struct Curve {}
|
pub struct Curve {}
|
||||||
|
|
||||||
|
@ -1,68 +1,61 @@
|
|||||||
use std::slice;
|
|
||||||
|
|
||||||
use fj_math::{Scalar, Winding};
|
use fj_math::{Scalar, Winding};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{geometry::SurfacePath, objects::HalfEdge, storage::Handle};
|
use crate::{geometry::SurfacePath, objects::Edge, storage::Handle};
|
||||||
|
|
||||||
/// A cycle of connected half-edges
|
/// A cycle of connected edges
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct Cycle {
|
pub struct Cycle {
|
||||||
half_edges: Vec<Handle<HalfEdge>>,
|
edges: Vec<Handle<Edge>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cycle {
|
impl Cycle {
|
||||||
/// Create an instance of `Cycle`
|
/// Create an instance of `Cycle`
|
||||||
pub fn new(half_edges: impl IntoIterator<Item = Handle<HalfEdge>>) -> Self {
|
pub fn new(edges: impl IntoIterator<Item = Handle<Edge>>) -> Self {
|
||||||
let half_edges = half_edges.into_iter().collect::<Vec<_>>();
|
let edges = edges.into_iter().collect::<Vec<_>>();
|
||||||
Self { half_edges }
|
Self { edges }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the half-edges that make up the cycle
|
/// Access the edges that make up the cycle
|
||||||
pub fn half_edges(&self) -> HalfEdgesOfCycle {
|
pub fn edges(&self) -> impl Iterator<Item = &Handle<Edge>> {
|
||||||
self.half_edges.iter()
|
self.edges.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the half-edges in pairs
|
/// Access neighboring edges in pairs
|
||||||
pub fn half_edge_pairs(
|
pub fn edge_pairs(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&Handle<HalfEdge>, &Handle<HalfEdge>)> {
|
) -> impl Iterator<Item = (&Handle<Edge>, &Handle<Edge>)> {
|
||||||
self.half_edges.iter().circular_tuple_windows()
|
self.edges.iter().circular_tuple_windows()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the half-edge with the provided index
|
/// Access the edge with the provided index
|
||||||
pub fn nth_half_edge(&self, index: usize) -> Option<&Handle<HalfEdge>> {
|
pub fn nth_edge(&self, index: usize) -> Option<&Handle<Edge>> {
|
||||||
self.half_edges.get(index)
|
self.edges.get(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the half-edge after the provided one
|
/// Access the edge after the provided one
|
||||||
///
|
///
|
||||||
/// Returns `None`, if the provided `HalfEdge` is not part of the cycle.
|
/// Returns `None`, if the provided [`Edge`] is not part of the cycle.
|
||||||
pub fn half_edge_after(
|
pub fn edge_after(&self, edge: &Handle<Edge>) -> Option<&Handle<Edge>> {
|
||||||
&self,
|
self.index_of(edge).map(|index| {
|
||||||
half_edge: &Handle<HalfEdge>,
|
let next_index = (index + 1) % self.edges.len();
|
||||||
) -> Option<&Handle<HalfEdge>> {
|
&self.edges[next_index]
|
||||||
self.index_of(half_edge).map(|index| {
|
|
||||||
let next_index = (index + 1) % self.half_edges.len();
|
|
||||||
&self.half_edges[next_index]
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the index of the provided half-edge, if it is in this cycle
|
/// Return the index of the provided edge, if it is in this cycle
|
||||||
pub fn index_of(&self, half_edge: &Handle<HalfEdge>) -> Option<usize> {
|
pub fn index_of(&self, edge: &Handle<Edge>) -> Option<usize> {
|
||||||
self.half_edges
|
self.edges.iter().position(|e| e.id() == edge.id())
|
||||||
.iter()
|
|
||||||
.position(|edge| edge.id() == half_edge.id())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the number of half-edges in the cycle
|
/// Return the number of edges in the cycle
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.half_edges.len()
|
self.edges.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicate whether the cycle is empty
|
/// Indicate whether the cycle is empty
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.half_edges.is_empty()
|
self.edges.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicate the cycle's winding, assuming a right-handed coordinate system
|
/// Indicate the cycle's winding, assuming a right-handed coordinate system
|
||||||
@ -74,11 +67,11 @@ impl Cycle {
|
|||||||
// The cycle could be made up of one or two circles. If that is the
|
// The cycle could be made up of one or two circles. If that is the
|
||||||
// case, the winding of the cycle is determined by the winding of the
|
// case, the winding of the cycle is determined by the winding of the
|
||||||
// first circle.
|
// first circle.
|
||||||
if self.half_edges.len() < 3 {
|
if self.edges.len() < 3 {
|
||||||
let first = self
|
let first = self
|
||||||
.half_edges()
|
.edges()
|
||||||
.next()
|
.next()
|
||||||
.expect("Invalid cycle: expected at least one half-edge");
|
.expect("Invalid cycle: expected at least one edge");
|
||||||
|
|
||||||
let [a, b] = first.boundary().inner;
|
let [a, b] = first.boundary().inner;
|
||||||
let edge_direction_positive = a < b;
|
let edge_direction_positive = a < b;
|
||||||
@ -104,8 +97,8 @@ impl Cycle {
|
|||||||
|
|
||||||
let mut sum = Scalar::ZERO;
|
let mut sum = Scalar::ZERO;
|
||||||
|
|
||||||
for (a, b) in self.half_edge_pairs() {
|
for (a, b) in self.edge_pairs() {
|
||||||
let [a, b] = [a, b].map(|half_edge| half_edge.start_position());
|
let [a, b] = [a, b].map(|edge| edge.start_position());
|
||||||
|
|
||||||
sum += (b.u - a.u) * (b.v + a.v);
|
sum += (b.u - a.u) * (b.v + a.v);
|
||||||
}
|
}
|
||||||
@ -120,8 +113,3 @@ impl Cycle {
|
|||||||
unreachable!("Encountered invalid cycle: {self:#?}");
|
unreachable!("Encountered invalid cycle: {self:#?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator over the half-edges of a [`Cycle`]
|
|
||||||
///
|
|
||||||
/// Returned by [`Cycle::half_edges`].
|
|
||||||
pub type HalfEdgesOfCycle<'a> = slice::Iter<'a, Handle<HalfEdge>>;
|
|
||||||
|
@ -9,36 +9,36 @@ use crate::{
|
|||||||
/// A directed edge, defined in a surface's 2D space
|
/// A directed edge, defined in a surface's 2D space
|
||||||
///
|
///
|
||||||
/// When multiple faces, which are bound by edges, are combined to form a solid,
|
/// 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 `Edge`s that bound the face on the surface are then coincident with the
|
||||||
/// the `HalfEdge`s of other faces, where those faces touch. Those coincident
|
/// `Edge`s of other faces, where those faces touch. Those coincident `Edge`s
|
||||||
/// `HalfEdge`s are different representations of the same edge, and this fact
|
/// are different representations of the same edge, and this fact must be
|
||||||
/// must be represented in the following way:
|
/// represented in the following way:
|
||||||
///
|
///
|
||||||
/// - The coincident `HalfEdge`s must refer to the same `Curve`.
|
/// - The coincident `Edge`s must refer to the same `Curve`.
|
||||||
/// - The coincident `HalfEdge`s must have the same boundary.
|
/// - The coincident `Edge`s must have the same boundary.
|
||||||
///
|
///
|
||||||
/// There is another, implicit requirement hidden here:
|
/// There is another, implicit requirement hidden here:
|
||||||
///
|
///
|
||||||
/// `HalfEdge`s that are coincident, i.e. located in the same space, must always
|
/// `Edge`s that are coincident, i.e. located in the same space, must always be
|
||||||
/// be congruent. This means they must coincide *exactly*. The overlap must be
|
/// congruent. This means they must coincide *exactly*. The overlap must be
|
||||||
/// complete. None of the coincident `HalfEdge`s must overlap with just a
|
/// complete. None of the coincident `Edge`s must overlap with just a section of
|
||||||
/// section of another.
|
/// another.
|
||||||
///
|
///
|
||||||
/// # Implementation Note
|
/// # Implementation Note
|
||||||
///
|
///
|
||||||
/// The limitation that coincident `HalfEdge`s must be congruent is currently
|
/// The limitation that coincident `Edge`s must be congruent is currently
|
||||||
/// being lifted:
|
/// being lifted:
|
||||||
/// <https://github.com/hannobraun/fornjot/issues/1937>
|
/// <https://github.com/hannobraun/fornjot/issues/1937>
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct HalfEdge {
|
pub struct Edge {
|
||||||
path: SurfacePath,
|
path: SurfacePath,
|
||||||
boundary: CurveBoundary<Point<1>>,
|
boundary: CurveBoundary<Point<1>>,
|
||||||
curve: HandleWrapper<Curve>,
|
curve: HandleWrapper<Curve>,
|
||||||
start_vertex: HandleWrapper<Vertex>,
|
start_vertex: HandleWrapper<Vertex>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HalfEdge {
|
impl Edge {
|
||||||
/// Create an instance of `HalfEdge`
|
/// Create an instance of `Edge`
|
||||||
pub fn new(
|
pub fn new(
|
||||||
path: SurfacePath,
|
path: SurfacePath,
|
||||||
boundary: impl Into<CurveBoundary<Point<1>>>,
|
boundary: impl Into<CurveBoundary<Point<1>>>,
|
||||||
@ -53,32 +53,32 @@ impl HalfEdge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the curve that defines the half-edge's geometry
|
/// Access the curve that defines the edge's geometry
|
||||||
pub fn path(&self) -> SurfacePath {
|
pub fn path(&self) -> SurfacePath {
|
||||||
self.path
|
self.path
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the boundary points of the half-edge on the curve
|
/// Access the boundary points of the edge on the curve
|
||||||
pub fn boundary(&self) -> CurveBoundary<Point<1>> {
|
pub fn boundary(&self) -> CurveBoundary<Point<1>> {
|
||||||
self.boundary
|
self.boundary
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the surface position where the half-edge starts
|
/// Compute the surface position where the edge starts
|
||||||
pub fn start_position(&self) -> Point<2> {
|
pub fn start_position(&self) -> Point<2> {
|
||||||
// Computing the surface position from the curve position is fine.
|
// Computing the surface position from the curve position is fine.
|
||||||
// `HalfEdge` "owns" its start position. There is no competing code that
|
// `Edge` "owns" its start position. There is no competing code that
|
||||||
// could compute the surface position from slightly different data.
|
// could compute the surface position from slightly different data.
|
||||||
|
|
||||||
let [start, _] = self.boundary.inner;
|
let [start, _] = self.boundary.inner;
|
||||||
self.path.point_from_path_coords(start)
|
self.path.point_from_path_coords(start)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the curve of the half-edge
|
/// Access the curve of the edge
|
||||||
pub fn curve(&self) -> &Handle<Curve> {
|
pub fn curve(&self) -> &Handle<Curve> {
|
||||||
&self.curve
|
&self.curve
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the vertex from where this half-edge starts
|
/// Access the vertex from where this edge starts
|
||||||
pub fn start_vertex(&self) -> &Handle<Vertex> {
|
pub fn start_vertex(&self) -> &Handle<Vertex> {
|
||||||
&self.start_vertex
|
&self.start_vertex
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,10 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// Interior cycles must have the opposite winding of the exterior cycle,
|
/// Interior cycles must have the opposite winding of the exterior cycle,
|
||||||
/// meaning on the front side of the face, they must appear clockwise. This
|
/// meaning on the front side of the face, they must appear clockwise. This
|
||||||
/// means that all [`HalfEdge`]s that bound a `Face` have the interior of the
|
/// means that all [`Edge`]s that bound a `Face` have the interior of the face
|
||||||
/// face on their left side (on the face's front side).
|
/// on their left side (on the face's front side).
|
||||||
///
|
///
|
||||||
/// [`HalfEdge`]: crate::objects::HalfEdge
|
/// [`Edge`]: crate::objects::Edge
|
||||||
/// [`Shell`]: crate::objects::Shell
|
/// [`Shell`]: crate::objects::Shell
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct Face {
|
pub struct Face {
|
||||||
|
@ -7,10 +7,10 @@ use crate::{objects::Cycle, storage::Handle};
|
|||||||
///
|
///
|
||||||
/// Interior cycles must have the opposite winding of the exterior cycle,
|
/// Interior cycles must have the opposite winding of the exterior cycle,
|
||||||
/// meaning on the front side of the region, they must appear clockwise. This
|
/// meaning on the front side of the region, they must appear clockwise. This
|
||||||
/// means that all [`HalfEdge`]s that bound a `Region` have the interior of the
|
/// means that all [`Edge`]s that bound a `Region` have the interior of the
|
||||||
/// region on their left side (on the region's front side).
|
/// region on their left side (on the region's front side).
|
||||||
///
|
///
|
||||||
/// [`HalfEdge`]: crate::objects::HalfEdge
|
/// [`Edge`]: crate::objects::Edge
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct Region {
|
pub struct Region {
|
||||||
exterior: Handle<Cycle>,
|
exterior: Handle<Cycle>,
|
||||||
|
@ -47,8 +47,8 @@ mod stores;
|
|||||||
pub use self::{
|
pub use self::{
|
||||||
kinds::{
|
kinds::{
|
||||||
curve::Curve,
|
curve::Curve,
|
||||||
cycle::{Cycle, HalfEdgesOfCycle},
|
cycle::Cycle,
|
||||||
edge::HalfEdge,
|
edge::Edge,
|
||||||
face::{Face, FaceSet, Handedness},
|
face::{Face, FaceSet, Handedness},
|
||||||
region::Region,
|
region::Region,
|
||||||
shell::Shell,
|
shell::Shell,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{
|
||||||
Curve, Cycle, Face, HalfEdge, Objects, Region, Shell, Sketch, Solid,
|
Curve, Cycle, Edge, Face, Objects, Region, Shell, Sketch, Solid,
|
||||||
Surface, Vertex,
|
Surface, Vertex,
|
||||||
},
|
},
|
||||||
storage::{Handle, HandleWrapper, ObjectId},
|
storage::{Handle, HandleWrapper, ObjectId},
|
||||||
@ -94,7 +94,7 @@ object!(
|
|||||||
Curve, "curve", curves;
|
Curve, "curve", curves;
|
||||||
Cycle, "cycle", cycles;
|
Cycle, "cycle", cycles;
|
||||||
Face, "face", faces;
|
Face, "face", faces;
|
||||||
HalfEdge, "half-edge", half_edges;
|
Edge, "edge", edges;
|
||||||
Region, "region", regions;
|
Region, "region", regions;
|
||||||
Shell, "shell", shells;
|
Shell, "shell", shells;
|
||||||
Sketch, "sketch", sketches;
|
Sketch, "sketch", sketches;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use std::collections::{btree_set, BTreeSet};
|
use std::collections::{btree_set, BTreeSet};
|
||||||
|
|
||||||
use super::{
|
use super::{BehindHandle, Curve, Cycle, Edge, Face, Object, Surface, Vertex};
|
||||||
BehindHandle, Curve, Cycle, Face, HalfEdge, Object, Surface, Vertex,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A graph of objects and their relationships
|
/// A graph of objects and their relationships
|
||||||
pub struct ObjectSet {
|
pub struct ObjectSet {
|
||||||
@ -63,9 +61,9 @@ impl InsertIntoSet for Curve {
|
|||||||
|
|
||||||
impl InsertIntoSet for Cycle {
|
impl InsertIntoSet for Cycle {
|
||||||
fn insert_into_set(&self, objects: &mut ObjectSet) {
|
fn insert_into_set(&self, objects: &mut ObjectSet) {
|
||||||
for half_edge in self.half_edges() {
|
for edge in self.edges() {
|
||||||
objects.inner.insert(half_edge.clone().into());
|
objects.inner.insert(edge.clone().into());
|
||||||
half_edge.insert_into_set(objects);
|
edge.insert_into_set(objects);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,7 +87,7 @@ impl InsertIntoSet for Face {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InsertIntoSet for HalfEdge {
|
impl InsertIntoSet for Edge {
|
||||||
fn insert_into_set(&self, objects: &mut ObjectSet) {
|
fn insert_into_set(&self, objects: &mut ObjectSet) {
|
||||||
objects.inner.insert(self.curve().clone().into());
|
objects.inner.insert(self.curve().clone().into());
|
||||||
self.curve().insert_into_set(objects);
|
self.curve().insert_into_set(objects);
|
||||||
|
@ -6,7 +6,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Curve, Cycle, Face, HalfEdge, Region, Shell, Sketch, Solid, Surface, Vertex,
|
Curve, Cycle, Edge, Face, Region, Shell, Sketch, Solid, Surface, Vertex,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The available object stores
|
/// The available object stores
|
||||||
@ -18,12 +18,12 @@ pub struct Objects {
|
|||||||
/// Store for [`Cycle`]s
|
/// Store for [`Cycle`]s
|
||||||
pub cycles: Store<Cycle>,
|
pub cycles: Store<Cycle>,
|
||||||
|
|
||||||
|
/// Store for [`Edge`]s
|
||||||
|
pub edges: Store<Edge>,
|
||||||
|
|
||||||
/// Store for [`Face`]s
|
/// Store for [`Face`]s
|
||||||
pub faces: Store<Face>,
|
pub faces: Store<Face>,
|
||||||
|
|
||||||
/// Store for [`HalfEdge`]s
|
|
||||||
pub half_edges: Store<HalfEdge>,
|
|
||||||
|
|
||||||
/// Store for [`Region`]s
|
/// Store for [`Region`]s
|
||||||
pub regions: Store<Region>,
|
pub regions: Store<Region>,
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ use fj_math::{Point, Scalar};
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{Cycle, HalfEdge},
|
objects::{Cycle, Edge},
|
||||||
operations::{BuildHalfEdge, Insert, UpdateCycle},
|
operations::{BuildEdge, Insert, UpdateCycle},
|
||||||
services::Services,
|
services::Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -20,9 +20,8 @@ pub trait BuildCycle {
|
|||||||
radius: impl Into<Scalar>,
|
radius: impl Into<Scalar>,
|
||||||
services: &mut Services,
|
services: &mut Services,
|
||||||
) -> Cycle {
|
) -> Cycle {
|
||||||
let circle =
|
let circle = Edge::circle(center, radius, services).insert(services);
|
||||||
HalfEdge::circle(center, radius, services).insert(services);
|
Cycle::empty().add_edges([circle])
|
||||||
Cycle::empty().add_half_edges([circle])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a polygon
|
/// Build a polygon
|
||||||
@ -32,16 +31,16 @@ pub trait BuildCycle {
|
|||||||
Ps: IntoIterator<Item = P>,
|
Ps: IntoIterator<Item = P>,
|
||||||
Ps::IntoIter: Clone + ExactSizeIterator,
|
Ps::IntoIter: Clone + ExactSizeIterator,
|
||||||
{
|
{
|
||||||
let half_edges = points
|
let edges = points
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.circular_tuple_windows()
|
.circular_tuple_windows()
|
||||||
.map(|(start, end)| {
|
.map(|(start, end)| {
|
||||||
HalfEdge::line_segment([start, end], None, services)
|
Edge::line_segment([start, end], None, services)
|
||||||
.insert(services)
|
.insert(services)
|
||||||
});
|
});
|
||||||
|
|
||||||
Cycle::new(half_edges)
|
Cycle::new(edges)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,23 +3,23 @@ use fj_math::{Arc, Point, Scalar};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{CurveBoundary, SurfacePath},
|
geometry::{CurveBoundary, SurfacePath},
|
||||||
objects::{Curve, HalfEdge, Vertex},
|
objects::{Curve, Edge, Vertex},
|
||||||
operations::Insert,
|
operations::Insert,
|
||||||
services::Services,
|
services::Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Build a [`HalfEdge`]
|
/// Build an [`Edge`]
|
||||||
pub trait BuildHalfEdge {
|
pub trait BuildEdge {
|
||||||
/// Create a half-edge that is not joined to another
|
/// Create an edge that is not joined to another
|
||||||
fn unjoined(
|
fn unjoined(
|
||||||
path: SurfacePath,
|
path: SurfacePath,
|
||||||
boundary: impl Into<CurveBoundary<Point<1>>>,
|
boundary: impl Into<CurveBoundary<Point<1>>>,
|
||||||
services: &mut Services,
|
services: &mut Services,
|
||||||
) -> HalfEdge {
|
) -> Edge {
|
||||||
let curve = Curve::new().insert(services);
|
let curve = Curve::new().insert(services);
|
||||||
let start_vertex = Vertex::new().insert(services);
|
let start_vertex = Vertex::new().insert(services);
|
||||||
|
|
||||||
HalfEdge::new(path, boundary, curve, start_vertex)
|
Edge::new(path, boundary, curve, start_vertex)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an arc
|
/// Create an arc
|
||||||
@ -32,7 +32,7 @@ pub trait BuildHalfEdge {
|
|||||||
end: impl Into<Point<2>>,
|
end: impl Into<Point<2>>,
|
||||||
angle_rad: impl Into<Scalar>,
|
angle_rad: impl Into<Scalar>,
|
||||||
services: &mut Services,
|
services: &mut Services,
|
||||||
) -> HalfEdge {
|
) -> Edge {
|
||||||
let angle_rad = angle_rad.into();
|
let angle_rad = angle_rad.into();
|
||||||
if angle_rad <= -Scalar::TAU || angle_rad >= Scalar::TAU {
|
if angle_rad <= -Scalar::TAU || angle_rad >= Scalar::TAU {
|
||||||
panic!("arc angle must be in the range (-2pi, 2pi) radians");
|
panic!("arc angle must be in the range (-2pi, 2pi) radians");
|
||||||
@ -45,7 +45,7 @@ pub trait BuildHalfEdge {
|
|||||||
let boundary =
|
let boundary =
|
||||||
[arc.start_angle, arc.end_angle].map(|coord| Point::from([coord]));
|
[arc.start_angle, arc.end_angle].map(|coord| Point::from([coord]));
|
||||||
|
|
||||||
HalfEdge::unjoined(path, boundary, services)
|
Edge::unjoined(path, boundary, services)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a circle
|
/// Create a circle
|
||||||
@ -53,12 +53,12 @@ pub trait BuildHalfEdge {
|
|||||||
center: impl Into<Point<2>>,
|
center: impl Into<Point<2>>,
|
||||||
radius: impl Into<Scalar>,
|
radius: impl Into<Scalar>,
|
||||||
services: &mut Services,
|
services: &mut Services,
|
||||||
) -> HalfEdge {
|
) -> Edge {
|
||||||
let path = SurfacePath::circle_from_center_and_radius(center, radius);
|
let path = SurfacePath::circle_from_center_and_radius(center, radius);
|
||||||
let boundary =
|
let boundary =
|
||||||
[Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord]));
|
[Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord]));
|
||||||
|
|
||||||
HalfEdge::unjoined(path, boundary, services)
|
Edge::unjoined(path, boundary, services)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a line segment
|
/// Create a line segment
|
||||||
@ -66,15 +66,15 @@ pub trait BuildHalfEdge {
|
|||||||
points_surface: [impl Into<Point<2>>; 2],
|
points_surface: [impl Into<Point<2>>; 2],
|
||||||
boundary: Option<[Point<1>; 2]>,
|
boundary: Option<[Point<1>; 2]>,
|
||||||
services: &mut Services,
|
services: &mut Services,
|
||||||
) -> HalfEdge {
|
) -> Edge {
|
||||||
let boundary =
|
let boundary =
|
||||||
boundary.unwrap_or_else(|| [[0.], [1.]].map(Point::from));
|
boundary.unwrap_or_else(|| [[0.], [1.]].map(Point::from));
|
||||||
let path = SurfacePath::line_from_points_with_coords(
|
let path = SurfacePath::line_from_points_with_coords(
|
||||||
boundary.zip_ext(points_surface),
|
boundary.zip_ext(points_surface),
|
||||||
);
|
);
|
||||||
|
|
||||||
HalfEdge::unjoined(path, boundary, services)
|
Edge::unjoined(path, boundary, services)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuildHalfEdge for HalfEdge {}
|
impl BuildEdge for Edge {}
|
||||||
|
@ -4,7 +4,7 @@ use fj_interop::ext::ArrayExt;
|
|||||||
use fj_math::Point;
|
use fj_math::Point;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{Cycle, Face, HalfEdge, Region, Surface, Vertex},
|
objects::{Cycle, Edge, Face, Region, Surface, Vertex},
|
||||||
operations::{
|
operations::{
|
||||||
BuildCycle, BuildRegion, BuildSurface, Insert, IsInserted, IsInsertedNo,
|
BuildCycle, BuildRegion, BuildSurface, Insert, IsInserted, IsInsertedNo,
|
||||||
},
|
},
|
||||||
@ -32,19 +32,20 @@ pub trait BuildFace {
|
|||||||
let face = Face::polygon(surface, points_surface, services);
|
let face = Face::polygon(surface, points_surface, services);
|
||||||
|
|
||||||
let edges = {
|
let edges = {
|
||||||
let mut half_edges = face.region().exterior().half_edges().cloned();
|
let mut edges = face.region().exterior().edges().cloned();
|
||||||
assert_eq!(half_edges.clone().count(), 3);
|
|
||||||
|
|
||||||
array::from_fn(|_| half_edges.next()).map(|half_edge| {
|
let array = array::from_fn(|_| edges.next()).map(|edge| {
|
||||||
half_edge
|
edge.expect("Just asserted that there are three edges")
|
||||||
.expect("Just asserted that there are three half-edges")
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let vertices =
|
|
||||||
edges.each_ref_ext().map(|half_edge: &Handle<HalfEdge>| {
|
|
||||||
half_edge.start_vertex().clone()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
assert!(edges.next().is_none());
|
||||||
|
|
||||||
|
array
|
||||||
|
};
|
||||||
|
let vertices = edges
|
||||||
|
.each_ref_ext()
|
||||||
|
.map(|edge: &Handle<Edge>| edge.start_vertex().clone());
|
||||||
|
|
||||||
Polygon {
|
Polygon {
|
||||||
face,
|
face,
|
||||||
edges,
|
edges,
|
||||||
@ -82,7 +83,7 @@ pub struct Polygon<const D: usize, I: IsInserted = IsInsertedNo> {
|
|||||||
pub face: I::T<Face>,
|
pub face: I::T<Face>,
|
||||||
|
|
||||||
/// The edges of the polygon
|
/// The edges of the polygon
|
||||||
pub edges: [Handle<HalfEdge>; D],
|
pub edges: [Handle<Edge>; D],
|
||||||
|
|
||||||
/// The vertices of the polygon
|
/// The vertices of the polygon
|
||||||
pub vertices: [Handle<Vertex>; D],
|
pub vertices: [Handle<Vertex>; D],
|
||||||
@ -98,7 +99,7 @@ impl<const D: usize, I: IsInserted> Polygon<D, I> {
|
|||||||
face.borrow()
|
face.borrow()
|
||||||
.region()
|
.region()
|
||||||
.exterior()
|
.exterior()
|
||||||
.nth_half_edge(i)
|
.nth_edge(i)
|
||||||
.expect("Operation should not have changed length of cycle")
|
.expect("Operation should not have changed length of cycle")
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
@ -109,7 +110,7 @@ impl<const D: usize, I: IsInserted> Polygon<D, I> {
|
|||||||
face.borrow()
|
face.borrow()
|
||||||
.region()
|
.region()
|
||||||
.exterior()
|
.exterior()
|
||||||
.nth_half_edge(i)
|
.nth_edge(i)
|
||||||
.expect("Operation should not have changed length of cycle")
|
.expect("Operation should not have changed length of cycle")
|
||||||
.start_vertex()
|
.start_vertex()
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -46,7 +46,7 @@ pub trait BuildShell {
|
|||||||
region
|
region
|
||||||
.update_exterior(|cycle| {
|
.update_exterior(|cycle| {
|
||||||
cycle
|
cycle
|
||||||
.update_nth_half_edge(0, |edge| {
|
.update_nth_edge(0, |edge| {
|
||||||
edge.reverse_curve_coordinate_systems(services)
|
edge.reverse_curve_coordinate_systems(services)
|
||||||
.insert(services)
|
.insert(services)
|
||||||
})
|
})
|
||||||
@ -64,7 +64,7 @@ pub trait BuildShell {
|
|||||||
region
|
region
|
||||||
.update_exterior(|cycle| {
|
.update_exterior(|cycle| {
|
||||||
cycle
|
cycle
|
||||||
.update_nth_half_edge(1, |edge| {
|
.update_nth_edge(1, |edge| {
|
||||||
edge.reverse_curve_coordinate_systems(services)
|
edge.reverse_curve_coordinate_systems(services)
|
||||||
.insert(services)
|
.insert(services)
|
||||||
})
|
})
|
||||||
@ -74,7 +74,7 @@ pub trait BuildShell {
|
|||||||
2..=2,
|
2..=2,
|
||||||
services,
|
services,
|
||||||
)
|
)
|
||||||
.update_nth_half_edge(0, |edge| {
|
.update_nth_edge(0, |edge| {
|
||||||
edge.reverse_curve_coordinate_systems(services)
|
edge.reverse_curve_coordinate_systems(services)
|
||||||
.insert(services)
|
.insert(services)
|
||||||
})
|
})
|
||||||
@ -92,7 +92,7 @@ pub trait BuildShell {
|
|||||||
region
|
region
|
||||||
.update_exterior(|cycle| {
|
.update_exterior(|cycle| {
|
||||||
cycle
|
cycle
|
||||||
.update_nth_half_edge(0, |edge| {
|
.update_nth_edge(0, |edge| {
|
||||||
edge.reverse_curve_coordinate_systems(services)
|
edge.reverse_curve_coordinate_systems(services)
|
||||||
.insert(services)
|
.insert(services)
|
||||||
})
|
})
|
||||||
@ -102,7 +102,7 @@ pub trait BuildShell {
|
|||||||
1..=1,
|
1..=1,
|
||||||
services,
|
services,
|
||||||
)
|
)
|
||||||
.update_nth_half_edge(1, |edge| {
|
.update_nth_edge(1, |edge| {
|
||||||
edge.reverse_curve_coordinate_systems(services)
|
edge.reverse_curve_coordinate_systems(services)
|
||||||
.insert(services)
|
.insert(services)
|
||||||
})
|
})
|
||||||
@ -112,7 +112,7 @@ pub trait BuildShell {
|
|||||||
2..=2,
|
2..=2,
|
||||||
services,
|
services,
|
||||||
)
|
)
|
||||||
.update_nth_half_edge(2, |edge| {
|
.update_nth_edge(2, |edge| {
|
||||||
edge.reverse_curve_coordinate_systems(services)
|
edge.reverse_curve_coordinate_systems(services)
|
||||||
.insert(services)
|
.insert(services)
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{
|
||||||
Curve, Cycle, Face, HalfEdge, Region, Shell, Sketch, Solid, Surface,
|
Curve, Cycle, Edge, Face, Region, Shell, Sketch, Solid, Surface, Vertex,
|
||||||
Vertex,
|
|
||||||
},
|
},
|
||||||
operations::{Polygon, TetrahedronShell},
|
operations::{Polygon, TetrahedronShell},
|
||||||
services::Services,
|
services::Services,
|
||||||
@ -47,7 +46,7 @@ impl_insert!(
|
|||||||
Curve, curves;
|
Curve, curves;
|
||||||
Cycle, cycles;
|
Cycle, cycles;
|
||||||
Face, faces;
|
Face, faces;
|
||||||
HalfEdge, half_edges;
|
Edge, edges;
|
||||||
Region, regions;
|
Region, regions;
|
||||||
Shell, shells;
|
Shell, shells;
|
||||||
Sketch, sketches;
|
Sketch, sketches;
|
||||||
|
@ -5,8 +5,8 @@ use itertools::Itertools;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{CurveBoundary, SurfacePath},
|
geometry::{CurveBoundary, SurfacePath},
|
||||||
objects::{Cycle, HalfEdge},
|
objects::{Cycle, Edge},
|
||||||
operations::{BuildHalfEdge, Insert, UpdateCycle, UpdateHalfEdge},
|
operations::{BuildEdge, Insert, UpdateCycle, UpdateEdge},
|
||||||
services::Services,
|
services::Services,
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
@ -18,18 +18,18 @@ pub trait JoinCycle {
|
|||||||
fn add_joined_edges<Es>(&self, edges: Es, services: &mut Services) -> Self
|
fn add_joined_edges<Es>(&self, edges: Es, services: &mut Services) -> Self
|
||||||
where
|
where
|
||||||
Es: IntoIterator<
|
Es: IntoIterator<
|
||||||
Item = (Handle<HalfEdge>, SurfacePath, CurveBoundary<Point<1>>),
|
Item = (Handle<Edge>, SurfacePath, CurveBoundary<Point<1>>),
|
||||||
>,
|
>,
|
||||||
Es::IntoIter: Clone + ExactSizeIterator;
|
Es::IntoIter: Clone + ExactSizeIterator;
|
||||||
|
|
||||||
/// Join the cycle to another
|
/// Join the cycle to another
|
||||||
///
|
///
|
||||||
/// Joins the cycle to the other at the provided ranges. The ranges specify
|
/// Joins the cycle to the other at the provided ranges. The ranges specify
|
||||||
/// the indices of the half-edges that are joined together.
|
/// the indices of the edges that are joined together.
|
||||||
///
|
///
|
||||||
/// A modulo operation is applied to all indices before use, so in a cycle
|
/// A modulo operation is applied to all indices before use, so in a cycle
|
||||||
/// of 3 half-edges, indices `0` and `3` refer to the same half-edge. This
|
/// of 3 edges, indices `0` and `3` refer to the same edge. This allows for
|
||||||
/// allows for specifying a range that crosses the "seam" of the cycle.
|
/// specifying a range that crosses the "seam" of the cycle.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -40,7 +40,7 @@ pub trait JoinCycle {
|
|||||||
/// This method makes some assumptions that need to be met, if the operation
|
/// This method makes some assumptions that need to be met, if the operation
|
||||||
/// is to result in a valid shape:
|
/// is to result in a valid shape:
|
||||||
///
|
///
|
||||||
/// - **The joined half-edges must be coincident.**
|
/// - **The joined edges must be coincident.**
|
||||||
/// - **The locally defined curve coordinate systems of the edges must
|
/// - **The locally defined curve coordinate systems of the edges must
|
||||||
/// match.**
|
/// match.**
|
||||||
///
|
///
|
||||||
@ -77,14 +77,14 @@ impl JoinCycle for Cycle {
|
|||||||
fn add_joined_edges<Es>(&self, edges: Es, services: &mut Services) -> Self
|
fn add_joined_edges<Es>(&self, edges: Es, services: &mut Services) -> Self
|
||||||
where
|
where
|
||||||
Es: IntoIterator<
|
Es: IntoIterator<
|
||||||
Item = (Handle<HalfEdge>, SurfacePath, CurveBoundary<Point<1>>),
|
Item = (Handle<Edge>, SurfacePath, CurveBoundary<Point<1>>),
|
||||||
>,
|
>,
|
||||||
Es::IntoIter: Clone + ExactSizeIterator,
|
Es::IntoIter: Clone + ExactSizeIterator,
|
||||||
{
|
{
|
||||||
self.add_half_edges(edges.into_iter().circular_tuple_windows().map(
|
self.add_edges(edges.into_iter().circular_tuple_windows().map(
|
||||||
|((prev, _, _), (half_edge, curve, boundary))| {
|
|((prev, _, _), (edge, curve, boundary))| {
|
||||||
HalfEdge::unjoined(curve, boundary, services)
|
Edge::unjoined(curve, boundary, services)
|
||||||
.replace_curve(half_edge.curve().clone())
|
.replace_curve(edge.curve().clone())
|
||||||
.replace_start_vertex(prev.start_vertex().clone())
|
.replace_start_vertex(prev.start_vertex().clone())
|
||||||
.insert(services)
|
.insert(services)
|
||||||
},
|
},
|
||||||
@ -110,34 +110,34 @@ impl JoinCycle for Cycle {
|
|||||||
let index = index % self.len();
|
let index = index % self.len();
|
||||||
let index_other = index_other % self.len();
|
let index_other = index_other % self.len();
|
||||||
|
|
||||||
let half_edge = self
|
let edge = self
|
||||||
.nth_half_edge(index)
|
.nth_edge(index)
|
||||||
.expect("Index must be valid, due to use of `%` above");
|
.expect("Index must be valid, due to use of `%` above");
|
||||||
let half_edge_other = other
|
let edge_other = other
|
||||||
.nth_half_edge(index_other)
|
.nth_edge(index_other)
|
||||||
.expect("Index must be valid, due to use of `%` above");
|
.expect("Index must be valid, due to use of `%` above");
|
||||||
|
|
||||||
let vertex_a = other
|
let vertex_a = other
|
||||||
.half_edge_after(half_edge_other)
|
.edge_after(edge_other)
|
||||||
.expect("Cycle must contain edge; just obtained edge from it")
|
.expect("Cycle must contain edge; just obtained edge from it")
|
||||||
.start_vertex()
|
.start_vertex()
|
||||||
.clone();
|
.clone();
|
||||||
let vertex_b = half_edge_other.start_vertex().clone();
|
let vertex_b = edge_other.start_vertex().clone();
|
||||||
|
|
||||||
let next_edge = cycle
|
let next_edge = cycle
|
||||||
.half_edge_after(half_edge)
|
.edge_after(edge)
|
||||||
.expect("Cycle must contain edge; just obtained edge from it");
|
.expect("Cycle must contain edge; just obtained edge from it");
|
||||||
|
|
||||||
let this_joined = half_edge
|
let this_joined = edge
|
||||||
.replace_curve(half_edge_other.curve().clone())
|
.replace_curve(edge_other.curve().clone())
|
||||||
.replace_start_vertex(vertex_a)
|
.replace_start_vertex(vertex_a)
|
||||||
.insert(services);
|
.insert(services);
|
||||||
let next_joined =
|
let next_joined =
|
||||||
next_edge.replace_start_vertex(vertex_b).insert(services);
|
next_edge.replace_start_vertex(vertex_b).insert(services);
|
||||||
|
|
||||||
cycle = cycle
|
cycle = cycle
|
||||||
.replace_half_edge(half_edge, this_joined)
|
.replace_edge(edge, this_joined)
|
||||||
.replace_half_edge(next_edge, next_joined)
|
.replace_edge(next_edge, next_joined)
|
||||||
}
|
}
|
||||||
|
|
||||||
cycle
|
cycle
|
||||||
|
@ -10,7 +10,7 @@ mod update;
|
|||||||
pub use self::{
|
pub use self::{
|
||||||
build::{
|
build::{
|
||||||
cycle::BuildCycle,
|
cycle::BuildCycle,
|
||||||
edge::BuildHalfEdge,
|
edge::BuildEdge,
|
||||||
face::{BuildFace, Polygon},
|
face::{BuildFace, Polygon},
|
||||||
region::BuildRegion,
|
region::BuildRegion,
|
||||||
shell::{BuildShell, TetrahedronShell},
|
shell::{BuildShell, TetrahedronShell},
|
||||||
@ -23,7 +23,7 @@ pub use self::{
|
|||||||
merge::Merge,
|
merge::Merge,
|
||||||
reverse::Reverse,
|
reverse::Reverse,
|
||||||
update::{
|
update::{
|
||||||
cycle::UpdateCycle, edge::UpdateHalfEdge, face::UpdateFace,
|
cycle::UpdateCycle, edge::UpdateEdge, face::UpdateFace,
|
||||||
region::UpdateRegion, shell::UpdateShell, sketch::UpdateSketch,
|
region::UpdateRegion, shell::UpdateShell, sketch::UpdateSketch,
|
||||||
solid::UpdateSolid,
|
solid::UpdateSolid,
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
objects::{Cycle, HalfEdge},
|
objects::{Cycle, Edge},
|
||||||
operations::Insert,
|
operations::Insert,
|
||||||
services::Services,
|
services::Services,
|
||||||
};
|
};
|
||||||
@ -9,9 +9,9 @@ use super::{Reverse, ReverseCurveCoordinateSystems};
|
|||||||
impl Reverse for Cycle {
|
impl Reverse for Cycle {
|
||||||
fn reverse(&self, services: &mut Services) -> Self {
|
fn reverse(&self, services: &mut Services) -> Self {
|
||||||
let mut edges = self
|
let mut edges = self
|
||||||
.half_edge_pairs()
|
.edge_pairs()
|
||||||
.map(|(current, next)| {
|
.map(|(current, next)| {
|
||||||
HalfEdge::new(
|
Edge::new(
|
||||||
current.path(),
|
current.path(),
|
||||||
current.boundary().reverse(),
|
current.boundary().reverse(),
|
||||||
current.curve().clone(),
|
current.curve().clone(),
|
||||||
@ -32,7 +32,7 @@ impl ReverseCurveCoordinateSystems for Cycle {
|
|||||||
&self,
|
&self,
|
||||||
services: &mut Services,
|
services: &mut Services,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let edges = self.half_edges().map(|edge| {
|
let edges = self.edges().map(|edge| {
|
||||||
edge.reverse_curve_coordinate_systems(services)
|
edge.reverse_curve_coordinate_systems(services)
|
||||||
.insert(services)
|
.insert(services)
|
||||||
});
|
});
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use crate::{objects::HalfEdge, services::Services};
|
use crate::{objects::Edge, services::Services};
|
||||||
|
|
||||||
use super::ReverseCurveCoordinateSystems;
|
use super::ReverseCurveCoordinateSystems;
|
||||||
|
|
||||||
impl ReverseCurveCoordinateSystems for HalfEdge {
|
impl ReverseCurveCoordinateSystems for Edge {
|
||||||
fn reverse_curve_coordinate_systems(&self, _: &mut Services) -> Self {
|
fn reverse_curve_coordinate_systems(&self, _: &mut Services) -> Self {
|
||||||
let path = self.path().reverse();
|
let path = self.path().reverse();
|
||||||
let boundary = self.boundary().reverse();
|
let boundary = self.boundary().reverse();
|
||||||
|
|
||||||
HalfEdge::new(
|
Edge::new(
|
||||||
path,
|
path,
|
||||||
boundary,
|
boundary,
|
||||||
self.curve().clone(),
|
self.curve().clone(),
|
||||||
|
@ -1,98 +1,92 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
objects::{Cycle, HalfEdge},
|
objects::{Cycle, Edge},
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Update a [`Cycle`]
|
/// Update a [`Cycle`]
|
||||||
pub trait UpdateCycle {
|
pub trait UpdateCycle {
|
||||||
/// Add half-edges to the cycle
|
/// Add edges to the cycle
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn add_half_edges(
|
fn add_edges(&self, edges: impl IntoIterator<Item = Handle<Edge>>) -> Self;
|
||||||
&self,
|
|
||||||
half_edges: impl IntoIterator<Item = Handle<HalfEdge>>,
|
|
||||||
) -> Self;
|
|
||||||
|
|
||||||
/// Replace the provided half-edge
|
/// Replace the provided edge
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics, unless this operation replaces exactly one half-edge.
|
/// Panics, unless this operation replaces exactly one edge.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn replace_half_edge(
|
fn replace_edge(
|
||||||
&self,
|
&self,
|
||||||
original: &Handle<HalfEdge>,
|
original: &Handle<Edge>,
|
||||||
replacement: Handle<HalfEdge>,
|
replacement: Handle<Edge>,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
|
|
||||||
/// Update the half-edge at the given index
|
/// Update the edge at the given index
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics, unless this operation updates exactly one half-edge.
|
/// Panics, unless this operation updates exactly one edge.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn update_nth_half_edge(
|
fn update_nth_edge(
|
||||||
&self,
|
&self,
|
||||||
index: usize,
|
index: usize,
|
||||||
f: impl FnMut(&Handle<HalfEdge>) -> Handle<HalfEdge>,
|
f: impl FnMut(&Handle<Edge>) -> Handle<Edge>,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateCycle for Cycle {
|
impl UpdateCycle for Cycle {
|
||||||
fn add_half_edges(
|
fn add_edges(&self, edges: impl IntoIterator<Item = Handle<Edge>>) -> Self {
|
||||||
&self,
|
let edges = self.edges().cloned().chain(edges);
|
||||||
half_edges: impl IntoIterator<Item = Handle<HalfEdge>>,
|
Cycle::new(edges)
|
||||||
) -> Self {
|
|
||||||
let half_edges = self.half_edges().cloned().chain(half_edges);
|
|
||||||
Cycle::new(half_edges)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_half_edge(
|
fn replace_edge(
|
||||||
&self,
|
&self,
|
||||||
original: &Handle<HalfEdge>,
|
original: &Handle<Edge>,
|
||||||
replacement: Handle<HalfEdge>,
|
replacement: Handle<Edge>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut num_replacements = 0;
|
let mut num_replacements = 0;
|
||||||
|
|
||||||
let half_edges = self.half_edges().map(|half_edge| {
|
let edges = self.edges().map(|edge| {
|
||||||
if half_edge.id() == original.id() {
|
if edge.id() == original.id() {
|
||||||
num_replacements += 1;
|
num_replacements += 1;
|
||||||
replacement.clone()
|
replacement.clone()
|
||||||
} else {
|
} else {
|
||||||
half_edge.clone()
|
edge.clone()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let cycle = Cycle::new(half_edges);
|
let cycle = Cycle::new(edges);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_replacements, 1,
|
num_replacements, 1,
|
||||||
"Expected operation to replace exactly one half-edge"
|
"Expected operation to replace exactly one edge"
|
||||||
);
|
);
|
||||||
|
|
||||||
cycle
|
cycle
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_nth_half_edge(
|
fn update_nth_edge(
|
||||||
&self,
|
&self,
|
||||||
index: usize,
|
index: usize,
|
||||||
mut f: impl FnMut(&Handle<HalfEdge>) -> Handle<HalfEdge>,
|
mut f: impl FnMut(&Handle<Edge>) -> Handle<Edge>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut num_replacements = 0;
|
let mut num_replacements = 0;
|
||||||
|
|
||||||
let half_edges = self.half_edges().enumerate().map(|(i, half_edge)| {
|
let edges = self.edges().enumerate().map(|(i, edge)| {
|
||||||
if i == index {
|
if i == index {
|
||||||
num_replacements += 1;
|
num_replacements += 1;
|
||||||
f(half_edge)
|
f(edge)
|
||||||
} else {
|
} else {
|
||||||
half_edge.clone()
|
edge.clone()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let cycle = Cycle::new(half_edges);
|
let cycle = Cycle::new(edges);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
num_replacements, 1,
|
num_replacements, 1,
|
||||||
"Expected operation to replace exactly one half-edge"
|
"Expected operation to replace exactly one edge"
|
||||||
);
|
);
|
||||||
|
|
||||||
cycle
|
cycle
|
||||||
|
@ -2,32 +2,32 @@ use fj_math::Point;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{CurveBoundary, SurfacePath},
|
geometry::{CurveBoundary, SurfacePath},
|
||||||
objects::{Curve, HalfEdge, Vertex},
|
objects::{Curve, Edge, Vertex},
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Update a [`HalfEdge`]
|
/// Update a [`Edge`]
|
||||||
pub trait UpdateHalfEdge {
|
pub trait UpdateEdge {
|
||||||
/// Replace the path of the half-edge
|
/// Replace the path of the edge
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn replace_path(&self, path: SurfacePath) -> Self;
|
fn replace_path(&self, path: SurfacePath) -> Self;
|
||||||
|
|
||||||
/// Replace the boundary of the half-edge
|
/// Replace the boundary of the edge
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn replace_boundary(&self, boundary: CurveBoundary<Point<1>>) -> Self;
|
fn replace_boundary(&self, boundary: CurveBoundary<Point<1>>) -> Self;
|
||||||
|
|
||||||
/// Replace the curve of the half-edge
|
/// Replace the curve of the edge
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn replace_curve(&self, curve: Handle<Curve>) -> Self;
|
fn replace_curve(&self, curve: Handle<Curve>) -> Self;
|
||||||
|
|
||||||
/// Replace the start vertex of the half-edge
|
/// Replace the start vertex of the edge
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn replace_start_vertex(&self, start_vertex: Handle<Vertex>) -> Self;
|
fn replace_start_vertex(&self, start_vertex: Handle<Vertex>) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateHalfEdge for HalfEdge {
|
impl UpdateEdge for Edge {
|
||||||
fn replace_path(&self, path: SurfacePath) -> Self {
|
fn replace_path(&self, path: SurfacePath) -> Self {
|
||||||
HalfEdge::new(
|
Edge::new(
|
||||||
path,
|
path,
|
||||||
self.boundary(),
|
self.boundary(),
|
||||||
self.curve().clone(),
|
self.curve().clone(),
|
||||||
@ -36,7 +36,7 @@ impl UpdateHalfEdge for HalfEdge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn replace_boundary(&self, boundary: CurveBoundary<Point<1>>) -> Self {
|
fn replace_boundary(&self, boundary: CurveBoundary<Point<1>>) -> Self {
|
||||||
HalfEdge::new(
|
Edge::new(
|
||||||
self.path(),
|
self.path(),
|
||||||
boundary,
|
boundary,
|
||||||
self.curve().clone(),
|
self.curve().clone(),
|
||||||
@ -45,7 +45,7 @@ impl UpdateHalfEdge for HalfEdge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn replace_curve(&self, curve: Handle<Curve>) -> Self {
|
fn replace_curve(&self, curve: Handle<Curve>) -> Self {
|
||||||
HalfEdge::new(
|
Edge::new(
|
||||||
self.path(),
|
self.path(),
|
||||||
self.boundary(),
|
self.boundary(),
|
||||||
curve,
|
curve,
|
||||||
@ -54,7 +54,7 @@ impl UpdateHalfEdge for HalfEdge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn replace_start_vertex(&self, start_vertex: Handle<Vertex>) -> Self {
|
fn replace_start_vertex(&self, start_vertex: Handle<Vertex>) -> Self {
|
||||||
HalfEdge::new(
|
Edge::new(
|
||||||
self.path(),
|
self.path(),
|
||||||
self.boundary(),
|
self.boundary(),
|
||||||
self.curve().clone(),
|
self.curve().clone(),
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
objects::{Face, HalfEdge, Shell, Surface},
|
objects::{Edge, Face, Shell, Surface},
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -8,21 +8,21 @@ pub trait AllEdgesWithSurface {
|
|||||||
/// Access all edges referenced by the object and the surface they're on
|
/// Access all edges referenced by the object and the surface they're on
|
||||||
fn all_edges_with_surface(
|
fn all_edges_with_surface(
|
||||||
&self,
|
&self,
|
||||||
result: &mut Vec<(Handle<HalfEdge>, Handle<Surface>)>,
|
result: &mut Vec<(Handle<Edge>, Handle<Surface>)>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AllEdgesWithSurface for Face {
|
impl AllEdgesWithSurface for Face {
|
||||||
fn all_edges_with_surface(
|
fn all_edges_with_surface(
|
||||||
&self,
|
&self,
|
||||||
result: &mut Vec<(Handle<HalfEdge>, Handle<Surface>)>,
|
result: &mut Vec<(Handle<Edge>, Handle<Surface>)>,
|
||||||
) {
|
) {
|
||||||
for cycle in self.region().all_cycles() {
|
for cycle in self.region().all_cycles() {
|
||||||
result.extend(
|
result.extend(
|
||||||
cycle
|
cycle
|
||||||
.half_edges()
|
.edges()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|half_edge| (half_edge, self.surface().clone())),
|
.map(|edge| (edge, self.surface().clone())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ impl AllEdgesWithSurface for Face {
|
|||||||
impl AllEdgesWithSurface for Shell {
|
impl AllEdgesWithSurface for Shell {
|
||||||
fn all_edges_with_surface(
|
fn all_edges_with_surface(
|
||||||
&self,
|
&self,
|
||||||
result: &mut Vec<(Handle<HalfEdge>, Handle<Surface>)>,
|
result: &mut Vec<(Handle<Edge>, Handle<Surface>)>,
|
||||||
) {
|
) {
|
||||||
for face in self.faces() {
|
for face in self.faces() {
|
||||||
face.all_edges_with_surface(result);
|
face.all_edges_with_surface(result);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
geometry::CurveBoundary,
|
geometry::CurveBoundary,
|
||||||
objects::{Cycle, Face, HalfEdge, Region, Shell, Vertex},
|
objects::{Cycle, Edge, Face, Region, Shell, Vertex},
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -12,17 +12,17 @@ pub trait BoundingVerticesOfEdge {
|
|||||||
/// method is called on.
|
/// method is called on.
|
||||||
fn bounding_vertices_of_edge(
|
fn bounding_vertices_of_edge(
|
||||||
&self,
|
&self,
|
||||||
edge: &Handle<HalfEdge>,
|
edge: &Handle<Edge>,
|
||||||
) -> Option<CurveBoundary<Vertex>>;
|
) -> Option<CurveBoundary<Vertex>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BoundingVerticesOfEdge for Cycle {
|
impl BoundingVerticesOfEdge for Cycle {
|
||||||
fn bounding_vertices_of_edge(
|
fn bounding_vertices_of_edge(
|
||||||
&self,
|
&self,
|
||||||
edge: &Handle<HalfEdge>,
|
edge: &Handle<Edge>,
|
||||||
) -> Option<CurveBoundary<Vertex>> {
|
) -> Option<CurveBoundary<Vertex>> {
|
||||||
let start = edge.start_vertex().clone();
|
let start = edge.start_vertex().clone();
|
||||||
let end = self.half_edge_after(edge)?.start_vertex().clone();
|
let end = self.edge_after(edge)?.start_vertex().clone();
|
||||||
|
|
||||||
Some(CurveBoundary::from([start, end]))
|
Some(CurveBoundary::from([start, end]))
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ impl BoundingVerticesOfEdge for Cycle {
|
|||||||
impl BoundingVerticesOfEdge for Region {
|
impl BoundingVerticesOfEdge for Region {
|
||||||
fn bounding_vertices_of_edge(
|
fn bounding_vertices_of_edge(
|
||||||
&self,
|
&self,
|
||||||
edge: &Handle<HalfEdge>,
|
edge: &Handle<Edge>,
|
||||||
) -> Option<CurveBoundary<Vertex>> {
|
) -> Option<CurveBoundary<Vertex>> {
|
||||||
for cycle in self.all_cycles() {
|
for cycle in self.all_cycles() {
|
||||||
if let Some(vertices) = cycle.bounding_vertices_of_edge(edge) {
|
if let Some(vertices) = cycle.bounding_vertices_of_edge(edge) {
|
||||||
@ -46,7 +46,7 @@ impl BoundingVerticesOfEdge for Region {
|
|||||||
impl BoundingVerticesOfEdge for Face {
|
impl BoundingVerticesOfEdge for Face {
|
||||||
fn bounding_vertices_of_edge(
|
fn bounding_vertices_of_edge(
|
||||||
&self,
|
&self,
|
||||||
edge: &Handle<HalfEdge>,
|
edge: &Handle<Edge>,
|
||||||
) -> Option<CurveBoundary<Vertex>> {
|
) -> Option<CurveBoundary<Vertex>> {
|
||||||
self.region().bounding_vertices_of_edge(edge)
|
self.region().bounding_vertices_of_edge(edge)
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ impl BoundingVerticesOfEdge for Face {
|
|||||||
impl BoundingVerticesOfEdge for Shell {
|
impl BoundingVerticesOfEdge for Shell {
|
||||||
fn bounding_vertices_of_edge(
|
fn bounding_vertices_of_edge(
|
||||||
&self,
|
&self,
|
||||||
edge: &Handle<HalfEdge>,
|
edge: &Handle<Edge>,
|
||||||
) -> Option<CurveBoundary<Vertex>> {
|
) -> Option<CurveBoundary<Vertex>> {
|
||||||
for face in self.faces() {
|
for face in self.faces() {
|
||||||
if let Some(vertices) = face.bounding_vertices_of_edge(edge) {
|
if let Some(vertices) = face.bounding_vertices_of_edge(edge) {
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
//! Objects have methods that provide access to anything that the object itself
|
//! Objects have methods that provide access to anything that the object itself
|
||||||
//! has direct access to. However, not all potentially interesting information
|
//! has direct access to. However, not all potentially interesting information
|
||||||
//! can be accessed that way. An example are the bounding vertices of an edge:
|
//! can be accessed that way. An example are the bounding vertices of an edge:
|
||||||
//! `HalfEdge` only stores its starting vertex, so you need a `Cycle` to get
|
//! `Edge` only stores its starting vertex, so you need a `Cycle` to get both
|
||||||
//! both vertices.
|
//! vertices.
|
||||||
//!
|
//!
|
||||||
//! This module provides traits express such non-trivial queries, and implements
|
//! This module provides traits express such non-trivial queries, and implements
|
||||||
//! them for various objects that have the information to answer the query.
|
//! them for various objects that have the information to answer the query.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use fj_math::{Point, Scalar};
|
use fj_math::{Point, Scalar};
|
||||||
|
|
||||||
use crate::objects::{Cycle, HalfEdge};
|
use crate::objects::{Cycle, Edge};
|
||||||
|
|
||||||
use super::{Validate, ValidationConfig, ValidationError};
|
use super::{Validate, ValidationConfig, ValidationError};
|
||||||
|
|
||||||
@ -10,60 +10,58 @@ impl Validate for Cycle {
|
|||||||
config: &ValidationConfig,
|
config: &ValidationConfig,
|
||||||
errors: &mut Vec<ValidationError>,
|
errors: &mut Vec<ValidationError>,
|
||||||
) {
|
) {
|
||||||
CycleValidationError::check_half_edges_disconnected(
|
CycleValidationError::check_edges_disconnected(self, config, errors);
|
||||||
self, config, errors,
|
CycleValidationError::check_enough_edges(self, config, errors);
|
||||||
);
|
|
||||||
CycleValidationError::check_enough_half_edges(self, config, errors);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`Cycle`] validation failed
|
/// [`Cycle`] validation failed
|
||||||
#[derive(Clone, Debug, thiserror::Error)]
|
#[derive(Clone, Debug, thiserror::Error)]
|
||||||
pub enum CycleValidationError {
|
pub enum CycleValidationError {
|
||||||
/// [`Cycle`]'s half-edges are not connected
|
/// [`Cycle`]'s edges are not connected
|
||||||
#[error(
|
#[error(
|
||||||
"Adjacent `HalfEdge`s are distinct\n\
|
"Adjacent `Edge`s are distinct\n\
|
||||||
- End position of first `HalfEdge`: {end_of_first:?}\n\
|
- End position of first `Edge`: {end_of_first:?}\n\
|
||||||
- Start position of second `HalfEdge`: {start_of_second:?}\n\
|
- Start position of second `Edge`: {start_of_second:?}\n\
|
||||||
- `HalfEdge`s: {half_edges:#?}"
|
- `Edge`s: {edges:#?}"
|
||||||
)]
|
)]
|
||||||
HalfEdgesDisconnected {
|
EdgesDisconnected {
|
||||||
/// The end position of the first [`HalfEdge`]
|
/// The end position of the first [`Edge`]
|
||||||
end_of_first: Point<2>,
|
end_of_first: Point<2>,
|
||||||
|
|
||||||
/// The start position of the second [`HalfEdge`]
|
/// The start position of the second [`Edge`]
|
||||||
start_of_second: Point<2>,
|
start_of_second: Point<2>,
|
||||||
|
|
||||||
/// The distance between the two vertices
|
/// The distance between the two vertices
|
||||||
distance: Scalar,
|
distance: Scalar,
|
||||||
|
|
||||||
/// The half-edge
|
/// The edges
|
||||||
half_edges: Box<(HalfEdge, HalfEdge)>,
|
edges: Box<(Edge, Edge)>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// [`Cycle`]'s should have at least one `HalfEdge`
|
/// [`Cycle`]'s should have at least one [`Edge`]
|
||||||
#[error("Expected at least one `HalfEdge`\n")]
|
#[error("Expected at least one `Edge`\n")]
|
||||||
NotEnoughHalfEdges,
|
NotEnoughEdges,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CycleValidationError {
|
impl CycleValidationError {
|
||||||
fn check_enough_half_edges(
|
fn check_enough_edges(
|
||||||
cycle: &Cycle,
|
cycle: &Cycle,
|
||||||
_config: &ValidationConfig,
|
_config: &ValidationConfig,
|
||||||
errors: &mut Vec<ValidationError>,
|
errors: &mut Vec<ValidationError>,
|
||||||
) {
|
) {
|
||||||
// If there are no half edges
|
// If there are no half edges
|
||||||
if cycle.half_edges().next().is_none() {
|
if cycle.edges().next().is_none() {
|
||||||
errors.push(Self::NotEnoughHalfEdges.into());
|
errors.push(Self::NotEnoughEdges.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_half_edges_disconnected(
|
fn check_edges_disconnected(
|
||||||
cycle: &Cycle,
|
cycle: &Cycle,
|
||||||
config: &ValidationConfig,
|
config: &ValidationConfig,
|
||||||
errors: &mut Vec<ValidationError>,
|
errors: &mut Vec<ValidationError>,
|
||||||
) {
|
) {
|
||||||
for (first, second) in cycle.half_edge_pairs() {
|
for (first, second) in cycle.edge_pairs() {
|
||||||
let end_of_first = {
|
let end_of_first = {
|
||||||
let [_, end] = first.boundary().inner;
|
let [_, end] = first.boundary().inner;
|
||||||
first.path().point_from_path_coords(end)
|
first.path().point_from_path_coords(end)
|
||||||
@ -74,11 +72,11 @@ impl CycleValidationError {
|
|||||||
|
|
||||||
if distance > config.identical_max_distance {
|
if distance > config.identical_max_distance {
|
||||||
errors.push(
|
errors.push(
|
||||||
Self::HalfEdgesDisconnected {
|
Self::EdgesDisconnected {
|
||||||
end_of_first,
|
end_of_first,
|
||||||
start_of_second,
|
start_of_second,
|
||||||
distance,
|
distance,
|
||||||
half_edges: Box::new((
|
edges: Box::new((
|
||||||
first.clone_object(),
|
first.clone_object(),
|
||||||
second.clone_object(),
|
second.clone_object(),
|
||||||
)),
|
)),
|
||||||
@ -95,14 +93,14 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assert_contains_err,
|
assert_contains_err,
|
||||||
objects::{Cycle, HalfEdge},
|
objects::{Cycle, Edge},
|
||||||
operations::{BuildCycle, BuildHalfEdge, Insert, UpdateCycle},
|
operations::{BuildCycle, BuildEdge, Insert, UpdateCycle},
|
||||||
services::Services,
|
services::Services,
|
||||||
validate::{cycle::CycleValidationError, Validate, ValidationError},
|
validate::{cycle::CycleValidationError, Validate, ValidationError},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn half_edges_connected() -> anyhow::Result<()> {
|
fn edges_connected() -> anyhow::Result<()> {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let valid =
|
let valid =
|
||||||
@ -111,35 +109,26 @@ mod tests {
|
|||||||
valid.validate_and_return_first_error()?;
|
valid.validate_and_return_first_error()?;
|
||||||
|
|
||||||
let disconnected = {
|
let disconnected = {
|
||||||
let half_edges = [
|
let edges = [
|
||||||
HalfEdge::line_segment(
|
Edge::line_segment([[0., 0.], [1., 0.]], None, &mut services),
|
||||||
[[0., 0.], [1., 0.]],
|
Edge::line_segment([[0., 0.], [1., 0.]], None, &mut services),
|
||||||
None,
|
|
||||||
&mut services,
|
|
||||||
),
|
|
||||||
HalfEdge::line_segment(
|
|
||||||
[[0., 0.], [1., 0.]],
|
|
||||||
None,
|
|
||||||
&mut services,
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
let half_edges =
|
let edges = edges.map(|edge| edge.insert(&mut services));
|
||||||
half_edges.map(|half_edge| half_edge.insert(&mut services));
|
|
||||||
|
|
||||||
Cycle::empty().add_half_edges(half_edges)
|
Cycle::empty().add_edges(edges)
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_contains_err!(
|
assert_contains_err!(
|
||||||
disconnected,
|
disconnected,
|
||||||
ValidationError::Cycle(
|
ValidationError::Cycle(
|
||||||
CycleValidationError::HalfEdgesDisconnected { .. }
|
CycleValidationError::EdgesDisconnected { .. }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let empty = Cycle::new([]);
|
let empty = Cycle::new([]);
|
||||||
assert_contains_err!(
|
assert_contains_err!(
|
||||||
empty,
|
empty,
|
||||||
ValidationError::Cycle(CycleValidationError::NotEnoughHalfEdges)
|
ValidationError::Cycle(CycleValidationError::NotEnoughEdges)
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
use fj_math::{Point, Scalar};
|
use fj_math::{Point, Scalar};
|
||||||
|
|
||||||
use crate::objects::HalfEdge;
|
use crate::objects::Edge;
|
||||||
|
|
||||||
use super::{Validate, ValidationConfig, ValidationError};
|
use super::{Validate, ValidationConfig, ValidationError};
|
||||||
|
|
||||||
impl Validate for HalfEdge {
|
impl Validate for Edge {
|
||||||
fn validate_with_config(
|
fn validate_with_config(
|
||||||
&self,
|
&self,
|
||||||
config: &ValidationConfig,
|
config: &ValidationConfig,
|
||||||
errors: &mut Vec<ValidationError>,
|
errors: &mut Vec<ValidationError>,
|
||||||
) {
|
) {
|
||||||
HalfEdgeValidationError::check_vertex_coincidence(self, config, errors);
|
EdgeValidationError::check_vertex_coincidence(self, config, errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`HalfEdge`] validation failed
|
/// [`Edge`] validation failed
|
||||||
#[derive(Clone, Debug, thiserror::Error)]
|
#[derive(Clone, Debug, thiserror::Error)]
|
||||||
pub enum HalfEdgeValidationError {
|
pub enum EdgeValidationError {
|
||||||
/// [`HalfEdge`]'s vertices are coincident
|
/// [`Edge`]'s vertices are coincident
|
||||||
#[error(
|
#[error(
|
||||||
"Vertices of `HalfEdge` on curve are coincident\n\
|
"Vertices of `Edge` on curve are coincident\n\
|
||||||
- Position of back vertex: {back_position:?}\n\
|
- Position of back vertex: {back_position:?}\n\
|
||||||
- Position of front vertex: {front_position:?}\n\
|
- Position of front vertex: {front_position:?}\n\
|
||||||
- `HalfEdge`: {half_edge:#?}"
|
- `Edge`: {edge:#?}"
|
||||||
)]
|
)]
|
||||||
VerticesAreCoincident {
|
VerticesAreCoincident {
|
||||||
/// The position of the back vertex
|
/// The position of the back vertex
|
||||||
@ -34,18 +34,18 @@ pub enum HalfEdgeValidationError {
|
|||||||
/// The distance between the two vertices
|
/// The distance between the two vertices
|
||||||
distance: Scalar,
|
distance: Scalar,
|
||||||
|
|
||||||
/// The half-edge
|
/// The edge
|
||||||
half_edge: HalfEdge,
|
edge: Edge,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HalfEdgeValidationError {
|
impl EdgeValidationError {
|
||||||
fn check_vertex_coincidence(
|
fn check_vertex_coincidence(
|
||||||
half_edge: &HalfEdge,
|
edge: &Edge,
|
||||||
config: &ValidationConfig,
|
config: &ValidationConfig,
|
||||||
errors: &mut Vec<ValidationError>,
|
errors: &mut Vec<ValidationError>,
|
||||||
) {
|
) {
|
||||||
let [back_position, front_position] = half_edge.boundary().inner;
|
let [back_position, front_position] = edge.boundary().inner;
|
||||||
let distance = (back_position - front_position).magnitude();
|
let distance = (back_position - front_position).magnitude();
|
||||||
|
|
||||||
if distance < config.distinct_min_distance {
|
if distance < config.distinct_min_distance {
|
||||||
@ -54,7 +54,7 @@ impl HalfEdgeValidationError {
|
|||||||
back_position,
|
back_position,
|
||||||
front_position,
|
front_position,
|
||||||
distance,
|
distance,
|
||||||
half_edge: half_edge.clone(),
|
edge: edge.clone(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
@ -68,22 +68,22 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assert_contains_err,
|
assert_contains_err,
|
||||||
objects::HalfEdge,
|
objects::Edge,
|
||||||
operations::BuildHalfEdge,
|
operations::BuildEdge,
|
||||||
services::Services,
|
services::Services,
|
||||||
validate::{HalfEdgeValidationError, Validate, ValidationError},
|
validate::{EdgeValidationError, Validate, ValidationError},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn half_edge_vertices_are_coincident() -> anyhow::Result<()> {
|
fn edge_vertices_are_coincident() -> anyhow::Result<()> {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let valid =
|
let valid =
|
||||||
HalfEdge::line_segment([[0., 0.], [1., 0.]], None, &mut services);
|
Edge::line_segment([[0., 0.], [1., 0.]], None, &mut services);
|
||||||
let invalid = {
|
let invalid = {
|
||||||
let boundary = [Point::from([0.]); 2];
|
let boundary = [Point::from([0.]); 2];
|
||||||
|
|
||||||
HalfEdge::new(
|
Edge::new(
|
||||||
valid.path(),
|
valid.path(),
|
||||||
boundary,
|
boundary,
|
||||||
valid.curve().clone(),
|
valid.curve().clone(),
|
||||||
@ -94,8 +94,8 @@ mod tests {
|
|||||||
valid.validate_and_return_first_error()?;
|
valid.validate_and_return_first_error()?;
|
||||||
assert_contains_err!(
|
assert_contains_err!(
|
||||||
invalid,
|
invalid,
|
||||||
ValidationError::HalfEdge(
|
ValidationError::Edge(
|
||||||
HalfEdgeValidationError::VerticesAreCoincident { .. }
|
EdgeValidationError::VerticesAreCoincident { .. }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -38,18 +38,18 @@ pub enum FaceValidationError {
|
|||||||
|
|
||||||
impl FaceValidationError {
|
impl FaceValidationError {
|
||||||
fn check_interior_winding(face: &Face, errors: &mut Vec<ValidationError>) {
|
fn check_interior_winding(face: &Face, errors: &mut Vec<ValidationError>) {
|
||||||
if face.region().exterior().half_edges().count() == 0 {
|
if face.region().exterior().edges().count() == 0 {
|
||||||
// Can't determine winding, if the cycle has no half-edges. Sounds
|
// Can't determine winding, if the cycle has no edges. Sounds like a
|
||||||
// like a job for a different validation check.
|
// job for a different validation check.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let exterior_winding = face.region().exterior().winding();
|
let exterior_winding = face.region().exterior().winding();
|
||||||
|
|
||||||
for interior in face.region().interiors() {
|
for interior in face.region().interiors() {
|
||||||
if interior.half_edges().count() == 0 {
|
if interior.edges().count() == 0 {
|
||||||
// Can't determine winding, if the cycle has no half-edges.
|
// Can't determine winding, if the cycle has no edges. Sounds
|
||||||
// Sounds like a job for a different validation check.
|
// like a job for a different validation check.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let interior_winding = interior.winding();
|
let interior_winding = interior.winding();
|
||||||
|
@ -12,7 +12,7 @@ mod surface;
|
|||||||
mod vertex;
|
mod vertex;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
cycle::CycleValidationError, edge::HalfEdgeValidationError,
|
cycle::CycleValidationError, edge::EdgeValidationError,
|
||||||
face::FaceValidationError, shell::ShellValidationError,
|
face::FaceValidationError, shell::ShellValidationError,
|
||||||
solid::SolidValidationError,
|
solid::SolidValidationError,
|
||||||
};
|
};
|
||||||
@ -103,14 +103,14 @@ pub enum ValidationError {
|
|||||||
#[error("`Cycle` validation error")]
|
#[error("`Cycle` validation error")]
|
||||||
Cycle(#[from] CycleValidationError),
|
Cycle(#[from] CycleValidationError),
|
||||||
|
|
||||||
|
/// `Edge` validation error
|
||||||
|
#[error("`Edge` validation error")]
|
||||||
|
Edge(#[from] EdgeValidationError),
|
||||||
|
|
||||||
/// `Face` validation error
|
/// `Face` validation error
|
||||||
#[error("`Face` validation error")]
|
#[error("`Face` validation error")]
|
||||||
Face(#[from] FaceValidationError),
|
Face(#[from] FaceValidationError),
|
||||||
|
|
||||||
/// `HalfEdge` validation error
|
|
||||||
#[error("`HalfEdge` validation error")]
|
|
||||||
HalfEdge(#[from] HalfEdgeValidationError),
|
|
||||||
|
|
||||||
/// `Shell` validation error
|
/// `Shell` validation error
|
||||||
#[error("`Shell` validation error")]
|
#[error("`Shell` validation error")]
|
||||||
Shell(#[from] ShellValidationError),
|
Shell(#[from] ShellValidationError),
|
||||||
|
@ -4,7 +4,7 @@ use fj_math::{Point, Scalar};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::SurfaceGeometry,
|
geometry::SurfaceGeometry,
|
||||||
objects::{HalfEdge, Shell, Surface},
|
objects::{Edge, Shell, Surface},
|
||||||
queries::{AllEdgesWithSurface, BoundingVerticesOfEdge},
|
queries::{AllEdgesWithSurface, BoundingVerticesOfEdge},
|
||||||
storage::{Handle, HandleWrapper},
|
storage::{Handle, HandleWrapper},
|
||||||
};
|
};
|
||||||
@ -35,23 +35,22 @@ pub enum ShellValidationError {
|
|||||||
)]
|
)]
|
||||||
CurveCoordinateSystemMismatch(Vec<CurveCoordinateSystemMismatch>),
|
CurveCoordinateSystemMismatch(Vec<CurveCoordinateSystemMismatch>),
|
||||||
|
|
||||||
/// [`Shell`] contains global_edges not referred to by two half-edges
|
/// [`Shell`] is not watertight
|
||||||
#[error("Shell is not watertight")]
|
#[error("Shell is not watertight")]
|
||||||
NotWatertight,
|
NotWatertight,
|
||||||
|
|
||||||
/// [`Shell`] contains half-edges that are coincident, but refer to
|
/// [`Shell`] contains edges that are coincident, but not identical
|
||||||
/// different global_edges
|
|
||||||
#[error(
|
#[error(
|
||||||
"`Shell` contains `HalfEdge`s that are coincident but refer to \
|
"`Shell` contains `Edge`s that are coincident but refer to different \
|
||||||
different `GlobalEdge`s\n\
|
`Curve`s\n\
|
||||||
Edge 1: {0:#?}\n\
|
Edge 1: {0:#?}\n\
|
||||||
Edge 2: {1:#?}"
|
Edge 2: {1:#?}"
|
||||||
)]
|
)]
|
||||||
CoincidentEdgesNotIdentical(Handle<HalfEdge>, Handle<HalfEdge>),
|
CoincidentEdgesNotIdentical(Handle<Edge>, Handle<Edge>),
|
||||||
|
|
||||||
/// [`Shell`] contains half-edges that are identical, but do not coincide
|
/// [`Shell`] contains edges that are identical, but do not coincide
|
||||||
#[error(
|
#[error(
|
||||||
"Shell contains HalfEdges that are identical but do not coincide\n\
|
"Shell contains `Edge`s that are identical but do not coincide\n\
|
||||||
Edge 1: {edge_a:#?}\n\
|
Edge 1: {edge_a:#?}\n\
|
||||||
Surface for edge 1: {surface_a:#?}\n\
|
Surface for edge 1: {surface_a:#?}\n\
|
||||||
Edge 2: {edge_b:#?}\n\
|
Edge 2: {edge_b:#?}\n\
|
||||||
@ -59,13 +58,13 @@ pub enum ShellValidationError {
|
|||||||
)]
|
)]
|
||||||
IdenticalEdgesNotCoincident {
|
IdenticalEdgesNotCoincident {
|
||||||
/// The first edge
|
/// The first edge
|
||||||
edge_a: Handle<HalfEdge>,
|
edge_a: Handle<Edge>,
|
||||||
|
|
||||||
/// The surface that the first edge is on
|
/// The surface that the first edge is on
|
||||||
surface_a: Handle<Surface>,
|
surface_a: Handle<Surface>,
|
||||||
|
|
||||||
/// The second edge
|
/// The second edge
|
||||||
edge_b: Handle<HalfEdge>,
|
edge_b: Handle<Edge>,
|
||||||
|
|
||||||
/// The surface that the second edge is on
|
/// The surface that the second edge is on
|
||||||
surface_b: Handle<Surface>,
|
surface_b: Handle<Surface>,
|
||||||
@ -80,14 +79,14 @@ pub enum ShellValidationError {
|
|||||||
///
|
///
|
||||||
/// Returns an [`Iterator`] of the distance at each sample.
|
/// Returns an [`Iterator`] of the distance at each sample.
|
||||||
fn distances(
|
fn distances(
|
||||||
edge_a: Handle<HalfEdge>,
|
edge_a: Handle<Edge>,
|
||||||
surface_a: Handle<Surface>,
|
surface_a: Handle<Surface>,
|
||||||
edge_b: Handle<HalfEdge>,
|
edge_b: Handle<Edge>,
|
||||||
surface_b: Handle<Surface>,
|
surface_b: Handle<Surface>,
|
||||||
) -> impl Iterator<Item = Scalar> {
|
) -> impl Iterator<Item = Scalar> {
|
||||||
fn sample(
|
fn sample(
|
||||||
percent: f64,
|
percent: f64,
|
||||||
(edge, surface): (&Handle<HalfEdge>, SurfaceGeometry),
|
(edge, surface): (&Handle<Edge>, SurfaceGeometry),
|
||||||
) -> Point<3> {
|
) -> Point<3> {
|
||||||
let [start, end] = edge.boundary().inner;
|
let [start, end] = edge.boundary().inner;
|
||||||
let path_coords = start + (end - start) * percent;
|
let path_coords = start + (end - start) * percent;
|
||||||
@ -133,9 +132,9 @@ impl ShellValidationError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn compare_curve_coords(
|
fn compare_curve_coords(
|
||||||
edge_a: &Handle<HalfEdge>,
|
edge_a: &Handle<Edge>,
|
||||||
surface_a: &Handle<Surface>,
|
surface_a: &Handle<Surface>,
|
||||||
edge_b: &Handle<HalfEdge>,
|
edge_b: &Handle<Edge>,
|
||||||
surface_b: &Handle<Surface>,
|
surface_b: &Handle<Surface>,
|
||||||
config: &ValidationConfig,
|
config: &ValidationConfig,
|
||||||
mismatches: &mut Vec<CurveCoordinateSystemMismatch>,
|
mismatches: &mut Vec<CurveCoordinateSystemMismatch>,
|
||||||
@ -299,13 +298,11 @@ impl ShellValidationError {
|
|||||||
|
|
||||||
for face in shell.faces() {
|
for face in shell.faces() {
|
||||||
for cycle in face.region().all_cycles() {
|
for cycle in face.region().all_cycles() {
|
||||||
for half_edge in cycle.half_edges() {
|
for edge in cycle.edges() {
|
||||||
let curve = HandleWrapper::from(half_edge.curve().clone());
|
let curve = HandleWrapper::from(edge.curve().clone());
|
||||||
let bounding_vertices = cycle
|
let bounding_vertices = cycle
|
||||||
.bounding_vertices_of_edge(half_edge)
|
.bounding_vertices_of_edge(edge)
|
||||||
.expect(
|
.expect("Cycle should provide bounds of its own edge")
|
||||||
"Cycle should provide bounds of its own half-edge",
|
|
||||||
)
|
|
||||||
.normalize();
|
.normalize();
|
||||||
|
|
||||||
let edge = (curve, bounding_vertices);
|
let edge = (curve, bounding_vertices);
|
||||||
@ -330,7 +327,7 @@ impl ShellValidationError {
|
|||||||
|
|
||||||
for face in shell.faces() {
|
for face in shell.faces() {
|
||||||
for cycle in face.region().all_cycles() {
|
for cycle in face.region().all_cycles() {
|
||||||
for edge in cycle.half_edges() {
|
for edge in cycle.edges() {
|
||||||
let curve = HandleWrapper::from(edge.curve().clone());
|
let curve = HandleWrapper::from(edge.curve().clone());
|
||||||
let boundary = cycle
|
let boundary = cycle
|
||||||
.bounding_vertices_of_edge(edge)
|
.bounding_vertices_of_edge(edge)
|
||||||
@ -365,8 +362,8 @@ impl ShellValidationError {
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CurveCoordinateSystemMismatch {
|
pub struct CurveCoordinateSystemMismatch {
|
||||||
pub edge_a: Handle<HalfEdge>,
|
pub edge_a: Handle<Edge>,
|
||||||
pub edge_b: Handle<HalfEdge>,
|
pub edge_b: Handle<Edge>,
|
||||||
pub point_curve: Point<1>,
|
pub point_curve: Point<1>,
|
||||||
pub point_a: Point<3>,
|
pub point_a: Point<3>,
|
||||||
pub point_b: Point<3>,
|
pub point_b: Point<3>,
|
||||||
@ -379,8 +376,8 @@ mod tests {
|
|||||||
assert_contains_err,
|
assert_contains_err,
|
||||||
objects::{Curve, Shell},
|
objects::{Curve, Shell},
|
||||||
operations::{
|
operations::{
|
||||||
BuildShell, Insert, Reverse, UpdateCycle, UpdateFace,
|
BuildShell, Insert, Reverse, UpdateCycle, UpdateEdge, UpdateFace,
|
||||||
UpdateHalfEdge, UpdateRegion, UpdateShell,
|
UpdateRegion, UpdateShell,
|
||||||
},
|
},
|
||||||
services::Services,
|
services::Services,
|
||||||
validate::{shell::ShellValidationError, Validate, ValidationError},
|
validate::{shell::ShellValidationError, Validate, ValidationError},
|
||||||
@ -403,13 +400,10 @@ mod tests {
|
|||||||
region
|
region
|
||||||
.update_exterior(|cycle| {
|
.update_exterior(|cycle| {
|
||||||
cycle
|
cycle
|
||||||
.update_nth_half_edge(0, |half_edge| {
|
.update_nth_edge(0, |edge| {
|
||||||
half_edge
|
edge.replace_path(edge.path().reverse())
|
||||||
.replace_path(
|
|
||||||
half_edge.path().reverse(),
|
|
||||||
)
|
|
||||||
.replace_boundary(
|
.replace_boundary(
|
||||||
half_edge.boundary().reverse(),
|
edge.boundary().reverse(),
|
||||||
)
|
)
|
||||||
.insert(&mut services)
|
.insert(&mut services)
|
||||||
})
|
})
|
||||||
@ -448,12 +442,11 @@ mod tests {
|
|||||||
region
|
region
|
||||||
.update_exterior(|cycle| {
|
.update_exterior(|cycle| {
|
||||||
cycle
|
cycle
|
||||||
.update_nth_half_edge(0, |half_edge| {
|
.update_nth_edge(0, |edge| {
|
||||||
let curve =
|
let curve =
|
||||||
Curve::new().insert(&mut services);
|
Curve::new().insert(&mut services);
|
||||||
|
|
||||||
half_edge
|
edge.replace_curve(curve)
|
||||||
.replace_curve(curve)
|
|
||||||
.insert(&mut services)
|
.insert(&mut services)
|
||||||
})
|
})
|
||||||
.insert(&mut services)
|
.insert(&mut services)
|
||||||
|
@ -74,7 +74,7 @@ impl SolidValidationError {
|
|||||||
.flat_map(|face| {
|
.flat_map(|face| {
|
||||||
face.region()
|
face.region()
|
||||||
.all_cycles()
|
.all_cycles()
|
||||||
.flat_map(|cycle| cycle.half_edges().cloned())
|
.flat_map(|cycle| cycle.edges().cloned())
|
||||||
.zip(repeat(face.surface().geometry()))
|
.zip(repeat(face.surface().geometry()))
|
||||||
})
|
})
|
||||||
.map(|(h, s)| {
|
.map(|(h, s)| {
|
||||||
|
Loading…
Reference in New Issue
Block a user