Lipschitz loft#93
Open
snowbldr wants to merge 2 commits into
Open
Conversation
Loft3D linearly interpolates between two 2D SDFs along z. The xy
gradient is a convex combination of the two Lipschitz-1 inner
gradients (so still ≤ 1), but the z gradient grows with
max|a1−a0|/(2·h_inner). The outer d = √(a² + b²) end-cap composition
inflates the Lipschitz constant further. Without correction the result
overstates 3D distance and the octree marching-cubes renderer's
|sdf(center)| ≥ half-diagonal pruning skips cubes that contain
surface, producing holes.
LoftSDF3 grows an invStretch field set at construction:
L_z = max|a1(p) − a0(p)|_{p ∈ bb} / (2·h_inner)
L_end² = 1 + L_z·(L_z + √(L_z² + 4))/2
invStretch = 1/√L_end² (clamped to ≤ 1)
The L_z² ≥ √(1 + L_z²) inflation factor comes from the joint
Lipschitz of (a, b) → √(a² + b²). The max|a1 − a0| upper bound is
sampled on a 16×16 xy grid plus a Lipschitz-2 between-samples safety
term (a0 − a1 is Lipschitz-2 since each sdf is Lipschitz-1).
render/loft_test.go: watertight tests across small-to-big box loft
(short and tall heights), box-to-circle loft, and growing circle
loft. All assert zero boundary edges from the octree at 80 cells.
15 loft configurations covering: - identity (sdf0 == sdf1) — must be a no-op - same-shape size change at varied heights (h=5, 2, 0.5) - cross-shape lofts (box↔circle, triangle→circle, star↔box, star→circle) - extreme size ratio (circle r=0.5 → r=3) - thin-shape loft (3×1 box, narrower bbox stresses L_z) - extra rounding 9 distinct 2D shapes (boxes from 1×1 to 5×5, thin rect, circles, triangle, star) so max|a1−a0| varies widely. All assert zero boundary edges from the octree at cells=80.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix Loft3D Lipschitz under-correction
Fixes one of the bugs in #85.
The bug
Loft3Dlinearly interpolates between two 2D SDFs along z:The xy gradient of
ais a convex combination of the two innerLipschitz-1 gradients (so still
≤ 1), but the z gradient growswith
max|a1 − a0| / (2·h_inner)and the outerd = √(a² + b²)end-cap composition inflates further. Without correction
Evaluateoverstates 3D distance and the octree's
|sdf(center)| ≥ half-diagonalpruning skips cubes that contain surface, producing holes.
The fix
LoftSDF3grows aninvStretch float64field set at construction:The
L_end² ≥ √(1 + L_z²)factor is the joint Lipschitz of the(a, b) → √(a² + b²)end-cap composition.max|a1 − a0|is sampled on a 16×16 xy grid plus a Lipschitz-2between-samples safety term (
a0 − a1is Lipschitz-2 since eachinner SDF is Lipschitz-1). This is conservative; in practice it's
within a few percent of the true max for typical input shapes.
Evaluatemultiplies its result byinvStretchso the SDF staysLipschitz-1.
Tests
render/loft_test.go:small_to_big_box_h2/small_to_big_box_h5— Box2D(1×1) →Box2D(5×5), short and tall heights (short → larger L_z).
box_to_circle— exercises a topology-changing loft.circle_to_circle_growing/circle_to_circle_short— same shapefamily, varying L_z by changing height.
All assert zero boundary edges from the octree at 80 cells. Pass on
macOS.
Architecture-specific note
Same family of bug as the high-taper screw rendering issue from #84 —
non-1-Lipschitz SDF that the octree's
isEmptyrule wrongly trusts.Borderline configurations may render holes on x86_64 (FMA / FTZ
rounding differences) but not on Apple Silicon. The closed-form L_end
bound has plenty of slack either way.