Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle intersection of coincident curve/edge #888

Merged
merged 7 commits into from
Jul 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 94 additions & 55 deletions crates/fj-kernel/src/algorithms/intersection/curve_edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@ use crate::objects::{Curve, Edge};

/// The intersection between a [`Curve`] and an [`Edge`], in curve coordinates
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct CurveEdgeIntersection {
point_on_curve: Point<1>,
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 first vertex of the edge, in curve coordinates
a_on_curve: Point<1>,

/// The second vertex of the edge, in curve coordinates
b_on_curve: Point<1>,
},
}

impl CurveEdgeIntersection {
/// Construct an instance from a point on a curve
pub fn from_point_on_curve(point_on_curve: impl Into<Point<1>>) -> Self {
let point_on_curve = point_on_curve.into();
Self { point_on_curve }
}

/// Compute the intersection
///
/// # Panics
Expand All @@ -29,26 +36,34 @@ impl CurveEdgeIntersection {
_ => todo!("Curve-edge intersection only supports lines"),
};

let edge_as_segment = {
let line = match edge.curve().local_form() {
Curve::Line(line) => line,
_ => {
todo!("Curve-edge intersection only supports line segments")
}
};

let vertices = match edge.vertices().get() {
Some(vertices) => vertices.map(|vertex| {
line.point_from_line_coords(vertex.position())
}),
None => todo!(
"Curve-edge intersection does not support continuous edges"
),
};

Segment::from_points(vertices)
let edge_curve_as_line = match edge.curve().local_form() {
Curve::Line(line) => line,
_ => {
todo!("Curve-edge intersection only supports line segments")
}
};

let edge_vertices = match edge.vertices().get() {
Some(vertices) => vertices.map(|vertex| {
edge_curve_as_line.point_from_line_coords(vertex.position())
}),
None => todo!(
"Curve-edge intersection does not support continuous edges"
),
};

let edge_as_segment = Segment::from_points(edge_vertices);

if curve_as_line.is_coincident_with(edge_curve_as_line) {
let [a_on_curve, b_on_curve] = edge_vertices
.map(|vertex| curve_as_line.point_to_line_coords(vertex));

return Some(Self::Coincident {
a_on_curve,
b_on_curve,
});
}

let ray = Ray {
origin: curve_as_line.origin.to_na(),
dir: curve_as_line.direction.to_na(),
Expand All @@ -70,23 +85,18 @@ impl CurveEdgeIntersection {
);

if let Some(result) = result {
return Some(Self {
return Some(Self::Point {
point_on_curve: Point::from([result]),
});
}
if let Some(result_inv) = result_inv {
return Some(Self {
return Some(Self::Point {
point_on_curve: Point::from([-result_inv]),
});
}

None
}

/// Access the intersection point on the curve
pub fn point_on_curve(&self) -> Point<1> {
self.point_on_curve
}
}

#[cfg(test)]
Expand All @@ -98,37 +108,66 @@ mod tests {
use super::CurveEdgeIntersection;

#[test]
fn compute() {
fn compute_edge_in_front_of_curve_origin() {
let surface = Surface::xy_plane();

let curve = Curve::u_axis();
let edge = Edge::build()
.line_segment_from_points(&surface, [[1., -1.], [1., 1.]]);

let intersection = CurveEdgeIntersection::compute(&curve, &edge);

let edge_left = Edge::build()
assert_eq!(
intersection,
Some(CurveEdgeIntersection::Point {
point_on_curve: Point::from([1.])
})
);
}

#[test]
fn compute_edge_behind_curve_origin() {
let surface = Surface::xy_plane();
let curve = Curve::u_axis();
let edge = Edge::build()
.line_segment_from_points(&surface, [[-1., -1.], [-1., 1.]]);
let edge_right = Edge::build()
.line_segment_from_points(&surface, [[1., -1.], [1., 1.]]);
let edge_below = Edge::build()
.line_segment_from_points(&surface, [[-1., -1.], [1., -1.]]);

let intersection_with_edge_left =
CurveEdgeIntersection::compute(&curve, &edge_left);
let intersection_with_edge_right =
CurveEdgeIntersection::compute(&curve, &edge_right);
let intersection_with_edge_below =
CurveEdgeIntersection::compute(&curve, &edge_below);
let intersection = CurveEdgeIntersection::compute(&curve, &edge);

assert_eq!(
intersection_with_edge_left,
Some(CurveEdgeIntersection::from_point_on_curve(Point::from([
-1.
])))
intersection,
Some(CurveEdgeIntersection::Point {
point_on_curve: Point::from([-1.])
})
);
}

#[test]
fn compute_edge_parallel_to_curve() {
let surface = Surface::xy_plane();
let curve = Curve::u_axis();
let edge = Edge::build()
.line_segment_from_points(&surface, [[-1., -1.], [1., -1.]]);

let intersection = CurveEdgeIntersection::compute(&curve, &edge);

assert!(intersection.is_none());
}

#[test]
fn compute_edge_on_curve() {
let surface = Surface::xy_plane();
let curve = Curve::u_axis();
let edge = Edge::build()
.line_segment_from_points(&surface, [[-1., 0.], [1., 0.]]);

let intersection = CurveEdgeIntersection::compute(&curve, &edge);

assert_eq!(
intersection_with_edge_right,
Some(CurveEdgeIntersection::from_point_on_curve(Point::from([
1.
])))
intersection,
Some(CurveEdgeIntersection::Coincident {
a_on_curve: Point::from([-1.]),
b_on_curve: Point::from([1.]),
})
);
assert!(intersection_with_edge_below.is_none());
}
}
23 changes: 15 additions & 8 deletions crates/fj-kernel/src/algorithms/intersection/curve_face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ impl CurveFaceIntersectionList {
) -> Self {
let intervals = intervals
.into_iter()
.map(|interval| {
interval
.map(Into::into)
.map(CurveEdgeIntersection::from_point_on_curve)
})
.map(|interval| interval.map(Into::into))
.collect();
Self { intervals }
}
Expand All @@ -44,7 +40,18 @@ impl CurveFaceIntersectionList {
let intersection = CurveEdgeIntersection::compute(curve, &edge);

if let Some(intersection) = intersection {
intersections.push(intersection);
match intersection {
CurveEdgeIntersection::Point { point_on_curve } => {
intersections.push(point_on_curve);
}
CurveEdgeIntersection::Coincident {
a_on_curve,
b_on_curve,
} => {
intersections.push(a_on_curve);
intersections.push(b_on_curve);
}
}
}
}

Expand All @@ -57,7 +64,7 @@ impl CurveFaceIntersectionList {
let intervals = intersections
.chunks(2)
.map(|chunk| {
// Can't panic, as we passed `2` to `windows`.
// Can't panic, as we passed `2` to `chunks`.
[chunk[0], chunk[1]]
})
.collect();
Expand Down Expand Up @@ -133,7 +140,7 @@ impl IntoIterator for CurveFaceIntersectionList {
}

/// An intersection between a curve and a face, in curve coordinates
pub type CurveFaceIntersection = [CurveEdgeIntersection; 2];
pub type CurveFaceIntersection = [Point<1>; 2];

#[cfg(test)]
mod tests {
Expand Down