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

Simplify intersection code that uses or produces curves #1614

Merged
merged 12 commits into from
Feb 24, 2023
36 changes: 13 additions & 23 deletions crates/fj-kernel/src/algorithms/intersect/curve_edge.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
use fj_math::{Point, Segment};

use crate::{
geometry::path::SurfacePath,
objects::{Curve, HalfEdge},
};
use crate::{geometry::path::SurfacePath, objects::HalfEdge};

use super::LineSegmentIntersection;

/// The intersection between a [`Curve`] and a [`HalfEdge`]
/// 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
Expand All @@ -29,10 +26,10 @@ impl CurveEdgeIntersection {
/// # Panics
///
/// 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 [`HalfEdge`] is
/// passed.
pub fn compute(curve: &Curve, half_edge: &HalfEdge) -> Option<Self> {
let curve_as_line = match curve.path() {
pub fn compute(curve: &SurfacePath, half_edge: &HalfEdge) -> Option<Self> {
let curve_as_line = match curve {
SurfacePath::Line(line) => line,
_ => todo!("Curve-edge intersection only supports lines"),
};
Expand All @@ -53,7 +50,7 @@ impl CurveEdgeIntersection {
};

let intersection =
LineSegmentIntersection::compute(&curve_as_line, &edge_as_segment)?;
LineSegmentIntersection::compute(curve_as_line, &edge_as_segment)?;

let intersection = match intersection {
LineSegmentIntersection::Point { point_on_line } => Self::Point {
Expand All @@ -75,8 +72,9 @@ mod tests {
use fj_math::Point;

use crate::{
builder::{CurveBuilder, HalfEdgeBuilder},
partial::{PartialCurve, PartialHalfEdge, PartialObject},
builder::HalfEdgeBuilder,
geometry::path::SurfacePath,
partial::{PartialHalfEdge, PartialObject},
services::Services,
};

Expand All @@ -87,9 +85,7 @@ mod tests {
let mut services = Services::new();

let surface = services.objects.surfaces.xy_plane();
let mut curve = PartialCurve::default();
curve.update_as_u_axis();
let curve = curve.build(&mut services.objects);
let curve = SurfacePath::u_axis();
let half_edge = {
let mut half_edge = PartialHalfEdge::default();
half_edge.update_as_line_segment_from_points([[1., -1.], [1., 1.]]);
Expand All @@ -113,9 +109,7 @@ mod tests {
let mut services = Services::new();

let surface = services.objects.surfaces.xy_plane();
let mut curve = PartialCurve::default();
curve.update_as_u_axis();
let curve = curve.build(&mut services.objects);
let curve = SurfacePath::u_axis();
let half_edge = {
let mut half_edge = PartialHalfEdge::default();
half_edge
Expand All @@ -140,9 +134,7 @@ mod tests {
let mut services = Services::new();

let surface = services.objects.surfaces.xy_plane();
let mut curve = PartialCurve::default();
curve.update_as_u_axis();
let curve = curve.build(&mut services.objects);
let curve = SurfacePath::u_axis();
let half_edge = {
let mut half_edge = PartialHalfEdge::default();
half_edge
Expand All @@ -162,9 +154,7 @@ mod tests {
let mut services = Services::new();

let surface = services.objects.surfaces.xy_plane();
let mut curve = PartialCurve::default();
curve.update_as_u_axis();
let curve = curve.build(&mut services.objects);
let curve = SurfacePath::u_axis();
let half_edge = {
let mut half_edge = PartialHalfEdge::default();
half_edge.update_as_line_segment_from_points([[-1., 0.], [1., 0.]]);
Expand Down
17 changes: 8 additions & 9 deletions crates/fj-kernel/src/algorithms/intersect/curve_face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use std::vec;
use fj_interop::ext::SliceExt;
use fj_math::Point;

use crate::objects::{Curve, Face};
use crate::{geometry::path::SurfacePath, objects::Face};

use super::CurveEdgeIntersection;

/// The intersections between a [`Curve`] and a [`Face`], in curve coordinates
/// 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
Expand All @@ -27,8 +27,8 @@ impl CurveFaceIntersection {
Self { intervals }
}

/// Compute the intersections between a [`Curve`] and a [`Face`]
pub fn compute(curve: &Curve, face: &Face) -> Self {
/// Compute the intersection
pub fn compute(curve: &SurfacePath, face: &Face) -> Self {
let half_edges = face.all_cycles().flat_map(|cycle| cycle.half_edges());

let mut intersections = Vec::new();
Expand Down Expand Up @@ -150,8 +150,9 @@ where
#[cfg(test)]
mod tests {
use crate::{
builder::{CurveBuilder, CycleBuilder, FaceBuilder},
partial::{Partial, PartialCurve, PartialFace, PartialObject},
builder::{CycleBuilder, FaceBuilder},
geometry::path::SurfacePath,
partial::{Partial, PartialFace, PartialObject},
services::Services,
};

Expand All @@ -161,9 +162,7 @@ mod tests {
fn compute() {
let mut services = Services::new();

let mut curve = PartialCurve::default();
curve.update_as_line_from_points([[-3., 0.], [-2., 0.]]);
let curve = curve.build(&mut services.objects);
let (curve, _) = SurfacePath::line_from_points([[-3., 0.], [-2., 0.]]);

#[rustfmt::skip]
let exterior = [
Expand Down
34 changes: 11 additions & 23 deletions crates/fj-kernel/src/algorithms/intersect/face_face.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
use fj_interop::ext::ArrayExt;
use iter_fixed::IntoIteratorFixed;

use crate::{
objects::{Curve, Face, Objects},
services::Service,
storage::Handle,
};
use crate::{geometry::path::SurfacePath, objects::Face};

use super::{CurveFaceIntersection, SurfaceSurfaceIntersection};

Expand All @@ -18,7 +14,7 @@ pub struct FaceFaceIntersection {
/// representation of the intersection on the respective face's surface.
///
/// They both represent the same global curve.
pub intersection_curves: [Handle<Curve>; 2],
pub intersection_curves: [SurfacePath; 2],

/// The interval of this intersection, in curve coordinates
///
Expand All @@ -28,14 +24,11 @@ pub struct FaceFaceIntersection {

impl FaceFaceIntersection {
/// Compute the intersections between two faces
pub fn compute(
faces: [&Face; 2],
objects: &mut Service<Objects>,
) -> Option<Self> {
pub fn compute(faces: [&Face; 2]) -> Option<Self> {
let surfaces = faces.map(|face| face.surface().clone());

let intersection_curves =
match SurfaceSurfaceIntersection::compute(surfaces, objects) {
match SurfaceSurfaceIntersection::compute(surfaces) {
Some(intersection) => intersection.intersection_curves,
None => return None,
};
Expand Down Expand Up @@ -69,9 +62,9 @@ mod tests {

use crate::{
algorithms::intersect::CurveFaceIntersection,
builder::{CurveBuilder, CycleBuilder},
insert::Insert,
partial::{Partial, PartialCurve, PartialFace, PartialObject},
builder::CycleBuilder,
geometry::path::SurfacePath,
partial::{Partial, PartialFace, PartialObject},
services::Services,
};

Expand Down Expand Up @@ -102,8 +95,7 @@ mod tests {
face.build(&mut services.objects)
});

let intersection =
FaceFaceIntersection::compute([&a, &b], &mut services.objects);
let intersection = FaceFaceIntersection::compute([&a, &b]);

assert!(intersection.is_none());
}
Expand Down Expand Up @@ -133,15 +125,11 @@ mod tests {
face.build(&mut services.objects)
});

let intersection =
FaceFaceIntersection::compute([&a, &b], &mut services.objects);
let intersection = FaceFaceIntersection::compute([&a, &b]);

let expected_curves = surfaces.map(|_| {
let mut curve = PartialCurve::default();
curve.update_as_line_from_points([[0., 0.], [1., 0.]]);
curve
.build(&mut services.objects)
.insert(&mut services.objects)
let (path, _) = SurfacePath::line_from_points([[0., 0.], [1., 0.]]);
path
});
let expected_intervals =
CurveFaceIntersection::from_intervals([[[-1.], [1.]]]);
Expand Down
57 changes: 16 additions & 41 deletions crates/fj-kernel/src/algorithms/intersect/surface_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,20 @@ use fj_math::{Line, Plane, Point, Scalar};

use crate::{
geometry::path::{GlobalPath, SurfacePath},
insert::Insert,
objects::{Curve, Objects, Surface},
services::Service,
objects::Surface,
storage::Handle,
};

/// The intersection between two surfaces
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct SurfaceSurfaceIntersection {
/// The intersection curves
pub intersection_curves: [Handle<Curve>; 2],
pub intersection_curves: [SurfacePath; 2],
}

impl SurfaceSurfaceIntersection {
/// Compute the intersection between two surfaces
pub fn compute(
surfaces: [Handle<Surface>; 2],
objects: &mut Service<Objects>,
) -> Option<Self> {
pub fn compute(surfaces: [Handle<Surface>; 2]) -> Option<Self> {
// Algorithm from Real-Time Collision Detection by Christer Ericson. See
// section 5.4.4, Intersection of Two Planes.
//
Expand Down Expand Up @@ -53,10 +48,8 @@ impl SurfaceSurfaceIntersection {

let line = Line::from_origin_and_direction(origin, direction);

let curves = planes.map(|plane| {
let path = SurfacePath::Line(plane.project_line(&line));
Curve::new(path).insert(objects)
});
let curves =
planes.map(|plane| SurfacePath::Line(plane.project_line(&line)));

Some(Self {
intersection_curves: curves,
Expand All @@ -83,10 +76,7 @@ mod tests {
use pretty_assertions::assert_eq;

use crate::{
algorithms::transform::TransformObject,
builder::CurveBuilder,
insert::Insert,
partial::{PartialCurve, PartialObject},
algorithms::transform::TransformObject, geometry::path::SurfacePath,
services::Services,
};

Expand All @@ -101,36 +91,21 @@ mod tests {

// Coincident and parallel planes don't have an intersection curve.
assert_eq!(
SurfaceSurfaceIntersection::compute(
[
xy.clone(),
xy.clone().transform(
&Transform::translation([0., 0., 1.],),
&mut services.objects
)
],
&mut services.objects
),
SurfaceSurfaceIntersection::compute([
xy.clone(),
xy.clone().transform(
&Transform::translation([0., 0., 1.],),
&mut services.objects
)
],),
None,
);

let mut expected_xy = PartialCurve::default();
expected_xy.update_as_u_axis();
let expected_xy = expected_xy
.build(&mut services.objects)
.insert(&mut services.objects);

let mut expected_xz = PartialCurve::default();
expected_xz.update_as_u_axis();
let expected_xz = expected_xz
.build(&mut services.objects)
.insert(&mut services.objects);
let expected_xy = SurfacePath::u_axis();
let expected_xz = SurfacePath::u_axis();

assert_eq!(
SurfaceSurfaceIntersection::compute(
[xy, xz],
&mut services.objects
),
SurfaceSurfaceIntersection::compute([xy, xz],),
Some(SurfaceSurfaceIntersection {
intersection_curves: [expected_xy, expected_xz],
})
Expand Down
16 changes: 7 additions & 9 deletions crates/fj-kernel/src/builder/curve.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use fj_math::{Point, Scalar, Vector};
use fj_math::{Point, Scalar};

use crate::{geometry::path::SurfacePath, partial::PartialCurve};

Expand Down Expand Up @@ -50,17 +50,15 @@ pub trait CurveBuilder {

impl CurveBuilder for PartialCurve {
fn update_as_u_axis(&mut self) -> SurfacePath {
let a = Point::origin();
let b = a + Vector::unit_u();

self.update_as_line_from_points([a, b])
let path = SurfacePath::u_axis();
self.path = Some(path.into());
path
}

fn update_as_v_axis(&mut self) -> SurfacePath {
let a = Point::origin();
let b = a + Vector::unit_v();

self.update_as_line_from_points([a, b])
let path = SurfacePath::v_axis();
self.path = Some(path.into());
path
}

fn update_as_circle_from_radius(
Expand Down
18 changes: 18 additions & 0 deletions crates/fj-kernel/src/geometry/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ impl SurfacePath {
Self::Circle(Circle::from_center_and_radius(center, radius))
}

/// Build a line that represents the u-axis of the surface its on
pub fn u_axis() -> Self {
let a = Point::origin();
let b = a + Vector::unit_u();

let (self_, _) = Self::line_from_points([a, b]);
self_
}

/// Build a line that represents the v-axis of the surface its on
pub fn v_axis() -> Self {
let a = Point::origin();
let b = a + Vector::unit_v();

let (self_, _) = Self::line_from_points([a, b]);
self_
}

/// Construct a line from two points
///
/// Also returns the coordinates of the points on the path.
Expand Down