Skip to content

[fix] 15-D mesh-edit correctness & input (weld/indexed, outside-click, hotkeys, contrast wire) - #93

Merged
AlexZ005 merged 2 commits into
release/nextfrom
feat/roadmap15-d-editmesh
Aug 8, 2026
Merged

[fix] 15-D mesh-edit correctness & input (weld/indexed, outside-click, hotkeys, contrast wire)#93
AlexZ005 merged 2 commits into
release/nextfrom
feat/roadmap15-d-editmesh

Conversation

@AlexZ005

@AlexZ005 AlexZ005 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

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

  • Weld snapshots are now INDEX-EXPANDED (trisToPositions(readTriangles())): applyMeshGeo rebuilds 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.
  • New refreshVertexEditSession(): rebuilds handles + InstancedMesh + wireframe overlay from the live geometry WITHOUT exit/enter (no re-lock, no selection-stash churn). applyMeshGeo reaches it via registerVertexSessionRefresher — registered at meshEdit module eval, because a dynamic import('./meshEdit') back binds a SECOND module instance under vite's ?t= HMR stamps (its editingObject is always null; caught live in this batch).
  • No toNonIndexed() at enterEditMode — the live verts channel 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

  • New local pref meshEditHotkeys (default ON) + toolbar keyboard-toggle button and a "?" popover listing the bindings.
  • While a session is active AND the pref is on: shortcuts.js skips bare mesh-edit keys (F also fired focusObject), and updateEditorNavigation parks W/A/S/D/Q/E fly (per-frame gate — the popup's preventDefault can't stop the fly key set). Deliberately NOT claimInput('keys'): the claim set isn't refcounted and possess/car would drop it mid-session.
  • Quiz 15-D3 resolved with the user: the pref itself is the escape hatch (toggle off -> camera keys return); a dedicated "allow camera keys during edit" setting stays backlog.
  • One bundled display-only row in Settings ▸ Shortcuts (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-ops 29/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-popup 21/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)
  • Regressions green: faces-toolbar, mesh-sculpt, undo, vr-face-edit, vr-face-polygon, vr-mesh-undo, collider-custom
  • npm run build green; svelte-check 419/62 (the release.yml gate, unchanged)

🤖 Generated with Claude Code

AlexZ005 and others added 2 commits August 8, 2026 20:53
…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>
@AlexZ005

AlexZ005 commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

+ D5 (user report, commit 7188d48): unified vertex selection. The weld flow exposed two parallel selection models — the gizmo's selectedHandle vs the ctrl-click vertexSelection set: the gizmo stayed on a vertex that wasn't in the weld set, a multi-selection left the gizmo moving one vertex, and a gizmo-picked vertex read "0 sel".

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 verts undo entry, multi-drags record ONE meshgeo entry (weld precedent), live replication streams the existing per-handle verts messages (no new wire types). Weld/create-face read the same set, so plain click + one ctrl-click now welds. VR untouched (isVRMode guard on the gizmo seat).

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.

@AlexZ005
AlexZ005 merged commit aecaeb6 into release/next Aug 8, 2026
@AlexZ005
AlexZ005 deleted the feat/roadmap15-d-editmesh branch August 9, 2026 02:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant