Gizmo — WYSIWYG Direct Manipulation#
Status: Implemented (Phase 1). Click-to-select, axis-constrained drag, visual gizmo arrows.
Problem#
Users need Fusion 360-style direct manipulation: click objects in the 3D viewport to select them, drag gizmo handles to transform objects interactively, with all transforms going through the Automerge op log.
Architecture#
Interaction State Machine (Rust)#
IDLE ──click object──> SELECTED ──drag arrow──> DRAGGING
^ │ ^ │
└──click empty───────────┘ └──mouseup / Escape─────┘Three modes in InteractionMode enum:
- Idle — no selection, normal camera orbit
- Selected — object highlighted, gizmo arrows visible, camera still works
- Dragging — constrained axis movement, camera orbit disabled, live preview
Picking#
- Bounding sphere per object, computed from solid AABB
Camera::ray(ndc)casts ray from screen coordinates- Ray-sphere intersection finds closest hit
- Gizmo arrow picking via ray-to-segment distance
Gizmo Geometry#
- 3 colored
WireFrameInstancearrows (X=red, Y=green, Z=blue) at selected object center - Arrows scaled proportionally to camera distance (constant screen size)
- During drag: active axis highlighted, others dimmed
Drag Mechanics#
- Screen-to-world: project mouse NDC delta onto constrained axis via ray-axis closest-point math
- Live preview:
translate_object()called incrementally during drag - Cumulative delta tracked for final commit
- Escape reverses cumulative translation (cancel)
JS ↔ WASM API#
Public methods called from JS:
select_object_at(ndc_x, ndc_y) → JsValue— pick and selectbegin_gizmo_drag(ndc_x, ndc_y) → JsValue— start drag if clicking gizmo arrowupdate_gizmo_drag(ndc_x, ndc_y, prev_ndc_x, prev_ndc_y)— live previewend_gizmo_drag() → JsValue— finish drag, return { objectId, dx, dy, dz }cancel_gizmo_drag() → bool— reverse translationset_on_select(f)/set_on_drag_complete(f)— JS callbacks
Automerge Integration#
On drag complete, JS commits a single translate operation to the Automerge op log.
No ops created during drag (live preview only). Undo reverses the committed op.
Future#
- Rotation gizmo — circular handles for rotate-around-axis
- Scale gizmo — square handles for uniform/non-uniform scale
- Mesh raycasting — upgrade from bounding sphere to triangle-level picking
- Snap-to-grid — constrain drag to grid increments
- Multi-select — Shift+click to select multiple objects