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
|
||||
|
||||
pub mod face_point;
|
||||
pub mod ray_edge;
|
||||
pub mod ray_segment;
|
||||
|
||||
mod curve_edge;
|
||||
mod curve_face;
|
||||
mod line_segment;
|
||||
|
||||
use fj_math::{Point, Vector};
|
||||
|
||||
pub use self::{
|
||||
curve_edge::CurveEdgeIntersection,
|
||||
curve_face::{CurveFaceIntersection, CurveFaceIntersectionInterval},
|
||||
line_segment::LineSegmentIntersection,
|
||||
};
|
||||
pub use self::line_segment::LineSegmentIntersection;
|
||||
|
||||
/// 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