feat: add Loft primitive that morphs between two 2D cross-sections - #58
Open
hbehrensj wants to merge 3 commits into
Open
feat: add Loft primitive that morphs between two 2D cross-sections#58hbehrensj wants to merge 3 commits into
hbehrensj wants to merge 3 commits into
Conversation
Ports the loft/morph algorithm from hbehrensj/loftmorph as a native parametric shape, mirroring the existing "gear" shape's integration pattern (toolbar entry, geometry builder, viewport render + boolean/ export dispatch, inspector panel, .skf format validation). Bottom and top ends are each independently configurable: shape (Oval/ Rectangle/Triangle/Pentagon/Hexagon), size (bottom follows the shape's own width/depth like every other primitive; top has its own absolute width/depth, mirroring how cone's topRadius works independently of its base), and rotation. Corner-anchored angle sampling keeps polygon corners crisp regardless of segment count or which two shapes are morphing between each other. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
gearGeometry.ts (the direct template for this file) has zero comments across 240 lines; keep only the ones that prevent a real regression (axis-swap/winding, why polygon ends aren't bbox-centered, and the buildAngles corner-anchoring summary) and tighten those to a line or two, per CONTRIBUTING.md's "keep comments short and useful". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ported from hbehrensj/loftmorph (commit d91b3df). The bottom/top ring correspondence used to sample both ends at the same absolute angle, so two same-cornered ends rotated relative to each other (e.g. two rectangles 25° apart) got walls that twisted/bulged instead of connecting corner to corner -- a bottom corner would land wherever the top happened to be smooth. Replaces it with an explicit best-matching correspondence (DP over a circular subsequence alignment, minimizing total angular mismatch), so each ring point carries independent aBottom/aTop angles instead of one shared angle. Two robustness fixes beyond the literal reference port, both found via a combinatorial smoke test across shape pairs and rotations: - Unequal corner counts (e.g. hexagon -> rectangle) leave some corners unmatched; falling back to the other end's raw angle (as in the reference) can land far outside the matched neighbors' range and break monotonicity. Interpolating between the nearest matched neighbors instead keeps it in range. - The segment-filling interpolation assumed both angle streams only wrap around at the same point (last anchor -> first). They can wrap independently, so each stream now advances by its own forward angular delta per segment instead of a single shared wrap check. Also wires "loft" into MCP: the sketchforge_create_shape tool schema and the create_shape command handler both recognize kind: "loft" with its own parameters now, matching every other native shape. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
Summary
I built loftmorph as a standalone tool for lofting a
solid between two 2D cross-sections. Found SketchForge and wanted that as a real, parametric
primitive in the editor rather than an import-a-static-STL workaround — so this ports the
algorithm in as a new "Loft" shape next to Box/Cylinder/Gear.
Oval/Rectangle/Triangle/Pentagon/Hexagon on each end, independent size + rotation (top mirrors
how cone's Top Radius works independently of its base). Wired into every place "gear" touches,
including the MCP bridge's
sketchforge_create_shapetool (kind: "loft"with its own params).Corner-twist fix: the initial port sampled both ends at the same shared lab-frame angle, so
two same-cornered ends rotated relative to each other (e.g. two rectangles 25° apart) got walls
that twisted/bulged instead of connecting corner to corner. Replaced with an explicit best-corner
matching (ported from a fix in loftmorph itself,
d91b3df) —DP over a circular subsequence alignment that minimizes total angular mismatch, so each ring point
carries independent bottom/top angles instead of one shared angle. Went a bit further than the
literal reference in two spots, both found by a combinatorial smoke test across shape pairs and
rotations: the unequal-corner-count fallback (e.g. hexagon → rectangle) now interpolates between
the nearest matched neighbors instead of copying the other end's raw angle (which could land
outside the matched range and self-intersect), and the segment-filling interpolation advances each
angle stream by its own forward wraparound instead of assuming both streams wrap at the same seam.
Affects: storage (new
loft*fields in.skf), export (STL/OBJ/STEP via the existingboolean/export pipeline), and undo/redo (new fields added to the shape-equality check that backs
history dedup). No changes to grouping/import mechanics themselves.
Testing
npm run typecheck+ test suite (loftGeometry.test.ts: closed/manifold + correct-windingchecks, a rotational-symmetry invariant that caught the original centering bug, a corner-
matching symmetry check — two squares 90° apart must reduce to an untwisted mesh — and a
combinatorial smoke test across all shape pairs and a spread of relative rotations that
caught the two robustness gaps above)
use as a boolean hole, export STL and re-import it, save a project and reload the page,
create a loft via the MCP bridge end to end (
sketchforge_create_shapewithkind: "loft")of a bulged/twisted wall (before/after screenshots on request)
Notes
slightly past its nominal bounding box, so the resize gizmo won't always hug the mesh exactly
in that combination. Cosmetic only — doesn't affect the generated geometry.