Skip to content

Commit

Permalink
Merge pull request #1003 from hannobraun/approx
Browse files Browse the repository at this point in the history
Clean up `approx` module
  • Loading branch information
hannobraun authored Aug 26, 2022
2 parents 8fe463a + a29e849 commit 2c9a5a9
Show file tree
Hide file tree
Showing 19 changed files with 133 additions and 100 deletions.
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

0 comments on commit 2c9a5a9

Please sign in to comment.