Skip to content

Commit

Permalink
Merge pull request #1013 from hannobraun/approx5
Browse files Browse the repository at this point in the history
Simplify cycle/edge/curve approximation code
  • Loading branch information
hannobraun authored Aug 29, 2022
2 parents b1d21ff + 972c635 commit 801af9c
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 98 deletions.
27 changes: 18 additions & 9 deletions crates/fj-kernel/src/algorithms/approx/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@ use crate::objects::{Curve, CurveKind, GlobalCurve};
use super::{Approx, Tolerance};

impl Approx for Curve {
type Approximation = Vec<(Point<1>, Point<3>)>;
type Approximation = Vec<(Point<2>, Point<3>)>;
type Params = RangeOnCurve;

fn approx(
&self,
tolerance: Tolerance,
range: Self::Params,
) -> Self::Approximation {
self.global().approx(tolerance, range)
self.global()
.approx(tolerance, range)
.into_iter()
.map(|(point_curve, point_global)| {
let point_surface =
self.kind().point_from_curve_coords(point_curve);
(point_surface, point_global)
})
.collect()
}
}

Expand All @@ -30,7 +38,7 @@ impl Approx for GlobalCurve {
) -> Self::Approximation {
match self.kind() {
CurveKind::Circle(curve) => approx_circle(curve, range, tolerance),
CurveKind::Line(_) => Vec::new(),
CurveKind::Line(_) => vec![range.start()],
}
}
}
Expand All @@ -56,9 +64,10 @@ fn approx_circle(
let n = number_of_vertices_for_circle(tolerance, radius, range.length());

let mut points = Vec::new();
points.push(range.start());

for i in 0..n {
let angle = range.start().t
for i in 1..n {
let angle = range.start().0.t
+ (Scalar::TAU / n as f64 * i as f64) * range.direction();

let point_curve = Point::from([angle]);
Expand All @@ -83,20 +92,20 @@ fn number_of_vertices_for_circle(
}

pub struct RangeOnCurve {
pub boundary: [Point<1>; 2],
pub boundary: [(Point<1>, Point<3>); 2],
}

impl RangeOnCurve {
fn start(&self) -> Point<1> {
fn start(&self) -> (Point<1>, Point<3>) {
self.boundary[0]
}

fn end(&self) -> Point<1> {
fn end(&self) -> (Point<1>, Point<3>) {
self.boundary[1]
}

fn signed_length(&self) -> Scalar {
(self.end() - self.start()).t
(self.end().0 - self.start().0).t
}

fn length(&self) -> Scalar {
Expand Down
15 changes: 4 additions & 11 deletions crates/fj-kernel/src/algorithms/approx/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,12 @@ impl Approx for Cycle {

for edge in self.edges() {
let edge_points = edge.approx(tolerance, ());

points.extend(edge_points.into_iter().map(|point| {
let (point_curve, point_global) = point;

let point_surface =
edge.curve().kind().point_from_curve_coords(point_curve);
(point_surface, point_global)
}));
points.extend(edge_points);
}

// Can't just rely on `dedup`, as the conversion from curve coordinates
// could lead to subtly different surface coordinates.
points.dedup_by(|(_, a), (_, b)| a == b);
if let Some(&point) = points.first() {
points.push(point);
}

CycleApprox { points }
}
Expand Down
101 changes: 23 additions & 78 deletions crates/fj-kernel/src/algorithms/approx/edge.rs
Original file line number Diff line number Diff line change
@@ -1,94 +1,39 @@
use fj_math::{Point, Scalar};

use crate::objects::{Edge, Vertex, VerticesOfEdge};
use crate::objects::Edge;

use super::{curve::RangeOnCurve, Approx};

impl Approx for Edge {
type Approximation = Vec<(Point<1>, Point<3>)>;
type Approximation = Vec<(Point<2>, Point<3>)>;
type Params = ();

fn approx(
&self,
tolerance: super::Tolerance,
(): Self::Params,
) -> Self::Approximation {
let mut points = self.curve().approx(
tolerance,
// The range is only used for circles right now.
RangeOnCurve {
boundary: [[Scalar::ZERO].into(), [Scalar::TAU].into()],
},
);
approx_edge(*self.vertices(), &mut points);

points
}
}

pub fn approx_edge(
vertices: VerticesOfEdge<Vertex>,
points: &mut Vec<(Point<1>, Point<3>)>,
) {
// Insert the exact vertices of this edge into the approximation. This means
// we don't rely on the curve approximation to deliver accurate
// representations of these vertices, which they might not be able to do.
//
// If we used inaccurate representations of those vertices here, then that
// would lead to bugs in the approximation, as points that should refer to
// the same vertex would be understood to refer to very close, but distinct
// vertices.
let vertices = vertices
.convert(|vertex| (vertex.position(), vertex.global().position()));
if let Some([a, b]) = vertices {
points.insert(0, a);
points.push(b);
}

if vertices.is_none() {
// The edge has no vertices, which means it connects to itself. We need
// to reflect that in the approximation.

if let Some(&point) = points.first() {
points.push(point);
}
}
}

#[cfg(test)]
mod test {
use fj_math::Point;

use crate::objects::{GlobalVertex, Vertex, VerticesOfEdge};

#[test]
fn approx_edge() {
let a = Point::from([1., 2., 3.]);
let b = Point::from([2., 3., 5.]);
let c = Point::from([3., 5., 8.]);
let d = Point::from([5., 8., 13.]);

let v1 = GlobalVertex::from_position(a);
let v2 = GlobalVertex::from_position(d);
// The range is only used for circles right now.
let range = {
let start_curve = Point::from([Scalar::ZERO]);
let end_curve = Point::from([Scalar::TAU]);

// We're dealing with a circle here. Start and end are identical
// points, in global coordinates.
let point_global = self
.global()
.curve()
.kind()
.point_from_curve_coords(start_curve);

let vertices = VerticesOfEdge::from_vertices([
Vertex::new(Point::from([0.]), v1),
Vertex::new(Point::from([1.]), v2),
]);

let a = (Point::from([0.0]), a);
let b = (Point::from([0.25]), b);
let c = (Point::from([0.75]), c);
let d = (Point::from([1.0]), d);

// Regular edge
let mut points = vec![b, c];
super::approx_edge(vertices, &mut points);
assert_eq!(points, vec![a, b, c, d]);

// Continuous edge
let mut points = vec![b, c];
super::approx_edge(VerticesOfEdge::none(), &mut points);
assert_eq!(points, vec![b, c, b]);
RangeOnCurve {
boundary: [
(start_curve, point_global),
(end_curve, point_global),
],
}
};

self.curve().approx(tolerance, range)
}
}

0 comments on commit 801af9c

Please sign in to comment.