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

Add convenient APIs to build Vertex and Edge #403

Merged
merged 10 commits into from
Mar 24, 2022
Merged
64 changes: 28 additions & 36 deletions fj-kernel/src/algorithms/approximation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,43 +120,40 @@ mod tests {
use crate::{
geometry::Surface,
shape::Shape,
topology::{Cycle, Face, Vertex},
topology::{Cycle, Edge, Face, Vertex},
};

use super::Approximation;

#[test]
fn approximate_edge() {
fn approximate_edge() -> anyhow::Result<()> {
let mut shape = Shape::new();

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 = shape.geometry().add_point(a);
let v2 = shape.geometry().add_point(d);

let v1 = shape.topology().add_vertex(Vertex { point: v1 }).unwrap();
let v2 = shape.topology().add_vertex(Vertex { point: v2 }).unwrap();

let points = vec![b, c];
let v1 = Vertex::build(&mut shape).from_point(a)?;
let v2 = Vertex::build(&mut shape).from_point(d)?;

// Regular edge
assert_eq!(
super::approximate_edge(
points.clone(),
vec![b, c],
Some([v1.get().clone(), v2.get().clone()])
),
vec![a, b, c, d],
);

// Continuous edge
assert_eq!(super::approximate_edge(points, None), vec![b, c, b],);
assert_eq!(super::approximate_edge(vec![b, c], None), vec![b, c, b],);

Ok(())
}

#[test]
fn for_face_closed() {
fn for_face_closed() -> anyhow::Result<()> {
// Test a closed face, i.e. one that is completely encircled by edges.

let tolerance = Scalar::ONE;
Expand All @@ -168,30 +165,23 @@ mod tests {
let c = Point::from([3., 5., 8.]);
let d = Point::from([5., 8., 13.]);

let v1 = shape.geometry().add_point(a);
let v2 = shape.geometry().add_point(b);
let v3 = shape.geometry().add_point(c);
let v4 = shape.geometry().add_point(d);

let v1 = shape.topology().add_vertex(Vertex { point: v1 }).unwrap();
let v2 = shape.topology().add_vertex(Vertex { point: v2 }).unwrap();
let v3 = shape.topology().add_vertex(Vertex { point: v3 }).unwrap();
let v4 = shape.topology().add_vertex(Vertex { point: v4 }).unwrap();

let ab = shape
.topology()
.add_line_segment([v1.clone(), v2.clone()])
.unwrap();
let bc = shape.topology().add_line_segment([v2, v3.clone()]).unwrap();
let cd = shape.topology().add_line_segment([v3, v4.clone()]).unwrap();
let da = shape.topology().add_line_segment([v4, v1]).unwrap();

let abcd = shape
.topology()
.add_cycle(Cycle {
edges: vec![ab, bc, cd, da],
})
.unwrap();
let v1 = Vertex::build(&mut shape).from_point(a)?;
let v2 = Vertex::build(&mut shape).from_point(b)?;
let v3 = Vertex::build(&mut shape).from_point(c)?;
let v4 = Vertex::build(&mut shape).from_point(d)?;

let ab = Edge::build(&mut shape)
.line_segment_from_vertices([v1.clone(), v2.clone()])?;
let bc = Edge::build(&mut shape)
.line_segment_from_vertices([v2, v3.clone()])?;
let cd = Edge::build(&mut shape)
.line_segment_from_vertices([v3, v4.clone()])?;
let da =
Edge::build(&mut shape).line_segment_from_vertices([v4, v1])?;

let abcd = shape.topology().add_cycle(Cycle {
edges: vec![ab, bc, cd, da],
})?;

let surface = shape.geometry().add_surface(Surface::x_y_plane());
let face = Face::Face {
Expand All @@ -213,5 +203,7 @@ mod tests {
],
}
);

Ok(())
}
}
49 changes: 21 additions & 28 deletions fj-kernel/src/algorithms/sweep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,14 +347,14 @@ mod tests {
use crate::{
geometry::{Surface, SweptCurve},
shape::{Handle, Shape},
topology::{Cycle, Face, Vertex},
topology::{Cycle, Edge, Face, Vertex},
};

use super::sweep_shape;

#[test]
fn sweep() {
let sketch = Triangle::new([[0., 0., 0.], [1., 0., 0.], [0., 1., 0.]]);
fn sweep() -> anyhow::Result<()> {
let sketch = Triangle::new([[0., 0., 0.], [1., 0., 0.], [0., 1., 0.]])?;

let mut swept = sweep_shape(
sketch.shape,
Expand All @@ -365,7 +365,7 @@ mod tests {

let bottom_face = sketch.face.get().clone();
let top_face =
Triangle::new([[0., 0., 1.], [1., 0., 1.], [0., 1., 1.]])
Triangle::new([[0., 0., 1.], [1., 0., 1.], [0., 1., 1.]])?
.face
.get()
.clone();
Expand All @@ -389,6 +389,8 @@ mod tests {

// Side faces are not tested, as those use triangle representation. The
// plan is to start testing them, as they are transitioned to b-rep.

Ok(())
}

pub struct Triangle {
Expand All @@ -397,36 +399,27 @@ mod tests {
}

impl Triangle {
fn new([a, b, c]: [impl Into<Point<3>>; 3]) -> Self {
fn new([a, b, c]: [impl Into<Point<3>>; 3]) -> anyhow::Result<Self> {
let mut shape = Shape::new();

let a = shape.geometry().add_point(a.into());
let b = shape.geometry().add_point(b.into());
let c = shape.geometry().add_point(c.into());

let a = shape.topology().add_vertex(Vertex { point: a }).unwrap();
let b = shape.topology().add_vertex(Vertex { point: b }).unwrap();
let c = shape.topology().add_vertex(Vertex { point: c }).unwrap();
let a = shape.topology().add_vertex(Vertex { point: a })?;
let b = shape.topology().add_vertex(Vertex { point: b })?;
let c = shape.topology().add_vertex(Vertex { point: c })?;

let ab = shape
.topology()
.add_line_segment([a.clone(), b.clone()])
.unwrap();
let bc = shape
.topology()
.add_line_segment([b.clone(), c.clone()])
.unwrap();
let ca = shape
.topology()
.add_line_segment([c.clone(), a.clone()])
.unwrap();
let ab = Edge::build(&mut shape)
.line_segment_from_vertices([a.clone(), b.clone()])?;
let bc = Edge::build(&mut shape)
.line_segment_from_vertices([b.clone(), c.clone()])?;
let ca = Edge::build(&mut shape)
.line_segment_from_vertices([c.clone(), a.clone()])?;

let cycles = shape
.topology()
.add_cycle(Cycle {
edges: vec![ab, bc, ca],
})
.unwrap();
let cycles = shape.topology().add_cycle(Cycle {
edges: vec![ab, bc, ca],
})?;

let surface = shape.geometry().add_surface(Surface::SweptCurve(
SweptCurve::plane_from_points(
Expand All @@ -440,9 +433,9 @@ mod tests {
color: [255, 0, 0, 255],
};

let face = shape.topology().add_face(abc).unwrap();
let face = shape.topology().add_face(abc)?;

Self { shape, face }
Ok(Self { shape, face })
}
}
}
72 changes: 17 additions & 55 deletions fj-kernel/src/shape/topology.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
use std::collections::HashSet;

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

use crate::{
geometry::{Circle, Curve, Line},
topology::{Cycle, Edge, Face, Vertex},
};
use crate::topology::{Cycle, Edge, Face, Vertex};

use super::{
handle::{Handle, Storage},
Cycles, Edges, Geometry, Iter, StructuralIssues, ValidationError,
ValidationResult, Vertices,
handle::Storage, Cycles, Edges, Geometry, Iter, StructuralIssues,
ValidationError, ValidationResult, Vertices,
};

/// The vertices of a shape
Expand Down Expand Up @@ -113,38 +109,6 @@ impl Topology<'_> {
Ok(handle)
}

/// Add a circle to the shape
///
/// Calls [`Edges::add`] internally, and is subject to the same
/// restrictions.
pub fn add_circle(&mut self, radius: Scalar) -> ValidationResult<Edge> {
let curve = self.geometry.add_curve(Curve::Circle(Circle {
center: Point::origin(),
radius: Vector::from([radius, Scalar::ZERO]),
}));
self.add_edge(Edge {
curve,
vertices: None,
})
}

/// Add a line segment to the shape
///
/// Calls [`Edges::add`] internally, and is subject to the same
/// restrictions.
pub fn add_line_segment(
&mut self,
vertices: [Handle<Vertex>; 2],
) -> ValidationResult<Edge> {
let curve = self.geometry.add_curve(Curve::Line(Line::from_points(
vertices.clone().map(|vertex| vertex.get().point()),
)));
self.add_edge(Edge {
curve,
vertices: Some(vertices),
})
}

/// Add a cycle to the shape
///
/// Validates that the cycle is structurally sound (i.e. the edges it refers
Expand Down Expand Up @@ -298,8 +262,8 @@ mod tests {
let mut other = TestShape::new();

let curve = other.add_curve();
let a = other.add_vertex()?;
let b = other.add_vertex()?;
let a = Vertex::build(&mut other).from_point([1., 0., 0.])?;
let b = Vertex::build(&mut other).from_point([2., 0., 0.])?;

// Shouldn't work. Nothing has been added to `shape`.
let err = shape
Expand All @@ -314,8 +278,8 @@ mod tests {
assert!(err.missing_vertex(&b));

let curve = shape.add_curve();
let a = shape.add_vertex()?;
let b = shape.add_vertex()?;
let a = Vertex::build(&mut shape).from_point([1., 0., 0.])?;
let b = Vertex::build(&mut shape).from_point([2., 0., 0.])?;

// Everything has been added to `shape` now. Should work!
shape.topology().add_edge(Edge {
Expand Down Expand Up @@ -404,19 +368,17 @@ mod tests {
self.geometry().add_surface(Surface::x_y_plane())
}

fn add_vertex(&mut self) -> anyhow::Result<Handle<Vertex>> {
let point = self.next_point;
self.next_point.x += Scalar::ONE;

let point = self.geometry().add_point(point);
let vertex = self.topology().add_vertex(Vertex { point })?;
fn add_edge(&mut self) -> anyhow::Result<Handle<Edge>> {
let vertices = [(); 2].map(|()| {
let point = self.next_point;
self.next_point.x += Scalar::ONE;

Ok(vertex)
}
let point = self.geometry().add_point(point);
self.topology().add_vertex(Vertex { point }).unwrap()
});
let edge = Edge::build(&mut self.inner)
.line_segment_from_vertices(vertices)?;

fn add_edge(&mut self) -> anyhow::Result<Handle<Edge>> {
let vertices = [(); 2].map(|()| self.add_vertex().unwrap());
let edge = self.topology().add_line_segment(vertices)?;
Ok(edge)
}

Expand Down
76 changes: 76 additions & 0 deletions fj-kernel/src/topology/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use fj_math::{Point, Scalar, Vector};

use crate::{
geometry::{Circle, Curve, Line},
shape::{Handle, Shape, ValidationResult},
};

use super::{Edge, Vertex};

/// API for building a [`Vertex`]
pub struct VertexBuilder<'r> {
shape: &'r mut Shape,
}

impl<'r> VertexBuilder<'r> {
/// Construct a new instance of `VertexBuilder`
pub fn new(shape: &'r mut Shape) -> Self {
Self { shape }
}

/// Build a [`Vertex`] from a point
pub fn from_point(
self,
point: impl Into<Point<3>>,
) -> ValidationResult<Vertex> {
let point = self.shape.geometry().add_point(point.into());
let vertex = self.shape.topology().add_vertex(Vertex { point })?;

Ok(vertex)
}
}

/// API for building an [`Edge`]
pub struct EdgeBuilder<'r> {
shape: &'r mut Shape,
}

impl<'r> EdgeBuilder<'r> {
/// Construct a new instance of `EdgeBuilder`
pub fn new(shape: &'r mut Shape) -> Self {
Self { shape }
}

/// Build a circle from a radius
pub fn circle(self, radius: Scalar) -> ValidationResult<Edge> {
let curve = self.shape.geometry().add_curve(Curve::Circle(Circle {
center: Point::origin(),
radius: Vector::from([radius, Scalar::ZERO]),
}));
let edge = self.shape.topology().add_edge(Edge {
curve,
vertices: None,
})?;

Ok(edge)
}

/// Build a line segment from two vertices
pub fn line_segment_from_vertices(
self,
vertices: [Handle<Vertex>; 2],
) -> ValidationResult<Edge> {
let curve =
self.shape
.geometry()
.add_curve(Curve::Line(Line::from_points(
vertices.clone().map(|vertex| vertex.get().point()),
)));
let edge = self.shape.topology().add_edge(Edge {
curve,
vertices: Some(vertices),
})?;

Ok(edge)
}
}
Loading