Merge pull request #1610 from hannobraun/curve

Merge `GlobalCurve` into `GlobalEdge`
This commit is contained in:
Hanno Braun 2023-02-22 14:05:53 +01:00 committed by GitHub
commit c757811554
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 356 additions and 505 deletions

View File

@ -1,332 +0,0 @@
//! Curve approximation
//!
//! Since curves are infinite (even circles have an infinite coordinate space,
//! even though they connect to themselves in global coordinates), a range must
//! be provided to approximate them. The approximation then returns points
//! within that range.
//!
//! The boundaries of the range are not included in the approximation. This is
//! done, to give the caller (who knows the boundary anyway) more options on how
//! to further process the approximation.
use std::collections::BTreeMap;
use crate::{
geometry::path::{GlobalPath, SurfacePath},
objects::{Curve, GlobalCurve, Surface},
storage::{Handle, ObjectId},
};
use super::{path::RangeOnPath, Approx, ApproxPoint, Tolerance};
impl Approx for (&Handle<Curve>, &Surface, Handle<GlobalCurve>, RangeOnPath) {
type Approximation = CurveApprox;
type Cache = CurveCache;
fn approx_with_cache(
self,
tolerance: impl Into<Tolerance>,
cache: &mut Self::Cache,
) -> Self::Approximation {
let (curve, surface, global_curve, range) = self;
let global_curve_approx = match cache.get(global_curve.clone(), range) {
Some(approx) => approx,
None => {
let approx =
approx_global_curve(curve, surface, range, tolerance);
cache.insert(global_curve, range, approx)
}
};
CurveApprox::empty().with_points(
global_curve_approx.points.into_iter().map(|point| {
let point_surface =
curve.path().point_from_path_coords(point.local_form);
ApproxPoint::new(point_surface, point.global_form)
.with_source((curve.clone(), point.local_form))
}),
)
}
}
fn approx_global_curve(
curve: &Curve,
surface: &Surface,
range: RangeOnPath,
tolerance: impl Into<Tolerance>,
) -> GlobalCurveApprox {
// There are different cases of varying complexity. Circles are the hard
// part here, as they need to be approximated, while lines don't need to be.
//
// This will probably all be unified eventually, as `SurfacePath` and
// `GlobalPath` grow APIs that are better suited to implementing this code
// in a more abstract way.
let points = match (curve.path(), surface.geometry().u) {
(SurfacePath::Circle(_), GlobalPath::Circle(_)) => {
todo!(
"Approximating a circle on a curved surface not supported yet."
)
}
(SurfacePath::Circle(_), GlobalPath::Line(_)) => {
(curve.path(), range)
.approx_with_cache(tolerance, &mut ())
.into_iter()
.map(|(point_curve, point_surface)| {
// We're throwing away `point_surface` here, which is a bit
// weird, as we're recomputing it later (outside of this
// function).
//
// It should be fine though:
//
// 1. We're throwing this version away, so there's no danger
// of inconsistency between this and the later version.
// 2. This version should have been computed using the same
// path and parameters and the later version will be, so
// they should be the same anyway.
// 3. Not all other cases handled in this function have a
// surface point available, so it needs to be computed
// later anyway, in the general case.
let point_global = surface
.geometry()
.point_from_surface_coords(point_surface);
(point_curve, point_global)
})
.collect()
}
(SurfacePath::Line(line), _) => {
let range_u =
RangeOnPath::from(range.boundary.map(|point_curve| {
[curve.path().point_from_path_coords(point_curve).u]
}));
let approx_u = (surface.geometry().u, range_u)
.approx_with_cache(tolerance, &mut ());
let mut points = Vec::new();
for (u, _) in approx_u {
let t = (u.t - line.origin().u) / line.direction().u;
let point_surface = curve.path().point_from_path_coords([t]);
let point_global =
surface.geometry().point_from_surface_coords(point_surface);
points.push((u, point_global));
}
points
}
};
let points = points
.into_iter()
.map(|(point_curve, point_global)| {
ApproxPoint::new(point_curve, point_global)
})
.collect();
GlobalCurveApprox { points }
}
/// An approximation of a [`Curve`]
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct CurveApprox {
/// The points that approximate the curve
pub points: Vec<ApproxPoint<2>>,
}
impl CurveApprox {
/// Create an empty instance of `CurveApprox`
pub fn empty() -> Self {
Self { points: Vec::new() }
}
/// Add points to the approximation
pub fn with_points(
mut self,
points: impl IntoIterator<Item = ApproxPoint<2>>,
) -> Self {
self.points.extend(points);
self
}
}
/// A cache for results of an approximation
#[derive(Default)]
pub struct CurveCache {
inner: BTreeMap<(ObjectId, RangeOnPath), GlobalCurveApprox>,
}
impl CurveCache {
/// Create an empty cache
pub fn new() -> Self {
Self::default()
}
/// Insert the approximation of a [`GlobalCurve`]
pub fn insert(
&mut self,
handle: Handle<GlobalCurve>,
range: RangeOnPath,
approx: GlobalCurveApprox,
) -> GlobalCurveApprox {
self.inner.insert((handle.id(), range), approx.clone());
approx
}
/// Access the approximation for the given [`GlobalCurve`], if available
pub fn get(
&self,
handle: Handle<GlobalCurve>,
range: RangeOnPath,
) -> Option<GlobalCurveApprox> {
if let Some(approx) = self.inner.get(&(handle.id(), range)) {
return Some(approx.clone());
}
if let Some(approx) = self.inner.get(&(handle.id(), range.reverse())) {
// If we have a cache entry for the reverse range, we need to use
// that too!
return Some(approx.clone().reverse());
}
None
}
}
/// An approximation of a [`GlobalCurve`]
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct GlobalCurveApprox {
/// The points that approximate the curve
pub points: Vec<ApproxPoint<1>>,
}
impl GlobalCurveApprox {
/// Reverse the order of the approximation
pub fn reverse(mut self) -> Self {
self.points.reverse();
self
}
}
#[cfg(test)]
mod tests {
use std::{f64::consts::TAU, ops::Deref};
use pretty_assertions::assert_eq;
use crate::{
algorithms::approx::{path::RangeOnPath, Approx, ApproxPoint},
builder::{CurveBuilder, SurfaceBuilder},
geometry::path::GlobalPath,
insert::Insert,
objects::GlobalCurve,
partial::{PartialCurve, PartialObject, PartialSurface},
services::Services,
};
use super::CurveApprox;
#[test]
fn approx_line_on_flat_surface() {
let mut services = Services::new();
let surface = services.objects.surfaces.xz_plane();
let mut curve = PartialCurve::default();
curve.update_as_line_from_points([[1., 1.], [2., 1.]]);
let curve = curve
.build(&mut services.objects)
.insert(&mut services.objects);
let global_curve = GlobalCurve.insert(&mut services.objects);
let range = RangeOnPath::from([[0.], [1.]]);
let approx = (&curve, surface.deref(), global_curve, range).approx(1.);
assert_eq!(approx, CurveApprox::empty());
}
#[test]
fn approx_line_on_curved_surface_but_not_along_curve() {
let mut services = Services::new();
let surface = PartialSurface::from_axes(
GlobalPath::circle_from_radius(1.),
[0., 0., 1.],
)
.build(&mut services.objects)
.insert(&mut services.objects);
let mut curve = PartialCurve::default();
curve.update_as_line_from_points([[1., 1.], [1., 2.]]);
let curve = curve
.build(&mut services.objects)
.insert(&mut services.objects);
let global_curve = GlobalCurve.insert(&mut services.objects);
let range = RangeOnPath::from([[0.], [1.]]);
let approx = (&curve, surface.deref(), global_curve, range).approx(1.);
assert_eq!(approx, CurveApprox::empty());
}
#[test]
fn approx_line_on_curved_surface_along_curve() {
let mut services = Services::new();
let path = GlobalPath::circle_from_radius(1.);
let surface = PartialSurface::from_axes(path, [0., 0., 1.])
.build(&mut services.objects)
.insert(&mut services.objects);
let mut curve = PartialCurve::default();
curve.update_as_line_from_points([[0., 1.], [1., 1.]]);
let curve = curve
.build(&mut services.objects)
.insert(&mut services.objects);
let global_curve = GlobalCurve.insert(&mut services.objects);
let range = RangeOnPath::from([[0.], [TAU]]);
let tolerance = 1.;
let approx =
(&curve, surface.deref(), global_curve, range).approx(tolerance);
let expected_approx = (path, range)
.approx(tolerance)
.into_iter()
.map(|(point_local, _)| {
let point_surface =
curve.path().point_from_path_coords(point_local);
let point_global =
surface.geometry().point_from_surface_coords(point_surface);
ApproxPoint::new(point_surface, point_global)
})
.collect::<Vec<_>>();
assert_eq!(approx.points, expected_approx);
}
#[test]
fn approx_circle_on_flat_surface() {
let mut services = Services::new();
let surface = services.objects.surfaces.xz_plane();
let mut curve = PartialCurve::default();
curve.update_as_circle_from_radius(1.);
let curve = curve
.build(&mut services.objects)
.insert(&mut services.objects);
let global_curve = GlobalCurve.insert(&mut services.objects);
let range = RangeOnPath::from([[0.], [TAU]]);
let tolerance = 1.;
let approx =
(&curve, surface.deref(), global_curve, range).approx(tolerance);
let expected_approx = (curve.path(), range)
.approx(tolerance)
.into_iter()
.map(|(_, point_surface)| {
let point_global =
surface.geometry().point_from_surface_coords(point_surface);
ApproxPoint::new(point_surface, point_global)
})
.collect::<Vec<_>>();
assert_eq!(approx.points, expected_approx);
}
}

View File

@ -7,12 +7,13 @@ use fj_math::Segment;
use crate::objects::{Cycle, Surface};
use super::{
curve::CurveCache, edge::HalfEdgeApprox, Approx, ApproxPoint, Tolerance,
edge::{EdgeCache, HalfEdgeApprox},
Approx, ApproxPoint, Tolerance,
};
impl Approx for (&Cycle, &Surface) {
type Approximation = CycleApprox;
type Cache = CurveCache;
type Cache = EdgeCache;
fn approx_with_cache(
self,

View File

@ -5,20 +5,19 @@
//! approximations are usually used to build cycle approximations, and this way,
//! the caller doesn't have to call with duplicate vertices.
use std::collections::BTreeMap;
use crate::{
objects::{HalfEdge, Surface},
storage::Handle,
geometry::path::{GlobalPath, SurfacePath},
objects::{Curve, GlobalEdge, HalfEdge, Surface},
storage::{Handle, ObjectId},
};
use super::{
curve::{CurveApprox, CurveCache},
path::RangeOnPath,
Approx, ApproxPoint, Tolerance,
};
use super::{path::RangeOnPath, Approx, ApproxPoint, Tolerance};
impl Approx for (&Handle<HalfEdge>, &Surface) {
type Approximation = HalfEdgeApprox;
type Cache = CurveCache;
type Cache = EdgeCache;
fn approx_with_cache(
self,
@ -35,29 +34,52 @@ impl Approx for (&Handle<HalfEdge>, &Surface) {
half_edge.start_vertex().global_form().position(),
)
.with_source((half_edge.clone(), half_edge.boundary()[0]));
let curve_approx = (
half_edge.curve(),
surface,
half_edge.global_form().curve().clone(),
range,
)
.approx_with_cache(tolerance, cache);
HalfEdgeApprox {
first,
curve_approx,
}
let points = {
let approx = match cache.get(half_edge.global_form().clone(), range)
{
Some(approx) => approx,
None => {
let approx = approx_edge(
half_edge.curve(),
surface,
range,
tolerance,
);
cache.insert(half_edge.global_form().clone(), range, approx)
}
};
approx
.points
.into_iter()
.map(|point| {
let point_surface = half_edge
.curve()
.path()
.point_from_path_coords(point.local_form);
ApproxPoint::new(point_surface, point.global_form)
.with_source((
half_edge.curve().clone(),
point.local_form,
))
})
.collect()
};
HalfEdgeApprox { first, points }
}
}
/// An approximation of an [`HalfEdge`]
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct HalfEdgeApprox {
/// The point that approximates the first vertex of the curve
/// The point that approximates the first vertex of the edge
pub first: ApproxPoint<2>,
/// The approximation of the edge's curve
pub curve_approx: CurveApprox,
/// The approximation of the edge
pub points: Vec<ApproxPoint<2>>,
}
impl HalfEdgeApprox {
@ -66,8 +88,283 @@ impl HalfEdgeApprox {
let mut points = Vec::new();
points.push(self.first.clone());
points.extend(self.curve_approx.points.clone());
points.extend(self.points.iter().cloned());
points
}
}
fn approx_edge(
curve: &Curve,
surface: &Surface,
range: RangeOnPath,
tolerance: impl Into<Tolerance>,
) -> GlobalEdgeApprox {
// There are different cases of varying complexity. Circles are the hard
// part here, as they need to be approximated, while lines don't need to be.
//
// This will probably all be unified eventually, as `SurfacePath` and
// `GlobalPath` grow APIs that are better suited to implementing this code
// in a more abstract way.
let points = match (curve.path(), surface.geometry().u) {
(SurfacePath::Circle(_), GlobalPath::Circle(_)) => {
todo!(
"Approximating a circle on a curved surface not supported yet."
)
}
(SurfacePath::Circle(_), GlobalPath::Line(_)) => {
(curve.path(), range)
.approx_with_cache(tolerance, &mut ())
.into_iter()
.map(|(point_curve, point_surface)| {
// We're throwing away `point_surface` here, which is a bit
// weird, as we're recomputing it later (outside of this
// function).
//
// It should be fine though:
//
// 1. We're throwing this version away, so there's no danger
// of inconsistency between this and the later version.
// 2. This version should have been computed using the same
// path and parameters and the later version will be, so
// they should be the same anyway.
// 3. Not all other cases handled in this function have a
// surface point available, so it needs to be computed
// later anyway, in the general case.
let point_global = surface
.geometry()
.point_from_surface_coords(point_surface);
(point_curve, point_global)
})
.collect()
}
(SurfacePath::Line(line), _) => {
let range_u =
RangeOnPath::from(range.boundary.map(|point_curve| {
[curve.path().point_from_path_coords(point_curve).u]
}));
let approx_u = (surface.geometry().u, range_u)
.approx_with_cache(tolerance, &mut ());
let mut points = Vec::new();
for (u, _) in approx_u {
let t = (u.t - line.origin().u) / line.direction().u;
let point_surface = curve.path().point_from_path_coords([t]);
let point_global =
surface.geometry().point_from_surface_coords(point_surface);
points.push((u, point_global));
}
points
}
};
let points = points
.into_iter()
.map(|(point_curve, point_global)| {
ApproxPoint::new(point_curve, point_global)
})
.collect();
GlobalEdgeApprox { points }
}
/// A cache for results of an approximation
#[derive(Default)]
pub struct EdgeCache {
inner: BTreeMap<(ObjectId, RangeOnPath), GlobalEdgeApprox>,
}
impl EdgeCache {
/// Create an empty cache
pub fn new() -> Self {
Self::default()
}
/// Insert the approximation of a [`GlobalEdge`]
pub fn insert(
&mut self,
handle: Handle<GlobalEdge>,
range: RangeOnPath,
approx: GlobalEdgeApprox,
) -> GlobalEdgeApprox {
self.inner.insert((handle.id(), range), approx.clone());
approx
}
/// Access the approximation for the given [`GlobalEdge`], if available
pub fn get(
&self,
handle: Handle<GlobalEdge>,
range: RangeOnPath,
) -> Option<GlobalEdgeApprox> {
if let Some(approx) = self.inner.get(&(handle.id(), range)) {
return Some(approx.clone());
}
if let Some(approx) = self.inner.get(&(handle.id(), range.reverse())) {
// If we have a cache entry for the reverse range, we need to use
// that too!
return Some(approx.clone().reverse());
}
None
}
}
/// An approximation of a [`GlobalEdge`]
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct GlobalEdgeApprox {
/// The points that approximate the edge
pub points: Vec<ApproxPoint<1>>,
}
impl GlobalEdgeApprox {
/// Reverse the order of the approximation
pub fn reverse(mut self) -> Self {
self.points.reverse();
self
}
}
#[cfg(test)]
mod tests {
use std::{f64::consts::TAU, ops::Deref};
use pretty_assertions::assert_eq;
use crate::{
algorithms::approx::{path::RangeOnPath, Approx, ApproxPoint},
builder::{HalfEdgeBuilder, SurfaceBuilder},
geometry::path::GlobalPath,
insert::Insert,
partial::{PartialHalfEdge, PartialObject, PartialSurface},
services::Services,
};
#[test]
fn approx_line_on_flat_surface() {
let mut services = Services::new();
let surface = services.objects.surfaces.xz_plane();
let half_edge = {
let mut half_edge = PartialHalfEdge::default();
half_edge.update_as_line_segment_from_points([[1., 1.], [2., 1.]]);
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
half_edge
.build(&mut services.objects)
.insert(&mut services.objects)
};
let tolerance = 1.;
let approx = (&half_edge, surface.deref()).approx(tolerance);
assert_eq!(approx.points, Vec::new());
}
#[test]
fn approx_line_on_curved_surface_but_not_along_curve() {
let mut services = Services::new();
let surface = PartialSurface::from_axes(
GlobalPath::circle_from_radius(1.),
[0., 0., 1.],
)
.build(&mut services.objects)
.insert(&mut services.objects);
let half_edge = {
let mut half_edge = PartialHalfEdge::default();
half_edge.update_as_line_segment_from_points([[1., 1.], [2., 1.]]);
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
half_edge
.build(&mut services.objects)
.insert(&mut services.objects)
};
let tolerance = 1.;
let approx = (&half_edge, surface.deref()).approx(tolerance);
assert_eq!(approx.points, Vec::new());
}
#[test]
fn approx_line_on_curved_surface_along_curve() {
let mut services = Services::new();
let path = GlobalPath::circle_from_radius(1.);
let range = RangeOnPath::from([[0.], [TAU]]);
let surface = PartialSurface::from_axes(path, [0., 0., 1.])
.build(&mut services.objects)
.insert(&mut services.objects);
let half_edge = {
let mut half_edge = PartialHalfEdge::default();
half_edge.update_as_line_segment_from_points([[0., 1.], [1., 1.]]);
half_edge.vertices[0].0 = Some(range.boundary[0]);
half_edge.vertices[1].0 = Some(range.boundary[1]);
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
half_edge
.build(&mut services.objects)
.insert(&mut services.objects)
};
let tolerance = 1.;
let approx = (&half_edge, surface.deref()).approx(tolerance);
let expected_approx = (path, range)
.approx(tolerance)
.into_iter()
.map(|(point_local, _)| {
let point_surface = half_edge
.curve()
.path()
.point_from_path_coords(point_local);
let point_global =
surface.geometry().point_from_surface_coords(point_surface);
ApproxPoint::new(point_surface, point_global)
})
.collect::<Vec<_>>();
assert_eq!(approx.points, expected_approx);
}
#[test]
fn approx_circle_on_flat_surface() {
let mut services = Services::new();
let surface = services.objects.surfaces.xz_plane();
let half_edge = {
let mut half_edge = PartialHalfEdge::default();
half_edge.update_as_circle_from_radius(1.);
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
half_edge
.build(&mut services.objects)
.insert(&mut services.objects)
};
let tolerance = 1.;
let approx = (&half_edge, surface.deref()).approx(tolerance);
let expected_approx =
(half_edge.curve().path(), RangeOnPath::from([[0.], [TAU]]))
.approx(tolerance)
.into_iter()
.map(|(_, point_surface)| {
let point_global = surface
.geometry()
.point_from_surface_coords(point_surface);
ApproxPoint::new(point_surface, point_global)
})
.collect::<Vec<_>>();
assert_eq!(approx.points, expected_approx);
}
}

View File

@ -12,12 +12,12 @@ use crate::{
};
use super::{
curve::CurveCache, cycle::CycleApprox, Approx, ApproxPoint, Tolerance,
cycle::CycleApprox, edge::EdgeCache, Approx, ApproxPoint, Tolerance,
};
impl Approx for &FaceSet {
type Approximation = BTreeSet<FaceApprox>;
type Cache = CurveCache;
type Cache = EdgeCache;
fn approx_with_cache(
self,
@ -65,7 +65,7 @@ impl Approx for &FaceSet {
impl Approx for &Face {
type Approximation = FaceApprox;
type Cache = CurveCache;
type Cache = EdgeCache;
fn approx_with_cache(
self,

View File

@ -1,6 +1,5 @@
//! Approximation of objects
pub mod curve;
pub mod cycle;
pub mod edge;
pub mod face;

View File

@ -4,11 +4,11 @@ use std::collections::BTreeSet;
use crate::objects::Shell;
use super::{curve::CurveCache, face::FaceApprox, Approx, Tolerance};
use super::{edge::EdgeCache, face::FaceApprox, Approx, Tolerance};
impl Approx for &Shell {
type Approximation = BTreeSet<FaceApprox>;
type Cache = CurveCache;
type Cache = EdgeCache;
fn approx_with_cache(
self,

View File

@ -4,11 +4,11 @@ use std::collections::BTreeSet;
use crate::objects::Sketch;
use super::{curve::CurveCache, face::FaceApprox, Approx, Tolerance};
use super::{edge::EdgeCache, face::FaceApprox, Approx, Tolerance};
impl Approx for &Sketch {
type Approximation = BTreeSet<FaceApprox>;
type Cache = CurveCache;
type Cache = EdgeCache;
fn approx_with_cache(
self,

View File

@ -4,11 +4,11 @@ use std::collections::BTreeSet;
use crate::objects::Solid;
use super::{curve::CurveCache, face::FaceApprox, Approx, Tolerance};
use super::{edge::EdgeCache, face::FaceApprox, Approx, Tolerance};
impl Approx for &Solid {
type Approximation = BTreeSet<FaceApprox>;
type Cache = CurveCache;
type Cache = EdgeCache;
fn approx_with_cache(
self,

View File

@ -2,7 +2,7 @@ use fj_math::Vector;
use crate::{
insert::Insert,
objects::{GlobalCurve, GlobalEdge, GlobalVertex, Objects},
objects::{GlobalEdge, GlobalVertex, Objects},
services::Service,
storage::Handle,
};
@ -18,8 +18,6 @@ impl Sweep for Handle<GlobalVertex> {
cache: &mut SweepCache,
objects: &mut Service<Objects>,
) -> Self::Swept {
let curve = GlobalCurve.insert(objects);
let a = self.clone();
let b = cache
.global_vertex
@ -30,8 +28,7 @@ impl Sweep for Handle<GlobalVertex> {
.clone();
let vertices = [a, b];
let global_edge =
GlobalEdge::new(curve, vertices.clone()).insert(objects);
let global_edge = GlobalEdge::new(vertices.clone()).insert(objects);
// The vertices of the returned `GlobalEdge` are in normalized order,
// which means the order can't be relied upon by the caller. Return the

View File

@ -1,7 +1,7 @@
use fj_math::Transform;
use crate::{
objects::{Curve, GlobalCurve, Objects},
objects::{Curve, Objects},
services::Service,
};
@ -21,18 +21,3 @@ impl TransformObject for Curve {
Self::new(path)
}
}
impl TransformObject for GlobalCurve {
fn transform_with_cache(
self,
_: &Transform,
_: &mut Service<Objects>,
_: &mut TransformCache,
) -> Self {
// `GlobalCurve` doesn't contain any internal geometry. If it did, that
// would just be redundant with the geometry of other objects, and this
// other geometry is already being transformed by other implementations
// of this trait.
self
}
}

View File

@ -43,15 +43,11 @@ impl TransformObject for GlobalEdge {
objects: &mut Service<Objects>,
cache: &mut TransformCache,
) -> Self {
let curve = self
.curve()
.clone()
.transform_with_cache(transform, objects, cache);
let vertices =
self.vertices().access_in_normalized_order().map(|vertex| {
vertex.transform_with_cache(transform, objects, cache)
});
Self::new(curve, vertices)
Self::new(vertices)
}
}

View File

@ -223,9 +223,6 @@ impl HalfEdgeBuilder for PartialHalfEdge {
other: &Partial<HalfEdge>,
surface: &SurfaceGeometry,
) {
let global_curve = other.read().global_form.read().curve.clone();
self.global_form.write().curve = global_curve;
self.curve.write().path =
other.read().curve.read().path.as_ref().and_then(|path| {
// We have information about the other edge's surface available.

View File

@ -4,12 +4,9 @@
//!
//! # Implementation Note
//!
//! This is a bit of an in-between module. It is closely associated with curves
//! ([`Curve`]/[`GlobalCurve`]) and [`Surface`]s, but paths are not really
//! objects themselves, as logically speaking, they are owned and not referenced
//! (practically speaking, all objects are owned and not referenced, but that is
//! an implementation detail; see [#1021] for context on where things are
//! going).
//! This is a bit of an in-between module. It is closely associated with
//! [`Curve`] and [`Surface`]s, but paths are not really objects themselves, as
//! logically speaking, they are owned and not referenced.
//!
//! On the other hand, the types in this module don't follow the general style
//! of types in `fj-math`.
@ -18,9 +15,7 @@
//! move to `fj-math`, maybe something else entirely will happen.
//!
//! [`Curve`]: crate::objects::Curve
//! [`GlobalCurve`]: crate::objects::GlobalCurve
//! [`Surface`]: crate::objects::Surface
//! [#1021]: https://github.com/hannobraun/Fornjot/issues/1021
use fj_math::{Circle, Line, Point, Scalar, Transform, Vector};

View File

@ -4,8 +4,8 @@
use crate::{
objects::{
Curve, Cycle, Face, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge,
Objects, Shell, Sketch, Solid, Surface, SurfaceVertex,
Curve, Cycle, Face, GlobalEdge, GlobalVertex, HalfEdge, Objects, Shell,
Sketch, Solid, Surface, SurfaceVertex,
},
services::{Service, ServiceObjectsExt},
storage::Handle,
@ -37,7 +37,6 @@ impl_insert!(
Curve, curves;
Cycle, cycles;
Face, faces;
GlobalCurve, global_curves;
GlobalEdge, global_edges;
GlobalVertex, global_vertices;
HalfEdge, half_edges;

View File

@ -17,7 +17,3 @@ impl Curve {
self.path
}
}
/// A curve, defined in global (3D) coordinates
#[derive(Clone, Copy, Debug)]
pub struct GlobalCurve;

View File

@ -1,11 +1,9 @@
use std::fmt;
use fj_interop::ext::ArrayExt;
use fj_math::Point;
use crate::{
objects::{Curve, GlobalCurve, GlobalVertex, SurfaceVertex},
storage::{Handle, HandleWrapper},
objects::{Curve, GlobalVertex, SurfaceVertex},
storage::Handle,
};
/// A directed edge, defined in a surface's 2D space
@ -95,16 +93,6 @@ impl HalfEdge {
}
}
impl fmt::Display for HalfEdge {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let [a, b] = self.boundary();
write!(f, "edge from {a:?} to {b:?}")?;
write!(f, " on {:?}", self.global_form().curve())?;
Ok(())
}
}
/// An undirected edge, defined in global (3D) coordinates
///
/// In contrast to [`HalfEdge`], `GlobalEdge` is undirected, meaning it has no
@ -116,7 +104,6 @@ impl fmt::Display for HalfEdge {
/// between [`HalfEdge`] and `GlobalEdge`.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct GlobalEdge {
curve: HandleWrapper<GlobalCurve>,
vertices: VerticesInNormalizedOrder,
}
@ -126,19 +113,10 @@ impl GlobalEdge {
/// The order of `vertices` is irrelevant. Two `GlobalEdge`s with the same
/// `curve` and `vertices` will end up being equal, regardless of the order
/// of `vertices` here.
pub fn new(
curve: impl Into<HandleWrapper<GlobalCurve>>,
vertices: [Handle<GlobalVertex>; 2],
) -> Self {
let curve = curve.into();
pub fn new(vertices: [Handle<GlobalVertex>; 2]) -> Self {
let (vertices, _) = VerticesInNormalizedOrder::new(vertices);
Self { curve, vertices }
}
/// Access the curve that defines the edge's geometry
pub fn curve(&self) -> &Handle<GlobalCurve> {
&self.curve
Self { vertices }
}
/// Access the vertices that bound the edge on the curve

View File

@ -79,7 +79,7 @@ mod stores;
pub use self::{
full::{
curve::{Curve, GlobalCurve},
curve::Curve,
cycle::{Cycle, HalfEdgesOfCycle},
edge::{GlobalEdge, HalfEdge, VerticesInNormalizedOrder},
face::{Face, FaceSet, Handedness},

View File

@ -2,8 +2,8 @@ use std::any::Any;
use crate::{
objects::{
Curve, Cycle, Face, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge,
Objects, Shell, Sketch, Solid, Surface, SurfaceVertex,
Curve, Cycle, Face, GlobalEdge, GlobalVertex, HalfEdge, Objects, Shell,
Sketch, Solid, Surface, SurfaceVertex,
},
storage::{Handle, ObjectId},
validate::{Validate, ValidationError},
@ -111,7 +111,6 @@ object!(
Curve, "curve", curves;
Cycle, "cycle", cycles;
Face, "face", faces;
GlobalCurve, "global curve", global_curves;
GlobalEdge, "global edge", global_edges;
GlobalVertex, "global vertex", global_vertices;
HalfEdge, "half-edge", half_edges;

View File

@ -6,8 +6,8 @@ use crate::{
};
use super::{
Curve, Cycle, Face, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Shell,
Sketch, Solid, Surface, SurfaceVertex,
Curve, Cycle, Face, GlobalEdge, GlobalVertex, HalfEdge, Shell, Sketch,
Solid, Surface, SurfaceVertex,
};
/// The available object stores
@ -22,9 +22,6 @@ pub struct Objects {
/// Store for [`Face`]s
pub faces: Store<Face>,
/// Store for [`GlobalCurve`]s
pub global_curves: Store<GlobalCurve>,
/// Store for [`GlobalEdge`]s
pub global_edges: Store<GlobalEdge>,

View File

@ -16,7 +16,7 @@ mod wrapper;
pub use self::{
objects::{
curve::{MaybeSurfacePath, PartialCurve, PartialGlobalCurve},
curve::{MaybeSurfacePath, PartialCurve},
cycle::PartialCycle,
edge::{PartialGlobalEdge, PartialHalfEdge},
face::PartialFace,

View File

@ -2,7 +2,7 @@ use fj_math::Scalar;
use crate::{
geometry::path::SurfacePath,
objects::{Curve, GlobalCurve, Objects},
objects::{Curve, Objects},
partial::{FullToPartialCache, PartialObject},
services::Service,
};
@ -61,19 +61,3 @@ impl From<SurfacePath> for MaybeSurfacePath {
Self::Defined(path)
}
}
/// A partial [`GlobalCurve`]
#[derive(Clone, Debug, Default)]
pub struct PartialGlobalCurve;
impl PartialObject for PartialGlobalCurve {
type Full = GlobalCurve;
fn from_full(_: &Self::Full, _: &mut FullToPartialCache) -> Self {
Self
}
fn build(self, _: &mut Service<Objects>) -> Self::Full {
GlobalCurve
}
}

View File

@ -5,8 +5,7 @@ use fj_math::Point;
use crate::{
objects::{
Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects,
SurfaceVertex,
Curve, GlobalEdge, GlobalVertex, HalfEdge, Objects, SurfaceVertex,
},
partial::{FullToPartialCache, Partial, PartialObject},
services::Service,
@ -84,7 +83,6 @@ impl Default for PartialHalfEdge {
let global_form = Partial::from_partial(PartialGlobalEdge {
vertices: global_vertices,
..Default::default()
});
Self {
@ -98,9 +96,6 @@ impl Default for PartialHalfEdge {
/// A partial [`GlobalEdge`]
#[derive(Clone, Debug, Default)]
pub struct PartialGlobalEdge {
/// The curve that defines the edge's geometry
pub curve: Partial<GlobalCurve>,
/// The vertices that bound the edge on the curve
pub vertices: [Partial<GlobalVertex>; 2],
}
@ -113,7 +108,6 @@ impl PartialObject for PartialGlobalEdge {
cache: &mut FullToPartialCache,
) -> Self {
Self {
curve: Partial::from_full(global_edge.curve().clone(), cache),
vertices: global_edge
.vertices()
.access_in_normalized_order()
@ -122,9 +116,7 @@ impl PartialObject for PartialGlobalEdge {
}
fn build(self, objects: &mut Service<Objects>) -> Self::Full {
let curve = self.curve.build(objects);
let vertices = self.vertices.map(|vertex| vertex.build(objects));
GlobalEdge::new(curve, vertices)
GlobalEdge::new(vertices)
}
}

View File

@ -36,7 +36,6 @@ impl_trait!(
Curve, PartialCurve;
Cycle, PartialCycle;
Face, PartialFace;
GlobalCurve, PartialGlobalCurve;
GlobalEdge, PartialGlobalEdge;
GlobalVertex, PartialGlobalVertex;
HalfEdge, PartialHalfEdge;

View File

@ -1,4 +1,4 @@
use crate::objects::{Curve, GlobalCurve};
use crate::objects::Curve;
use super::{Validate, ValidationConfig, ValidationError};
@ -10,12 +10,3 @@ impl Validate for Curve {
) {
}
}
impl Validate for GlobalCurve {
fn validate_with_config(
&self,
_: &ValidationConfig,
_: &mut Vec<ValidationError>,
) {
}
}

View File

@ -1,7 +1,7 @@
use fj_math::{Point, Scalar};
use crate::{
objects::{GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface},
objects::{GlobalEdge, GlobalVertex, HalfEdge, Surface},
storage::Handle,
};
@ -30,25 +30,6 @@ impl Validate for GlobalEdge {
/// [`HalfEdge`] validation failed
#[derive(Clone, Debug, thiserror::Error)]
pub enum HalfEdgeValidationError {
/// [`HalfEdge`]'s [`GlobalCurve`]s do not match
#[error(
"Global form of `HalfEdge`'s `Curve` does not match `GlobalCurve` of \n\
the `HalfEdge`'s `GlobalEdge`\n\
- `GlobalCurve` from `Curve`: {global_curve_from_curve:#?}\n\
- `GlobalCurve` from `GlobalEdge`: {global_curve_from_global_form:#?}\n\
- `HalfEdge`: {half_edge:#?}",
)]
GlobalCurveMismatch {
/// The [`GlobalCurve`] from the [`HalfEdge`]'s `Curve`
global_curve_from_curve: Handle<GlobalCurve>,
/// The [`GlobalCurve`] from the [`HalfEdge`]'s global form
global_curve_from_global_form: Handle<GlobalCurve>,
/// The half-edge
half_edge: HalfEdge,
},
/// [`HalfEdge`]'s [`GlobalVertex`] objects do not match
#[error(
"Global forms of `HalfEdge` vertices do not match vertices of \n\