Skip to content

editor: keep room surfaces in sync with wall topology - #554

Open
sudhir9297 wants to merge 37 commits into
pascalorg:mainfrom
sudhir9297:floor-cutout
Open

editor: keep room surfaces in sync with wall topology#554
sudhir9297 wants to merge 37 commits into
pascalorg:mainfrom
sudhir9297:floor-cutout

Conversation

@sudhir9297

@sudhir9297 sudhir9297 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

  1. Splits divider walls and connected boundary walls into correct topology.

    • Splits an existing wall when a new divider connects to the middle of it.
    • Splits both boundary walls when a divider runs from one side of a room to the opposite side.
    • Splits crossing divider walls at their interior intersection so four wall segments join at the same center point.
    • Splits a newly drawn wall into multiple committed segments when it crosses more than one existing wall.
    • Prevents a wall from being created when existing collinear wall segments already cover the full requested segment.
    • Keeps the entire multi-wall split and create operation in a single undo-history step.
    • Allows repeated divider creation and deletion while rejoining the previously split boundary walls.
  2. Adds accurate support for dividers connected to curved walls.

    • Projects the divider endpoint directly onto the mathematical wall arc instead of relying on sampled approximation points.
    • Splits the curved host wall at the exact arc parameter.
    • Recomputes the two child curve offsets so both pieces preserve the original arc shape.
    • Treats a connection to a curved wall interior as a completed wall-chain junction.
    • Migrates doors, windows, and other wall-hosted children to the correct split segment.
    • Remaps hosted-node positions using arc length so attachments keep their physical location on the curve.
  3. Creates one generated slab and ceiling for every detected room.

    • Reconciles room surfaces after a straight or diagonal divider splits one room into multiple rooms.
    • Produces two slabs and two ceilings for a single divider split.
    • Produces four slabs and four ceilings when two crossing dividers create four rooms.
    • Adds every generated surface to the level's children so the scene tree matches the visible room topology.
    • Waits for the coalesced scene commit before running space detection, avoiding reconciliation against intermediate half-split wall graphs.
  4. Preserves slab customization when a room is split.

    • Carries forward elevation, thickness, recessed state, visibility, legacy material fields, material presets, and material slots.
    • Reuses the original slab for one resulting room and creates inherited slabs for the remaining rooms.
    • Partitions slab openings between the resulting rooms instead of copying every opening onto every slab.
    • Preserves opening metadata, including manual, stair, and elevator opening ownership.
  5. Preserves ceiling customization when a room is split.

    • Carries forward explicit height, visibility, legacy material fields, material presets, and material slots.
    • Keeps uncustomized generated ceilings in automatic follow-height mode.
    • Clamps inherited explicit heights against the valid ceiling bound of each resulting room.
    • Partitions ceiling openings and their metadata so each opening remains with the room that contains it.
  6. Handles slab and ceiling merges without losing user intent.

    • Detects compatible generated surfaces that contribute to a newly merged room.
    • Keeps one compatible surface as the survivor and removes redundant generated surfaces.
    • Unions compatible slabs' and ceilings' openings and preserves their opening metadata.
    • Detects conflicting finish or geometry settings between surfaces being merged.
    • Demotes conflicting generated surfaces to manual surfaces instead of deleting one or forcing both into a single configuration.
    • Avoids generating an overlapping replacement surface when conflicting manual surfaces are intentionally preserved.
  7. Keeps deliberately deleted generated surfaces deleted.

    • Detects when the user deletes an automatically generated slab or ceiling.
    • Stores suppression independently for slabs and ceilings on the owning level.
    • Keys suppression to the room footprint rather than the deleted node ID.
    • Prevents the surface from being recreated during later wall or room synchronization.
    • Persists suppression in scene data so deletion remains effective after synchronization is reinitialized or the page is reloaded.
    • Ignores paused/AI-driven synchronization changes so programmatic scene construction is not mistaken for a user deletion.
  8. Improves room detection across boundary sizes and shapes.

    • Removes the old lower-area cutoff that rejected valid rooms smaller than 0.5 m².
    • Removes the old upper-area cutoff that rejected valid rooms larger than 10,000 m².
    • Continues planning generated slabs and ceilings for these valid closed loops.
    • Preserves curved and simplified room boundaries when matching generated surfaces.
  9. Keeps 2D and mounted wall-tool continuation behavior aligned.

    • Stops the 2D draft when the mounted wall tool closes a loop, closes a room, reaches a T-junction, or finishes in single-segment mode.
    • Continues from the exact next start point published by the mounted wall tool when chaining remains active.
    • Leaves the 2D-only fallback committer responsible for its locally created wall and its own continuation decision.
    • Prevents the floor-plan draft from continuing from a stale endpoint after the mounted tool has intentionally ended the chain.
  10. Adds regression coverage for the complete workflow.

    • Covers straight, diagonal, crossing, curved, repeated-delete, tiny-room, and very-large-room wall scenarios.
    • Covers slab and ceiling split inheritance, merge compatibility, conflicting customization, and opening distribution.
    • Covers generated-surface deletion during the current session, after another wall edit, and after synchronization reinitialization.
    • Covers tree-graph child counts and single-undo behavior for multi-node divider commits.

How to test

  1. Run bun run check-types, bun run check, bun run lint, and bun run build; all commands should pass.
  2. Start the editor with bun dev, create a closed four-wall room, and confirm the tree contains one generated slab and one generated ceiling.
  3. Draw a divider from the middle of one boundary wall to the opposite boundary wall.
    • Confirm both boundary walls split at the junctions.
    • Confirm the tree contains two slabs and two ceilings.
    • Confirm each surface can receive a different material.
  4. Draw two diagonal dividers that cross inside the room.
    • Confirm both dividers split at the crossing.
    • Confirm four wall segments meet at the same center point.
    • Confirm the tree contains four slabs and four ceilings.
  5. Connect a divider to the middle of a curved wall.
    • Confirm the endpoint lands exactly on the curve.
    • Confirm the curved wall becomes two continuous curve segments.
    • If the curve hosts a door or window, confirm it stays at the same physical location on the correct segment.
  6. Customize a generated slab and ceiling, add openings, and then split the room.
    • Confirm material, slots, visibility, slab dimensions/elevation, and ceiling height are inherited.
    • Confirm each opening and its metadata appears only on the resulting room that contains it.
  7. Remove a divider to merge customized rooms.
    • Confirm compatible surfaces merge and their openings are combined.
    • Confirm surfaces with conflicting settings remain as separate manual surfaces without losing customization.
  8. Delete an automatically generated slab and ceiling, make another wall edit, and reload the page.
    • Confirm neither deleted surface is regenerated.
    • Confirm deleting only one surface type does not suppress the other type.
  9. Create a valid closed room smaller than 0.5 m² and another larger than 10,000 m².
    • Confirm both rooms are detected.
    • Confirm each receives its generated slab and ceiling.
  10. Verify wall drafting in split and 2D-only views.
    • Confirm chaining continues from the committed endpoint when appropriate.
    • Confirm it stops after a loop close, room close, T-junction, or single-segment commit.
    • Confirm each multi-segment divider operation produces one undo step.

Screenshots / screen recording

Browser-verified locally. A screenshot or short recording can be added during review if needed.

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
Touches core scene graph reconciliation, level metadata persistence, and wall commit/split behavior—bugs could corrupt rooms, resurrect deleted surfaces, or misplace attachments.

Overview
Keeps auto slabs and ceilings aligned when rooms split, merge, or when users delete generated surfaces. Planners now inherit materials, elevations/heights, and partition or union openings across splits and compatible merges; conflicting merged finishes are demoted to manual instead of forced into one surface. Deleted auto surfaces are recorded on the level as autoSurfaceSuppressions so they are not recreated on later wall edits or after reinitializing sync. Room detection no longer drops very small or very large closed loops.

Space detection sync waits for coalesced scene commits (skipping mid-transaction store updates) so divider commits that split walls and add segments do not reconcile against half-written topology.

Wall commits can emit multiple segments when a draft crosses existing walls (splitting hosts at intersections), with curved-wall splits preserving arc geometry and migrating doors/windows by arc length. 2D floor-plan drafting uses shouldStopWallDraftAfterCommit so chain termination matches the mounted wall tool.

Reviewed by Cursor Bugbot for commit b010bb1. 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>

@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 3 potential issues.

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit b010bb1. Configure here.

if (conflictingMergeSlabIds.has(slab.id)) {
slabDemotions.push({ id: slab.id, data: { autoFromWalls: false } })
continue
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suppression deletes restored auto surfaces

High Severity

Suppressed room signatures are excluded from matching in detected, but orphan cleanup still measures coverage against detectedAll. An auto slab or ceiling that still exists for a suppressed room—most commonly after undo restores a deleted generated surface while level autoSurfaceSuppressions remain—never rematches and is deleted as a merge orphan, so undo cannot bring the surface back.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b010bb1. Configure here.

start: segmentStart,
end: vertices[index + 1]!,
}),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Crossing splits mint sliver walls

Medium Severity

Interior crossing splits use a 1e-6 parametric epsilon and then create every [resolvedStart, ...splitPoints, resolvedEnd] segment with no WALL_MIN_LENGTH check. A crossing near an existing endpoint can split the host and divider into sub-minimum sliver walls, unlike endpoint splitting which rejects splits within WALL_SPLIT_ENDPOINT_EPSILON.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b010bb1. Configure here.

// decision in split and 2D-only views. It clears the published chain
// start whenever it stops drafting (loop close, room close,
// T-junction, single). Mirror that here instead of chaining the 2D
// draft from a dead point.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Failed 2D commits abort wall chains

Medium Severity

In 2D-only mode, a null createWallOnCurrentLevel result now hits shouldStopWallDraftAfterCommit and clears the draft. Previously the viewIs2DOnly guard skipped that branch, so a rejected commit kept the chain alive. Duplicate or fully covered segment attempts therefore discard an in-progress wall chain.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b010bb1. Configure here.

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