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

Use dedicated struct to represent boundaries on curves #1941

Merged
merged 24 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
49 changes: 24 additions & 25 deletions crates/fj-core/src/algorithms/approx/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ use std::collections::BTreeMap;
use fj_math::Point;

use crate::{
geometry::{GlobalPath, SurfacePath},
geometry::{BoundaryOnCurve, GlobalPath, SurfacePath},
objects::{GlobalEdge, HalfEdge, Surface, Vertex},
storage::{Handle, ObjectId},
};

use super::{path::RangeOnPath, Approx, ApproxPoint, Tolerance};
use super::{Approx, ApproxPoint, Tolerance};

impl Approx for (&HalfEdge, &Surface) {
type Approximation = HalfEdgeApprox;
Expand All @@ -28,9 +28,6 @@ impl Approx for (&HalfEdge, &Surface) {
) -> Self::Approximation {
let (half_edge, surface) = self;

let boundary = half_edge.boundary();
let range = RangeOnPath { boundary };

let position_surface = half_edge.start_position();
let position_global = match cache.get_position(half_edge.start_vertex())
{
Expand Down Expand Up @@ -87,20 +84,22 @@ impl Approx for (&HalfEdge, &Surface) {
//
// Only item 2. is something we can do right here. Item 1. requires
// a change to the object graph.
let cached_approx =
cache.get_edge(half_edge.global_form().clone(), range);
let cached_approx = cache.get_edge(
half_edge.global_form().clone(),
half_edge.boundary(),
);
let approx = match cached_approx {
Some(approx) => approx,
None => {
let approx = approx_edge(
&half_edge.path(),
surface,
range,
half_edge.boundary(),
tolerance,
);
cache.insert_edge(
half_edge.global_form().clone(),
range,
half_edge.boundary(),
approx,
)
}
Expand Down Expand Up @@ -148,7 +147,7 @@ impl HalfEdgeApprox {
fn approx_edge(
path: &SurfacePath,
surface: &Surface,
range: RangeOnPath,
boundary: BoundaryOnCurve,
tolerance: impl Into<Tolerance>,
) -> GlobalEdgeApprox {
// There are different cases of varying complexity. Circles are the hard
Expand All @@ -164,7 +163,7 @@ fn approx_edge(
)
}
(SurfacePath::Circle(_), GlobalPath::Line(_)) => {
(path, range)
(path, boundary)
.approx_with_cache(tolerance, &mut ())
.into_iter()
.map(|(point_curve, point_surface)| {
Expand Down Expand Up @@ -192,7 +191,7 @@ fn approx_edge(
}
(SurfacePath::Line(line), _) => {
let range_u =
RangeOnPath::from(range.boundary.map(|point_curve| {
BoundaryOnCurve::from(boundary.inner.map(|point_curve| {
[path.point_from_path_coords(point_curve).u]
}));

Expand Down Expand Up @@ -224,7 +223,7 @@ fn approx_edge(
/// A cache for results of an approximation
#[derive(Default)]
pub struct EdgeCache {
edge_approx: BTreeMap<(ObjectId, RangeOnPath), GlobalEdgeApprox>,
edge_approx: BTreeMap<(ObjectId, BoundaryOnCurve), GlobalEdgeApprox>,
vertex_approx: BTreeMap<ObjectId, Point<3>>,
}

Expand All @@ -238,15 +237,15 @@ impl EdgeCache {
pub fn get_edge(
&self,
handle: Handle<GlobalEdge>,
range: RangeOnPath,
boundary: BoundaryOnCurve,
) -> Option<GlobalEdgeApprox> {
if let Some(approx) = self.edge_approx.get(&(handle.id(), range)) {
if let Some(approx) = self.edge_approx.get(&(handle.id(), boundary)) {
return Some(approx.clone());
}
if let Some(approx) =
self.edge_approx.get(&(handle.id(), range.reverse()))
self.edge_approx.get(&(handle.id(), boundary.reverse()))
{
// If we have a cache entry for the reverse range, we need to use
// If we have a cache entry for the reverse boundary, we need to use
// that too!
return Some(approx.clone().reverse());
}
Expand All @@ -258,11 +257,11 @@ impl EdgeCache {
pub fn insert_edge(
&mut self,
handle: Handle<GlobalEdge>,
range: RangeOnPath,
boundary: BoundaryOnCurve,
approx: GlobalEdgeApprox,
) -> GlobalEdgeApprox {
self.edge_approx
.insert((handle.id(), range), approx.clone())
.insert((handle.id(), boundary), approx.clone())
.unwrap_or(approx)
}

Expand Down Expand Up @@ -303,8 +302,8 @@ mod tests {
use pretty_assertions::assert_eq;

use crate::{
algorithms::approx::{path::RangeOnPath, Approx, ApproxPoint},
geometry::{GlobalPath, SurfaceGeometry},
algorithms::approx::{Approx, ApproxPoint},
geometry::{BoundaryOnCurve, GlobalPath, SurfaceGeometry},
objects::{HalfEdge, Surface},
operations::BuildHalfEdge,
services::Services,
Expand Down Expand Up @@ -346,22 +345,22 @@ mod tests {
let mut services = Services::new();

let path = GlobalPath::circle_from_radius(1.);
let range = RangeOnPath::from([[0.], [TAU]]);
let boundary = BoundaryOnCurve::from([[0.], [TAU]]);

let surface = Surface::new(SurfaceGeometry {
u: path,
v: [0., 0., 1.].into(),
});
let half_edge = HalfEdge::line_segment(
[[0., 1.], [TAU, 1.]],
Some(range.boundary),
Some(boundary.inner),
&mut services,
);

let tolerance = 1.;
let approx = (&half_edge, &surface).approx(tolerance);

let expected_approx = (path, range)
let expected_approx = (path, boundary)
.approx(tolerance)
.into_iter()
.map(|(point_local, _)| {
Expand All @@ -386,7 +385,7 @@ mod tests {
let approx = (&half_edge, surface.deref()).approx(tolerance);

let expected_approx =
(&half_edge.path(), RangeOnPath::from([[0.], [TAU]]))
(&half_edge.path(), BoundaryOnCurve::from([[0.], [TAU]]))
.approx(tolerance)
.into_iter()
.map(|(_, point_surface)| {
Expand Down
49 changes: 12 additions & 37 deletions crates/fj-core/src/algorithms/approx/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ use std::iter;

use fj_math::{Circle, Point, Scalar, Sign};

use crate::geometry::{GlobalPath, SurfacePath};
use crate::geometry::{BoundaryOnCurve, GlobalPath, SurfacePath};

use super::{Approx, Tolerance};

impl Approx for (&SurfacePath, RangeOnPath) {
impl Approx for (&SurfacePath, BoundaryOnCurve) {
type Approximation = Vec<(Point<1>, Point<2>)>;
type Cache = ();

Expand All @@ -56,7 +56,7 @@ impl Approx for (&SurfacePath, RangeOnPath) {
}
}

impl Approx for (GlobalPath, RangeOnPath) {
impl Approx for (GlobalPath, BoundaryOnCurve) {
type Approximation = Vec<(Point<1>, Point<3>)>;
type Cache = ();

Expand All @@ -76,46 +76,21 @@ impl Approx for (GlobalPath, RangeOnPath) {
}
}

/// The range on which a path should be approximated
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct RangeOnPath {
/// The boundary of the range
pub boundary: [Point<1>; 2],
}

impl RangeOnPath {
/// Reverse the direction of the range
pub fn reverse(self) -> Self {
let [a, b] = self.boundary;
Self { boundary: [b, a] }
}
}

impl<T> From<[T; 2]> for RangeOnPath
where
T: Into<Point<1>>,
{
fn from(boundary: [T; 2]) -> Self {
let boundary = boundary.map(Into::into);
Self { boundary }
}
}

/// Approximate a circle
///
/// `tolerance` specifies how much the approximation is allowed to deviate
/// from the circle.
fn approx_circle<const D: usize>(
circle: &Circle<D>,
range: impl Into<RangeOnPath>,
boundary: impl Into<BoundaryOnCurve>,
tolerance: Tolerance,
) -> Vec<(Point<1>, Point<D>)> {
let range = range.into();
let boundary = boundary.into();

let params = PathApproxParams::for_circle(circle, tolerance);
let mut points = Vec::new();

for point_curve in params.points(range) {
for point_curve in params.points(boundary) {
let point_global = circle.point_from_circle_coords(point_curve);
points.push((point_curve, point_global));
}
Expand Down Expand Up @@ -152,11 +127,11 @@ impl PathApproxParams {

pub fn points(
&self,
range: impl Into<RangeOnPath>,
boundary: impl Into<BoundaryOnCurve>,
) -> impl Iterator<Item = Point<1>> + '_ {
let range = range.into();
let boundary = boundary.into();

let [a, b] = range.boundary.map(|point| point.t / self.increment());
let [a, b] = boundary.inner.map(|point| point.t / self.increment());
let direction = (b - a).sign();
let [min, max] = if a < b { [a, b] } else { [b, a] };

Expand Down Expand Up @@ -195,7 +170,7 @@ mod tests {

use fj_math::{Circle, Point, Scalar};

use crate::algorithms::approx::{path::RangeOnPath, Tolerance};
use crate::algorithms::approx::{path::BoundaryOnCurve, Tolerance};

use super::PathApproxParams;

Expand Down Expand Up @@ -245,7 +220,7 @@ mod tests {
test_path([[TAU - 2.], [0.]], [2., 1.]);

fn test_path(
range: impl Into<RangeOnPath>,
boundary: impl Into<BoundaryOnCurve>,
expected_coords: impl IntoIterator<Item = impl Into<Scalar>>,
) {
// Choose radius and tolerance such, that we need 4 vertices to
Expand All @@ -257,7 +232,7 @@ mod tests {
let circle = Circle::from_center_and_radius([0., 0.], radius);
let params = PathApproxParams::for_circle(&circle, tolerance);

let points = params.points(range).collect::<Vec<_>>();
let points = params.points(boundary).collect::<Vec<_>>();

let expected_points = expected_coords
.into_iter()
Expand Down
2 changes: 1 addition & 1 deletion crates/fj-core/src/algorithms/bounding_volume/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl super::BoundingVolume<2> for HalfEdge {
})
}
SurfacePath::Line(_) => {
let points = self.boundary().map(|point_curve| {
let points = self.boundary().inner.map(|point_curve| {
self.path().point_from_path_coords(point_curve)
});

Expand Down
1 change: 1 addition & 0 deletions crates/fj-core/src/algorithms/intersect/curve_edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ impl CurveEdgeIntersection {

let edge_vertices = half_edge
.boundary()
.inner
.map(|point| edge_path_as_line.point_from_line_coords(point));

Segment::from_points(edge_vertices)
Expand Down
1 change: 1 addition & 0 deletions crates/fj-core/src/algorithms/intersect/ray_edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ impl Intersect for (&HorizontalRayToTheRight<2>, &Handle<HalfEdge>) {

let points = edge
.boundary()
.inner
.map(|point| line.point_from_line_coords(point));
let segment = Segment::from_points(points);

Expand Down
4 changes: 2 additions & 2 deletions crates/fj-core/src/algorithms/sweep/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Sweep for (&HalfEdge, &Handle<Vertex>, &Surface, Option<Color>) {

// Let's figure out the surface coordinates of the edge vertices.
let surface_points = {
let [a, b] = edge.boundary();
let [a, b] = edge.boundary().inner;

[
[a.t, Scalar::ZERO],
Expand All @@ -65,7 +65,7 @@ impl Sweep for (&HalfEdge, &Handle<Vertex>, &Surface, Option<Color>) {

// Now, the boundaries of each edge.
let boundaries = {
let [a, b] = edge.boundary();
let [a, b] = edge.boundary().inner;
let [c, d] = [0., 1.].map(|coord| Point::from([coord]));

[[a, b], [c, d], [b, a], [d, c]]
Expand Down
26 changes: 26 additions & 0 deletions crates/fj-core/src/geometry/boundary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use fj_math::Point;

/// A boundary on a curve
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct BoundaryOnCurve {
/// The raw representation of the boundary
pub inner: [Point<1>; 2],
}

impl BoundaryOnCurve {
/// Reverse the direction of the boundary
pub fn reverse(self) -> Self {
let [a, b] = self.inner;
Self { inner: [b, a] }
}
}

impl<T> From<[T; 2]> for BoundaryOnCurve
where
T: Into<Point<1>>,
{
fn from(boundary: [T; 2]) -> Self {
let inner = boundary.map(Into::into);
Self { inner }
}
}
2 changes: 2 additions & 0 deletions crates/fj-core/src/geometry/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! Types that are tied to objects, but aren't objects themselves

mod boundary;
mod path;
mod surface;

pub use self::{
boundary::BoundaryOnCurve,
path::{GlobalPath, SurfacePath},
surface::SurfaceGeometry,
};
2 changes: 1 addition & 1 deletion crates/fj-core/src/objects/kinds/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl Cycle {
.next()
.expect("Invalid cycle: expected at least one half-edge");

let [a, b] = first.boundary();
let [a, b] = first.boundary().inner;
let edge_direction_positive = a < b;

let circle = match first.path() {
Expand Down
Loading