mirror of
https://github.com/hannobraun/Fornjot
synced 2025-01-30 03:45:55 +00:00
Merge pull request #1610 from hannobraun/curve
Merge `GlobalCurve` into `GlobalEdge`
This commit is contained in:
commit
c757811554
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,12 +7,13 @@ use fj_math::Segment;
|
|||||||
use crate::objects::{Cycle, Surface};
|
use crate::objects::{Cycle, Surface};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
curve::CurveCache, edge::HalfEdgeApprox, Approx, ApproxPoint, Tolerance,
|
edge::{EdgeCache, HalfEdgeApprox},
|
||||||
|
Approx, ApproxPoint, Tolerance,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Approx for (&Cycle, &Surface) {
|
impl Approx for (&Cycle, &Surface) {
|
||||||
type Approximation = CycleApprox;
|
type Approximation = CycleApprox;
|
||||||
type Cache = CurveCache;
|
type Cache = EdgeCache;
|
||||||
|
|
||||||
fn approx_with_cache(
|
fn approx_with_cache(
|
||||||
self,
|
self,
|
||||||
|
@ -5,20 +5,19 @@
|
|||||||
//! approximations are usually used to build cycle approximations, and this way,
|
//! approximations are usually used to build cycle approximations, and this way,
|
||||||
//! the caller doesn't have to call with duplicate vertices.
|
//! the caller doesn't have to call with duplicate vertices.
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{HalfEdge, Surface},
|
geometry::path::{GlobalPath, SurfacePath},
|
||||||
storage::Handle,
|
objects::{Curve, GlobalEdge, HalfEdge, Surface},
|
||||||
|
storage::{Handle, ObjectId},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{path::RangeOnPath, Approx, ApproxPoint, Tolerance};
|
||||||
curve::{CurveApprox, CurveCache},
|
|
||||||
path::RangeOnPath,
|
|
||||||
Approx, ApproxPoint, Tolerance,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl Approx for (&Handle<HalfEdge>, &Surface) {
|
impl Approx for (&Handle<HalfEdge>, &Surface) {
|
||||||
type Approximation = HalfEdgeApprox;
|
type Approximation = HalfEdgeApprox;
|
||||||
type Cache = CurveCache;
|
type Cache = EdgeCache;
|
||||||
|
|
||||||
fn approx_with_cache(
|
fn approx_with_cache(
|
||||||
self,
|
self,
|
||||||
@ -35,29 +34,52 @@ impl Approx for (&Handle<HalfEdge>, &Surface) {
|
|||||||
half_edge.start_vertex().global_form().position(),
|
half_edge.start_vertex().global_form().position(),
|
||||||
)
|
)
|
||||||
.with_source((half_edge.clone(), half_edge.boundary()[0]));
|
.with_source((half_edge.clone(), half_edge.boundary()[0]));
|
||||||
let 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(),
|
half_edge.curve(),
|
||||||
surface,
|
surface,
|
||||||
half_edge.global_form().curve().clone(),
|
|
||||||
range,
|
range,
|
||||||
)
|
tolerance,
|
||||||
.approx_with_cache(tolerance, cache);
|
);
|
||||||
|
cache.insert(half_edge.global_form().clone(), range, approx)
|
||||||
HalfEdgeApprox {
|
|
||||||
first,
|
|
||||||
curve_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`]
|
/// An approximation of an [`HalfEdge`]
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct HalfEdgeApprox {
|
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>,
|
pub first: ApproxPoint<2>,
|
||||||
|
|
||||||
/// The approximation of the edge's curve
|
/// The approximation of the edge
|
||||||
pub curve_approx: CurveApprox,
|
pub points: Vec<ApproxPoint<2>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HalfEdgeApprox {
|
impl HalfEdgeApprox {
|
||||||
@ -66,8 +88,283 @@ impl HalfEdgeApprox {
|
|||||||
let mut points = Vec::new();
|
let mut points = Vec::new();
|
||||||
|
|
||||||
points.push(self.first.clone());
|
points.push(self.first.clone());
|
||||||
points.extend(self.curve_approx.points.clone());
|
points.extend(self.points.iter().cloned());
|
||||||
|
|
||||||
points
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,12 +12,12 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
curve::CurveCache, cycle::CycleApprox, Approx, ApproxPoint, Tolerance,
|
cycle::CycleApprox, edge::EdgeCache, Approx, ApproxPoint, Tolerance,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Approx for &FaceSet {
|
impl Approx for &FaceSet {
|
||||||
type Approximation = BTreeSet<FaceApprox>;
|
type Approximation = BTreeSet<FaceApprox>;
|
||||||
type Cache = CurveCache;
|
type Cache = EdgeCache;
|
||||||
|
|
||||||
fn approx_with_cache(
|
fn approx_with_cache(
|
||||||
self,
|
self,
|
||||||
@ -65,7 +65,7 @@ impl Approx for &FaceSet {
|
|||||||
|
|
||||||
impl Approx for &Face {
|
impl Approx for &Face {
|
||||||
type Approximation = FaceApprox;
|
type Approximation = FaceApprox;
|
||||||
type Cache = CurveCache;
|
type Cache = EdgeCache;
|
||||||
|
|
||||||
fn approx_with_cache(
|
fn approx_with_cache(
|
||||||
self,
|
self,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
//! Approximation of objects
|
//! Approximation of objects
|
||||||
|
|
||||||
pub mod curve;
|
|
||||||
pub mod cycle;
|
pub mod cycle;
|
||||||
pub mod edge;
|
pub mod edge;
|
||||||
pub mod face;
|
pub mod face;
|
||||||
|
@ -4,11 +4,11 @@ use std::collections::BTreeSet;
|
|||||||
|
|
||||||
use crate::objects::Shell;
|
use crate::objects::Shell;
|
||||||
|
|
||||||
use super::{curve::CurveCache, face::FaceApprox, Approx, Tolerance};
|
use super::{edge::EdgeCache, face::FaceApprox, Approx, Tolerance};
|
||||||
|
|
||||||
impl Approx for &Shell {
|
impl Approx for &Shell {
|
||||||
type Approximation = BTreeSet<FaceApprox>;
|
type Approximation = BTreeSet<FaceApprox>;
|
||||||
type Cache = CurveCache;
|
type Cache = EdgeCache;
|
||||||
|
|
||||||
fn approx_with_cache(
|
fn approx_with_cache(
|
||||||
self,
|
self,
|
||||||
|
@ -4,11 +4,11 @@ use std::collections::BTreeSet;
|
|||||||
|
|
||||||
use crate::objects::Sketch;
|
use crate::objects::Sketch;
|
||||||
|
|
||||||
use super::{curve::CurveCache, face::FaceApprox, Approx, Tolerance};
|
use super::{edge::EdgeCache, face::FaceApprox, Approx, Tolerance};
|
||||||
|
|
||||||
impl Approx for &Sketch {
|
impl Approx for &Sketch {
|
||||||
type Approximation = BTreeSet<FaceApprox>;
|
type Approximation = BTreeSet<FaceApprox>;
|
||||||
type Cache = CurveCache;
|
type Cache = EdgeCache;
|
||||||
|
|
||||||
fn approx_with_cache(
|
fn approx_with_cache(
|
||||||
self,
|
self,
|
||||||
|
@ -4,11 +4,11 @@ use std::collections::BTreeSet;
|
|||||||
|
|
||||||
use crate::objects::Solid;
|
use crate::objects::Solid;
|
||||||
|
|
||||||
use super::{curve::CurveCache, face::FaceApprox, Approx, Tolerance};
|
use super::{edge::EdgeCache, face::FaceApprox, Approx, Tolerance};
|
||||||
|
|
||||||
impl Approx for &Solid {
|
impl Approx for &Solid {
|
||||||
type Approximation = BTreeSet<FaceApprox>;
|
type Approximation = BTreeSet<FaceApprox>;
|
||||||
type Cache = CurveCache;
|
type Cache = EdgeCache;
|
||||||
|
|
||||||
fn approx_with_cache(
|
fn approx_with_cache(
|
||||||
self,
|
self,
|
||||||
|
@ -2,7 +2,7 @@ use fj_math::Vector;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
insert::Insert,
|
insert::Insert,
|
||||||
objects::{GlobalCurve, GlobalEdge, GlobalVertex, Objects},
|
objects::{GlobalEdge, GlobalVertex, Objects},
|
||||||
services::Service,
|
services::Service,
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
@ -18,8 +18,6 @@ impl Sweep for Handle<GlobalVertex> {
|
|||||||
cache: &mut SweepCache,
|
cache: &mut SweepCache,
|
||||||
objects: &mut Service<Objects>,
|
objects: &mut Service<Objects>,
|
||||||
) -> Self::Swept {
|
) -> Self::Swept {
|
||||||
let curve = GlobalCurve.insert(objects);
|
|
||||||
|
|
||||||
let a = self.clone();
|
let a = self.clone();
|
||||||
let b = cache
|
let b = cache
|
||||||
.global_vertex
|
.global_vertex
|
||||||
@ -30,8 +28,7 @@ impl Sweep for Handle<GlobalVertex> {
|
|||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
let vertices = [a, b];
|
let vertices = [a, b];
|
||||||
let global_edge =
|
let global_edge = GlobalEdge::new(vertices.clone()).insert(objects);
|
||||||
GlobalEdge::new(curve, vertices.clone()).insert(objects);
|
|
||||||
|
|
||||||
// The vertices of the returned `GlobalEdge` are in normalized order,
|
// The vertices of the returned `GlobalEdge` are in normalized order,
|
||||||
// which means the order can't be relied upon by the caller. Return the
|
// which means the order can't be relied upon by the caller. Return the
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use fj_math::Transform;
|
use fj_math::Transform;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{Curve, GlobalCurve, Objects},
|
objects::{Curve, Objects},
|
||||||
services::Service,
|
services::Service,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,18 +21,3 @@ impl TransformObject for Curve {
|
|||||||
Self::new(path)
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -43,15 +43,11 @@ impl TransformObject for GlobalEdge {
|
|||||||
objects: &mut Service<Objects>,
|
objects: &mut Service<Objects>,
|
||||||
cache: &mut TransformCache,
|
cache: &mut TransformCache,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let curve = self
|
|
||||||
.curve()
|
|
||||||
.clone()
|
|
||||||
.transform_with_cache(transform, objects, cache);
|
|
||||||
let vertices =
|
let vertices =
|
||||||
self.vertices().access_in_normalized_order().map(|vertex| {
|
self.vertices().access_in_normalized_order().map(|vertex| {
|
||||||
vertex.transform_with_cache(transform, objects, cache)
|
vertex.transform_with_cache(transform, objects, cache)
|
||||||
});
|
});
|
||||||
|
|
||||||
Self::new(curve, vertices)
|
Self::new(vertices)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,9 +223,6 @@ impl HalfEdgeBuilder for PartialHalfEdge {
|
|||||||
other: &Partial<HalfEdge>,
|
other: &Partial<HalfEdge>,
|
||||||
surface: &SurfaceGeometry,
|
surface: &SurfaceGeometry,
|
||||||
) {
|
) {
|
||||||
let global_curve = other.read().global_form.read().curve.clone();
|
|
||||||
self.global_form.write().curve = global_curve;
|
|
||||||
|
|
||||||
self.curve.write().path =
|
self.curve.write().path =
|
||||||
other.read().curve.read().path.as_ref().and_then(|path| {
|
other.read().curve.read().path.as_ref().and_then(|path| {
|
||||||
// We have information about the other edge's surface available.
|
// We have information about the other edge's surface available.
|
||||||
|
@ -4,12 +4,9 @@
|
|||||||
//!
|
//!
|
||||||
//! # Implementation Note
|
//! # Implementation Note
|
||||||
//!
|
//!
|
||||||
//! This is a bit of an in-between module. It is closely associated with curves
|
//! This is a bit of an in-between module. It is closely associated with
|
||||||
//! ([`Curve`]/[`GlobalCurve`]) and [`Surface`]s, but paths are not really
|
//! [`Curve`] and [`Surface`]s, but paths are not really objects themselves, as
|
||||||
//! objects themselves, as logically speaking, they are owned and not referenced
|
//! 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).
|
|
||||||
//!
|
//!
|
||||||
//! On the other hand, the types in this module don't follow the general style
|
//! On the other hand, the types in this module don't follow the general style
|
||||||
//! of types in `fj-math`.
|
//! of types in `fj-math`.
|
||||||
@ -18,9 +15,7 @@
|
|||||||
//! move to `fj-math`, maybe something else entirely will happen.
|
//! move to `fj-math`, maybe something else entirely will happen.
|
||||||
//!
|
//!
|
||||||
//! [`Curve`]: crate::objects::Curve
|
//! [`Curve`]: crate::objects::Curve
|
||||||
//! [`GlobalCurve`]: crate::objects::GlobalCurve
|
|
||||||
//! [`Surface`]: crate::objects::Surface
|
//! [`Surface`]: crate::objects::Surface
|
||||||
//! [#1021]: https://github.com/hannobraun/Fornjot/issues/1021
|
|
||||||
|
|
||||||
use fj_math::{Circle, Line, Point, Scalar, Transform, Vector};
|
use fj_math::{Circle, Line, Point, Scalar, Transform, Vector};
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{
|
||||||
Curve, Cycle, Face, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge,
|
Curve, Cycle, Face, GlobalEdge, GlobalVertex, HalfEdge, Objects, Shell,
|
||||||
Objects, Shell, Sketch, Solid, Surface, SurfaceVertex,
|
Sketch, Solid, Surface, SurfaceVertex,
|
||||||
},
|
},
|
||||||
services::{Service, ServiceObjectsExt},
|
services::{Service, ServiceObjectsExt},
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
@ -37,7 +37,6 @@ impl_insert!(
|
|||||||
Curve, curves;
|
Curve, curves;
|
||||||
Cycle, cycles;
|
Cycle, cycles;
|
||||||
Face, faces;
|
Face, faces;
|
||||||
GlobalCurve, global_curves;
|
|
||||||
GlobalEdge, global_edges;
|
GlobalEdge, global_edges;
|
||||||
GlobalVertex, global_vertices;
|
GlobalVertex, global_vertices;
|
||||||
HalfEdge, half_edges;
|
HalfEdge, half_edges;
|
||||||
|
@ -17,7 +17,3 @@ impl Curve {
|
|||||||
self.path
|
self.path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A curve, defined in global (3D) coordinates
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct GlobalCurve;
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
use std::fmt;
|
|
||||||
|
|
||||||
use fj_interop::ext::ArrayExt;
|
use fj_interop::ext::ArrayExt;
|
||||||
use fj_math::Point;
|
use fj_math::Point;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{Curve, GlobalCurve, GlobalVertex, SurfaceVertex},
|
objects::{Curve, GlobalVertex, SurfaceVertex},
|
||||||
storage::{Handle, HandleWrapper},
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A directed edge, defined in a surface's 2D space
|
/// 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
|
/// An undirected edge, defined in global (3D) coordinates
|
||||||
///
|
///
|
||||||
/// In contrast to [`HalfEdge`], `GlobalEdge` is undirected, meaning it has no
|
/// In contrast to [`HalfEdge`], `GlobalEdge` is undirected, meaning it has no
|
||||||
@ -116,7 +104,6 @@ impl fmt::Display for HalfEdge {
|
|||||||
/// between [`HalfEdge`] and `GlobalEdge`.
|
/// between [`HalfEdge`] and `GlobalEdge`.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct GlobalEdge {
|
pub struct GlobalEdge {
|
||||||
curve: HandleWrapper<GlobalCurve>,
|
|
||||||
vertices: VerticesInNormalizedOrder,
|
vertices: VerticesInNormalizedOrder,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,19 +113,10 @@ impl GlobalEdge {
|
|||||||
/// The order of `vertices` is irrelevant. Two `GlobalEdge`s with the same
|
/// The order of `vertices` is irrelevant. Two `GlobalEdge`s with the same
|
||||||
/// `curve` and `vertices` will end up being equal, regardless of the order
|
/// `curve` and `vertices` will end up being equal, regardless of the order
|
||||||
/// of `vertices` here.
|
/// of `vertices` here.
|
||||||
pub fn new(
|
pub fn new(vertices: [Handle<GlobalVertex>; 2]) -> Self {
|
||||||
curve: impl Into<HandleWrapper<GlobalCurve>>,
|
|
||||||
vertices: [Handle<GlobalVertex>; 2],
|
|
||||||
) -> Self {
|
|
||||||
let curve = curve.into();
|
|
||||||
let (vertices, _) = VerticesInNormalizedOrder::new(vertices);
|
let (vertices, _) = VerticesInNormalizedOrder::new(vertices);
|
||||||
|
|
||||||
Self { curve, vertices }
|
Self { vertices }
|
||||||
}
|
|
||||||
|
|
||||||
/// Access the curve that defines the edge's geometry
|
|
||||||
pub fn curve(&self) -> &Handle<GlobalCurve> {
|
|
||||||
&self.curve
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the vertices that bound the edge on the curve
|
/// Access the vertices that bound the edge on the curve
|
||||||
|
@ -79,7 +79,7 @@ mod stores;
|
|||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
full::{
|
full::{
|
||||||
curve::{Curve, GlobalCurve},
|
curve::Curve,
|
||||||
cycle::{Cycle, HalfEdgesOfCycle},
|
cycle::{Cycle, HalfEdgesOfCycle},
|
||||||
edge::{GlobalEdge, HalfEdge, VerticesInNormalizedOrder},
|
edge::{GlobalEdge, HalfEdge, VerticesInNormalizedOrder},
|
||||||
face::{Face, FaceSet, Handedness},
|
face::{Face, FaceSet, Handedness},
|
||||||
|
@ -2,8 +2,8 @@ use std::any::Any;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{
|
||||||
Curve, Cycle, Face, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge,
|
Curve, Cycle, Face, GlobalEdge, GlobalVertex, HalfEdge, Objects, Shell,
|
||||||
Objects, Shell, Sketch, Solid, Surface, SurfaceVertex,
|
Sketch, Solid, Surface, SurfaceVertex,
|
||||||
},
|
},
|
||||||
storage::{Handle, ObjectId},
|
storage::{Handle, ObjectId},
|
||||||
validate::{Validate, ValidationError},
|
validate::{Validate, ValidationError},
|
||||||
@ -111,7 +111,6 @@ object!(
|
|||||||
Curve, "curve", curves;
|
Curve, "curve", curves;
|
||||||
Cycle, "cycle", cycles;
|
Cycle, "cycle", cycles;
|
||||||
Face, "face", faces;
|
Face, "face", faces;
|
||||||
GlobalCurve, "global curve", global_curves;
|
|
||||||
GlobalEdge, "global edge", global_edges;
|
GlobalEdge, "global edge", global_edges;
|
||||||
GlobalVertex, "global vertex", global_vertices;
|
GlobalVertex, "global vertex", global_vertices;
|
||||||
HalfEdge, "half-edge", half_edges;
|
HalfEdge, "half-edge", half_edges;
|
||||||
|
@ -6,8 +6,8 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Curve, Cycle, Face, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Shell,
|
Curve, Cycle, Face, GlobalEdge, GlobalVertex, HalfEdge, Shell, Sketch,
|
||||||
Sketch, Solid, Surface, SurfaceVertex,
|
Solid, Surface, SurfaceVertex,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The available object stores
|
/// The available object stores
|
||||||
@ -22,9 +22,6 @@ pub struct Objects {
|
|||||||
/// Store for [`Face`]s
|
/// Store for [`Face`]s
|
||||||
pub faces: Store<Face>,
|
pub faces: Store<Face>,
|
||||||
|
|
||||||
/// Store for [`GlobalCurve`]s
|
|
||||||
pub global_curves: Store<GlobalCurve>,
|
|
||||||
|
|
||||||
/// Store for [`GlobalEdge`]s
|
/// Store for [`GlobalEdge`]s
|
||||||
pub global_edges: Store<GlobalEdge>,
|
pub global_edges: Store<GlobalEdge>,
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ mod wrapper;
|
|||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
objects::{
|
objects::{
|
||||||
curve::{MaybeSurfacePath, PartialCurve, PartialGlobalCurve},
|
curve::{MaybeSurfacePath, PartialCurve},
|
||||||
cycle::PartialCycle,
|
cycle::PartialCycle,
|
||||||
edge::{PartialGlobalEdge, PartialHalfEdge},
|
edge::{PartialGlobalEdge, PartialHalfEdge},
|
||||||
face::PartialFace,
|
face::PartialFace,
|
||||||
|
@ -2,7 +2,7 @@ use fj_math::Scalar;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::path::SurfacePath,
|
geometry::path::SurfacePath,
|
||||||
objects::{Curve, GlobalCurve, Objects},
|
objects::{Curve, Objects},
|
||||||
partial::{FullToPartialCache, PartialObject},
|
partial::{FullToPartialCache, PartialObject},
|
||||||
services::Service,
|
services::Service,
|
||||||
};
|
};
|
||||||
@ -61,19 +61,3 @@ impl From<SurfacePath> for MaybeSurfacePath {
|
|||||||
Self::Defined(path)
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -5,8 +5,7 @@ use fj_math::Point;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{
|
||||||
Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects,
|
Curve, GlobalEdge, GlobalVertex, HalfEdge, Objects, SurfaceVertex,
|
||||||
SurfaceVertex,
|
|
||||||
},
|
},
|
||||||
partial::{FullToPartialCache, Partial, PartialObject},
|
partial::{FullToPartialCache, Partial, PartialObject},
|
||||||
services::Service,
|
services::Service,
|
||||||
@ -84,7 +83,6 @@ impl Default for PartialHalfEdge {
|
|||||||
|
|
||||||
let global_form = Partial::from_partial(PartialGlobalEdge {
|
let global_form = Partial::from_partial(PartialGlobalEdge {
|
||||||
vertices: global_vertices,
|
vertices: global_vertices,
|
||||||
..Default::default()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -98,9 +96,6 @@ impl Default for PartialHalfEdge {
|
|||||||
/// A partial [`GlobalEdge`]
|
/// A partial [`GlobalEdge`]
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct PartialGlobalEdge {
|
pub struct PartialGlobalEdge {
|
||||||
/// The curve that defines the edge's geometry
|
|
||||||
pub curve: Partial<GlobalCurve>,
|
|
||||||
|
|
||||||
/// The vertices that bound the edge on the curve
|
/// The vertices that bound the edge on the curve
|
||||||
pub vertices: [Partial<GlobalVertex>; 2],
|
pub vertices: [Partial<GlobalVertex>; 2],
|
||||||
}
|
}
|
||||||
@ -113,7 +108,6 @@ impl PartialObject for PartialGlobalEdge {
|
|||||||
cache: &mut FullToPartialCache,
|
cache: &mut FullToPartialCache,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
curve: Partial::from_full(global_edge.curve().clone(), cache),
|
|
||||||
vertices: global_edge
|
vertices: global_edge
|
||||||
.vertices()
|
.vertices()
|
||||||
.access_in_normalized_order()
|
.access_in_normalized_order()
|
||||||
@ -122,9 +116,7 @@ impl PartialObject for PartialGlobalEdge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build(self, objects: &mut Service<Objects>) -> Self::Full {
|
fn build(self, objects: &mut Service<Objects>) -> Self::Full {
|
||||||
let curve = self.curve.build(objects);
|
|
||||||
let vertices = self.vertices.map(|vertex| vertex.build(objects));
|
let vertices = self.vertices.map(|vertex| vertex.build(objects));
|
||||||
|
GlobalEdge::new(vertices)
|
||||||
GlobalEdge::new(curve, vertices)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,6 @@ impl_trait!(
|
|||||||
Curve, PartialCurve;
|
Curve, PartialCurve;
|
||||||
Cycle, PartialCycle;
|
Cycle, PartialCycle;
|
||||||
Face, PartialFace;
|
Face, PartialFace;
|
||||||
GlobalCurve, PartialGlobalCurve;
|
|
||||||
GlobalEdge, PartialGlobalEdge;
|
GlobalEdge, PartialGlobalEdge;
|
||||||
GlobalVertex, PartialGlobalVertex;
|
GlobalVertex, PartialGlobalVertex;
|
||||||
HalfEdge, PartialHalfEdge;
|
HalfEdge, PartialHalfEdge;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::objects::{Curve, GlobalCurve};
|
use crate::objects::Curve;
|
||||||
|
|
||||||
use super::{Validate, ValidationConfig, ValidationError};
|
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>,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use fj_math::{Point, Scalar};
|
use fj_math::{Point, Scalar};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface},
|
objects::{GlobalEdge, GlobalVertex, HalfEdge, Surface},
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -30,25 +30,6 @@ impl Validate for GlobalEdge {
|
|||||||
/// [`HalfEdge`] validation failed
|
/// [`HalfEdge`] validation failed
|
||||||
#[derive(Clone, Debug, thiserror::Error)]
|
#[derive(Clone, Debug, thiserror::Error)]
|
||||||
pub enum HalfEdgeValidationError {
|
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
|
/// [`HalfEdge`]'s [`GlobalVertex`] objects do not match
|
||||||
#[error(
|
#[error(
|
||||||
"Global forms of `HalfEdge` vertices do not match vertices of \n\
|
"Global forms of `HalfEdge` vertices do not match vertices of \n\
|
||||||
|
Loading…
Reference in New Issue
Block a user