Merge pull request #1340 from hannobraun/surface

Clean up surface-related code; prepare for deeper integration into partial object API
This commit is contained in:
Hanno Braun 2022-11-12 18:11:19 +01:00 committed by GitHub
commit 35a02544ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 336 additions and 193 deletions

View File

@ -12,8 +12,8 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::{ use crate::{
geometry::path::{GlobalPath, SurfacePath},
objects::{Curve, GlobalCurve}, objects::{Curve, GlobalCurve},
path::{GlobalPath, SurfacePath},
storage::{Handle, ObjectId}, storage::{Handle, ObjectId},
}; };
@ -62,7 +62,7 @@ fn approx_global_curve(
// This will probably all be unified eventually, as `SurfacePath` and // This will probably all be unified eventually, as `SurfacePath` and
// `GlobalPath` grow APIs that are better suited to implementing this code // `GlobalPath` grow APIs that are better suited to implementing this code
// in a more abstract way. // in a more abstract way.
let points = match (curve.path(), curve.surface().u()) { let points = match (curve.path(), curve.surface().geometry().u) {
(SurfacePath::Circle(_), GlobalPath::Circle(_)) => { (SurfacePath::Circle(_), GlobalPath::Circle(_)) => {
todo!( todo!(
"Approximating a circle on a curved surface not supported yet." "Approximating a circle on a curved surface not supported yet."
@ -90,6 +90,7 @@ fn approx_global_curve(
let point_global = curve let point_global = curve
.surface() .surface()
.geometry()
.point_from_surface_coords(point_surface); .point_from_surface_coords(point_surface);
(point_curve, point_global) (point_curve, point_global)
}) })
@ -101,15 +102,17 @@ fn approx_global_curve(
[curve.path().point_from_path_coords(point_curve).u] [curve.path().point_from_path_coords(point_curve).u]
})); }));
let approx_u = (curve.surface().u(), range_u) let approx_u = (curve.surface().geometry().u, range_u)
.approx_with_cache(tolerance, &mut ()); .approx_with_cache(tolerance, &mut ());
let mut points = Vec::new(); let mut points = Vec::new();
for (u, _) in approx_u { for (u, _) in approx_u {
let t = (u.t - line.origin().u) / line.direction().u; let t = (u.t - line.origin().u) / line.direction().u;
let point_surface = curve.path().point_from_path_coords([t]); let point_surface = curve.path().point_from_path_coords([t]);
let point_global = let point_global = curve
curve.surface().point_from_surface_coords(point_surface); .surface()
.geometry()
.point_from_surface_coords(point_surface);
points.push((u, point_global)); points.push((u, point_global));
} }
@ -197,11 +200,11 @@ mod tests {
use crate::{ use crate::{
algorithms::approx::{path::RangeOnPath, Approx, ApproxPoint}, algorithms::approx::{path::RangeOnPath, Approx, ApproxPoint},
builder::CurveBuilder, builder::{CurveBuilder, SurfaceBuilder},
geometry::path::GlobalPath,
insert::Insert, insert::Insert,
objects::{Objects, Surface}, objects::Objects,
partial::PartialCurve, partial::{PartialCurve, PartialSurface},
path::GlobalPath,
}; };
use super::CurveApprox; use super::CurveApprox;
@ -210,9 +213,10 @@ mod tests {
fn approx_line_on_flat_surface() -> anyhow::Result<()> { fn approx_line_on_flat_surface() -> anyhow::Result<()> {
let objects = Objects::new(); let objects = Objects::new();
let surface = objects let surface =
.surfaces PartialSurface::from_axes(GlobalPath::x_axis(), [0., 0., 1.])
.insert(Surface::new(GlobalPath::x_axis(), [0., 0., 1.]))?; .build(&objects)?
.insert(&objects)?;
let mut curve = PartialCurve { let mut curve = PartialCurve {
surface: Some(surface), surface: Some(surface),
..Default::default() ..Default::default()
@ -232,10 +236,12 @@ mod tests {
{ {
let objects = Objects::new(); let objects = Objects::new();
let surface = objects.surfaces.insert(Surface::new( let surface = PartialSurface::from_axes(
GlobalPath::circle_from_radius(1.), GlobalPath::circle_from_radius(1.),
[0., 0., 1.], [0., 0., 1.],
))?; )
.build(&objects)?
.insert(&objects)?;
let mut curve = PartialCurve { let mut curve = PartialCurve {
surface: Some(surface), surface: Some(surface),
..Default::default() ..Default::default()
@ -255,8 +261,9 @@ mod tests {
let objects = Objects::new(); let objects = Objects::new();
let path = GlobalPath::circle_from_radius(1.); let path = GlobalPath::circle_from_radius(1.);
let surface = let surface = PartialSurface::from_axes(path, [0., 0., 1.])
objects.surfaces.insert(Surface::new(path, [0., 0., 1.]))?; .build(&objects)?
.insert(&objects)?;
let mut curve = PartialCurve { let mut curve = PartialCurve {
surface: Some(surface.clone()), surface: Some(surface.clone()),
..Default::default() ..Default::default()
@ -276,7 +283,7 @@ mod tests {
let point_surface = let point_surface =
curve.path().point_from_path_coords(point_local); curve.path().point_from_path_coords(point_local);
let point_global = let point_global =
surface.point_from_surface_coords(point_surface); surface.geometry().point_from_surface_coords(point_surface);
ApproxPoint::new(point_surface, point_global) ApproxPoint::new(point_surface, point_global)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -288,9 +295,10 @@ mod tests {
fn approx_circle_on_flat_surface() -> anyhow::Result<()> { fn approx_circle_on_flat_surface() -> anyhow::Result<()> {
let objects = Objects::new(); let objects = Objects::new();
let surface = objects let surface =
.surfaces PartialSurface::from_axes(GlobalPath::x_axis(), [0., 0., 1.])
.insert(Surface::new(GlobalPath::x_axis(), [0., 0., 1.]))?; .build(&objects)?
.insert(&objects)?;
let mut curve = PartialCurve { let mut curve = PartialCurve {
surface: Some(surface), surface: Some(surface),
..Default::default() ..Default::default()
@ -306,8 +314,10 @@ mod tests {
.approx(tolerance) .approx(tolerance)
.into_iter() .into_iter()
.map(|(_, point_surface)| { .map(|(_, point_surface)| {
let point_global = let point_global = curve
curve.surface().point_from_surface_coords(point_surface); .surface()
.geometry()
.point_from_surface_coords(point_surface);
ApproxPoint::new(point_surface, point_global) ApproxPoint::new(point_surface, point_global)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View File

@ -32,7 +32,7 @@ use std::iter;
use fj_math::{Circle, Point, Scalar, Sign}; use fj_math::{Circle, Point, Scalar, Sign};
use crate::path::{GlobalPath, SurfacePath}; use crate::geometry::path::{GlobalPath, SurfacePath};
use super::{Approx, Tolerance}; use super::{Approx, Tolerance};

View File

@ -1,8 +1,8 @@
use fj_math::{Point, Segment}; use fj_math::{Point, Segment};
use crate::{ use crate::{
geometry::path::SurfacePath,
objects::{Curve, HalfEdge}, objects::{Curve, HalfEdge},
path::SurfacePath,
}; };
use super::LineSegmentIntersection; use super::LineSegmentIntersection;

View File

@ -4,8 +4,8 @@ use fj_math::Segment;
use crate::{ use crate::{
algorithms::intersect::{HorizontalRayToTheRight, Intersect}, algorithms::intersect::{HorizontalRayToTheRight, Intersect},
geometry::path::SurfacePath,
objects::HalfEdge, objects::HalfEdge,
path::SurfacePath,
storage::Handle, storage::Handle,
}; };

View File

@ -4,8 +4,8 @@ use fj_math::{Plane, Point, Scalar};
use crate::{ use crate::{
algorithms::intersect::face_point::FacePointIntersection, algorithms::intersect::face_point::FacePointIntersection,
geometry::path::GlobalPath,
objects::{Face, HalfEdge, Vertex}, objects::{Face, HalfEdge, Vertex},
path::GlobalPath,
storage::Handle, storage::Handle,
}; };
@ -17,14 +17,14 @@ impl Intersect for (&HorizontalRayToTheRight<3>, &Handle<Face>) {
fn intersect(self) -> Option<Self::Intersection> { fn intersect(self) -> Option<Self::Intersection> {
let (ray, face) = self; let (ray, face) = self;
let plane = match face.surface().u() { let plane = match face.surface().geometry().u {
GlobalPath::Circle(_) => todo!( GlobalPath::Circle(_) => todo!(
"Casting a ray against a swept circle is not supported yet" "Casting a ray against a swept circle is not supported yet"
), ),
GlobalPath::Line(line) => Plane::from_parametric( GlobalPath::Line(line) => Plane::from_parametric(
line.origin(), line.origin(),
line.direction(), line.direction(),
face.surface().v(), face.surface().geometry().v,
), ),
}; };

View File

@ -2,8 +2,8 @@ use fj_interop::ext::ArrayExt;
use fj_math::{Line, Plane, Point, Scalar}; use fj_math::{Line, Plane, Point, Scalar};
use crate::{ use crate::{
geometry::path::{GlobalPath, SurfacePath},
objects::{Curve, GlobalCurve, Objects, Surface}, objects::{Curve, GlobalCurve, Objects, Surface},
path::{GlobalPath, SurfacePath},
storage::Handle, storage::Handle,
validate::ValidationError, validate::ValidationError,
}; };
@ -74,12 +74,12 @@ impl SurfaceSurfaceIntersection {
fn plane_from_surface(surface: &Surface) -> Plane { fn plane_from_surface(surface: &Surface) -> Plane {
let (line, path) = { let (line, path) = {
let line = match surface.u() { let line = match surface.geometry().u {
GlobalPath::Line(line) => line, GlobalPath::Line(line) => line,
_ => todo!("Only plane-plane intersection is currently supported."), _ => todo!("Only plane-plane intersection is currently supported."),
}; };
(line, surface.v()) (line, surface.geometry().v)
}; };
Plane::from_parametric(line.origin(), line.direction(), path) Plane::from_parametric(line.origin(), line.direction(), path)

View File

@ -1,8 +1,11 @@
use fj_math::{Circle, Line, Vector}; use fj_math::{Circle, Line, Vector};
use crate::{ use crate::{
builder::SurfaceBuilder,
geometry::path::{GlobalPath, SurfacePath},
insert::Insert,
objects::{Curve, Objects, Surface}, objects::{Curve, Objects, Surface},
path::{GlobalPath, SurfacePath}, partial::PartialSurface,
storage::Handle, storage::Handle,
validate::ValidationError, validate::ValidationError,
}; };
@ -18,7 +21,7 @@ impl Sweep for Handle<Curve> {
_: &mut SweepCache, _: &mut SweepCache,
objects: &Objects, objects: &Objects,
) -> Result<Self::Swept, ValidationError> { ) -> Result<Self::Swept, ValidationError> {
match self.surface().u() { match self.surface().geometry().u {
GlobalPath::Circle(_) => { GlobalPath::Circle(_) => {
// Sweeping a `Curve` creates a `Surface`. The u-axis of that // Sweeping a `Curve` creates a `Surface`. The u-axis of that
// `Surface` is a `GlobalPath`, which we are computing below. // `Surface` is a `GlobalPath`, which we are computing below.
@ -44,20 +47,32 @@ impl Sweep for Handle<Curve> {
let u = match self.path() { let u = match self.path() {
SurfacePath::Circle(circle) => { SurfacePath::Circle(circle) => {
let center = let center = self
self.surface().point_from_surface_coords(circle.center()); .surface()
let a = self.surface().vector_from_surface_coords(circle.a()); .geometry()
let b = self.surface().vector_from_surface_coords(circle.b()); .point_from_surface_coords(circle.center());
let a = self
.surface()
.geometry()
.vector_from_surface_coords(circle.a());
let b = self
.surface()
.geometry()
.vector_from_surface_coords(circle.b());
let circle = Circle::new(center, a, b); let circle = Circle::new(center, a, b);
GlobalPath::Circle(circle) GlobalPath::Circle(circle)
} }
SurfacePath::Line(line) => { SurfacePath::Line(line) => {
let origin = let origin = self
self.surface().point_from_surface_coords(line.origin()); .surface()
let direction = .geometry()
self.surface().vector_from_surface_coords(line.direction()); .point_from_surface_coords(line.origin());
let direction = self
.surface()
.geometry()
.vector_from_surface_coords(line.direction());
let line = Line::from_origin_and_direction(origin, direction); let line = Line::from_origin_and_direction(origin, direction);
@ -65,6 +80,9 @@ impl Sweep for Handle<Curve> {
} }
}; };
Ok(objects.surfaces.insert(Surface::new(u, path))?) let surface = PartialSurface::from_axes(u, path)
.build(objects)?
.insert(objects)?;
Ok(surface)
} }
} }

View File

@ -4,13 +4,13 @@ use iter_fixed::IntoIteratorFixed;
use crate::{ use crate::{
algorithms::{reverse::Reverse, transform::TransformObject}, algorithms::{reverse::Reverse, transform::TransformObject},
geometry::path::SurfacePath,
insert::Insert, insert::Insert,
objects::{ objects::{
Curve, Cycle, Face, GlobalEdge, HalfEdge, Objects, SurfaceVertex, Curve, Cycle, Face, GlobalEdge, HalfEdge, Objects, SurfaceVertex,
Vertex, Vertex,
}, },
partial::HasPartial, partial::HasPartial,
path::SurfacePath,
storage::Handle, storage::Handle,
validate::ValidationError, validate::ValidationError,
}; };

View File

@ -2,8 +2,8 @@ use fj_math::{Scalar, Vector};
use crate::{ use crate::{
algorithms::{reverse::Reverse, transform::TransformObject}, algorithms::{reverse::Reverse, transform::TransformObject},
geometry::path::GlobalPath,
objects::{Face, Objects, Shell}, objects::{Face, Objects, Shell},
path::GlobalPath,
storage::Handle, storage::Handle,
validate::ValidationError, validate::ValidationError,
}; };
@ -24,14 +24,14 @@ impl Sweep for Handle<Face> {
let mut faces = Vec::new(); let mut faces = Vec::new();
let is_negative_sweep = { let is_negative_sweep = {
let u = match self.surface().u() { let u = match self.surface().geometry().u {
GlobalPath::Circle(_) => todo!( GlobalPath::Circle(_) => todo!(
"Sweeping from faces defined in round surfaces is not \ "Sweeping from faces defined in round surfaces is not \
supported" supported"
), ),
GlobalPath::Line(line) => line.direction(), GlobalPath::Line(line) => line.direction(),
}; };
let v = self.surface().v(); let v = self.surface().geometry().v;
let normal = u.cross(&v); let normal = u.cross(&v);

View File

@ -3,11 +3,11 @@ use fj_math::{Line, Point, Scalar, Vector};
use try_insert_ext::EntryInsertExt; use try_insert_ext::EntryInsertExt;
use crate::{ use crate::{
geometry::path::SurfacePath,
objects::{ objects::{
Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects, Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects,
Surface, SurfaceVertex, Vertex, Surface, SurfaceVertex, Vertex,
}, },
path::SurfacePath,
storage::Handle, storage::Handle,
validate::ValidationError, validate::ValidationError,
}; };
@ -56,7 +56,7 @@ impl Sweep for (Handle<Vertex>, Handle<Surface>) {
// not, we have no way of knowing the surface coordinates of the input // not, we have no way of knowing the surface coordinates of the input
// `Vertex` on the `Surface`, and we're going to need to do that further // `Vertex` on the `Surface`, and we're going to need to do that further
// down. There's no way to check for that, unfortunately. // down. There's no way to check for that, unfortunately.
assert_eq!(path, surface.v()); assert_eq!(path, surface.geometry().v);
// With that out of the way, let's start by creating the `GlobalEdge`, // With that out of the way, let's start by creating the `GlobalEdge`,
// as that is the most straight-forward part of this operations, and // as that is the most straight-forward part of this operations, and

View File

@ -1,22 +1,25 @@
use fj_math::Transform; use fj_math::Transform;
use crate::{ use crate::{
objects::{Objects, Surface}, geometry::surface::SurfaceGeometry, objects::Objects,
storage::Handle, partial::PartialSurface, validate::ValidationError,
validate::ValidationError,
}; };
use super::TransformObject; use super::TransformObject;
impl TransformObject for Handle<Surface> { impl TransformObject for PartialSurface {
fn transform( fn transform(
self, self,
transform: &Transform, transform: &Transform,
objects: &Objects, _: &Objects,
) -> Result<Self, ValidationError> { ) -> Result<Self, ValidationError> {
Ok(objects.surfaces.insert(Surface::new( let geometry = self.geometry.map(|geometry| {
self.u().transform(transform), let u = geometry.u.transform(transform);
transform.transform_vector(&self.v()), let v = transform.transform_vector(&geometry.v);
))?)
SurfaceGeometry { u, v }
});
Ok(Self { geometry })
} }
} }

View File

@ -148,12 +148,12 @@ mod tests {
let triangles = triangulate(face)?; let triangles = triangulate(face)?;
let a = surface.point_from_surface_coords(a); let a = surface.geometry().point_from_surface_coords(a);
let b = surface.point_from_surface_coords(b); let b = surface.geometry().point_from_surface_coords(b);
let e = surface.point_from_surface_coords(e); let e = surface.geometry().point_from_surface_coords(e);
let f = surface.point_from_surface_coords(f); let f = surface.geometry().point_from_surface_coords(f);
let g = surface.point_from_surface_coords(g); let g = surface.geometry().point_from_surface_coords(g);
let h = surface.point_from_surface_coords(h); let h = surface.geometry().point_from_surface_coords(h);
// Let's test that some correct triangles are present. We don't need to // Let's test that some correct triangles are present. We don't need to
// test them all. // test them all.
@ -209,11 +209,11 @@ mod tests {
let triangles = triangulate(face)?; let triangles = triangulate(face)?;
let a3 = surface.point_from_surface_coords(a); let a3 = surface.geometry().point_from_surface_coords(a);
let b3 = surface.point_from_surface_coords(b); let b3 = surface.geometry().point_from_surface_coords(b);
let c3 = surface.point_from_surface_coords(c); let c3 = surface.geometry().point_from_surface_coords(c);
let d3 = surface.point_from_surface_coords(d); let d3 = surface.geometry().point_from_surface_coords(d);
let e3 = surface.point_from_surface_coords(e); let e3 = surface.geometry().point_from_surface_coords(e);
assert!(triangles.contains_triangle([a3, b3, d3])); assert!(triangles.contains_triangle([a3, b3, d3]));
assert!(triangles.contains_triangle([b3, c3, d3])); assert!(triangles.contains_triangle([b3, c3, d3]));

View File

@ -1,6 +1,6 @@
use fj_math::{Point, Scalar, Vector}; use fj_math::{Point, Scalar, Vector};
use crate::{partial::PartialCurve, path::SurfacePath}; use crate::{geometry::path::SurfacePath, partial::PartialCurve};
/// Builder API for [`PartialCurve`] /// Builder API for [`PartialCurve`]
pub trait CurveBuilder { pub trait CurveBuilder {

View File

@ -5,8 +5,9 @@ use crate::{
insert::Insert, insert::Insert,
objects::{Curve, Objects, Surface, Vertex, VerticesInNormalizedOrder}, objects::{Curve, Objects, Surface, Vertex, VerticesInNormalizedOrder},
partial::{ partial::{
MaybePartial, PartialCurve, PartialGlobalEdge, PartialGlobalVertex, MaybePartial, MergeWith, PartialCurve, PartialGlobalEdge,
PartialHalfEdge, PartialSurfaceVertex, PartialVertex, PartialGlobalVertex, PartialHalfEdge, PartialSurfaceVertex,
PartialVertex,
}, },
storage::Handle, storage::Handle,
validate::ValidationError, validate::ValidationError,
@ -135,8 +136,8 @@ impl HalfEdgeBuilder for PartialHalfEdge {
let surface = self let surface = self
.curve() .curve()
.surface() .surface()
.or_else(|| from_surface.surface()) .merge_with(from_surface.surface())
.or_else(|| to_surface.surface()) .merge_with(to_surface.surface())
.expect("Can't infer line segment without a surface"); .expect("Can't infer line segment without a surface");
let points = [&from_surface, &to_surface].map(|vertex| { let points = [&from_surface, &to_surface].map(|vertex| {
vertex vertex

View File

@ -12,6 +12,7 @@ mod curve;
mod cycle; mod cycle;
mod edge; mod edge;
mod face; mod face;
mod surface;
mod vertex; mod vertex;
pub use self::{ pub use self::{
@ -22,5 +23,6 @@ pub use self::{
shell::ShellBuilder, shell::ShellBuilder,
sketch::SketchBuilder, sketch::SketchBuilder,
solid::SolidBuilder, solid::SolidBuilder,
surface::SurfaceBuilder,
vertex::{GlobalVertexBuilder, SurfaceVertexBuilder, VertexBuilder}, vertex::{GlobalVertexBuilder, SurfaceVertexBuilder, VertexBuilder},
}; };

View File

@ -6,10 +6,13 @@ use iter_fixed::IntoIteratorFixed;
use crate::{ use crate::{
algorithms::transform::TransformObject, algorithms::transform::TransformObject,
builder::{FaceBuilder, HalfEdgeBuilder}, builder::{FaceBuilder, HalfEdgeBuilder, SurfaceBuilder},
insert::Insert, insert::Insert,
objects::{Cycle, Face, FaceSet, HalfEdge, Objects, Shell, Surface}, objects::{Cycle, Face, FaceSet, HalfEdge, Objects, Shell},
partial::{HasPartial, PartialCurve, PartialSurfaceVertex, PartialVertex}, partial::{
HasPartial, PartialCurve, PartialSurface, PartialSurfaceVertex,
PartialVertex,
},
storage::Handle, storage::Handle,
}; };
@ -78,9 +81,10 @@ impl<'a> ShellBuilder<'a> {
.map(|vertex| vertex.global_form().position()); .map(|vertex| vertex.global_form().position());
let c = a + [Z, Z, edge_length]; let c = a + [Z, Z, edge_length];
self.objects PartialSurface::plane_from_points([a, b, c])
.surfaces .build(self.objects)
.insert(Surface::plane_from_points([a, b, c])) .unwrap()
.insert(self.objects)
.unwrap() .unwrap()
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View File

@ -0,0 +1,36 @@
use fj_math::{Line, Point, Vector};
use crate::{
geometry::{path::GlobalPath, surface::SurfaceGeometry},
partial::PartialSurface,
};
/// Builder API for [`PartialSurface`]
pub trait SurfaceBuilder {
/// Build a surface from its two axes
fn from_axes(u: GlobalPath, v: impl Into<Vector<3>>) -> Self;
/// Construct a plane from 3 points
fn plane_from_points(points: [impl Into<Point<3>>; 3]) -> Self;
}
impl SurfaceBuilder for PartialSurface {
fn from_axes(u: GlobalPath, v: impl Into<Vector<3>>) -> Self {
let v = v.into();
Self {
geometry: Some(SurfaceGeometry { u, v }),
}
}
fn plane_from_points(points: [impl Into<Point<3>>; 3]) -> Self {
let [a, b, c] = points.map(Into::into);
let u = GlobalPath::Line(Line::from_points([a, b]));
let v = c - a;
Self {
geometry: Some(SurfaceGeometry { u, v }),
}
}
}

View File

@ -1,7 +1,8 @@
use fj_math::Point; use fj_math::Point;
use crate::{ use crate::{
objects::{Curve, GlobalVertex, Surface}, geometry::surface::SurfaceGeometry,
objects::{Curve, GlobalVertex},
partial::{ partial::{
HasPartial, MaybePartial, PartialGlobalVertex, PartialSurfaceVertex, HasPartial, MaybePartial, PartialGlobalVertex, PartialSurfaceVertex,
PartialVertex, PartialVertex,
@ -44,7 +45,7 @@ pub trait GlobalVertexBuilder {
/// Update partial global vertex from the given surface and position on it /// Update partial global vertex from the given surface and position on it
fn from_surface_and_position( fn from_surface_and_position(
surface: &Surface, surface: &SurfaceGeometry,
position: impl Into<Point<2>>, position: impl Into<Point<2>>,
) -> Self; ) -> Self;
} }
@ -59,9 +60,12 @@ impl GlobalVertexBuilder for PartialGlobalVertex {
let path = curve.path.expect( let path = curve.path.expect(
"Need path to create `GlobalVertex` from curve and position", "Need path to create `GlobalVertex` from curve and position",
); );
let surface = curve.surface.expect( let surface = curve
"Need surface to create `GlobalVertex` from curve and position", .surface
); .expect(
"Need surface to create `GlobalVertex` from curve and position",
)
.geometry();
let position_surface = path.point_from_path_coords(position); let position_surface = path.point_from_path_coords(position);
@ -69,7 +73,7 @@ impl GlobalVertexBuilder for PartialGlobalVertex {
} }
fn from_surface_and_position( fn from_surface_and_position(
surface: &Surface, surface: &SurfaceGeometry,
position: impl Into<Point<2>>, position: impl Into<Point<2>>,
) -> Self { ) -> Self {
PartialGlobalVertex { PartialGlobalVertex {

View File

@ -0,0 +1,4 @@
//! Types that are tied to objects, but aren't objects themselves
pub mod path;
pub mod surface;

View File

@ -0,0 +1,81 @@
//! The geometry that defines a surface
use fj_math::{Line, Point, Vector};
use super::path::GlobalPath;
/// The geometry that defines a surface
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct SurfaceGeometry {
/// The u-axis of the surface
pub u: GlobalPath,
/// The v-axis of the surface
pub v: Vector<3>,
}
impl SurfaceGeometry {
/// Convert a point in surface coordinates to model coordinates
pub fn point_from_surface_coords(
&self,
point: impl Into<Point<2>>,
) -> Point<3> {
let point = point.into();
self.u.point_from_path_coords([point.u])
+ self.path_to_line().vector_from_line_coords([point.v])
}
/// Convert a vector in surface coordinates to model coordinates
pub fn vector_from_surface_coords(
&self,
vector: impl Into<Vector<2>>,
) -> Vector<3> {
let vector = vector.into();
self.u.vector_from_path_coords([vector.u])
+ self.path_to_line().vector_from_line_coords([vector.v])
}
fn path_to_line(&self) -> Line<3> {
Line::from_origin_and_direction(self.u.origin(), self.v)
}
}
#[cfg(test)]
mod tests {
use fj_math::{Line, Point, Vector};
use pretty_assertions::assert_eq;
use crate::geometry::{path::GlobalPath, surface::SurfaceGeometry};
#[test]
fn point_from_surface_coords() {
let surface = SurfaceGeometry {
u: GlobalPath::Line(Line::from_origin_and_direction(
Point::from([1., 1., 1.]),
Vector::from([0., 2., 0.]),
)),
v: Vector::from([0., 0., 2.]),
};
assert_eq!(
surface.point_from_surface_coords([2., 4.]),
Point::from([1., 5., 9.]),
);
}
#[test]
fn vector_from_surface_coords() {
let surface = SurfaceGeometry {
u: GlobalPath::Line(Line::from_origin_and_direction(
Point::from([1., 0., 0.]),
Vector::from([0., 2., 0.]),
)),
v: Vector::from([0., 0., 2.]),
};
assert_eq!(
surface.vector_from_surface_coords([2., 4.]),
Vector::from([0., 4., 8.]),
);
}
}

View File

@ -89,10 +89,10 @@
pub mod algorithms; pub mod algorithms;
pub mod builder; pub mod builder;
pub mod geometry;
pub mod insert; pub mod insert;
pub mod iter; pub mod iter;
pub mod objects; pub mod objects;
pub mod partial; pub mod partial;
pub mod path;
pub mod storage; pub mod storage;
pub mod validate; pub mod validate;

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
path::SurfacePath, geometry::path::SurfacePath,
storage::{Handle, HandleWrapper}, storage::{Handle, HandleWrapper},
}; };

View File

@ -3,7 +3,7 @@ use std::slice;
use fj_interop::ext::SliceExt; use fj_interop::ext::SliceExt;
use fj_math::{Scalar, Winding}; use fj_math::{Scalar, Winding};
use crate::{path::SurfacePath, storage::Handle}; use crate::{geometry::path::SurfacePath, storage::Handle};
use super::{HalfEdge, Surface}; use super::{HalfEdge, Surface};

View File

@ -100,7 +100,7 @@ use std::convert::Infallible;
use fj_math::Vector; use fj_math::Vector;
use crate::{ use crate::{
path::GlobalPath, geometry::{path::GlobalPath, surface::SurfaceGeometry},
storage::{Handle, Store}, storage::{Handle, Store},
validate::{ validate::{
CycleValidationError, FaceValidationError, HalfEdgeValidationError, CycleValidationError, FaceValidationError, HalfEdgeValidationError,
@ -380,12 +380,18 @@ impl Default for Surfaces {
fn default() -> Self { fn default() -> Self {
let store = Store::new(); let store = Store::new();
let xy_plane = let xy_plane = store.insert(Surface::new(SurfaceGeometry {
store.insert(Surface::new(GlobalPath::x_axis(), Vector::unit_y())); u: GlobalPath::x_axis(),
let xz_plane = v: Vector::unit_y(),
store.insert(Surface::new(GlobalPath::x_axis(), Vector::unit_z())); }));
let yz_plane = let xz_plane = store.insert(Surface::new(SurfaceGeometry {
store.insert(Surface::new(GlobalPath::y_axis(), Vector::unit_z())); u: GlobalPath::x_axis(),
v: Vector::unit_z(),
}));
let yz_plane = store.insert(Surface::new(SurfaceGeometry {
u: GlobalPath::y_axis(),
v: Vector::unit_z(),
}));
Self { Self {
store, store,

View File

@ -1,104 +1,19 @@
use fj_math::{Line, Point, Vector}; use crate::geometry::surface::SurfaceGeometry;
use crate::path::GlobalPath;
/// A two-dimensional shape /// A two-dimensional shape
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Surface { pub struct Surface {
u: GlobalPath, geometry: SurfaceGeometry,
v: Vector<3>,
} }
impl Surface { impl Surface {
/// Construct a `Surface` from two paths that define its coordinate system /// Construct a `Surface` from two paths that define its coordinate system
pub fn new(u: GlobalPath, v: impl Into<Vector<3>>) -> Self { pub fn new(geometry: SurfaceGeometry) -> Self {
let v = v.into(); Self { geometry }
Self { u, v }
} }
/// Construct a plane from 3 points /// Access the surface's geometry
pub fn plane_from_points(points: [impl Into<Point<3>>; 3]) -> Self { pub fn geometry(&self) -> SurfaceGeometry {
let [a, b, c] = points.map(Into::into); self.geometry
let u = GlobalPath::Line(Line::from_points([a, b]));
let v = c - a;
Self { u, v }
}
/// Access the path that defines the u-coordinate of this surface
pub fn u(&self) -> GlobalPath {
self.u
}
/// Access the path that defines the v-coordinate of this surface
pub fn v(&self) -> Vector<3> {
self.v
}
/// Convert a point in surface coordinates to model coordinates
pub fn point_from_surface_coords(
&self,
point: impl Into<Point<2>>,
) -> Point<3> {
let point = point.into();
self.u.point_from_path_coords([point.u])
+ self.path_to_line().vector_from_line_coords([point.v])
}
/// Convert a vector in surface coordinates to model coordinates
pub fn vector_from_surface_coords(
&self,
vector: impl Into<Vector<2>>,
) -> Vector<3> {
let vector = vector.into();
self.u.vector_from_path_coords([vector.u])
+ self.path_to_line().vector_from_line_coords([vector.v])
}
fn path_to_line(&self) -> Line<3> {
Line::from_origin_and_direction(self.u.origin(), self.v)
}
}
#[cfg(test)]
mod tests {
use fj_math::{Line, Point, Vector};
use pretty_assertions::assert_eq;
use crate::path::GlobalPath;
use super::Surface;
#[test]
fn point_from_surface_coords() {
let swept = Surface {
u: GlobalPath::Line(Line::from_origin_and_direction(
Point::from([1., 1., 1.]),
Vector::from([0., 2., 0.]),
)),
v: Vector::from([0., 0., 2.]),
};
assert_eq!(
swept.point_from_surface_coords([2., 4.]),
Point::from([1., 5., 9.]),
);
}
#[test]
fn vector_from_surface_coords() {
let swept = Surface {
u: GlobalPath::Line(Line::from_origin_and_direction(
Point::from([1., 0., 0.]),
Vector::from([0., 2., 0.]),
)),
v: Vector::from([0., 0., 2.]),
};
assert_eq!(
swept.vector_from_surface_coords([2., 4.]),
Vector::from([0., 4., 8.]),
);
} }
} }

View File

@ -1,12 +1,12 @@
use fj_math::Point; use fj_math::Point;
use crate::{ use crate::{
geometry::{path::SurfacePath, surface::SurfaceGeometry},
insert::Insert, insert::Insert,
objects::{ objects::{
Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects, Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects,
Surface, SurfaceVertex, Vertex, Surface, SurfaceVertex, Vertex,
}, },
path::SurfacePath,
storage::Handle, storage::Handle,
validate::{Validate, ValidationError}, validate::{Validate, ValidationError},
}; };
@ -212,6 +212,16 @@ impl MaybePartial<HalfEdge> {
} }
} }
impl MaybePartial<Surface> {
/// Access the geometry
pub fn geometry(&self) -> Option<SurfaceGeometry> {
match self {
Self::Full(full) => Some(full.geometry()),
Self::Partial(partial) => partial.geometry,
}
}
}
impl MaybePartial<SurfaceVertex> { impl MaybePartial<SurfaceVertex> {
/// Access the position /// Access the position
pub fn position(&self) -> Option<Point<2>> { pub fn position(&self) -> Option<Point<2>> {

View File

@ -47,6 +47,7 @@ pub use self::{
cycle::PartialCycle, cycle::PartialCycle,
edge::{PartialGlobalEdge, PartialHalfEdge}, edge::{PartialGlobalEdge, PartialHalfEdge},
face::PartialFace, face::PartialFace,
surface::PartialSurface,
vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex}, vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex},
}, },
traits::{HasPartial, Partial}, traits::{HasPartial, Partial},

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
geometry::path::SurfacePath,
objects::{Curve, GlobalCurve, Objects, Surface}, objects::{Curve, GlobalCurve, Objects, Surface},
partial::{MaybePartial, MergeWith, Mergeable}, partial::{MaybePartial, MergeWith, Mergeable},
path::SurfacePath,
storage::Handle, storage::Handle,
validate::ValidationError, validate::ValidationError,
}; };

View File

@ -2,17 +2,18 @@ pub mod curve;
pub mod cycle; pub mod cycle;
pub mod edge; pub mod edge;
pub mod face; pub mod face;
pub mod surface;
pub mod vertex; pub mod vertex;
use crate::objects::{ use crate::objects::{
Curve, Cycle, Face, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Curve, Cycle, Face, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge,
Objects, SurfaceVertex, Vertex, Objects, Surface, SurfaceVertex, Vertex,
}; };
use super::{ use super::{
HasPartial, MaybePartial, Partial, PartialCurve, PartialCycle, PartialFace, HasPartial, MaybePartial, Partial, PartialCurve, PartialCycle, PartialFace,
PartialGlobalCurve, PartialGlobalEdge, PartialGlobalVertex, PartialGlobalCurve, PartialGlobalEdge, PartialGlobalVertex,
PartialHalfEdge, PartialSurfaceVertex, PartialVertex, PartialHalfEdge, PartialSurface, PartialSurfaceVertex, PartialVertex,
}; };
macro_rules! impl_traits { macro_rules! impl_traits {
@ -52,6 +53,7 @@ impl_traits!(
GlobalEdge, PartialGlobalEdge; GlobalEdge, PartialGlobalEdge;
GlobalVertex, PartialGlobalVertex; GlobalVertex, PartialGlobalVertex;
HalfEdge, PartialHalfEdge; HalfEdge, PartialHalfEdge;
Surface, PartialSurface;
SurfaceVertex, PartialSurfaceVertex; SurfaceVertex, PartialSurfaceVertex;
Vertex, PartialVertex; Vertex, PartialVertex;
); );

View File

@ -0,0 +1,44 @@
use crate::{
geometry::surface::SurfaceGeometry,
objects::{Objects, Surface},
partial::MergeWith,
validate::ValidationError,
};
/// A partial [`Surface`]
///
/// See [`crate::partial`] for more information
#[derive(Clone, Debug, Default)]
pub struct PartialSurface {
/// The geometry that defines the [`Surface`]
pub geometry: Option<SurfaceGeometry>,
}
impl PartialSurface {
/// Build a full [`Surface`] from the partial surface
pub fn build(self, _: &Objects) -> Result<Surface, ValidationError> {
let geometry = self
.geometry
.expect("Can't build `Surface` without geometry");
Ok(Surface::new(geometry))
}
}
impl MergeWith for PartialSurface {
fn merge_with(self, other: impl Into<Self>) -> Self {
let other = other.into();
Self {
geometry: self.geometry.merge_with(other.geometry),
}
}
}
impl From<&Surface> for PartialSurface {
fn from(surface: &Surface) -> Self {
Self {
geometry: Some(surface.geometry()),
}
}
}

View File

@ -80,7 +80,7 @@ impl From<&Vertex> for PartialVertex {
/// A partial [`SurfaceVertex`] /// A partial [`SurfaceVertex`]
/// ///
/// See [`crate::partial`] for more information. /// See [`crate::partial`] for more information.
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] #[derive(Clone, Debug, Default)]
pub struct PartialSurfaceVertex { pub struct PartialSurfaceVertex {
/// The position of the [`SurfaceVertex`] /// The position of the [`SurfaceVertex`]
pub position: Option<Point<2>>, pub position: Option<Point<2>>,
@ -108,7 +108,8 @@ impl PartialSurfaceVertex {
let global_form = self let global_form = self
.global_form .global_form
.merge_with(PartialGlobalVertex::from_surface_and_position( .merge_with(PartialGlobalVertex::from_surface_and_position(
&surface, position, &surface.geometry(),
position,
)) ))
.into_full(objects)?; .into_full(objects)?;

View File

@ -158,6 +158,7 @@ impl SurfaceVertexValidationError {
) -> Result<(), Self> { ) -> Result<(), Self> {
let surface_position_as_global = surface_vertex let surface_position_as_global = surface_vertex
.surface() .surface()
.geometry()
.point_from_surface_coords(surface_vertex.position()); .point_from_surface_coords(surface_vertex.position());
let global_position = surface_vertex.global_form().position(); let global_position = surface_vertex.global_form().position();