mirror of
https://github.com/hannobraun/Fornjot
synced 2025-01-12 03:06:59 +00:00
Merge pull request #2268 from hannobraun/intersect
Remove unused intersection checks
This commit is contained in:
commit
68a4c0008f
@ -1,147 +0,0 @@
|
|||||||
use fj_math::{Point, Segment};
|
|
||||||
|
|
||||||
use crate::{geometry::SurfacePath, objects::HalfEdge};
|
|
||||||
|
|
||||||
use super::LineSegmentIntersection;
|
|
||||||
|
|
||||||
/// The intersection between a curve and a [`HalfEdge`]
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
|
||||||
pub enum CurveEdgeIntersection {
|
|
||||||
/// The curve and edge intersect at a point
|
|
||||||
Point {
|
|
||||||
/// The intersection point, in curve coordinates on the curve
|
|
||||||
point_on_curve: Point<1>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// The edge lies on the curve
|
|
||||||
Coincident {
|
|
||||||
/// The end points of the edge, in curve coordinates on the curve
|
|
||||||
points_on_curve: [Point<1>; 2],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CurveEdgeIntersection {
|
|
||||||
/// Compute the intersection
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Currently, only intersections between lines and line segments can be
|
|
||||||
/// computed. Panics, if a different type of curve or [`HalfEdge`] is passed.
|
|
||||||
pub fn compute(path: &SurfacePath, edge: &HalfEdge) -> Option<Self> {
|
|
||||||
let path_as_line = match path {
|
|
||||||
SurfacePath::Line(line) => line,
|
|
||||||
_ => todo!("Curve-edge intersection only supports lines"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let edge_as_segment = {
|
|
||||||
let edge_path_as_line = match edge.path() {
|
|
||||||
SurfacePath::Line(line) => line,
|
|
||||||
_ => {
|
|
||||||
todo!("Curve-edge intersection only supports line segments")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let edge_vertices = edge
|
|
||||||
.boundary()
|
|
||||||
.inner
|
|
||||||
.map(|point| edge_path_as_line.point_from_line_coords(point));
|
|
||||||
|
|
||||||
Segment::from_points(edge_vertices)
|
|
||||||
};
|
|
||||||
|
|
||||||
let intersection =
|
|
||||||
LineSegmentIntersection::compute(path_as_line, &edge_as_segment)?;
|
|
||||||
|
|
||||||
let intersection = match intersection {
|
|
||||||
LineSegmentIntersection::Point { point_on_line } => Self::Point {
|
|
||||||
point_on_curve: point_on_line,
|
|
||||||
},
|
|
||||||
LineSegmentIntersection::Coincident { points_on_line } => {
|
|
||||||
Self::Coincident {
|
|
||||||
points_on_curve: points_on_line,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(intersection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use fj_math::Point;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
geometry::SurfacePath, objects::HalfEdge,
|
|
||||||
operations::build::BuildHalfEdge, Core,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::CurveEdgeIntersection;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn compute_edge_in_front_of_curve_origin() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let path = SurfacePath::u_axis();
|
|
||||||
let edge =
|
|
||||||
HalfEdge::line_segment([[1., -1.], [1., 1.]], None, &mut core);
|
|
||||||
|
|
||||||
let intersection = CurveEdgeIntersection::compute(&path, &edge);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
intersection,
|
|
||||||
Some(CurveEdgeIntersection::Point {
|
|
||||||
point_on_curve: Point::from([1.])
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn compute_edge_behind_curve_origin() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let path = SurfacePath::u_axis();
|
|
||||||
let edge =
|
|
||||||
HalfEdge::line_segment([[-1., -1.], [-1., 1.]], None, &mut core);
|
|
||||||
|
|
||||||
let intersection = CurveEdgeIntersection::compute(&path, &edge);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
intersection,
|
|
||||||
Some(CurveEdgeIntersection::Point {
|
|
||||||
point_on_curve: Point::from([-1.])
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn compute_edge_parallel_to_curve() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let path = SurfacePath::u_axis();
|
|
||||||
let edge =
|
|
||||||
HalfEdge::line_segment([[-1., -1.], [1., -1.]], None, &mut core);
|
|
||||||
|
|
||||||
let intersection = CurveEdgeIntersection::compute(&path, &edge);
|
|
||||||
|
|
||||||
assert!(intersection.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn compute_edge_on_curve() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let path = SurfacePath::u_axis();
|
|
||||||
let edge =
|
|
||||||
HalfEdge::line_segment([[-1., 0.], [1., 0.]], None, &mut core);
|
|
||||||
|
|
||||||
let intersection = CurveEdgeIntersection::compute(&path, &edge);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
intersection,
|
|
||||||
Some(CurveEdgeIntersection::Coincident {
|
|
||||||
points_on_curve: [Point::from([-1.]), Point::from([1.]),]
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,279 +0,0 @@
|
|||||||
use std::vec;
|
|
||||||
|
|
||||||
use fj_interop::ext::SliceExt;
|
|
||||||
use fj_math::Point;
|
|
||||||
|
|
||||||
use crate::{geometry::SurfacePath, objects::Face};
|
|
||||||
|
|
||||||
use super::CurveEdgeIntersection;
|
|
||||||
|
|
||||||
/// The intersections between a curve and a [`Face`], in curve coordinates
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct CurveFaceIntersection {
|
|
||||||
/// The intervals where the curve and face intersect, in curve coordinates
|
|
||||||
pub intervals: Vec<CurveFaceIntersectionInterval>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CurveFaceIntersection {
|
|
||||||
/// Create a new instance from the intersection intervals
|
|
||||||
///
|
|
||||||
/// This method is useful for test code.
|
|
||||||
pub fn from_intervals(
|
|
||||||
intervals: impl IntoIterator<
|
|
||||||
Item = impl Into<CurveFaceIntersectionInterval>,
|
|
||||||
>,
|
|
||||||
) -> Self {
|
|
||||||
let intervals = intervals.into_iter().map(Into::into).collect();
|
|
||||||
Self { intervals }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute the intersection
|
|
||||||
pub fn compute(path: &SurfacePath, face: &Face) -> Self {
|
|
||||||
let edges = face
|
|
||||||
.region()
|
|
||||||
.all_cycles()
|
|
||||||
.flat_map(|cycle| cycle.half_edges());
|
|
||||||
|
|
||||||
let mut intersections = Vec::new();
|
|
||||||
|
|
||||||
for edge in edges {
|
|
||||||
let intersection = CurveEdgeIntersection::compute(path, edge);
|
|
||||||
|
|
||||||
if let Some(intersection) = intersection {
|
|
||||||
match intersection {
|
|
||||||
CurveEdgeIntersection::Point { point_on_curve } => {
|
|
||||||
intersections.push(point_on_curve);
|
|
||||||
}
|
|
||||||
CurveEdgeIntersection::Coincident { points_on_curve } => {
|
|
||||||
intersections.extend(points_on_curve);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(intersections.len() % 2 == 0);
|
|
||||||
|
|
||||||
intersections.sort();
|
|
||||||
|
|
||||||
let intervals = intersections
|
|
||||||
.as_slice()
|
|
||||||
.array_chunks_ext()
|
|
||||||
.map(|&[start, end]| CurveFaceIntersectionInterval { start, end })
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Self { intervals }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Merge this intersection list with another
|
|
||||||
///
|
|
||||||
/// The merged list will contain all overlaps of the intervals from the two
|
|
||||||
/// other lists.
|
|
||||||
pub fn merge(&self, other: &Self) -> Self {
|
|
||||||
let mut self_intervals = self.intervals.iter().copied();
|
|
||||||
let mut other_interval = other.intervals.iter().copied();
|
|
||||||
|
|
||||||
let mut next_self = self_intervals.next();
|
|
||||||
let mut next_other = other_interval.next();
|
|
||||||
|
|
||||||
let mut intervals = Vec::new();
|
|
||||||
|
|
||||||
while let (Some(self_), Some(other)) = (next_self, next_other) {
|
|
||||||
// If we're starting another loop iteration, we have another
|
|
||||||
// interval available from both `self` and `other` each. Only if
|
|
||||||
// that's the case, is there a chance for an overlap.
|
|
||||||
|
|
||||||
// Build the overlap of the two next intervals, by comparing them.
|
|
||||||
// At this point we don't know yet, if this is a valid interval.
|
|
||||||
let overlap_start = self_.start.max(other.start);
|
|
||||||
let overlap_end = self_.end.min(other.end);
|
|
||||||
|
|
||||||
if overlap_start < overlap_end {
|
|
||||||
// This is indeed a valid overlap. Add it to our list of
|
|
||||||
// results.
|
|
||||||
intervals.push(CurveFaceIntersectionInterval {
|
|
||||||
start: overlap_start,
|
|
||||||
end: overlap_end,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only if the end of the overlap interval has overtaken one of the
|
|
||||||
// input ones are we done with it. An input interval that hasn't
|
|
||||||
// been overtaken by the overlap, could still overlap with another
|
|
||||||
// interval.
|
|
||||||
if self_.end <= overlap_end {
|
|
||||||
// Current interval from `self` has been overtaken. Let's grab
|
|
||||||
// the next one.
|
|
||||||
next_self = self_intervals.next();
|
|
||||||
}
|
|
||||||
if other.end <= overlap_end {
|
|
||||||
// Current interval from `other` has been overtaken. Let's grab
|
|
||||||
// the next one.
|
|
||||||
next_other = other_interval.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Self { intervals }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Indicate whether the intersection list is empty
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.intervals.is_empty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoIterator for CurveFaceIntersection {
|
|
||||||
type Item = CurveFaceIntersectionInterval;
|
|
||||||
type IntoIter = vec::IntoIter<Self::Item>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.intervals.into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An intersection between a curve and a face
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
|
||||||
pub struct CurveFaceIntersectionInterval {
|
|
||||||
/// The start of the intersection interval, in curve coordinates
|
|
||||||
pub start: Point<1>,
|
|
||||||
|
|
||||||
/// The end of the intersection interval, in curve coordinates
|
|
||||||
pub end: Point<1>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P> From<[P; 2]> for CurveFaceIntersectionInterval
|
|
||||||
where
|
|
||||||
P: Into<Point<1>>,
|
|
||||||
{
|
|
||||||
fn from(interval: [P; 2]) -> Self {
|
|
||||||
let [start, end] = interval.map(Into::into);
|
|
||||||
Self { start, end }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::{
|
|
||||||
geometry::SurfacePath,
|
|
||||||
objects::{Cycle, Face},
|
|
||||||
operations::{
|
|
||||||
build::{BuildCycle, BuildFace},
|
|
||||||
update::{UpdateFace, UpdateRegion},
|
|
||||||
},
|
|
||||||
Core,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::CurveFaceIntersection;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn compute() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let (path, _) = SurfacePath::line_from_points([[-3., 0.], [-2., 0.]]);
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
let exterior_points = [
|
|
||||||
[-2., -2.],
|
|
||||||
[ 2., -2.],
|
|
||||||
[ 2., 2.],
|
|
||||||
[-2., 2.],
|
|
||||||
];
|
|
||||||
#[rustfmt::skip]
|
|
||||||
let interior_points = [
|
|
||||||
[-1., -1.],
|
|
||||||
[-1., 1.],
|
|
||||||
[ 1., 1.],
|
|
||||||
[ 1., -1.],
|
|
||||||
];
|
|
||||||
|
|
||||||
let face =
|
|
||||||
Face::unbound(core.layers.objects.surfaces.xy_plane(), &mut core)
|
|
||||||
.update_region(
|
|
||||||
|region, core| {
|
|
||||||
region
|
|
||||||
.update_exterior(
|
|
||||||
|_, core| Cycle::polygon(exterior_points, core),
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
.add_interiors(
|
|
||||||
[Cycle::polygon(interior_points, core)],
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
&mut core,
|
|
||||||
);
|
|
||||||
|
|
||||||
let expected =
|
|
||||||
CurveFaceIntersection::from_intervals([[[1.], [2.]], [[4.], [5.]]]);
|
|
||||||
assert_eq!(CurveFaceIntersection::compute(&path, &face), expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn merge() {
|
|
||||||
let a = CurveFaceIntersection::from_intervals([
|
|
||||||
[[0.], [1.]], // 1: `a` and `b` are equal
|
|
||||||
[[2.], [5.]], // 2: `a` contains `b`
|
|
||||||
[[7.], [8.]], // 3: `b` contains `a`
|
|
||||||
[[9.], [11.]], // 4: overlap; `a` is left
|
|
||||||
[[14.], [16.]], // 5: overlap; `a` is right
|
|
||||||
[[18.], [21.]], // 6: one of `a` partially overlaps two of `b`
|
|
||||||
[[23.], [25.]], // 7: two of `a` partially overlap one of `b`
|
|
||||||
[[26.], [28.]], // 7
|
|
||||||
[[31.], [35.]], // 8: partial/complete: one of `a`, two of `b`;
|
|
||||||
[[36.], [38.]], // 9: partial/complete: two of `a`, one of `b`
|
|
||||||
[[39.], [40.]], // 9
|
|
||||||
[[41.], [45.]], // 10: complete/partial: one of `a`, two of `b`
|
|
||||||
[[48.], [49.]], // 11: complete/partial: two of `a`, one of `b`
|
|
||||||
[[50.], [52.]], // 11
|
|
||||||
[[53.], [58.]], // 12: one of `a` overlaps two of `b` completely
|
|
||||||
[[60.], [61.]], // 13: one of `b` overlaps two of `a` completely
|
|
||||||
[[62.], [63.]], // 13
|
|
||||||
[[65.], [66.]], // 14: one of `a` with no overlap in `b`
|
|
||||||
]);
|
|
||||||
let b = CurveFaceIntersection::from_intervals([
|
|
||||||
[[0.], [1.]], // 1
|
|
||||||
[[3.], [4.]], // 2
|
|
||||||
[[6.], [9.]], // 3
|
|
||||||
[[10.], [12.]], // 4
|
|
||||||
[[13.], [15.]], // 5
|
|
||||||
[[17.], [19.]], // 6
|
|
||||||
[[20.], [22.]], // 6
|
|
||||||
[[24.], [27.]], // 7
|
|
||||||
[[30.], [32.]], // 8
|
|
||||||
[[33.], [34.]], // 8
|
|
||||||
[[37.], [41.]], // 9
|
|
||||||
[[42.], [43.]], // 10
|
|
||||||
[[44.], [46.]], // 10
|
|
||||||
[[47.], [51.]], // 11
|
|
||||||
[[54.], [55.]], // 12
|
|
||||||
[[56.], [57.]], // 12
|
|
||||||
[[59.], [64.]], // 13
|
|
||||||
]);
|
|
||||||
|
|
||||||
let merged = a.merge(&b);
|
|
||||||
|
|
||||||
let expected = CurveFaceIntersection::from_intervals([
|
|
||||||
[[0.], [1.]], // 1
|
|
||||||
[[3.], [4.]], // 2
|
|
||||||
[[7.], [8.]], // 3
|
|
||||||
[[10.], [11.]], // 4
|
|
||||||
[[14.], [15.]], // 5
|
|
||||||
[[18.], [19.]], // 6
|
|
||||||
[[20.], [21.]], // 6
|
|
||||||
[[24.], [25.]], // 7
|
|
||||||
[[26.], [27.]], // 7
|
|
||||||
[[31.], [32.]], // 8
|
|
||||||
[[33.], [34.]], // 8
|
|
||||||
[[37.], [38.]], // 9
|
|
||||||
[[39.], [40.]], // 9
|
|
||||||
[[42.], [43.]], // 10
|
|
||||||
[[44.], [45.]], // 10
|
|
||||||
[[48.], [49.]], // 11
|
|
||||||
[[50.], [51.]], // 11
|
|
||||||
[[54.], [55.]], // 12
|
|
||||||
[[56.], [57.]], // 12
|
|
||||||
[[60.], [61.]], // 13
|
|
||||||
[[62.], [63.]], // 13
|
|
||||||
]);
|
|
||||||
assert_eq!(merged, expected);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,397 +0,0 @@
|
|||||||
//! Intersection between faces and points in 2D
|
|
||||||
|
|
||||||
use fj_math::Point;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
objects::{Face, HalfEdge},
|
|
||||||
storage::Handle,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
ray_segment::RaySegmentIntersection, HorizontalRayToTheRight, Intersect,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl Intersect for (&Face, &Point<2>) {
|
|
||||||
type Intersection = FacePointIntersection;
|
|
||||||
|
|
||||||
fn intersect(self) -> Option<Self::Intersection> {
|
|
||||||
let (face, point) = self;
|
|
||||||
|
|
||||||
let ray = HorizontalRayToTheRight { origin: *point };
|
|
||||||
|
|
||||||
let mut num_hits = 0;
|
|
||||||
|
|
||||||
for cycle in face.region().all_cycles() {
|
|
||||||
// We need to properly detect the ray passing the boundary at the
|
|
||||||
// "seam" of the polygon, i.e. the vertex between the last and the
|
|
||||||
// first segment. The logic in the loop properly takes care of that,
|
|
||||||
// as long as we initialize the `previous_hit` variable with the
|
|
||||||
// result of the last segment.
|
|
||||||
let mut previous_hit = cycle
|
|
||||||
.half_edges()
|
|
||||||
.iter()
|
|
||||||
.last()
|
|
||||||
.and_then(|edge| (&ray, edge).intersect());
|
|
||||||
|
|
||||||
for (edge, next_edge) in cycle.half_edges().pairs() {
|
|
||||||
let hit = (&ray, edge).intersect();
|
|
||||||
|
|
||||||
let count_hit = match (hit, previous_hit) {
|
|
||||||
(
|
|
||||||
Some(RaySegmentIntersection::RayStartsOnSegment),
|
|
||||||
_,
|
|
||||||
) => {
|
|
||||||
// If the ray starts on the boundary of the face,
|
|
||||||
// there's nothing to else check.
|
|
||||||
return Some(FacePointIntersection::PointIsOnEdge(
|
|
||||||
edge.clone()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
(Some(RaySegmentIntersection::RayStartsOnOnFirstVertex), _) => {
|
|
||||||
let vertex = edge.start_position();
|
|
||||||
return Some(
|
|
||||||
FacePointIntersection::PointIsOnVertex(vertex)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
(Some(RaySegmentIntersection::RayStartsOnSecondVertex), _) => {
|
|
||||||
let vertex = next_edge.start_position();
|
|
||||||
return Some(
|
|
||||||
FacePointIntersection::PointIsOnVertex(vertex)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
(Some(RaySegmentIntersection::RayHitsSegment), _) => {
|
|
||||||
// We're hitting a segment right-on. Clear case.
|
|
||||||
true
|
|
||||||
}
|
|
||||||
(
|
|
||||||
Some(RaySegmentIntersection::RayHitsUpperVertex),
|
|
||||||
Some(RaySegmentIntersection::RayHitsLowerVertex),
|
|
||||||
)
|
|
||||||
| (
|
|
||||||
Some(RaySegmentIntersection::RayHitsLowerVertex),
|
|
||||||
Some(RaySegmentIntersection::RayHitsUpperVertex),
|
|
||||||
) => {
|
|
||||||
// If we're hitting a vertex, only count it if we've hit
|
|
||||||
// the other kind of vertex right before.
|
|
||||||
//
|
|
||||||
// That means, we're passing through the polygon
|
|
||||||
// boundary at where two edges touch. Depending on the
|
|
||||||
// order in which edges are checked, we're seeing this
|
|
||||||
// as a hit to one edge's lower/upper vertex, then the
|
|
||||||
// other edge's opposite vertex.
|
|
||||||
//
|
|
||||||
// If we're seeing two of the same vertices in a row,
|
|
||||||
// we're not actually passing through the polygon
|
|
||||||
// boundary. Then we're just touching a vertex without
|
|
||||||
// passing through anything.
|
|
||||||
true
|
|
||||||
}
|
|
||||||
(Some(RaySegmentIntersection::RayHitsSegmentAndAreParallel), _) => {
|
|
||||||
// A parallel edge must be completely ignored. Its
|
|
||||||
// presence won't change anything, so we can treat it as
|
|
||||||
// if it wasn't there, and its neighbors were connected
|
|
||||||
// to each other.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// Any other case is not a valid hit.
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if count_hit {
|
|
||||||
num_hits += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
previous_hit = hit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if num_hits % 2 == 1 {
|
|
||||||
Some(FacePointIntersection::PointIsInsideFace)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The intersection between a face and a point
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
|
||||||
pub enum FacePointIntersection {
|
|
||||||
/// The point is inside of the face
|
|
||||||
PointIsInsideFace,
|
|
||||||
|
|
||||||
/// The point is coincident with an edge
|
|
||||||
PointIsOnEdge(Handle<HalfEdge>),
|
|
||||||
|
|
||||||
/// The point is coincident with a vertex
|
|
||||||
PointIsOnVertex(Point<2>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use fj_math::Point;
|
|
||||||
use pretty_assertions::assert_eq;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
algorithms::intersect::{face_point::FacePointIntersection, Intersect},
|
|
||||||
objects::{Cycle, Face},
|
|
||||||
operations::{
|
|
||||||
build::{BuildCycle, BuildFace},
|
|
||||||
update::{UpdateFace, UpdateRegion},
|
|
||||||
},
|
|
||||||
Core,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn point_is_outside_face() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let face =
|
|
||||||
Face::unbound(core.layers.objects.surfaces.xy_plane(), &mut core)
|
|
||||||
.update_region(
|
|
||||||
|region, core| {
|
|
||||||
region.update_exterior(
|
|
||||||
|_, core| {
|
|
||||||
Cycle::polygon(
|
|
||||||
[[0., 0.], [1., 1.], [0., 2.]],
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
&mut core,
|
|
||||||
);
|
|
||||||
let point = Point::from([2., 1.]);
|
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
|
||||||
assert_eq!(intersection, None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ray_hits_vertex_while_passing_outside() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let face =
|
|
||||||
Face::unbound(core.layers.objects.surfaces.xy_plane(), &mut core)
|
|
||||||
.update_region(
|
|
||||||
|region, core| {
|
|
||||||
region.update_exterior(
|
|
||||||
|_, core| {
|
|
||||||
Cycle::polygon(
|
|
||||||
[[0., 0.], [2., 1.], [0., 2.]],
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
&mut core,
|
|
||||||
);
|
|
||||||
let point = Point::from([1., 1.]);
|
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
|
||||||
assert_eq!(
|
|
||||||
intersection,
|
|
||||||
Some(FacePointIntersection::PointIsInsideFace)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ray_hits_vertex_at_cycle_seam() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let face =
|
|
||||||
Face::unbound(core.layers.objects.surfaces.xy_plane(), &mut core)
|
|
||||||
.update_region(
|
|
||||||
|region, core| {
|
|
||||||
region.update_exterior(
|
|
||||||
|_, core| {
|
|
||||||
Cycle::polygon(
|
|
||||||
[[4., 2.], [0., 4.], [0., 0.]],
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
&mut core,
|
|
||||||
);
|
|
||||||
let point = Point::from([1., 2.]);
|
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
|
||||||
assert_eq!(
|
|
||||||
intersection,
|
|
||||||
Some(FacePointIntersection::PointIsInsideFace)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ray_hits_vertex_while_staying_inside() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let face =
|
|
||||||
Face::unbound(core.layers.objects.surfaces.xy_plane(), &mut core)
|
|
||||||
.update_region(
|
|
||||||
|region, core| {
|
|
||||||
region.update_exterior(
|
|
||||||
|_, core| {
|
|
||||||
Cycle::polygon(
|
|
||||||
[[0., 0.], [2., 1.], [3., 0.], [3., 4.]],
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
&mut core,
|
|
||||||
);
|
|
||||||
let point = Point::from([1., 1.]);
|
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
|
||||||
assert_eq!(
|
|
||||||
intersection,
|
|
||||||
Some(FacePointIntersection::PointIsInsideFace)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ray_hits_parallel_edge_and_leaves_face_at_vertex() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let face =
|
|
||||||
Face::unbound(core.layers.objects.surfaces.xy_plane(), &mut core)
|
|
||||||
.update_region(
|
|
||||||
|region, core| {
|
|
||||||
region.update_exterior(
|
|
||||||
|_, core| {
|
|
||||||
Cycle::polygon(
|
|
||||||
[[0., 0.], [2., 1.], [3., 1.], [0., 2.]],
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
&mut core,
|
|
||||||
);
|
|
||||||
let point = Point::from([1., 1.]);
|
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
|
||||||
assert_eq!(
|
|
||||||
intersection,
|
|
||||||
Some(FacePointIntersection::PointIsInsideFace)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ray_hits_parallel_edge_and_does_not_leave_face_there() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let face =
|
|
||||||
Face::unbound(core.layers.objects.surfaces.xy_plane(), &mut core)
|
|
||||||
.update_region(
|
|
||||||
|region, core| {
|
|
||||||
region.update_exterior(
|
|
||||||
|_, core| {
|
|
||||||
Cycle::polygon(
|
|
||||||
[
|
|
||||||
[0., 0.],
|
|
||||||
[2., 1.],
|
|
||||||
[3., 1.],
|
|
||||||
[4., 0.],
|
|
||||||
[4., 5.],
|
|
||||||
],
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
&mut core,
|
|
||||||
);
|
|
||||||
let point = Point::from([1., 1.]);
|
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
|
||||||
assert_eq!(
|
|
||||||
intersection,
|
|
||||||
Some(FacePointIntersection::PointIsInsideFace)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn point_is_coincident_with_edge() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let face =
|
|
||||||
Face::unbound(core.layers.objects.surfaces.xy_plane(), &mut core)
|
|
||||||
.update_region(
|
|
||||||
|region, core| {
|
|
||||||
region.update_exterior(
|
|
||||||
|_, core| {
|
|
||||||
Cycle::polygon(
|
|
||||||
[[0., 0.], [2., 0.], [0., 1.]],
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
&mut core,
|
|
||||||
);
|
|
||||||
let point = Point::from([1., 0.]);
|
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
|
||||||
|
|
||||||
let edge = face
|
|
||||||
.region()
|
|
||||||
.exterior()
|
|
||||||
.half_edges()
|
|
||||||
.iter()
|
|
||||||
.find(|edge| edge.start_position() == Point::from([0., 0.]))
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
intersection,
|
|
||||||
Some(FacePointIntersection::PointIsOnEdge(edge.clone()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn point_is_coincident_with_vertex() {
|
|
||||||
let mut core = Core::new();
|
|
||||||
|
|
||||||
let face =
|
|
||||||
Face::unbound(core.layers.objects.surfaces.xy_plane(), &mut core)
|
|
||||||
.update_region(
|
|
||||||
|region, core| {
|
|
||||||
region.update_exterior(
|
|
||||||
|_, core| {
|
|
||||||
Cycle::polygon(
|
|
||||||
[[0., 0.], [1., 0.], [0., 1.]],
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
core,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
&mut core,
|
|
||||||
);
|
|
||||||
let point = Point::from([1., 0.]);
|
|
||||||
|
|
||||||
let intersection = (&face, &point).intersect();
|
|
||||||
|
|
||||||
let vertex = face
|
|
||||||
.region()
|
|
||||||
.exterior()
|
|
||||||
.half_edges()
|
|
||||||
.iter()
|
|
||||||
.find(|edge| edge.start_position() == Point::from([1., 0.]))
|
|
||||||
.map(|edge| edge.start_position())
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
intersection,
|
|
||||||
Some(FacePointIntersection::PointIsOnVertex(vertex))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +1,12 @@
|
|||||||
//! Intersection algorithms
|
//! Intersection algorithms
|
||||||
|
|
||||||
pub mod face_point;
|
|
||||||
pub mod ray_edge;
|
|
||||||
pub mod ray_segment;
|
pub mod ray_segment;
|
||||||
|
|
||||||
mod curve_edge;
|
|
||||||
mod curve_face;
|
|
||||||
mod line_segment;
|
mod line_segment;
|
||||||
|
|
||||||
use fj_math::{Point, Vector};
|
use fj_math::{Point, Vector};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::line_segment::LineSegmentIntersection;
|
||||||
curve_edge::CurveEdgeIntersection,
|
|
||||||
curve_face::{CurveFaceIntersection, CurveFaceIntersectionInterval},
|
|
||||||
line_segment::LineSegmentIntersection,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Compute the intersection between a tuple of objects
|
/// Compute the intersection between a tuple of objects
|
||||||
///
|
///
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
//! Intersection between a ray and an edge in 2D
|
|
||||||
|
|
||||||
use fj_math::Segment;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
algorithms::intersect::{HorizontalRayToTheRight, Intersect},
|
|
||||||
geometry::SurfacePath,
|
|
||||||
objects::HalfEdge,
|
|
||||||
storage::Handle,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::ray_segment::RaySegmentIntersection;
|
|
||||||
|
|
||||||
impl Intersect for (&HorizontalRayToTheRight<2>, &Handle<HalfEdge>) {
|
|
||||||
type Intersection = RaySegmentIntersection;
|
|
||||||
|
|
||||||
fn intersect(self) -> Option<Self::Intersection> {
|
|
||||||
let (ray, edge) = self;
|
|
||||||
|
|
||||||
let line = match edge.path() {
|
|
||||||
SurfacePath::Line(line) => line,
|
|
||||||
SurfacePath::Circle(_) => {
|
|
||||||
todo!("Casting rays against circles is not supported yet")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let points = edge
|
|
||||||
.boundary()
|
|
||||||
.inner
|
|
||||||
.map(|point| line.point_from_line_coords(point));
|
|
||||||
let segment = Segment::from_points(points);
|
|
||||||
|
|
||||||
(ray, &segment).intersect()
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user