Skip to content

editor: add lean-to roof extensions and automatic drainage - #651

Merged
wass08 merged 58 commits into
pascalorg:mainfrom
sudhir9297:t3code/add-shed-roof-extensions
Aug 19, 2026
Merged

editor: add lean-to roof extensions and automatic drainage#651
wass08 merged 58 commits into
pascalorg:mainfrom
sudhir9297:t3code/add-shed-roof-extensions

Conversation

@sudhir9297

@sudhir9297 sudhir9297 commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

  • Adds configurable wall-hosted lean-to roof extensions with registry-driven 2D/3D placement, movement, snapping, collision validation, selection proxying, parametric controls, framing, supports, flashing, paint slots, and managed roof children.
  • Supports straight and short curved host walls on either side, including inner and outer L/V corner joins. Adjacent extensions coordinate their roof footprints, straight terminal cuts, side infill, rafters, posts, and mitres without duplicate or protruding assembly pieces.
  • Adds automatic roof gutters, outlets, and terrain-aware downspouts. Straight and curved gutter runs are clipped and joined around lean-to corners, generated outlets cut the gutter floor, and manual drainage adjustments remain stable across later roof edits.
  • Keeps derived extension and drainage assemblies synchronized through the shared Scene API, preserves old scenes through schema defaults/migrations, and follows the editor interaction, overlay, snapping, and 2D/3D parity contracts.

How to test

  1. Run bun dev, open the editor, create a building, and place lean-to extensions from the Build panel on both faces of a straight wall; verify previews snap correctly and the extension is selected after placement.
  2. Create internal and external L- and V-shaped wall corners, then place an extension on each adjoining wall. Verify the roof surfaces, flashing, rafters, posts, and gutters meet cleanly with no duplicate pillars, gray infill wedges, circular terminal caps, gaps, or protruding pieces.
  3. Create a 2–3 m curved wall in both inward and outward configurations, connect it to a non-collinear straight wall, and place extensions on both. Verify the curved roof follows a concentric band, ends with a straight joint edge, and mitres into the neighboring straight extension without a triangular hole.
  4. Move and resize extensions in both 3D and floorplan views. Switch snapping modes and dependency locks, and verify the high/low edges, pitch, overhangs, framing, supports, corner joins, and drainage remain synchronized.
  5. Enable automatic gutters and downspouts on straight, segmented, and curved roofs. Verify gutters form connected corner joints, each generated outlet opens the gutter floor, and downspouts reach the resolved slab or terrain ground. Move a generated outlet/downspout manually and verify the adjustment survives another roof edit.
  6. Run bun run lint, bun run check, bun run check-types, bun run test, and bun run build.

Screenshots / screen recording

Recording to be added — this is a visual and interactive change.

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

Note

High Risk
Large cross-cutting change to scene mutations, roof drainage generation, and new lean-to assembly sync; bugs could corrupt child trees, duplicate/delete gutters incorrectly, or desync 2D/3D edits.

Overview
Introduces a lean-to-extension node (wall child, clone remap for host roof refs, canvas selection proxy to the extension) and surfaces it under Build → Roof Features & extensions via presentation.paletteGroup: 'roof-features'.

Automatic drainage on roof segments: autoGutter metadata drives generated gutter runs (roof-type sides, trim/sibling clipping, lean-to edge exclusions), planAutomaticDownspouts placement, and default downspouts with lengthMode: 'to-ground' and terrain-aware length resolution. Store create/update/delete paths refresh gutters and downspouts while preserving manual gutters and nearest-run ID stability.

Gutter/downspout modeling gains concentric arc runs (geometry bend, outlet lookup, corner mitres), lean-to-prescribed eave height/mitres metadata, grid/magnetic length handles, and a downspout system that keeps automatic lengths in sync via SceneApi.subscribeNodes / applyChanges.

SceneApi expands with createMany, applyChanges, and subscribeNodes; floorplan affordances/move targets and registry move tools receive sceneApi. Parametric derive can take previous; lean-to managed columns hide layout edits and trim handles.

Roof segments add optional arc and shed infill fields; accessory kinds get dedicated palette icons. Editor polish: resize snap non-finite fallback, contextual helpers during arrow resize, disabled segmented controls.

Reviewed by Cursor Bugbot for commit 0c7eb6a. Bugbot is set up for automated code reviews on this repo. Configure here.

sudhir9297 and others added 30 commits May 19, 2026 02:59
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>
Comment thread packages/nodes/src/lean-to-extension/system.tsx
Comment thread packages/nodes/src/lean-to-extension/roof-attachment.ts Outdated
Comment thread packages/nodes/src/lean-to-extension/system.tsx
sudhir9297 and others added 7 commits August 17, 2026 15:36
Represent the host wall's curve as a true-arc descriptor (center + radius)
in each managed node's local frame, replacing the scalar sagitta that was
re-derived through three synthetic chords and ballooned the deck into a
filled disc. Every consumer — deck, gutter, downspout outlet, posts/beam/
rafters, and the 2D floorplan — now sweeps one shared concentric circle, so
the canopy hugs the arc as a thin band for any extent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
if (parametrics?.derive && node) {
const next = { ...node, ...patch } as AnyNode
patch = { ...patch, ...parametrics.derive(next, patch) }
patch = { ...patch, ...parametrics.derive(next, patch, node as AnyNode) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-edit skips derive previous

Medium Severity

derive now takes a previous node so resize locks can compare pre-edit values, and the single-node inspector passes it, but buildMultiNodePatches still calls derive(next, patch) only. Lean-to derive defaults previous to next, so multi-select projection/pitch/height edits apply locks against already-patched values and can preserve the wrong edge.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 732accb. Configure here.

Comment thread packages/core/src/schema/nodes/automatic-downspout.ts
Bend the shared corner post and corner rafter onto the wall arc like
every other member, instead of placing them at raw flat arc-length
coordinates. Identity for straight walls.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread packages/core/src/schema/nodes/automatic-downspout.ts
Comment thread packages/core/src/store/actions/node-actions.ts
Address Cursor Bugbot findings: curved-gutter downspout mapping and
prescribed eave-Y, nearest-gutter reuse on segment refresh, asymmetric
overhang reach in roof-edge attachment, manual-mode no longer silently
reattaches, terrain/slab changes now re-trigger lean-to reconcile, and
multi-node patches derive from the pre-patch node.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(prescribedGutterEaveY(gutter) ?? computeGutterEaveY(segment)) -
Math.max(0.04, gutter.size)

return Math.max(0.1, outletWorldY - groundY)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Downspouts ignore slab ground

High Severity

Automatic downspout length samples only persisted terrain (or 0) and never the slab under the drop. Posts already resolve slab support and level-base elevation, so pipes keep falling through decks and patios, and later slab moves are not treated as length dependencies.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit baaa4f2. Configure here.

sudhir9297 and others added 2 commits August 19, 2026 22:31
Resolve the quality CI failure: apply Biome formatter to the downspout
and gutter-refresh edits and sort the lean-to system imports.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The test assembles and renders a full gutter joint across ~127 angles,
running ~5s and tripping bun's 5000ms default per-test timeout on slower
CI hardware. Give it an explicit 30s budget; assertions are unchanged.

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

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0c7eb6a. Configure here.

leftMitre = mitreBetween(subj.minus, otherEnd)
}
if (rightMitre === 0 && planDistSq(subj.plus.pos, corner) <= CORNER_EPSILON_SQ) {
if (planDistSq(subj.plus.pos, corner) <= CORNER_EPSILON_SQ) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prescribed gutter mitres exit too early

High Severity

computeGutterMitres now seeds both ends from prescribed lean-to mitres and still breaks once both values are nonzero. Adjacent lean-to runs therefore stop scanning after the first sibling, overwrite a prescribed corner with a geometric hit inside the 10 cm epsilon, or skip the true mate. Joined eaves can get the wrong cut or keep a cap.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0c7eb6a. Configure here.

@wass08
wass08 merged commit 0ce54ba into pascalorg:main Aug 19, 2026
4 checks passed
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.

2 participants