[fix] 15-D mesh-edit correctness & input (weld/indexed, outside-click, hotkeys, contrast wire) - #93
Conversation
…ide-click deselect, mesh-edit hotkeys toggle + bindings popup, contrast wire - D1 weld: snapshot INDEX-EXPANDED positions (trisToPositions(readTriangles())) - applyMeshGeo rebuilds non-indexed, so the raw attribute of a fresh indexed Box (24 pos / 36 idx) was reinterpreted as 8 arbitrary triangles and undo replayed the same wrong representation - D1 refresh: refreshVertexEditSession() rebuilds handles/InstancedMesh/overlay from the live geometry without exit/enter; applyMeshGeo reaches it through registerVertexSessionRefresher (registered at meshEdit eval - a dynamic import back is a SECOND module instance under vite ?t= HMR stamps) - D2: an outside click clears the vertex/face multi-pick; session, object selection and gizmo stay - D3: meshEditHotkeys local pref (default ON) + toolbar keyboard toggle and "?" bindings popover; while ON, shortcuts.js skips bare mesh-edit keys (F also focused) and editorNavigation parks W/A/S/D/Q/E fly (quiz 15-D3: the pref is the escape hatch, no extra setting); one bundled display row in Settings > Shortcuts; popup typing guard gains SELECT - D4: edit wireframe color picked at build time from material luminance (light material -> dark #1f2937 wire) - e2e: mesh-ops +7 (weld on INDEXED box, undo restores exactly, session survives undo with rebuilt handles), mesh-edit-popup +12 (outside-click both modes, hotkeys on/off A/B incl. camera-fly proof, popover, persist) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rag moves the set, anchor rides the last pick
User report on the weld flow: the gizmo stayed on a vertex that was not in
the weld set, multi-select left the gizmo doing single-vertex moves, and a
gizmo-selected vertex read "0 sel". Root cause: TWO parallel selection
models (selectedHandle for the gizmo, vertexSelection for weld/create-face).
- ONE model now: plain click = selection {A} + gizmo ("1 sel"); ctrl-click
toggles membership; the ANCHOR (selectedHandle) rides the last-added
vertex and carries the gizmo; removing the anchor promotes another
member; emptying the selection parks the gizmo (outside-click included)
- a gizmo drag moves the WHOLE selection rigidly (anchor delta applied to
every member); single drags keep the 'verts' undo entry, multi-drags
record ONE meshgeo entry (the weld precedent, index-expanded per D1);
live replication streams the existing per-handle verts messages - no new
wire types
- weld/create-face read the same set, so the reported flow (plain click +
one ctrl-click) now welds; toasts/titles updated
- VR paths untouched: setAnchor never seats the desktop gizmo in VR
(isVRMode guard); VR toggle/clear/drag semantics unchanged
- e2e: mesh-ops +6 (1-sel gizmo pick, anchor re-seat, rigid 6/24-entry
multi-drag, one-undo restore, gizmo parks, reported weld flow),
mesh-edit-popup +1 ("1 sel" counter); VR regressions green
(vr-face-weld, vr-face-edit, vr-mesh-undo); build green; 419/62 held
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
+ D5 (user report, commit 7188d48): unified vertex selection. The weld flow exposed two parallel selection models — the gizmo's Now ONE model (Blender-style): plain click = selection {A} + gizmo ("1 sel"); ctrl-click toggles; the anchor rides the last pick and carries the gizmo (removal promotes another member, empty parks it); a gizmo drag moves the whole selection rigidly — single drags keep the e2e: mesh-ops 35/35 (+6), mesh-edit-popup 22/22 (+1 counter check); vr-face-weld / vr-face-edit / vr-mesh-undo green; build green; 419/62 held. |
Roadmap #15 mesh lane, batch D (plan: cloud plans-core/pending/15-d-editmesh-correctness.md). First of D -> E -> F.
D1 — weld fix + vertex session refresh
trisToPositions(readTriangles())):applyMeshGeorebuilds a NON-indexed geometry, so snapshotting the raw position attribute of a fresh INDEXED Box (24 positions / 36 indices) mangled the mesh and undo replayed the same wrong representation. The committed suite only passed because its box had been de-indexed by earlier insets.refreshVertexEditSession(): rebuilds handles + InstancedMesh + wireframe overlay from the live geometry WITHOUT exit/enter (no re-lock, no selection-stash churn).applyMeshGeoreaches it viaregisterVertexSessionRefresher— registered at meshEdit module eval, because a dynamicimport('./meshEdit')back binds a SECOND module instance under vite's?t=HMR stamps (itseditingObjectis always null; caught live in this batch).toNonIndexed()at enterEditMode — the livevertschannel replicates attribute indices, both peers must agree on the layout; only the meshgeo commit changes representation.D2 — outside-click deselect
A viewport miss clears the vertex multi-pick / face multi-pick; the edit session, object selection and single-handle gizmo deliberately stay.
D3 — hotkeys toggle + "?" popup + camera-fly guard
meshEditHotkeys(default ON) + toolbar keyboard-toggle button and a "?" popover listing the bindings.shortcuts.jsskips bare mesh-edit keys (F also fired focusObject), andupdateEditorNavigationparks W/A/S/D/Q/E fly (per-frame gate — the popup's preventDefault can't stop the fly key set). Deliberately NOTclaimInput('keys'): the claim set isn't refcounted and possess/car would drop it mid-session.E I G S B F X / W, group "Mesh edit") — registerShortcut dedupes exact keys, so a bare F row would collide.D4 — contrast-aware edit wireframe
Wire color picked at build time from the material's relative luminance (light material -> dark
#1f2937; else the existing blue). Rebuilds on every geometry swap pick up material changes incidentally.Verification
mesh-ops29/29 (+7: weld on a truly INDEXED box keeps 12 tris / drops one welded key / no NaN; undo restores exactly; the session survives undo with handles rebuilt 7->8)mesh-edit-popup21/21 (+12: outside-click both modes; hotkeys A/B incl. a camera-fly proof — 0.00000 moved suppressed vs 1.500 with the pref off; "?" popover; pref persists across reload)npm run buildgreen; svelte-check 419/62 (the release.yml gate, unchanged)🤖 Generated with Claude Code