11 Commits

Author SHA1 Message Date
Sudhir Yadav
a71de82ccb
nodes: add MEP movement controls and DWV parity (#438)
* Add roof surface placement support for items

Items (e.g. solar panels) can now be placed on sloped roof surfaces.
The placement system computes euler rotation from the roof surface
normal so items sit flush on the slope instead of going inside.

- Add roofStrategy to placement-strategies with enter/move/click/leave
- Wire roof:enter/move/click/leave events in the placement coordinator
- Add calculateRoofRotation in placement-math using surface normals
- Support full 3D cursor rotation for sloped surfaces
- Items on roofs are parented to the level with world-space rotation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fixed conflict

* feat(duct): ceiling-snap drawing + connected-joint endpoint move

Duct draw tool's ceiling mode now hangs each path point just below the
ceiling actually covering it (per-room heights tracked), with a
translucent surface highlight and a plumb line to the floor so the
in-flight point reads clearly from any angle.

Dragging a duct corner that sits on a fitting now carries the fitting's
other ducts along (port-connectivity second hop), so the joint moves
together instead of tearing apart.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(mep): detach + vertical modifiers for duct/pipe joint editing

Alt detaches a dragged duct/pipe endpoint or fitting from its connected
joint (no elbow re-aim, no connectivity follow); Ctrl/Cmd drives vertical
riser movement on the fitting move. Behavioral parity across 2D and 3D.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(mep): full DWV pipe parity for joint editing

Bring pipe-segment endpoint drags and pipe-fitting moves to parity with
duct: free-drag endpoints, Alt-detach, Ctrl/Cmd-vertical riser, elbow
re-aim, and connectivity follow. Generalizes the shared elbow-reaim and
auto-fitting helpers to dispatch by run kind so 2D and 3D share one path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(mep): wall-style arrow handles for duct fittings + segments

Add violet directional arrow affordances to duct-fitting selection (height,
move cross, rotate arc) mirroring the duct-segment rig: portaled into the
parent frame to stay out of the selection outline, rendered via the shared
HandleArrow, and carrying mated-run connectivity through the single-undo
dance. The move cross engages press-drag-release (placementDragMode) the same
way the floating drag does, so the markup hit-areas go inert and the fitting
move tool commits on pointer-up.

Also re-export the HandleArrow primitives from @pascal-app/editor and extend
the duct-segment side-move/floorplan affordances.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(mep): click-to-latch cube handles for duct + fitting editing

Replace the hover-reveal / multi-handle selection rigs with a single
click-to-latch cube that opens a directional cluster, shared between
duct segments and fittings via a new selection-handles module
(HandleCube / MoveChevron / RotateArc, all sized to the roof pitch cube).

- Duct segment: per-vertex + run-center cubes reveal axis-locked move
  chevrons (down arrow always shown), plus a roll arc at the run center.
- Duct fitting: center cube reveals six ±XYZ move arrows and three
  per-axis rotation arcs (oriented in place), replacing the old
  height/move/rotate trio with axis-cycling.
- Rotation (fitting arcs + duct roll) snaps to 45° steps; Shift = smooth.
- thin chevron profile + press-drag-release commit retained.

* fix(mep): orient duct roll arc consistently + drop Ctrl-vertical drag

Build a fully-determined basis for the duct roll gizmo so the curved
arrow always seats at the top-outer 45° corner regardless of run
direction, instead of an arbitrary apex from a single setFromUnitVectors.
The selection-rig ±Y arrows now own vertical movement, so the redundant
Ctrl-modifier riser drag is removed from the fitting move tool.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(mep): run-aligned duct handles, swing snapping, elbow flatten

Align the duct run-center cube + horizontal arrows to the run axis
(matching the per-vertex handles) while keeping whole-run translate.
Endpoint side / up-down swing arrows now follow grid snap points and
port-snap onto nearby collars (Shift sweeps smoothly). Relax elbow
realign + fitting schemas to flatten to a straight 0° coupling. Surface
HVAC-specific hints in the select-mode helper panel for duct / fitting.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(mep): per-segment linesets/liquid-lines with joint-follow editing

Linesets and liquid lines now commit one independent two-point node per
drawn segment instead of folding into a single mitered polyline, so each
line selects and deletes on its own. Endpoint caps fill shared-coordinate
joints so connected segments still read as continuous pipe.

Dragging a shared endpoint carries mated segments along via port
connectivity (Alt detaches), so a run still edits as one welded piece.
Liquid-line follow mode traces the whole connected lineset run, laying a
per-segment parallel line down its full assembled length.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(mep): vertical-offset auto-routing on duct center-cube ±Y drag

Lifting/lowering a connected run with the run-center cube now keeps each
connected end welded to its stationary partner instead of dragging the
whole network. Run-to-run ends get the classic S/Z offset (two elbows +
plumb riser, partner trimmed back one leg); elbow-connected ends form a
clean L — the existing elbow stays put and re-aims its collar vertical,
with one new top elbow + riser reconnecting to the lifted endpoint. The
offset is ghosted live and minted as a single undo step on release.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Add roof accessory placement guides

Measure roof accessory placement against the active roof face using visible surface bounds and preview geometry footprints. Add dormer-local guides and special linear handling for ridge vents and gutters.

* Improve duct and placement routing

* Fix duct vertical movement routing

* Fix duct vertical offsets and roof accessory movement

* Add DWV movement parity and line endpoint controls

* Fix MEP handle review issues

* Fix chimney placement and duct offset cleanup

* Use snapped targets for roof accessory commits

* fix(nodes): repair MEP movement review issues

- auto-fitting: tee branch now follows the drawn lateral angle; update the
  stale square-tee test + doc comment that contradicted the rewrite
- duct-segment: re-enable the vertical auto-offset rewind (the disabled stub
  left mintedIds empty, so re-dragging a tagged duct stranded old elbows/risers
  and stacked duplicates); remove the dead stub
- duct-segment: strip the stale auto-offset tag on manual corner/roll commits
  so the horizontal-move path no longer trusts an out-of-date base
- chimney: resume history before mutating segment children arrays so a
  cross-segment move reparents in one tracked transaction (undo stays consistent)
- dormer: align schema test with the new windowSill=false default

* fix: address mep movement review issues

* fix: address follow-up mep review comments

* fix: address additional mep review comments

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: pascal-open-bot <open@pascal.app>
2026-06-23 08:37:15 -04:00
Wassim SAMAD
267dac7b1a perf(editor): convert UI icons from PNG to WebP
The editor's /icons assets were ~10MB of oversized PNGs (a single
toolbar icon up to 1.4MB). Convert every non-PWA icon to WebP (quality
92) and repoint all /icons/*.png references to .webp across
packages/editor, packages/nodes, and apps/editor. PWA/platform icons
(apple-touch-icon, icon-192, icon-512) stay PNG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 10:41:50 -04:00
Open Pascal
54a24e4c5c fix: address review findings from PR #402 MEP systems
- core: fix layer violation in level-height.ts — extract sceneRegistry
  import, replace with optional WallBaseYResolver callback so core stays
  pure (no Three.js mesh state); viewer callers pass resolver, headless
  callers (MCP/tests) get deterministic node-data-only result

- core: generalise port-connectivity service from duct-only to all
  distribution families — match partners by distributionRole ('run' →
  endpoint stretch, 'fitting' → rigid follow) instead of hard-coded
  duct-segment/duct-fitting type names; add system-compat guard so
  cross-system ports (e.g. supply duct vs waste pipe) don't fuse

- editor: fix port-snap rotation bug in move tool — pass preview node
  at live rotation into resolvePortSnap so own-port positions reflect
  any mid-drag R/T rotation before computing the snap delta

- editor: wire pipe-trap into UI — add to StructureTool union,
  MepToolKind, MEP_ITEMS Build-tab tile, and structure-tools action menu

- test: add port-connectivity-pipe.test.ts — 2 tests covering
  pipe-fitting → pipe-segment endpoint drag and cross-system isolation

- test: fix stale pipe-auto-fitting.test.ts wye expectation — author
  deliberately chose square sanitary-tee for DWV side-taps (documented
  in PR description and PipeFittingNode schema); update the one test
  that still expected wye to match the implemented behaviour

- nit: fix optional-chain biome warning in validate-dwv.ts
2026-06-16 19:30:51 +00:00
Sudhir Yadav
5551500d98
feat: HVAC ductwork + DWV plumbing systems (#402)
Adds two new MEP node families (HVAC ductwork, DWV plumbing) built on a shared port-connectivity model. Co-authored by @sudhir9297.
2026-06-16 15:30:39 -04:00
Aymeric Rabot
aa3b0ef758 refactor: release-review cleanup for roof wall openings
Dual review pass (Claude multi-angle + Codex release-quality). One
correctness fix and the agreed do-now cleanups:

- fix: clone-scene-graph remaps roofSegmentId like wallId in both
  clone paths — duplicated scenes/levels kept pointing roof-hosted
  children at the original segments.
- extract the settled, stateless roof target/cursor math shared by the
  four door/window tools into shared/roof-wall-opening-placement.ts
  (resolveRoofWallOpeningTarget + getRoofWallOpeningCursorPose +
  worldToSelectedBuildingLocal); tools keep the stateful lifecycle
  (drafts, undo/temporal, commit field lists). −199 net lines.
- rename host-generic state: currentWallId→currentHostId,
  markWallDirty→markHostDirty (they hold segment ids too); capability
  cascadesViaHostSegment→dirtyHandledByOwnSystem (behavior-facing,
  before the public API hardens).
- drop getRoofAccessoryKinds from core's public API — its only caller
  was the standalone Build tab, which now enumerates the registry
  inline with its app-specific filter.
- window move-tool uses the shared stripPlacementMetadataFlags; stale
  "segment-local" comment fixed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 14:33:13 -04:00
Aymeric Rabot
bdfee058bd feat: doors and windows on roof-segment wall faces
Openings now host on the walls a roof segment generates — the base
walls under the roof and the coplanar gable/shed/gambrel end faces, so
a window can sit in a gable pediment.

- core: roof-segment-walls.ts models the four vertical faces as 2D
  frames (u along face, v height) with convex profile polygons that
  mirror the wall volume getRoofSegmentBrushes builds; rect-in-profile
  clamping and anchored resize limits via half-plane algebra.
- schemas: optional roofSegmentId on door/window; position is the
  segment-local wall mid-plane center, rotation[1] the face yaw.
- cut: reuses capabilities.roofAccessory.buildCut; new cutScope: 'wall'
  subtracts from the wall brush only. cascadesViaHostSegment keeps the
  roof-merge loop from consuming door/window dirty marks (their own
  systems cascade via parentId).
- tools: roof:* handlers in door/window tool + move-tool (the Build-tab
  preset path), with roofSegmentId cleared/restored across every
  roof<->wall re-anchor and revert; shared hit resolver normalizes
  normals through world space (merged mesh vs painted segment frames).
- fix: RoofSystem no longer rebuilds per-segment CSG in accessory-reveal
  mode — the uncut rebuild used to draw over the merged shell's fresh
  opening until deselect.
- fix: the walkthrough collider world now prunes by renderer-effective
  visibility; stale uncut segment CSG inside the hidden segments-wrapper
  blocked the player at openings the merged shell had cut through.

Known gap: painted segments render per-segment CSG without accessory
cuts (pre-existing, also affects skylight/dormer). Twice Codex-reviewed;
details in private-editor plans/editor-roof-wall-openings.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 00:39:13 -04:00
Aymeric Rabot
478f0910f7 feat(editor): roof features in standalone Build tab + thumbnail flag fix
- registry: getRoofAccessoryKinds() enumerates kinds declaring the
  roofAccessory capability, in deterministic builtin-list order (mirrors
  getSelectableKinds); exported from the registry barrel.
- apps/editor build-tab: a "Features" group under the Roof tile, discovered
  from the registry (no DB) and activating each kind's roof-attach tool —
  parity with the community editor's roof features.
- site-boundary-editor: hide the flag handle group around thumbnail captures
  (they render on SCENE_LAYER so the thumbnail camera can't layer-filter them),
  matching handle-arrow.tsx — keeps flags out of preset/snapshot thumbnails.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 19:55:53 -04:00
Aymeric Rabot
b33059ef81 feat: add persistent site boundary handles 2026-06-08 17:42:20 -04:00
Wassim SAMAD
1419709131
editor: structure build-start/end cues + menu hover/click SFX (#378)
* wip sfx

* wip audios

* feat(editor): structure build-start/end cues + menu hover/click SFX

Sound design pass for the build/items experience:

- Split structure-build into a start cue (draft begins) and end cue
  (commit). Wall/fence fire start on the first click; slab/ceiling/zone
  tick start on every non-closing vertex; roof on the first corner.
- Doors and windows now use the structure-end cue on place + move;
  shelves use item-place instead.
- New menu_hover (round-robin retired in favour of a single sample) and
  menu_click cues wired to the Build/Items main categories, item-catalog
  tiles, the icon rail / tab bar, and the bottom action menu (via the
  shared ActionButton).
- Refresh item_pick / item_place / item_rotate samples; drop the
  now-unused numbered menu_hover variations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(editor): silence forEach return lint in updateSFXVolumes

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(editor): convert SFX wavs to mp3 to shrink assets

Re-encode all SFX samples as 128kbps mp3 (~94% smaller, transparent for
short UI cues) and point the player at the .mp3 sources. Drops the .wav
originals; the freshly converted item_pick/place/rotate.mp3 override the
older committed mp3s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 20:15:24 -04:00
Wassim SAMAD
ee98c55b65
Polish: window preset preview + smooth shelf item placement + Shelf build tool (#367)
* fix(nodes,viewer): window preset live wall preview + shaped-cutout move tracking

MoveWindowTool marked the moving window `isTransient` unconditionally, but
WindowSystem only rebuilds the host wall's cutout for non-transient windows —
so a window *preset* (isNew) showed no live hole on the wall and couldn't be
placed consecutively without leaving/re-entering. Guard the transient mark on
`!isNew`, matching MoveDoorTool.

Separately, shaped openings (arch / rounded / `opening`) rebuild their cutout
brush from `node.position`, which a same-wall move doesn't write (it mutates the
mesh directly and publishes to `useLiveTransforms`). The wall-system's
`getEffectiveNode` only merges `useLiveNodeOverrides` (resize arrows), so shaped
cutouts lagged the move while rectangular ones (rebuilt from the live mesh
matrixWorld) tracked. Fold `useLiveTransforms` into door/window children before
collecting cutouts so shaped holes follow the live move too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(editor,viewer,nodes): smooth item placement on shelves

Three issues made hosting an item on a shelf janky (item-on-item was already
smooth because plain items have no `def.geometry` to rebuild):

- Jitter: the draft mesh intercepted the cursor ray, so the shelf-row hit was
  re-derived from the moving item each frame. Disable raycasting on the draft
  during placement (incl. async GLB children, reconciled per frame) so the ray
  passes through to the surface beneath — mirrors MoveRegistryNodeTool.

- Reparent/vanish at the edges: `onShelfLeave` flipped state to floor without
  reparenting the draft off the shelf, so the floor strategy's level-local
  position rendered compounded with the shelf transform. The grid handler now
  owns the shelf→floor transition.

- In/out oscillation: reparenting the draft onto the shelf dirtied the shelf,
  and GeometrySystem disposed+rebuilt its boards, making r3f fire a spurious
  shelf:leave→enter that thrashed placement between the row and the floor. Add
  an opt-in `def.geometryKey` so GeometrySystem skips the rebuild when geometry
  inputs are unchanged (shelf boards don't depend on hosted children). Keep the
  item sticky by testing the cursor ray against the shelf's bounding box: a ray
  that slips through a gap and lands on the floor behind the shelf still counts
  as "on the shelf"; only a ray that misses the shelf detaches to the floor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(editor-app): add Shelf to the standalone editor Build tab

The shelf kind is fully wired (def.tool, presentation icon, StructureTool id)
but was absent from the standalone editor's Build palette. Add it between
Column and Spawn Point. Community has its own build-tab and is left untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(mcp): biome-format coordinate-conventions-demo.json

The committed example violated Biome formatting (expanded polygon arrays),
which broke the `mcp-ci` Biome check on main (#356) and every branch since.
Format it so CI is green. Pure formatting — no content change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 16:10:22 -04:00
Wassim SAMAD
e450d8b474
feat(editor): build sidebar + slimmed items panel for standalone editor (#361)
Bring the open-source standalone editor closer to the community editor's
v2 sidebar without pulling in any preset/catalog infrastructure.

- Add a preset-less Build tab (apps/editor) that mirrors the community
  Build sidebar: wall, fence, slab, ceiling, roof, stair, elevator,
  door, window, column, spawn, plus the material-paint panel. Clicking a
  type activates the raw structure tool drawn with the kind's defaults.
- Wire the Build tab into both editor mount points (local + saved scene)
  and give the left rail proper image icons (Scene/Build/Items/Settings)
  instead of letter fallbacks.
- Gate the ItemsPanel Library/Community/Mine source chips and tag filter
  rows behind `showSourceFilter` / `showTagFilters` props (default true,
  so community and external consumers are unchanged). The standalone
  editor passes both off, leaving plain category tabs + full-width search.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 14:51:09 -04:00