mirror of
https://github.com/hannobraun/Fornjot
synced 2025-02-12 18:26:00 +00:00
Merge pull request #1598 from hannobraun/surface
Move reference to `Surface` from `HalfEdge` to `Cycle`
This commit is contained in:
commit
06950a8e47
@ -2,6 +2,8 @@
|
|||||||
//!
|
//!
|
||||||
//! See [`CycleApprox`].
|
//! See [`CycleApprox`].
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use fj_math::Segment;
|
use fj_math::Segment;
|
||||||
|
|
||||||
use crate::objects::Cycle;
|
use crate::objects::Cycle;
|
||||||
@ -23,7 +25,10 @@ impl Approx for &Cycle {
|
|||||||
|
|
||||||
let half_edges = self
|
let half_edges = self
|
||||||
.half_edges()
|
.half_edges()
|
||||||
.map(|half_edge| half_edge.approx_with_cache(tolerance, cache))
|
.map(|half_edge| {
|
||||||
|
(half_edge, self.surface().deref())
|
||||||
|
.approx_with_cache(tolerance, cache)
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
CycleApprox { half_edges }
|
CycleApprox { half_edges }
|
||||||
|
@ -5,9 +5,10 @@
|
|||||||
//! 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::ops::Deref;
|
use crate::{
|
||||||
|
objects::{HalfEdge, Surface},
|
||||||
use crate::{objects::HalfEdge, storage::Handle};
|
storage::Handle,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
curve::{CurveApprox, CurveCache},
|
curve::{CurveApprox, CurveCache},
|
||||||
@ -15,7 +16,7 @@ use super::{
|
|||||||
Approx, ApproxPoint, Tolerance,
|
Approx, ApproxPoint, Tolerance,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Approx for &Handle<HalfEdge> {
|
impl Approx for (&Handle<HalfEdge>, &Surface) {
|
||||||
type Approximation = HalfEdgeApprox;
|
type Approximation = HalfEdgeApprox;
|
||||||
type Cache = CurveCache;
|
type Cache = CurveCache;
|
||||||
|
|
||||||
@ -24,15 +25,17 @@ impl Approx for &Handle<HalfEdge> {
|
|||||||
tolerance: impl Into<Tolerance>,
|
tolerance: impl Into<Tolerance>,
|
||||||
cache: &mut Self::Cache,
|
cache: &mut Self::Cache,
|
||||||
) -> Self::Approximation {
|
) -> Self::Approximation {
|
||||||
let boundary = self.boundary();
|
let (half_edge, surface) = self;
|
||||||
|
|
||||||
|
let boundary = half_edge.boundary();
|
||||||
let range = RangeOnPath { boundary };
|
let range = RangeOnPath { boundary };
|
||||||
|
|
||||||
let first = ApproxPoint::new(
|
let first = ApproxPoint::new(
|
||||||
self.start_vertex().position(),
|
half_edge.start_vertex().position(),
|
||||||
self.start_vertex().global_form().position(),
|
half_edge.start_vertex().global_form().position(),
|
||||||
)
|
)
|
||||||
.with_source((self.clone(), self.boundary()[0]));
|
.with_source((half_edge.clone(), half_edge.boundary()[0]));
|
||||||
let curve_approx = (self.curve(), self.surface().deref(), range)
|
let curve_approx = (half_edge.curve(), surface, range)
|
||||||
.approx_with_cache(tolerance, cache);
|
.approx_with_cache(tolerance, cache);
|
||||||
|
|
||||||
HalfEdgeApprox {
|
HalfEdgeApprox {
|
||||||
|
@ -76,7 +76,7 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
builder::{CurveBuilder, HalfEdgeBuilder},
|
builder::{CurveBuilder, HalfEdgeBuilder},
|
||||||
partial::{Partial, PartialCurve, PartialHalfEdge, PartialObject},
|
partial::{PartialCurve, PartialHalfEdge, PartialObject},
|
||||||
services::Services,
|
services::Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -86,16 +86,14 @@ mod tests {
|
|||||||
fn compute_edge_in_front_of_curve_origin() {
|
fn compute_edge_in_front_of_curve_origin() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let surface = Partial::from(services.objects.surfaces.xy_plane());
|
let surface = services.objects.surfaces.xy_plane();
|
||||||
let mut curve = PartialCurve::default();
|
let mut curve = PartialCurve::default();
|
||||||
curve.update_as_u_axis();
|
curve.update_as_u_axis();
|
||||||
let curve = curve.build(&mut services.objects);
|
let curve = curve.build(&mut services.objects);
|
||||||
let half_edge = {
|
let half_edge = {
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(
|
half_edge.update_as_line_segment_from_points([[1., -1.], [1., 1.]]);
|
||||||
surface,
|
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
[[1., -1.], [1., 1.]],
|
|
||||||
);
|
|
||||||
|
|
||||||
half_edge.build(&mut services.objects)
|
half_edge.build(&mut services.objects)
|
||||||
};
|
};
|
||||||
@ -114,16 +112,15 @@ mod tests {
|
|||||||
fn compute_edge_behind_curve_origin() {
|
fn compute_edge_behind_curve_origin() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let surface = Partial::from(services.objects.surfaces.xy_plane());
|
let surface = services.objects.surfaces.xy_plane();
|
||||||
let mut curve = PartialCurve::default();
|
let mut curve = PartialCurve::default();
|
||||||
curve.update_as_u_axis();
|
curve.update_as_u_axis();
|
||||||
let curve = curve.build(&mut services.objects);
|
let curve = curve.build(&mut services.objects);
|
||||||
let half_edge = {
|
let half_edge = {
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(
|
half_edge
|
||||||
surface,
|
.update_as_line_segment_from_points([[-1., -1.], [-1., 1.]]);
|
||||||
[[-1., -1.], [-1., 1.]],
|
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
);
|
|
||||||
|
|
||||||
half_edge.build(&mut services.objects)
|
half_edge.build(&mut services.objects)
|
||||||
};
|
};
|
||||||
@ -142,16 +139,15 @@ mod tests {
|
|||||||
fn compute_edge_parallel_to_curve() {
|
fn compute_edge_parallel_to_curve() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let surface = Partial::from(services.objects.surfaces.xy_plane());
|
let surface = services.objects.surfaces.xy_plane();
|
||||||
let mut curve = PartialCurve::default();
|
let mut curve = PartialCurve::default();
|
||||||
curve.update_as_u_axis();
|
curve.update_as_u_axis();
|
||||||
let curve = curve.build(&mut services.objects);
|
let curve = curve.build(&mut services.objects);
|
||||||
let half_edge = {
|
let half_edge = {
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(
|
half_edge
|
||||||
surface,
|
.update_as_line_segment_from_points([[-1., -1.], [1., -1.]]);
|
||||||
[[-1., -1.], [1., -1.]],
|
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
);
|
|
||||||
|
|
||||||
half_edge.build(&mut services.objects)
|
half_edge.build(&mut services.objects)
|
||||||
};
|
};
|
||||||
@ -165,16 +161,14 @@ mod tests {
|
|||||||
fn compute_edge_on_curve() {
|
fn compute_edge_on_curve() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let surface = Partial::from(services.objects.surfaces.xy_plane());
|
let surface = services.objects.surfaces.xy_plane();
|
||||||
let mut curve = PartialCurve::default();
|
let mut curve = PartialCurve::default();
|
||||||
curve.update_as_u_axis();
|
curve.update_as_u_axis();
|
||||||
let curve = curve.build(&mut services.objects);
|
let curve = curve.build(&mut services.objects);
|
||||||
let half_edge = {
|
let half_edge = {
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(
|
half_edge.update_as_line_segment_from_points([[-1., 0.], [1., 0.]]);
|
||||||
surface,
|
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
[[-1., 0.], [1., 0.]],
|
|
||||||
);
|
|
||||||
|
|
||||||
half_edge.build(&mut services.objects)
|
half_edge.build(&mut services.objects)
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,6 @@ impl Reverse for Handle<Cycle> {
|
|||||||
|
|
||||||
edges.reverse();
|
edges.reverse();
|
||||||
|
|
||||||
Cycle::new(edges).insert(objects)
|
Cycle::new(self.surface().clone(), edges).insert(objects)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ impl Reverse for Handle<HalfEdge> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HalfEdge::new(
|
HalfEdge::new(
|
||||||
self.surface().clone(),
|
|
||||||
self.curve().clone(),
|
self.curve().clone(),
|
||||||
vertices,
|
vertices,
|
||||||
self.global_form().clone(),
|
self.global_form().clone(),
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use fj_interop::{ext::ArrayExt, mesh::Color};
|
use fj_interop::{ext::ArrayExt, mesh::Color};
|
||||||
use fj_math::{Point, Scalar, Vector};
|
use fj_math::{Point, Scalar, Vector};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
builder::{CycleBuilder, HalfEdgeBuilder},
|
builder::{CycleBuilder, HalfEdgeBuilder},
|
||||||
insert::Insert,
|
insert::Insert,
|
||||||
objects::{Face, HalfEdge, Objects},
|
objects::{Face, HalfEdge, Objects, Surface},
|
||||||
partial::{Partial, PartialFace, PartialObject},
|
partial::{Partial, PartialFace, PartialObject},
|
||||||
services::Service,
|
services::Service,
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
@ -14,7 +12,7 @@ use crate::{
|
|||||||
|
|
||||||
use super::{Sweep, SweepCache};
|
use super::{Sweep, SweepCache};
|
||||||
|
|
||||||
impl Sweep for (Handle<HalfEdge>, Color) {
|
impl Sweep for (Handle<HalfEdge>, &Surface, Color) {
|
||||||
type Swept = (Handle<Face>, Handle<HalfEdge>);
|
type Swept = (Handle<Face>, Handle<HalfEdge>);
|
||||||
|
|
||||||
fn sweep_with_cache(
|
fn sweep_with_cache(
|
||||||
@ -23,7 +21,7 @@ impl Sweep for (Handle<HalfEdge>, Color) {
|
|||||||
cache: &mut SweepCache,
|
cache: &mut SweepCache,
|
||||||
objects: &mut Service<Objects>,
|
objects: &mut Service<Objects>,
|
||||||
) -> Self::Swept {
|
) -> Self::Swept {
|
||||||
let (edge, color) = self;
|
let (edge, surface, color) = self;
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
|
|
||||||
// The result of sweeping an edge is a face. Let's create that.
|
// The result of sweeping an edge is a face. Let's create that.
|
||||||
@ -36,7 +34,7 @@ impl Sweep for (Handle<HalfEdge>, Color) {
|
|||||||
// be created by sweeping a curve, so let's sweep the curve of the edge
|
// be created by sweeping a curve, so let's sweep the curve of the edge
|
||||||
// we're sweeping.
|
// we're sweeping.
|
||||||
face.exterior.write().surface = Partial::from(
|
face.exterior.write().surface = Partial::from(
|
||||||
(edge.curve().clone(), edge.surface().deref())
|
(edge.curve().clone(), surface)
|
||||||
.sweep_with_cache(path, cache, objects),
|
.sweep_with_cache(path, cache, objects),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -150,6 +148,8 @@ impl Sweep for (Handle<HalfEdge>, Color) {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use fj_interop::{ext::ArrayExt, mesh::Color};
|
use fj_interop::{ext::ArrayExt, mesh::Color};
|
||||||
use fj_math::Point;
|
use fj_math::Point;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
@ -168,38 +168,33 @@ mod tests {
|
|||||||
fn sweep() {
|
fn sweep() {
|
||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
|
let surface = services.objects.surfaces.xy_plane();
|
||||||
|
|
||||||
let half_edge = {
|
let half_edge = {
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(
|
half_edge.update_as_line_segment_from_points([[0., 0.], [1., 0.]]);
|
||||||
services.objects.surfaces.xy_plane(),
|
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
[[0., 0.], [1., 0.]],
|
|
||||||
);
|
|
||||||
|
|
||||||
half_edge
|
half_edge
|
||||||
.build(&mut services.objects)
|
.build(&mut services.objects)
|
||||||
.insert(&mut services.objects)
|
.insert(&mut services.objects)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (face, _) = (half_edge, Color::default())
|
let (face, _) = (half_edge, surface.deref(), Color::default())
|
||||||
.sweep([0., 0., 1.], &mut services.objects);
|
.sweep([0., 0., 1.], &mut services.objects);
|
||||||
|
|
||||||
let expected_face = {
|
let expected_face = {
|
||||||
let surface = Partial::from(services.objects.surfaces.xz_plane());
|
let surface = services.objects.surfaces.xz_plane();
|
||||||
|
|
||||||
let bottom = {
|
let bottom = {
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(
|
half_edge
|
||||||
surface.clone(),
|
.update_as_line_segment_from_points([[0., 0.], [1., 0.]]);
|
||||||
[[0., 0.], [1., 0.]],
|
|
||||||
);
|
|
||||||
|
|
||||||
half_edge
|
half_edge
|
||||||
};
|
};
|
||||||
let side_up = {
|
let side_up = {
|
||||||
let mut side_up = PartialHalfEdge {
|
let mut side_up = PartialHalfEdge::default();
|
||||||
surface: surface.clone(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let [back, front] = side_up
|
let [back, front] = side_up
|
||||||
@ -219,10 +214,7 @@ mod tests {
|
|||||||
side_up
|
side_up
|
||||||
};
|
};
|
||||||
let top = {
|
let top = {
|
||||||
let mut top = PartialHalfEdge {
|
let mut top = PartialHalfEdge::default();
|
||||||
surface: surface.clone(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let [(back, back_surface), (front, front_surface)] =
|
let [(back, back_surface), (front, front_surface)] =
|
||||||
@ -238,6 +230,7 @@ mod tests {
|
|||||||
|
|
||||||
top.infer_global_form();
|
top.infer_global_form();
|
||||||
top.update_as_line_segment();
|
top.update_as_line_segment();
|
||||||
|
top.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
|
|
||||||
Partial::from(
|
Partial::from(
|
||||||
top.build(&mut services.objects)
|
top.build(&mut services.objects)
|
||||||
@ -247,10 +240,7 @@ mod tests {
|
|||||||
.clone()
|
.clone()
|
||||||
};
|
};
|
||||||
let side_down = {
|
let side_down = {
|
||||||
let mut side_down = PartialHalfEdge {
|
let mut side_down = PartialHalfEdge::default();
|
||||||
surface,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let [(back, back_surface), (front, front_surface)] =
|
let [(back, back_surface), (front, front_surface)] =
|
||||||
side_down.vertices.each_mut_ext();
|
side_down.vertices.each_mut_ext();
|
||||||
@ -263,6 +253,8 @@ mod tests {
|
|||||||
|
|
||||||
side_down.infer_global_form();
|
side_down.infer_global_form();
|
||||||
side_down.update_as_line_segment();
|
side_down.update_as_line_segment();
|
||||||
|
side_down
|
||||||
|
.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
|
|
||||||
Partial::from(
|
Partial::from(
|
||||||
side_down
|
side_down
|
||||||
@ -273,7 +265,10 @@ mod tests {
|
|||||||
.clone()
|
.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut cycle = PartialCycle::default();
|
let mut cycle = PartialCycle {
|
||||||
|
surface: Partial::from(surface),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
cycle.half_edges.extend(
|
cycle.half_edges.extend(
|
||||||
[bottom, side_up, top, side_down].map(Partial::from_partial),
|
[bottom, side_up, top, side_down].map(Partial::from_partial),
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use fj_interop::ext::ArrayExt;
|
use fj_interop::ext::ArrayExt;
|
||||||
use fj_math::{Scalar, Vector};
|
use fj_math::{Scalar, Vector};
|
||||||
|
|
||||||
@ -70,7 +72,8 @@ impl Sweep for Handle<Face> {
|
|||||||
let mut original_edges = Vec::new();
|
let mut original_edges = Vec::new();
|
||||||
let mut top_edges = Vec::new();
|
let mut top_edges = Vec::new();
|
||||||
for half_edge in cycle.half_edges().cloned() {
|
for half_edge in cycle.half_edges().cloned() {
|
||||||
let (face, top_edge) = (half_edge.clone(), self.color())
|
let (face, top_edge) =
|
||||||
|
(half_edge.clone(), self.surface().deref(), self.color())
|
||||||
.sweep_with_cache(path, cache, objects);
|
.sweep_with_cache(path, cache, objects);
|
||||||
|
|
||||||
faces.push(face);
|
faces.push(face);
|
||||||
@ -128,6 +131,8 @@ impl Sweep for Handle<Face> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use fj_interop::{ext::SliceExt, mesh::Color};
|
use fj_interop::{ext::SliceExt, mesh::Color};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -182,7 +187,7 @@ mod tests {
|
|||||||
.reverse(&mut services.objects);
|
.reverse(&mut services.objects);
|
||||||
let mut top = PartialFace::default();
|
let mut top = PartialFace::default();
|
||||||
top.exterior.write().surface =
|
top.exterior.write().surface =
|
||||||
Partial::from(surface.translate(UP, &mut services.objects));
|
Partial::from(surface.clone().translate(UP, &mut services.objects));
|
||||||
top.exterior.write().update_as_polygon_from_points(TRIANGLE);
|
top.exterior.write().update_as_polygon_from_points(TRIANGLE);
|
||||||
let top = top
|
let top = top
|
||||||
.build(&mut services.objects)
|
.build(&mut services.objects)
|
||||||
@ -195,17 +200,16 @@ mod tests {
|
|||||||
let side_faces = triangle.array_windows_ext().map(|&[a, b]| {
|
let side_faces = triangle.array_windows_ext().map(|&[a, b]| {
|
||||||
let half_edge = {
|
let half_edge = {
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(
|
half_edge.update_as_line_segment_from_points([a, b]);
|
||||||
services.objects.surfaces.xy_plane(),
|
half_edge
|
||||||
[a, b],
|
.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
);
|
|
||||||
|
|
||||||
half_edge
|
half_edge
|
||||||
.build(&mut services.objects)
|
.build(&mut services.objects)
|
||||||
.insert(&mut services.objects)
|
.insert(&mut services.objects)
|
||||||
};
|
};
|
||||||
let (face, _) =
|
let (face, _) = (half_edge, surface.deref(), Color::default())
|
||||||
(half_edge, Color::default()).sweep(UP, &mut services.objects);
|
.sweep(UP, &mut services.objects);
|
||||||
face
|
face
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -250,7 +254,7 @@ mod tests {
|
|||||||
.insert(&mut services.objects)
|
.insert(&mut services.objects)
|
||||||
.reverse(&mut services.objects);
|
.reverse(&mut services.objects);
|
||||||
let mut top = PartialFace::default();
|
let mut top = PartialFace::default();
|
||||||
top.exterior.write().surface = Partial::from(surface);
|
top.exterior.write().surface = Partial::from(surface.clone());
|
||||||
top.exterior.write().update_as_polygon_from_points(TRIANGLE);
|
top.exterior.write().update_as_polygon_from_points(TRIANGLE);
|
||||||
let top = top
|
let top = top
|
||||||
.build(&mut services.objects)
|
.build(&mut services.objects)
|
||||||
@ -263,17 +267,16 @@ mod tests {
|
|||||||
let side_faces = triangle.array_windows_ext().map(|&[a, b]| {
|
let side_faces = triangle.array_windows_ext().map(|&[a, b]| {
|
||||||
let half_edge = {
|
let half_edge = {
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(
|
half_edge.update_as_line_segment_from_points([a, b]);
|
||||||
services.objects.surfaces.xy_plane(),
|
half_edge
|
||||||
[a, b],
|
.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
);
|
|
||||||
|
|
||||||
half_edge
|
half_edge
|
||||||
.build(&mut services.objects)
|
.build(&mut services.objects)
|
||||||
.insert(&mut services.objects)
|
.insert(&mut services.objects)
|
||||||
.reverse(&mut services.objects)
|
.reverse(&mut services.objects)
|
||||||
};
|
};
|
||||||
let (face, _) = (half_edge, Color::default())
|
let (face, _) = (half_edge, surface.deref(), Color::default())
|
||||||
.sweep(DOWN, &mut services.objects);
|
.sweep(DOWN, &mut services.objects);
|
||||||
face
|
face
|
||||||
});
|
});
|
||||||
|
@ -14,12 +14,16 @@ impl TransformObject for Cycle {
|
|||||||
objects: &mut Service<Objects>,
|
objects: &mut Service<Objects>,
|
||||||
cache: &mut TransformCache,
|
cache: &mut TransformCache,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let surface = self
|
||||||
|
.surface()
|
||||||
|
.clone()
|
||||||
|
.transform_with_cache(transform, objects, cache);
|
||||||
let half_edges = self.half_edges().map(|half_edge| {
|
let half_edges = self.half_edges().map(|half_edge| {
|
||||||
half_edge
|
half_edge
|
||||||
.clone()
|
.clone()
|
||||||
.transform_with_cache(transform, objects, cache)
|
.transform_with_cache(transform, objects, cache)
|
||||||
});
|
});
|
||||||
|
|
||||||
Self::new(half_edges)
|
Self::new(surface, half_edges)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,6 @@ impl TransformObject for HalfEdge {
|
|||||||
objects: &mut Service<Objects>,
|
objects: &mut Service<Objects>,
|
||||||
cache: &mut TransformCache,
|
cache: &mut TransformCache,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let surface = self
|
|
||||||
.surface()
|
|
||||||
.clone()
|
|
||||||
.transform_with_cache(transform, objects, cache);
|
|
||||||
let curve = self
|
let curve = self
|
||||||
.curve()
|
.curve()
|
||||||
.clone()
|
.clone()
|
||||||
@ -36,7 +32,7 @@ impl TransformObject for HalfEdge {
|
|||||||
.clone()
|
.clone()
|
||||||
.transform_with_cache(transform, objects, cache);
|
.transform_with_cache(transform, objects, cache);
|
||||||
|
|
||||||
Self::new(surface, curve, boundary, global_form)
|
Self::new(curve, boundary, global_form)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +149,6 @@ impl CycleBuilder for PartialCycle {
|
|||||||
|
|
||||||
let [_, vertex] = &mut new_half_edge.vertices;
|
let [_, vertex] = &mut new_half_edge.vertices;
|
||||||
vertex.1 = shared_surface_vertex;
|
vertex.1 = shared_surface_vertex;
|
||||||
new_half_edge.surface = self.surface.clone();
|
|
||||||
new_half_edge.infer_global_form();
|
new_half_edge.infer_global_form();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +246,8 @@ impl CycleBuilder for PartialCycle {
|
|||||||
let mut this = half_edges.pop_front().expect(
|
let mut this = half_edges.pop_front().expect(
|
||||||
"Pushed correct number of half-edges; should be able to pop",
|
"Pushed correct number of half-edges; should be able to pop",
|
||||||
);
|
);
|
||||||
this.write().update_from_other_edge(&other);
|
this.write()
|
||||||
|
.update_from_other_edge(&other, &self.surface.read().geometry);
|
||||||
this
|
this
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -261,7 +261,8 @@ impl CycleBuilder for PartialCycle {
|
|||||||
{
|
{
|
||||||
edges.map(|other| {
|
edges.map(|other| {
|
||||||
let mut this = self.add_half_edge();
|
let mut this = self.add_half_edge();
|
||||||
this.write().update_from_other_edge(&other);
|
this.write()
|
||||||
|
.update_from_other_edge(&other, &self.surface.read().geometry);
|
||||||
this
|
this
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,11 @@ use fj_interop::ext::ArrayExt;
|
|||||||
use fj_math::{Point, Scalar};
|
use fj_math::{Point, Scalar};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::path::{GlobalPath, SurfacePath},
|
geometry::{
|
||||||
objects::{GlobalEdge, HalfEdge, Surface},
|
path::{GlobalPath, SurfacePath},
|
||||||
|
surface::SurfaceGeometry,
|
||||||
|
},
|
||||||
|
objects::{GlobalEdge, HalfEdge},
|
||||||
partial::{MaybeSurfacePath, Partial, PartialGlobalEdge, PartialHalfEdge},
|
partial::{MaybeSurfacePath, Partial, PartialGlobalEdge, PartialHalfEdge},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -25,7 +28,6 @@ pub trait HalfEdgeBuilder {
|
|||||||
/// Update partial half-edge to be a line segment, from the given points
|
/// Update partial half-edge to be a line segment, from the given points
|
||||||
fn update_as_line_segment_from_points(
|
fn update_as_line_segment_from_points(
|
||||||
&mut self,
|
&mut self,
|
||||||
surface: impl Into<Partial<Surface>>,
|
|
||||||
points: [impl Into<Point<2>>; 2],
|
points: [impl Into<Point<2>>; 2],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -38,6 +40,12 @@ pub trait HalfEdgeBuilder {
|
|||||||
/// it.
|
/// it.
|
||||||
fn infer_global_form(&mut self) -> Partial<GlobalEdge>;
|
fn infer_global_form(&mut self) -> Partial<GlobalEdge>;
|
||||||
|
|
||||||
|
/// Infer the vertex positions (surface and global), if not already set
|
||||||
|
fn infer_vertex_positions_if_necessary(
|
||||||
|
&mut self,
|
||||||
|
surface: &SurfaceGeometry,
|
||||||
|
);
|
||||||
|
|
||||||
/// Update this edge from another
|
/// Update this edge from another
|
||||||
///
|
///
|
||||||
/// Infers as much information about this edge from the other, under the
|
/// Infers as much information about this edge from the other, under the
|
||||||
@ -47,7 +55,11 @@ pub trait HalfEdgeBuilder {
|
|||||||
/// under various circumstances. As long as you're only dealing with lines
|
/// under various circumstances. As long as you're only dealing with lines
|
||||||
/// and planes, you should be fine. Otherwise, please read the code of this
|
/// and planes, you should be fine. Otherwise, please read the code of this
|
||||||
/// method carefully, to make sure you don't run into trouble.
|
/// method carefully, to make sure you don't run into trouble.
|
||||||
fn update_from_other_edge(&mut self, other: &Partial<HalfEdge>);
|
fn update_from_other_edge(
|
||||||
|
&mut self,
|
||||||
|
other: &Partial<HalfEdge>,
|
||||||
|
surface: &Option<SurfaceGeometry>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HalfEdgeBuilder for PartialHalfEdge {
|
impl HalfEdgeBuilder for PartialHalfEdge {
|
||||||
@ -111,11 +123,8 @@ impl HalfEdgeBuilder for PartialHalfEdge {
|
|||||||
|
|
||||||
fn update_as_line_segment_from_points(
|
fn update_as_line_segment_from_points(
|
||||||
&mut self,
|
&mut self,
|
||||||
surface: impl Into<Partial<Surface>>,
|
|
||||||
points: [impl Into<Point<2>>; 2],
|
points: [impl Into<Point<2>>; 2],
|
||||||
) {
|
) {
|
||||||
self.surface = surface.into();
|
|
||||||
|
|
||||||
for (vertex, point) in self.vertices.each_mut_ext().zip_ext(points) {
|
for (vertex, point) in self.vertices.each_mut_ext().zip_ext(points) {
|
||||||
let mut surface_form = vertex.1.write();
|
let mut surface_form = vertex.1.write();
|
||||||
surface_form.position = Some(point.into());
|
surface_form.position = Some(point.into());
|
||||||
@ -166,14 +175,62 @@ impl HalfEdgeBuilder for PartialHalfEdge {
|
|||||||
self.global_form.clone()
|
self.global_form.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_from_other_edge(&mut self, other: &Partial<HalfEdge>) {
|
fn infer_vertex_positions_if_necessary(
|
||||||
|
&mut self,
|
||||||
|
surface: &SurfaceGeometry,
|
||||||
|
) {
|
||||||
|
let path = self
|
||||||
|
.curve
|
||||||
|
.read()
|
||||||
|
.path
|
||||||
|
.expect("Can't infer vertex positions without curve");
|
||||||
|
let MaybeSurfacePath::Defined(path) = path else {
|
||||||
|
panic!("Can't infer vertex positions with undefined path");
|
||||||
|
};
|
||||||
|
|
||||||
|
for vertex in &mut self.vertices {
|
||||||
|
let position_curve = vertex
|
||||||
|
.0
|
||||||
|
.expect("Can't infer surface position without curve position");
|
||||||
|
|
||||||
|
let position_surface = vertex.1.read().position;
|
||||||
|
|
||||||
|
// Infer surface position, if not available.
|
||||||
|
let position_surface = match position_surface {
|
||||||
|
Some(position_surface) => position_surface,
|
||||||
|
None => {
|
||||||
|
let position_surface =
|
||||||
|
path.point_from_path_coords(position_curve);
|
||||||
|
|
||||||
|
vertex.1.write().position = Some(position_surface);
|
||||||
|
|
||||||
|
position_surface
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Infer global position, if not available.
|
||||||
|
let position_global = vertex.1.read().global_form.read().position;
|
||||||
|
if position_global.is_none() {
|
||||||
|
let position_global =
|
||||||
|
surface.point_from_surface_coords(position_surface);
|
||||||
|
vertex.1.write().global_form.write().position =
|
||||||
|
Some(position_global);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_from_other_edge(
|
||||||
|
&mut self,
|
||||||
|
other: &Partial<HalfEdge>,
|
||||||
|
surface: &Option<SurfaceGeometry>,
|
||||||
|
) {
|
||||||
let global_curve = other.read().curve.read().global_form.clone();
|
let global_curve = other.read().curve.read().global_form.clone();
|
||||||
self.curve.write().global_form = global_curve.clone();
|
self.curve.write().global_form = global_curve.clone();
|
||||||
self.global_form.write().curve = global_curve;
|
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| {
|
||||||
match other.read().surface.read().geometry {
|
match surface {
|
||||||
Some(surface) => {
|
Some(surface) => {
|
||||||
// We have information about the other edge's surface
|
// We have information about the other edge's surface
|
||||||
// available. We need to use that to interpret what the
|
// available. We need to use that to interpret what the
|
||||||
|
@ -12,6 +12,7 @@ use crate::{
|
|||||||
/// A cycle of connected half-edges
|
/// A cycle of connected half-edges
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct Cycle {
|
pub struct Cycle {
|
||||||
|
surface: Handle<Surface>,
|
||||||
half_edges: Vec<Handle<HalfEdge>>,
|
half_edges: Vec<Handle<HalfEdge>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,7 +22,10 @@ impl Cycle {
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics, if `half_edges` does not yield at least one half-edge.
|
/// Panics, if `half_edges` does not yield at least one half-edge.
|
||||||
pub fn new(half_edges: impl IntoIterator<Item = Handle<HalfEdge>>) -> Self {
|
pub fn new(
|
||||||
|
surface: Handle<Surface>,
|
||||||
|
half_edges: impl IntoIterator<Item = Handle<HalfEdge>>,
|
||||||
|
) -> Self {
|
||||||
let half_edges = half_edges.into_iter().collect::<Vec<_>>();
|
let half_edges = half_edges.into_iter().collect::<Vec<_>>();
|
||||||
|
|
||||||
// This is not a validation check, and thus not part of the validation
|
// This is not a validation check, and thus not part of the validation
|
||||||
@ -33,18 +37,15 @@ impl Cycle {
|
|||||||
"Cycle must contain at least one half-edge"
|
"Cycle must contain at least one half-edge"
|
||||||
);
|
);
|
||||||
|
|
||||||
Self { half_edges }
|
Self {
|
||||||
|
surface,
|
||||||
|
half_edges,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the surface that the cycle is in
|
/// Access the surface that the cycle is in
|
||||||
pub fn surface(&self) -> &Handle<Surface> {
|
pub fn surface(&self) -> &Handle<Surface> {
|
||||||
if let Some(half_edge) = self.half_edges.first() {
|
&self.surface
|
||||||
return half_edge.surface();
|
|
||||||
}
|
|
||||||
|
|
||||||
unreachable!(
|
|
||||||
"Cycle has no half-edges, which the constructor should prevent."
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the half-edges that make up the cycle
|
/// Access the half-edges that make up the cycle
|
||||||
|
@ -4,14 +4,13 @@ use fj_interop::ext::ArrayExt;
|
|||||||
use fj_math::Point;
|
use fj_math::Point;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{Curve, GlobalCurve, GlobalVertex, Surface, SurfaceVertex},
|
objects::{Curve, GlobalCurve, GlobalVertex, SurfaceVertex},
|
||||||
storage::{Handle, HandleWrapper},
|
storage::{Handle, HandleWrapper},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A half-edge
|
/// A half-edge
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||||
pub struct HalfEdge {
|
pub struct HalfEdge {
|
||||||
surface: Handle<Surface>,
|
|
||||||
curve: Handle<Curve>,
|
curve: Handle<Curve>,
|
||||||
boundary: [(Point<1>, Handle<SurfaceVertex>); 2],
|
boundary: [(Point<1>, Handle<SurfaceVertex>); 2],
|
||||||
global_form: Handle<GlobalEdge>,
|
global_form: Handle<GlobalEdge>,
|
||||||
@ -20,24 +19,17 @@ pub struct HalfEdge {
|
|||||||
impl HalfEdge {
|
impl HalfEdge {
|
||||||
/// Create an instance of `HalfEdge`
|
/// Create an instance of `HalfEdge`
|
||||||
pub fn new(
|
pub fn new(
|
||||||
surface: Handle<Surface>,
|
|
||||||
curve: Handle<Curve>,
|
curve: Handle<Curve>,
|
||||||
boundary: [(Point<1>, Handle<SurfaceVertex>); 2],
|
boundary: [(Point<1>, Handle<SurfaceVertex>); 2],
|
||||||
global_form: Handle<GlobalEdge>,
|
global_form: Handle<GlobalEdge>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
surface,
|
|
||||||
curve,
|
curve,
|
||||||
boundary,
|
boundary,
|
||||||
global_form,
|
global_form,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Access the surface that the half-edge is defined in
|
|
||||||
pub fn surface(&self) -> &Handle<Surface> {
|
|
||||||
&self.surface
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Access the curve that defines the half-edge's geometry
|
/// Access the curve that defines the half-edge's geometry
|
||||||
pub fn curve(&self) -> &Handle<Curve> {
|
pub fn curve(&self) -> &Handle<Curve> {
|
||||||
&self.curve
|
&self.curve
|
||||||
@ -173,14 +165,15 @@ mod tests {
|
|||||||
|
|
||||||
let a_to_b = {
|
let a_to_b = {
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge
|
half_edge.update_as_line_segment_from_points([a, b]);
|
||||||
.update_as_line_segment_from_points(surface.clone(), [a, b]);
|
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
|
|
||||||
half_edge.build(&mut services.objects)
|
half_edge.build(&mut services.objects)
|
||||||
};
|
};
|
||||||
let b_to_a = {
|
let b_to_a = {
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(surface, [b, a]);
|
half_edge.update_as_line_segment_from_points([b, a]);
|
||||||
|
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
|
|
||||||
half_edge.build(&mut services.objects)
|
half_edge.build(&mut services.objects)
|
||||||
};
|
};
|
||||||
|
@ -46,7 +46,7 @@ impl PartialObject for PartialCurve {
|
|||||||
///
|
///
|
||||||
/// Can be a fully defined [`SurfacePath`], or just the type of path might be
|
/// Can be a fully defined [`SurfacePath`], or just the type of path might be
|
||||||
/// known.
|
/// known.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum MaybeSurfacePath {
|
pub enum MaybeSurfacePath {
|
||||||
/// The surface path is fully defined
|
/// The surface path is fully defined
|
||||||
Defined(SurfacePath),
|
Defined(SurfacePath),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
builder::HalfEdgeBuilder,
|
||||||
objects::{Cycle, HalfEdge, Objects, Surface},
|
objects::{Cycle, HalfEdge, Objects, Surface},
|
||||||
partial::{FullToPartialCache, Partial, PartialObject},
|
partial::{FullToPartialCache, Partial, PartialObject},
|
||||||
services::Service,
|
services::Service,
|
||||||
@ -29,11 +30,15 @@ impl PartialObject for PartialCycle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build(self, objects: &mut Service<Objects>) -> Self::Full {
|
fn build(self, objects: &mut Service<Objects>) -> Self::Full {
|
||||||
let half_edges = self
|
let surface = self.surface.build(objects);
|
||||||
.half_edges
|
let surface_geometry = surface.geometry();
|
||||||
.into_iter()
|
let half_edges = self.half_edges.into_iter().map(|mut half_edge| {
|
||||||
.map(|half_edge| half_edge.build(objects));
|
half_edge
|
||||||
|
.write()
|
||||||
|
.infer_vertex_positions_if_necessary(&surface_geometry);
|
||||||
|
half_edge.build(objects)
|
||||||
|
});
|
||||||
|
|
||||||
Cycle::new(half_edges)
|
Cycle::new(surface, half_edges)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ use fj_math::Point;
|
|||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{
|
||||||
Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects,
|
Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects,
|
||||||
Surface, SurfaceVertex,
|
SurfaceVertex,
|
||||||
},
|
},
|
||||||
partial::{FullToPartialCache, Partial, PartialObject},
|
partial::{FullToPartialCache, Partial, PartialObject},
|
||||||
services::Service,
|
services::Service,
|
||||||
@ -15,9 +15,6 @@ use crate::{
|
|||||||
/// A partial [`HalfEdge`]
|
/// A partial [`HalfEdge`]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PartialHalfEdge {
|
pub struct PartialHalfEdge {
|
||||||
/// The surface that the half-edge is defined in
|
|
||||||
pub surface: Partial<Surface>,
|
|
||||||
|
|
||||||
/// The curve that the half-edge is defined in
|
/// The curve that the half-edge is defined in
|
||||||
pub curve: Partial<Curve>,
|
pub curve: Partial<Curve>,
|
||||||
|
|
||||||
@ -36,7 +33,6 @@ impl PartialObject for PartialHalfEdge {
|
|||||||
cache: &mut FullToPartialCache,
|
cache: &mut FullToPartialCache,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
surface: Partial::from_full(half_edge.surface().clone(), cache),
|
|
||||||
curve: Partial::from_full(half_edge.curve().clone(), cache),
|
curve: Partial::from_full(half_edge.curve().clone(), cache),
|
||||||
vertices: half_edge
|
vertices: half_edge
|
||||||
.boundary()
|
.boundary()
|
||||||
@ -55,53 +51,23 @@ impl PartialObject for PartialHalfEdge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build(self, objects: &mut Service<Objects>) -> Self::Full {
|
fn build(self, objects: &mut Service<Objects>) -> Self::Full {
|
||||||
let surface = self.surface.build(objects);
|
|
||||||
let curve = self.curve.build(objects);
|
let curve = self.curve.build(objects);
|
||||||
let vertices = self.vertices.map(|mut vertex| {
|
let vertices = self.vertices.map(|vertex| {
|
||||||
let position_surface = vertex.1.read().position;
|
let position_curve = vertex
|
||||||
|
.0
|
||||||
// Infer surface position, if not available.
|
.expect("Can't build `HalfEdge` without boundary positions");
|
||||||
let position_surface = match position_surface {
|
|
||||||
Some(position_surface) => position_surface,
|
|
||||||
None => {
|
|
||||||
let position_curve = vertex.0.expect(
|
|
||||||
"Can't infer surface position without curve position",
|
|
||||||
);
|
|
||||||
let position_surface =
|
|
||||||
curve.path().point_from_path_coords(position_curve);
|
|
||||||
|
|
||||||
vertex.1.write().position = Some(position_surface);
|
|
||||||
|
|
||||||
position_surface
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Infer global position, if not available.
|
|
||||||
let position_global = vertex.1.read().global_form.read().position;
|
|
||||||
if position_global.is_none() {
|
|
||||||
let position_global = surface
|
|
||||||
.geometry()
|
|
||||||
.point_from_surface_coords(position_surface);
|
|
||||||
vertex.1.write().global_form.write().position =
|
|
||||||
Some(position_global);
|
|
||||||
}
|
|
||||||
|
|
||||||
let position =
|
|
||||||
vertex.0.expect("Can't build `Vertex` without position");
|
|
||||||
let surface_form = vertex.1.build(objects);
|
let surface_form = vertex.1.build(objects);
|
||||||
|
|
||||||
(position, surface_form)
|
(position_curve, surface_form)
|
||||||
});
|
});
|
||||||
let global_form = self.global_form.build(objects);
|
let global_form = self.global_form.build(objects);
|
||||||
|
|
||||||
HalfEdge::new(surface, curve, vertices, global_form)
|
HalfEdge::new(curve, vertices, global_form)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PartialHalfEdge {
|
impl Default for PartialHalfEdge {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let surface = Partial::new();
|
|
||||||
|
|
||||||
let curve = Partial::<Curve>::new();
|
let curve = Partial::<Curve>::new();
|
||||||
let vertices = array::from_fn(|_| {
|
let vertices = array::from_fn(|_| {
|
||||||
let surface_form = Partial::default();
|
let surface_form = Partial::default();
|
||||||
@ -123,7 +89,6 @@ impl Default for PartialHalfEdge {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
surface,
|
|
||||||
curve,
|
curve,
|
||||||
vertices,
|
vertices,
|
||||||
global_form,
|
global_form,
|
||||||
|
@ -17,6 +17,7 @@ impl Validate for Cycle {
|
|||||||
) {
|
) {
|
||||||
CycleValidationError::check_half_edge_connections(self, errors);
|
CycleValidationError::check_half_edge_connections(self, errors);
|
||||||
CycleValidationError::check_half_edge_boundaries(self, config, errors);
|
CycleValidationError::check_half_edge_boundaries(self, config, errors);
|
||||||
|
CycleValidationError::check_vertex_positions(self, config, errors);
|
||||||
|
|
||||||
// We don't need to check that all half-edges are defined in the same
|
// We don't need to check that all half-edges are defined in the same
|
||||||
// surface. We already check that they are connected by identical
|
// surface. We already check that they are connected by identical
|
||||||
@ -70,6 +71,33 @@ pub enum CycleValidationError {
|
|||||||
/// The half-edge
|
/// The half-edge
|
||||||
half_edge: Handle<HalfEdge>,
|
half_edge: Handle<HalfEdge>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Mismatch between [`SurfaceVertex`] and `GlobalVertex` positions
|
||||||
|
#[error(
|
||||||
|
"`SurfaceVertex` position doesn't match position of its global form\n\
|
||||||
|
- Surface position: {surface_position:?}\n\
|
||||||
|
- Surface position converted to global position: \
|
||||||
|
{surface_position_as_global:?}\n\
|
||||||
|
- Global position: {global_position:?}\n\
|
||||||
|
- Distance between the positions: {distance}\n\
|
||||||
|
- `SurfaceVertex`: {surface_vertex:#?}"
|
||||||
|
)]
|
||||||
|
VertexSurfacePositionMismatch {
|
||||||
|
/// The position of the surface vertex
|
||||||
|
surface_position: Point<2>,
|
||||||
|
|
||||||
|
/// The surface position converted into a global position
|
||||||
|
surface_position_as_global: Point<3>,
|
||||||
|
|
||||||
|
/// The position of the global vertex
|
||||||
|
global_position: Point<3>,
|
||||||
|
|
||||||
|
/// The distance between the positions
|
||||||
|
distance: Scalar,
|
||||||
|
|
||||||
|
/// The surface vertex
|
||||||
|
surface_vertex: Handle<SurfaceVertex>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CycleValidationError {
|
impl CycleValidationError {
|
||||||
@ -130,15 +158,49 @@ impl CycleValidationError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_vertex_positions(
|
||||||
|
cycle: &Cycle,
|
||||||
|
config: &ValidationConfig,
|
||||||
|
errors: &mut Vec<ValidationError>,
|
||||||
|
) {
|
||||||
|
for half_edge in cycle.half_edges() {
|
||||||
|
for surface_vertex in half_edge.surface_vertices() {
|
||||||
|
let surface_position_as_global = cycle
|
||||||
|
.surface()
|
||||||
|
.geometry()
|
||||||
|
.point_from_surface_coords(surface_vertex.position());
|
||||||
|
let global_position = surface_vertex.global_form().position();
|
||||||
|
|
||||||
|
let distance =
|
||||||
|
surface_position_as_global.distance_to(&global_position);
|
||||||
|
|
||||||
|
if distance > config.identical_max_distance {
|
||||||
|
errors.push(
|
||||||
|
Self::VertexSurfacePositionMismatch {
|
||||||
|
surface_position: surface_vertex.position(),
|
||||||
|
surface_position_as_global,
|
||||||
|
global_position,
|
||||||
|
distance,
|
||||||
|
surface_vertex: surface_vertex.clone(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use fj_math::Point;
|
use fj_interop::ext::ArrayExt;
|
||||||
|
use fj_math::{Point, Scalar, Vector};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
builder::CycleBuilder,
|
builder::{CycleBuilder, HalfEdgeBuilder},
|
||||||
objects::Cycle,
|
insert::Insert,
|
||||||
|
objects::{Cycle, HalfEdge, SurfaceVertex},
|
||||||
partial::{Partial, PartialCycle, PartialObject},
|
partial::{Partial, PartialCycle, PartialObject},
|
||||||
services::Services,
|
services::Services,
|
||||||
validate::Validate,
|
validate::Validate,
|
||||||
@ -176,7 +238,7 @@ mod tests {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|half_edge| half_edge.build(&mut services.objects));
|
.map(|half_edge| half_edge.build(&mut services.objects));
|
||||||
|
|
||||||
Cycle::new(half_edges)
|
Cycle::new(valid.surface().clone(), half_edges)
|
||||||
};
|
};
|
||||||
|
|
||||||
valid.validate_and_return_first_error()?;
|
valid.validate_and_return_first_error()?;
|
||||||
@ -212,7 +274,69 @@ mod tests {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|half_edge| half_edge.build(&mut services.objects));
|
.map(|half_edge| half_edge.build(&mut services.objects));
|
||||||
|
|
||||||
Cycle::new(half_edges)
|
Cycle::new(valid.surface().clone(), half_edges)
|
||||||
|
};
|
||||||
|
|
||||||
|
valid.validate_and_return_first_error()?;
|
||||||
|
assert!(invalid.validate_and_return_first_error().is_err());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn surface_vertex_position_mismatch() -> anyhow::Result<()> {
|
||||||
|
let mut services = Services::new();
|
||||||
|
|
||||||
|
let valid = {
|
||||||
|
let surface = services.objects.surfaces.xy_plane();
|
||||||
|
|
||||||
|
let mut cycle = PartialCycle {
|
||||||
|
surface: Partial::from(surface.clone()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut half_edge = cycle.add_half_edge();
|
||||||
|
half_edge.write().update_as_circle_from_radius(1.);
|
||||||
|
half_edge
|
||||||
|
.write()
|
||||||
|
.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
|
|
||||||
|
cycle.build(&mut services.objects)
|
||||||
|
};
|
||||||
|
let invalid = {
|
||||||
|
let half_edge = {
|
||||||
|
let half_edge = valid.half_edges().next().unwrap();
|
||||||
|
|
||||||
|
let boundary = half_edge
|
||||||
|
.boundary()
|
||||||
|
.map(|point| point + Vector::from([Scalar::PI / 2.]));
|
||||||
|
|
||||||
|
let mut surface_vertices =
|
||||||
|
half_edge.surface_vertices().map(Clone::clone);
|
||||||
|
|
||||||
|
let mut invalid = None;
|
||||||
|
for surface_vertex in surface_vertices.each_mut_ext() {
|
||||||
|
let invalid = invalid.get_or_insert_with(|| {
|
||||||
|
SurfaceVertex::new(
|
||||||
|
[0., 1.],
|
||||||
|
surface_vertex.global_form().clone(),
|
||||||
|
)
|
||||||
|
.insert(&mut services.objects)
|
||||||
|
});
|
||||||
|
*surface_vertex = invalid.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
let boundary = boundary.zip_ext(surface_vertices);
|
||||||
|
|
||||||
|
HalfEdge::new(
|
||||||
|
half_edge.curve().clone(),
|
||||||
|
boundary,
|
||||||
|
half_edge.global_form().clone(),
|
||||||
|
)
|
||||||
|
.insert(&mut services.objects)
|
||||||
|
};
|
||||||
|
|
||||||
|
Cycle::new(valid.surface().clone(), [half_edge])
|
||||||
};
|
};
|
||||||
|
|
||||||
valid.validate_and_return_first_error()?;
|
valid.validate_and_return_first_error()?;
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use fj_math::{Point, Scalar};
|
use fj_math::{Point, Scalar};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{
|
objects::{GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface},
|
||||||
GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface, SurfaceVertex,
|
|
||||||
},
|
|
||||||
storage::Handle,
|
storage::Handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -18,7 +16,6 @@ impl Validate for HalfEdge {
|
|||||||
HalfEdgeValidationError::check_global_curve_identity(self, errors);
|
HalfEdgeValidationError::check_global_curve_identity(self, errors);
|
||||||
HalfEdgeValidationError::check_global_vertex_identity(self, errors);
|
HalfEdgeValidationError::check_global_vertex_identity(self, errors);
|
||||||
HalfEdgeValidationError::check_vertex_coincidence(self, config, errors);
|
HalfEdgeValidationError::check_vertex_coincidence(self, config, errors);
|
||||||
HalfEdgeValidationError::check_vertex_positions(self, config, errors);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,33 +104,6 @@ pub enum HalfEdgeValidationError {
|
|||||||
/// The half-edge
|
/// The half-edge
|
||||||
half_edge: HalfEdge,
|
half_edge: HalfEdge,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Mismatch between [`SurfaceVertex`] and [`GlobalVertex`] positions
|
|
||||||
#[error(
|
|
||||||
"`SurfaceVertex` position doesn't match position of its global form\n\
|
|
||||||
- Surface position: {surface_position:?}\n\
|
|
||||||
- Surface position converted to global position: \
|
|
||||||
{surface_position_as_global:?}\n\
|
|
||||||
- Global position: {global_position:?}\n\
|
|
||||||
- Distance between the positions: {distance}\n\
|
|
||||||
- `SurfaceVertex`: {surface_vertex:#?}"
|
|
||||||
)]
|
|
||||||
VertexSurfacePositionMismatch {
|
|
||||||
/// The position of the surface vertex
|
|
||||||
surface_position: Point<2>,
|
|
||||||
|
|
||||||
/// The surface position converted into a global position
|
|
||||||
surface_position_as_global: Point<3>,
|
|
||||||
|
|
||||||
/// The position of the global vertex
|
|
||||||
global_position: Point<3>,
|
|
||||||
|
|
||||||
/// The distance between the positions
|
|
||||||
distance: Scalar,
|
|
||||||
|
|
||||||
/// The surface vertex
|
|
||||||
surface_vertex: Handle<SurfaceVertex>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HalfEdgeValidationError {
|
impl HalfEdgeValidationError {
|
||||||
@ -206,36 +176,6 @@ impl HalfEdgeValidationError {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_vertex_positions(
|
|
||||||
half_edge: &HalfEdge,
|
|
||||||
config: &ValidationConfig,
|
|
||||||
errors: &mut Vec<ValidationError>,
|
|
||||||
) {
|
|
||||||
for surface_vertex in half_edge.surface_vertices() {
|
|
||||||
let surface_position_as_global = half_edge
|
|
||||||
.surface()
|
|
||||||
.geometry()
|
|
||||||
.point_from_surface_coords(surface_vertex.position());
|
|
||||||
let global_position = surface_vertex.global_form().position();
|
|
||||||
|
|
||||||
let distance =
|
|
||||||
surface_position_as_global.distance_to(&global_position);
|
|
||||||
|
|
||||||
if distance > config.identical_max_distance {
|
|
||||||
errors.push(
|
|
||||||
Box::new(Self::VertexSurfacePositionMismatch {
|
|
||||||
surface_position: surface_vertex.position(),
|
|
||||||
surface_position_as_global,
|
|
||||||
global_position,
|
|
||||||
distance,
|
|
||||||
surface_vertex: surface_vertex.clone(),
|
|
||||||
})
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -246,7 +186,7 @@ mod tests {
|
|||||||
use crate::{
|
use crate::{
|
||||||
builder::HalfEdgeBuilder,
|
builder::HalfEdgeBuilder,
|
||||||
insert::Insert,
|
insert::Insert,
|
||||||
objects::{GlobalCurve, HalfEdge, SurfaceVertex},
|
objects::{GlobalCurve, HalfEdge},
|
||||||
partial::{Partial, PartialHalfEdge, PartialObject},
|
partial::{Partial, PartialHalfEdge, PartialObject},
|
||||||
services::Services,
|
services::Services,
|
||||||
validate::Validate,
|
validate::Validate,
|
||||||
@ -257,11 +197,11 @@ mod tests {
|
|||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let valid = {
|
let valid = {
|
||||||
|
let surface = services.objects.surfaces.xy_plane();
|
||||||
|
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(
|
half_edge.update_as_line_segment_from_points([[0., 0.], [1., 0.]]);
|
||||||
services.objects.surfaces.xy_plane(),
|
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
[[0., 0.], [1., 0.]],
|
|
||||||
);
|
|
||||||
|
|
||||||
half_edge.build(&mut services.objects)
|
half_edge.build(&mut services.objects)
|
||||||
};
|
};
|
||||||
@ -277,12 +217,7 @@ mod tests {
|
|||||||
.boundary()
|
.boundary()
|
||||||
.zip_ext(valid.surface_vertices().map(Clone::clone));
|
.zip_ext(valid.surface_vertices().map(Clone::clone));
|
||||||
|
|
||||||
HalfEdge::new(
|
HalfEdge::new(valid.curve().clone(), vertices, global_form)
|
||||||
valid.surface().clone(),
|
|
||||||
valid.curve().clone(),
|
|
||||||
vertices,
|
|
||||||
global_form,
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
valid.validate_and_return_first_error()?;
|
valid.validate_and_return_first_error()?;
|
||||||
@ -296,11 +231,11 @@ mod tests {
|
|||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let valid = {
|
let valid = {
|
||||||
|
let surface = services.objects.surfaces.xy_plane();
|
||||||
|
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(
|
half_edge.update_as_line_segment_from_points([[0., 0.], [1., 0.]]);
|
||||||
services.objects.surfaces.xy_plane(),
|
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
[[0., 0.], [1., 0.]],
|
|
||||||
);
|
|
||||||
|
|
||||||
half_edge.build(&mut services.objects)
|
half_edge.build(&mut services.objects)
|
||||||
};
|
};
|
||||||
@ -324,12 +259,7 @@ mod tests {
|
|||||||
.boundary()
|
.boundary()
|
||||||
.zip_ext(valid.surface_vertices().map(Clone::clone));
|
.zip_ext(valid.surface_vertices().map(Clone::clone));
|
||||||
|
|
||||||
HalfEdge::new(
|
HalfEdge::new(valid.curve().clone(), vertices, global_form)
|
||||||
valid.surface().clone(),
|
|
||||||
valid.curve().clone(),
|
|
||||||
vertices,
|
|
||||||
global_form,
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
valid.validate_and_return_first_error()?;
|
valid.validate_and_return_first_error()?;
|
||||||
@ -343,11 +273,11 @@ mod tests {
|
|||||||
let mut services = Services::new();
|
let mut services = Services::new();
|
||||||
|
|
||||||
let valid = {
|
let valid = {
|
||||||
|
let surface = services.objects.surfaces.xy_plane();
|
||||||
|
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
half_edge.update_as_line_segment_from_points(
|
half_edge.update_as_line_segment_from_points([[0., 0.], [1., 0.]]);
|
||||||
services.objects.surfaces.xy_plane(),
|
half_edge.infer_vertex_positions_if_necessary(&surface.geometry());
|
||||||
[[0., 0.], [1., 0.]],
|
|
||||||
);
|
|
||||||
|
|
||||||
half_edge.build(&mut services.objects)
|
half_edge.build(&mut services.objects)
|
||||||
};
|
};
|
||||||
@ -357,7 +287,6 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
HalfEdge::new(
|
HalfEdge::new(
|
||||||
valid.surface().clone(),
|
|
||||||
valid.curve().clone(),
|
valid.curve().clone(),
|
||||||
vertices,
|
vertices,
|
||||||
valid.global_form().clone(),
|
valid.global_form().clone(),
|
||||||
@ -369,44 +298,4 @@ mod tests {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn surface_vertex_position_mismatch() -> anyhow::Result<()> {
|
|
||||||
let mut services = Services::new();
|
|
||||||
|
|
||||||
let valid = {
|
|
||||||
let mut half_edge = PartialHalfEdge::default();
|
|
||||||
half_edge.update_as_line_segment_from_points(
|
|
||||||
services.objects.surfaces.xy_plane(),
|
|
||||||
[[0., 0.], [1., 0.]],
|
|
||||||
);
|
|
||||||
|
|
||||||
half_edge.build(&mut services.objects)
|
|
||||||
};
|
|
||||||
let invalid = {
|
|
||||||
let mut surface_vertices =
|
|
||||||
valid.surface_vertices().map(Clone::clone);
|
|
||||||
|
|
||||||
let [_, surface_vertex] = surface_vertices.each_mut_ext();
|
|
||||||
*surface_vertex = SurfaceVertex::new(
|
|
||||||
[2., 0.],
|
|
||||||
surface_vertex.global_form().clone(),
|
|
||||||
)
|
|
||||||
.insert(&mut services.objects);
|
|
||||||
|
|
||||||
let boundary = valid.boundary().zip_ext(surface_vertices);
|
|
||||||
|
|
||||||
HalfEdge::new(
|
|
||||||
valid.surface().clone(),
|
|
||||||
valid.curve().clone(),
|
|
||||||
boundary,
|
|
||||||
valid.global_form().clone(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
valid.validate_and_return_first_error()?;
|
|
||||||
assert!(invalid.validate_and_return_first_error().is_err());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -27,19 +27,19 @@ impl Shape for fj::Sketch {
|
|||||||
|
|
||||||
let face = match self.chain() {
|
let face = match self.chain() {
|
||||||
fj::Chain::Circle(circle) => {
|
fj::Chain::Circle(circle) => {
|
||||||
let half_edge = {
|
|
||||||
let surface = Partial::from(surface);
|
let surface = Partial::from(surface);
|
||||||
|
|
||||||
let mut half_edge = PartialHalfEdge {
|
let half_edge = {
|
||||||
surface,
|
let mut half_edge = PartialHalfEdge::default();
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
half_edge.update_as_circle_from_radius(circle.radius());
|
half_edge.update_as_circle_from_radius(circle.radius());
|
||||||
|
|
||||||
Partial::from_partial(half_edge)
|
Partial::from_partial(half_edge)
|
||||||
};
|
};
|
||||||
let exterior = {
|
let exterior = {
|
||||||
let mut cycle = PartialCycle::default();
|
let mut cycle = PartialCycle {
|
||||||
|
surface,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
cycle.half_edges.push(half_edge);
|
cycle.half_edges.push(half_edge);
|
||||||
Partial::from_partial(cycle)
|
Partial::from_partial(cycle)
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user