From 4d2b4d35a6cf87777dee476a5c59fdc904ff88b4 Mon Sep 17 00:00:00 2001 From: jyviko <2501648+jyviko@users.noreply.github.com> Date: Wed, 4 Nov 2026 21:53:05 -0800 Subject: [PATCH] offset: the extended surface's margin is five offsets either way, none for a zero offset (#2769) GetCreateExtendedSurface passes the signed offset * 5 as the extension distance. The exact extension refuses a distance that is not finite and positive, so an inward offset (negative distance) or a zero one could not extend its surfaces (71 offset.offset_executive_p1 cases refused). The extension is a margin round the base for the offset to be trimmed in: its size is |offset| * 5, and a zero offset needs none (the base itself). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NvoxKMbMFSQU8tzRtoJhtT --- .../src/offset/offset_executive/p1.rs | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/crates/solidcore/src/offset/offset_executive/p1.rs b/crates/solidcore/src/offset/offset_executive/p1.rs index 56a042d634..caf4f74646 100644 --- a/crates/solidcore/src/offset/offset_executive/p1.rs +++ b/crates/solidcore/src/offset/offset_executive/p1.rs @@ -621,7 +621,8 @@ impl OffsetExecutive { Ok(out) } - /// Returns a cached extension, creating it with distance `offset_distance * 5.0`, every + /// Returns a cached extension, creating it with distance `|offset_distance| * 5.0` (none + /// for a zero offset), every /// side exactly ruled ([`Surface::extended_sides_keeping_parameters`], [`Continuity::Ruled`]): a plane, cone, /// sphere, torus or ball patch as itself, a procedural blend by its definition, an /// offset surface as the offset of its base extended. A hit never re-extends or replaces @@ -646,15 +647,21 @@ impl OffsetExecutive { std::collections::btree_map::Entry::Vacant(entry) => { // The obsolete offset-specific `else` at cpp:2158 is unreachable: // NER(pBase) already established pOrigSurf == pBase != NULL. - let dist = self.offset_distance * 5.0; - let extension = base - .extended_sides_keeping_parameters( + // The extension is a margin round the base for the offset to be trimmed in: + // its size is five offsets either way (an inward offset's distance is + // negative), and an offset of zero needs none. + let dist = (self.offset_distance * 5.0).abs(); + let extension = if dist == 0.0 { + base.clone() + } else { + base.extended_sides_keeping_parameters( kernel, &BoxSide::ALL, Continuity::Ruled, dist, )? - .surface; + .surface + }; Ok(entry.insert(extension)) } } @@ -3819,6 +3826,35 @@ mod tests { assert_eq!(executive.extended_surfaces.len(), 2); } + #[test] + fn an_inward_offset_extends_by_five_offsets_and_a_zero_one_not_at_all() { + let kernel = Kernel::default(); + let base = plane(0.0); + let expected = base + .extended_sides_keeping_parameters( + &kernel, + &BoxSide::ALL, + Continuity::Ruled, + 0.125 * 5.0, + ) + .unwrap() + .surface; + let mut inward = OffsetExecutive::new(OffsetOperation::SolidOffset, -0.125, 1.0e-6); + let got = inward + .get_create_extended_surface(&kernel, 1, &base) + .unwrap(); + assert_eq!( + got.natural_uv_domain_if_built(), + expected.natural_uv_domain_if_built() + ); + let mut zero = OffsetExecutive::new(OffsetOperation::SolidOffset, 0.0, 1.0e-6); + let same = zero.get_create_extended_surface(&kernel, 1, &base).unwrap(); + assert_eq!( + same.natural_uv_domain_if_built(), + base.natural_uv_domain_if_built() + ); + } + #[test] fn failed_extensions_are_not_cached_and_can_be_retried() { let kernel = Kernel::default();