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

Clean up approx module #1003

Merged
merged 11 commits into from
Aug 26, 2022
2 changes: 1 addition & 1 deletion crates/fj-app/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{path::PathBuf, str::FromStr as _};

use anyhow::anyhow;
use fj_host::Parameters;
use fj_kernel::algorithms::Tolerance;
use fj_kernel::algorithms::approx::Tolerance;
use fj_math::Scalar;

/// Fornjot - Experimental CAD System
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,46 @@ use std::cmp::max;

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

use crate::objects::{CurveKind, GlobalCurve};
use crate::objects::{Curve, CurveKind, GlobalCurve};

use super::{Local, Tolerance};
use super::{Approx, Local, Tolerance};

/// Compute an approximation of the curve
///
/// `tolerance` defines how far the approximation is allowed to deviate from the
/// actual edge.
///
/// # Implementation Note
///
/// This only works as it is, because edges are severely limited and don't
/// define which section of the curve they inhabit. Once they do that, we need
/// an `approximate_between(a, b)` method instead, where `a` and `b` are the
/// vertices that bound the edge on the curve.
///
/// The `approximate_between` methods of the curves then need to make sure to
/// only return points in between those vertices, not the vertices themselves.
pub fn approx_curve(
curve: &GlobalCurve,
tolerance: Tolerance,
out: &mut Vec<Local<Point<1>>>,
) {
match curve.kind() {
CurveKind::Circle(curve) => approx_circle(curve, tolerance, out),
CurveKind::Line(_) => {}
impl Approx for Curve {
type Approximation = Vec<Local<Point<1>>>;

fn approx(&self, tolerance: Tolerance) -> Self::Approximation {
self.global().approx(tolerance)
}
}

impl Approx for GlobalCurve {
type Approximation = Vec<Local<Point<1>>>;

/// Approximate the global curve
///
/// # Implementation Note
///
/// This only works as-is, because only circles need to be approximated
/// right now and because only edges that are full circles are supported, as
/// opposed to edges that only inhabit part of the circle.
///
/// To support that, we will need additional information here, to define
/// between which points the curve needs to be approximated.
fn approx(&self, tolerance: Tolerance) -> Self::Approximation {
let mut points = Vec::new();

match self.kind() {
CurveKind::Circle(curve) => {
approx_circle(curve, tolerance, &mut points)
}
CurveKind::Line(_) => {}
}

points
}
}

/// Approximate the circle
/// Approximate a circle
///
/// `tolerance` specifies how much the approximation is allowed to deviate
/// from the circle.
Expand Down Expand Up @@ -69,7 +79,7 @@ fn number_of_vertices_for_circle(tolerance: Tolerance, radius: Scalar) -> u64 {
mod tests {
use fj_math::Scalar;

use crate::algorithms::Tolerance;
use crate::algorithms::approx::Tolerance;

#[test]
fn number_of_vertices_for_circle() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,16 @@ use fj_math::{Point, Segment};

use crate::objects::Cycle;

use super::{curves::approx_curve, edges::approx_edge, Local, Tolerance};
use super::{Approx, Local, Tolerance};

/// An approximation of a [`Cycle`]
#[derive(Debug, Eq, PartialEq, Hash)]
pub struct CycleApprox {
/// The points that approximate the cycle
pub points: Vec<Local<Point<2>>>,
}
impl Approx for Cycle {
type Approximation = CycleApprox;

impl CycleApprox {
/// Compute the approximation of a cycle
///
/// `tolerance` defines how far the approximation is allowed to deviate from
/// the actual face.
pub fn new(cycle: &Cycle, tolerance: Tolerance) -> Self {
fn approx(&self, tolerance: Tolerance) -> Self::Approximation {
let mut points = Vec::new();

for edge in cycle.edges() {
let mut edge_points = Vec::new();
approx_curve(edge.curve().global(), tolerance, &mut edge_points);
approx_edge(*edge.vertices(), &mut edge_points);
for edge in self.edges() {
let edge_points = edge.approx(tolerance);

points.extend(edge_points.into_iter().map(|point| {
let local = edge
Expand All @@ -37,9 +26,18 @@ impl CycleApprox {
// could lead to subtly different surface coordinates.
points.dedup_by(|a, b| a.global_form() == b.global_form());

Self { points }
CycleApprox { points }
}
}

/// An approximation of a [`Cycle`]
#[derive(Debug, Eq, PartialEq, Hash)]
pub struct CycleApprox {
/// The points that approximate the cycle
pub points: Vec<Local<Point<2>>>,
}

impl CycleApprox {
/// Construct the segments that approximate the cycle
pub fn segments(&self) -> Vec<Segment<3>> {
let mut segments = Vec::new();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
use fj_math::Point;

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

use super::Local;
use super::{Approx, Local};

impl Approx for Edge {
type Approximation = Vec<Local<Point<1>>>;

fn approx(&self, tolerance: super::Tolerance) -> Self::Approximation {
let mut points = self.curve().approx(tolerance);
approx_edge(*self.vertices(), &mut points);

points
}
}

pub fn approx_edge(
vertices: VerticesOfEdge<Vertex>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,12 @@ use fj_math::Point;

use crate::objects::Face;

use super::{CycleApprox, Local, Tolerance};
use super::{Approx, CycleApprox, Local, Tolerance};

/// An approximation of a [`Face`]
#[derive(Debug, Eq, PartialEq)]
pub struct FaceApprox {
/// All points that make up the approximation
///
/// These could be actual vertices from the model, points that approximate
/// an edge, or points that approximate a face.
pub points: HashSet<Local<Point<2>>>,
impl Approx for Face {
type Approximation = FaceApprox;

/// Approximation of the exterior cycle
pub exterior: CycleApprox,

/// Approximations of the interior cycles
pub interiors: HashSet<CycleApprox>,
}

impl FaceApprox {
/// Compute the approximation of a face
///
/// `tolerance` defines how far the approximation is allowed to deviate from
/// the actual face.
pub fn new(face: &Face, tolerance: Tolerance) -> Self {
fn approx(&self, tolerance: Tolerance) -> Self::Approximation {
// Curved faces whose curvature is not fully defined by their edges
// are not supported yet. For that reason, we can fully ignore `face`'s
// `surface` field and just pass the edges to `Self::for_edges`.
Expand All @@ -45,14 +27,14 @@ impl FaceApprox {
let mut exteriors = Vec::new();
let mut interiors = HashSet::new();

for cycle in face.exteriors() {
let cycle = CycleApprox::new(cycle, tolerance);
for cycle in self.exteriors() {
let cycle = cycle.approx(tolerance);

points.extend(cycle.points.iter().copied());
exteriors.push(cycle);
}
for cycle in face.interiors() {
let cycle = CycleApprox::new(cycle, tolerance);
for cycle in self.interiors() {
let cycle = cycle.approx(tolerance);

points.extend(cycle.points.iter().copied());
interiors.insert(cycle);
Expand All @@ -70,21 +52,37 @@ impl FaceApprox {
"Approximation only supports faces with one exterior cycle",
);

Self {
FaceApprox {
points,
exterior,
interiors,
}
}
}

/// An approximation of a [`Face`]
#[derive(Debug, Eq, PartialEq)]
pub struct FaceApprox {
/// All points that make up the approximation
///
/// These could be actual vertices from the model, points that approximate
/// an edge, or points that approximate a face.
pub points: HashSet<Local<Point<2>>>,

/// Approximation of the exterior cycle
pub exterior: CycleApprox,

/// Approximations of the interior cycles
pub interiors: HashSet<CycleApprox>,
}

#[cfg(test)]
mod tests {
use fj_math::{Point, Scalar};
use map_macro::set;

use crate::{
algorithms::approx::Local,
algorithms::approx::{Approx, Local},
objects::{Face, Surface},
};

Expand Down Expand Up @@ -120,7 +118,7 @@ mod tests {
let g = Local::new(g, g.to_xyz());
let h = Local::new(h, h.to_xyz());

let approx = FaceApprox::new(&face, tolerance);
let approx = face.approx(tolerance);
let expected = FaceApprox {
points: set![a, b, c, d, e, f, g, h],
exterior: CycleApprox {
Expand Down
26 changes: 20 additions & 6 deletions crates/fj-kernel/src/algorithms/approx/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
mod curves;
mod cycles;
mod edges;
mod faces;
//! Approximation of objects

mod curve;
mod cycle;
mod edge;
mod face;
mod local;
mod tolerance;

pub use self::{
cycles::CycleApprox,
faces::FaceApprox,
cycle::CycleApprox,
face::FaceApprox,
local::{Local, LocalForm},
tolerance::{InvalidTolerance, Tolerance},
};

/// Approximate an object
pub trait Approx {
/// The approximation of the object
type Approximation;

/// Approximate the object
///
/// `tolerance` defines how far the approximation is allowed to deviate from
/// the actual object.
fn approx(&self, tolerance: Tolerance) -> Self::Approximation;
}
3 changes: 1 addition & 2 deletions crates/fj-kernel/src/algorithms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@
//! Algorithmic code is collected in this module, to keep other modules focused
//! on their respective purpose.

mod approx;
mod reverse;
mod transform;
mod triangulate;

pub mod approx;
pub mod intersect;
pub mod sweep;

pub use self::{
approx::{CycleApprox, FaceApprox, InvalidTolerance, Tolerance},
reverse::reverse_face,
transform::{transform_faces, TransformObject},
triangulate::triangulate,
Expand Down
4 changes: 2 additions & 2 deletions crates/fj-kernel/src/algorithms/sweep/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use fj_interop::mesh::Color;
use fj_math::{Point, Transform, Triangle};

use crate::{
algorithms::{CycleApprox, Tolerance},
algorithms::approx::{Approx, Tolerance},
objects::{
Curve, CurveKind, Cycle, Edge, Face, GlobalCurve, GlobalVertex,
Surface, Vertex, VerticesOfEdge,
Expand Down Expand Up @@ -119,7 +119,7 @@ fn create_continuous_side_face(
let placeholder = Surface::xy_plane();

let cycle = Cycle::new(placeholder).with_edges([edge]);
let approx = CycleApprox::new(&cycle, tolerance);
let approx = cycle.approx(tolerance);

let mut quads = Vec::new();
for segment in approx.segments() {
Expand Down
2 changes: 1 addition & 1 deletion crates/fj-kernel/src/algorithms/sweep/face.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use fj_interop::mesh::Color;

use crate::{
algorithms::{reverse_face, Tolerance, TransformObject},
algorithms::{approx::Tolerance, reverse_face, TransformObject},
objects::{Face, Shell},
};

Expand Down
2 changes: 1 addition & 1 deletion crates/fj-kernel/src/algorithms/sweep/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod sketch;
use fj_interop::mesh::Color;
use fj_math::{Scalar, Vector};

use super::Tolerance;
use super::approx::Tolerance;

/// Sweep an object along a path to create another object
pub trait Sweep {
Expand Down
4 changes: 2 additions & 2 deletions crates/fj-kernel/src/algorithms/sweep/sketch.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use fj_interop::mesh::Color;

use crate::{
algorithms::Tolerance,
algorithms::approx::Tolerance,
objects::{Sketch, Solid},
};

Expand Down Expand Up @@ -35,7 +35,7 @@ mod tests {
use fj_math::{Point, Scalar, Vector};

use crate::{
algorithms::Tolerance,
algorithms::approx::Tolerance,
iter::ObjectIters,
objects::{Face, Sketch, Surface},
};
Expand Down
Loading