Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't use self.path in HalfEdgeGeom #2383

Merged
merged 3 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion crates/fj-core/src/algorithms/approx/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ impl Approx for (&Handle<HalfEdge>, &Handle<Surface>) {
let tolerance = tolerance.into();

let start_position_surface =
geometry.of_half_edge(half_edge).start_position();
geometry.of_half_edge(half_edge).start_position(
&geometry
.of_curve(half_edge.curve())
.unwrap()
.local_on(surface)
.unwrap()
.path,
);
let start_position =
match cache.start_position.get(half_edge.start_vertex()) {
Some(position) => position,
Expand Down
4 changes: 2 additions & 2 deletions crates/fj-core/src/geometry/half_edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ impl HalfEdgeGeom {
}

/// Compute the surface position where the half-edge starts
pub fn start_position(&self) -> Point<2> {
pub fn start_position(&self, path: &SurfacePath) -> Point<2> {
// Computing the surface position from the curve position is fine.
// `HalfEdge` "owns" its start position. There is no competing code that
// could compute the surface position from slightly different data.

let [start, _] = self.boundary.inner;
self.path.point_from_path_coords(start)
path.point_from_path_coords(start)
}
}
22 changes: 20 additions & 2 deletions crates/fj-core/src/operations/split/face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,26 @@ impl SplitFace for Shell {
let dividing_half_edge_a_to_d = {
let half_edge = HalfEdge::line_segment(
[
core.layers.geometry.of_half_edge(&b).start_position(),
core.layers.geometry.of_half_edge(&d).start_position(),
core.layers.geometry.of_half_edge(&b).start_position(
&core
.layers
.geometry
.of_curve(b.curve())
.unwrap()
.local_on(face.surface())
.unwrap()
.path,
),
core.layers.geometry.of_half_edge(&d).start_position(
&core
.layers
.geometry
.of_curve(d.curve())
.unwrap()
.local_on(face.surface())
.unwrap()
.path,
),
],
None,
face.surface().clone(),
Expand Down
2 changes: 1 addition & 1 deletion crates/fj-core/src/operations/sweep/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl SweepSketch for Sketch {
// clockwise. Let's check that real quick.
assert!(region
.exterior()
.winding(&core.layers.geometry)
.winding(&core.layers.geometry, self.surface())
.is_ccw());

let is_negative_sweep = {
Expand Down
17 changes: 15 additions & 2 deletions crates/fj-core/src/topology/objects/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use crate::{
topology::{HalfEdge, ObjectSet},
};

use super::surface::Surface;

/// A cycle of connected edges
#[derive(Clone, Debug)]
pub struct Cycle {
Expand All @@ -29,7 +31,11 @@ impl Cycle {
/// Please note that this is not *the* winding of the cycle, only one of the
/// two possible windings, depending on the direction you look at the
/// surface that the cycle is defined on from.
pub fn winding(&self, geometry: &Geometry) -> Winding {
pub fn winding(
&self,
geometry: &Geometry,
surface: &Handle<Surface>,
) -> Winding {
// The cycle could be made up of one or two circles. If that is the
// case, the winding of the cycle is determined by the winding of the
// first circle.
Expand Down Expand Up @@ -68,7 +74,14 @@ impl Cycle {

for (a, b) in self.half_edges().pairs() {
let [a, b] = [a, b].map(|half_edge| {
geometry.of_half_edge(half_edge).start_position()
geometry.of_half_edge(half_edge).start_position(
&geometry
.of_curve(half_edge.curve())
.unwrap()
.local_on(surface)
.unwrap()
.path,
)
});

sum += (b.u - a.u) * (b.v + a.v);
Expand Down
2 changes: 1 addition & 1 deletion crates/fj-core/src/topology/objects/face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ impl Face {
/// back sides. The front side is the side, where the face's exterior cycle
/// is wound counter-clockwise.
pub fn coord_handedness(&self, geometry: &Geometry) -> Handedness {
match self.region.exterior().winding(geometry) {
match self.region.exterior().winding(geometry, self.surface()) {
Winding::Ccw => Handedness::RightHanded,
Winding::Cw => Handedness::LeftHanded,
}
Expand Down
6 changes: 4 additions & 2 deletions crates/fj-core/src/validate/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl SketchValidationError {
) {
sketch.regions().iter().for_each(|region| {
let cycle = region.exterior();
if cycle.winding(geometry) == Winding::Cw {
if cycle.winding(geometry, sketch.surface()) == Winding::Cw {
errors.push(ValidationError::Sketch(
SketchValidationError::ClockwiseExteriorCycle {
cycle: cycle.clone(),
Expand All @@ -97,7 +97,9 @@ impl SketchValidationError {
region
.interiors()
.iter()
.filter(|interior| interior.winding(geometry) == Winding::Ccw)
.filter(|interior| {
interior.winding(geometry, sketch.surface()) == Winding::Ccw
})
.for_each(|cycle| {
errors.push(ValidationError::Sketch(
SketchValidationError::CounterClockwiseInteriorCycle {
Expand Down
18 changes: 14 additions & 4 deletions crates/fj-core/src/validate/solid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,23 @@ impl SolidValidationError {
.flat_map(|cycle| cycle.half_edges().iter().cloned())
.zip(repeat(face.surface()))
})
.map(|(h, s)| {
(
.filter_map(|(h, s)| {
let Some(local_curve_geometry) =
geometry.of_curve(h.curve()).unwrap().local_on(s)
else {
// If the curve geometry has no local definition,
// there's nothing we can check.
return None;
};

Some((
geometry.of_surface(s).point_from_surface_coords(
geometry.of_half_edge(&h).start_position(),
geometry
.of_half_edge(&h)
.start_position(&local_curve_geometry.path),
),
h.start_vertex().clone(),
)
))
})
.collect();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ mod tests {
)
.is_err());

// Ignore remaining validation errors.
let _ = core.layers.validation.take_errors();

Ok(())
}
}
4 changes: 2 additions & 2 deletions crates/fj-core/src/validation/checks/face_winding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ impl ValidationCheck<Face> for InteriorCycleHasInvalidWinding {
return None;
}

let exterior_winding = exterior.winding(geometry);
let interior_winding = interior.winding(geometry);
let exterior_winding = exterior.winding(geometry, object.surface());
let interior_winding = interior.winding(geometry, object.surface());

if exterior_winding == interior_winding {
return Some(InteriorCycleHasInvalidWinding {
Expand Down
29 changes: 20 additions & 9 deletions crates/fj-core/src/validation/checks/half_edge_connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use fj_math::{Point, Scalar};
use crate::{
geometry::Geometry,
storage::Handle,
topology::{Cycle, Face, HalfEdge, Region, Sketch},
topology::{Cycle, Face, HalfEdge, Region, Sketch, Surface},
validation::{validation_check::ValidationCheck, ValidationConfig},
};

Expand Down Expand Up @@ -63,7 +63,7 @@ impl ValidationCheck<Face> for AdjacentHalfEdgesNotConnected {
geometry: &'r Geometry,
config: &'r ValidationConfig,
) -> impl Iterator<Item = Self> + 'r {
check_region(object.region(), geometry, config)
check_region(object.region(), object.surface(), geometry, config)
}
}

Expand All @@ -73,26 +73,27 @@ impl ValidationCheck<Sketch> for AdjacentHalfEdgesNotConnected {
geometry: &'r Geometry,
config: &'r ValidationConfig,
) -> impl Iterator<Item = Self> + 'r {
object
.regions()
.iter()
.flat_map(|region| check_region(region, geometry, config))
object.regions().iter().flat_map(|region| {
check_region(region, object.surface(), geometry, config)
})
}
}

fn check_region<'r>(
region: &'r Region,
surface: &'r Handle<Surface>,
geometry: &'r Geometry,
config: &'r ValidationConfig,
) -> impl Iterator<Item = AdjacentHalfEdgesNotConnected> + 'r {
[region.exterior()]
.into_iter()
.chain(region.interiors())
.flat_map(|cycle| check_cycle(cycle, geometry, config))
.flat_map(|cycle| check_cycle(cycle, surface, geometry, config))
}

fn check_cycle<'r>(
cycle: &'r Cycle,
surface: &'r Handle<Surface>,
geometry: &'r Geometry,
config: &'r ValidationConfig,
) -> impl Iterator<Item = AdjacentHalfEdgesNotConnected> + 'r {
Expand All @@ -104,8 +105,18 @@ fn check_cycle<'r>(
.path
.point_from_path_coords(end)
};
let start_pos_of_second_half_edge =
geometry.of_half_edge(second).start_position();

let Some(local_curve_geometry) =
geometry.of_curve(second.curve()).unwrap().local_on(surface)
else {
// If the curve geometry is not defined for our local surface,
// there's nothing we can check.
return None;
};

let start_pos_of_second_half_edge = geometry
.of_half_edge(second)
.start_position(&local_curve_geometry.path);

let distance_between_positions = (end_pos_of_first_half_edge
- start_pos_of_second_half_edge)
Expand Down