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 cycle/edge/curve approximation code #1013

Merged
merged 7 commits into from
Aug 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
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)
}
}