Skip to content

feat(mcp): detect floorplan openings stairs and furniture - #875

Open
zoechow412-ui wants to merge 1 commit into
pascalorg:mainfrom
zoechow412-ui:feat/photo-to-scene-floorplan-furniture
Open

zoechow412-ui wants to merge 1 commit into
pascalorg:mainfrom
zoechow412-ui:feat/photo-to-scene-floorplan-furniture

Conversation

@zoechow412-ui

@zoechow412-ui zoechow412-ui commented Sep 15, 2026

Copy link
Copy Markdown

What does this PR do?

How to test

Screenshots / screen recording

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

Medium Risk
Expands vision parsing and scene graph construction (wall parenting, placement heuristics); failures are mostly soft via notes, but bad placements or opening attachment could produce misleading scenes.

Overview
Extends floor-plan vision and photo_to_scene so a single photo can yield more than walls and rooms: doors, windows, stairs, and catalog furniture.

Vision contract: analyze_floorplan_image and the photo_to_scene sampling prompt/Zod schema now include optional doors, windows, stairs, and furniture arrays (defaults to empty for backward-compatible JSON).

Scene build: After walls/zones, openings are snapped to the nearest wall (skipped with notes if farther than ~0.75 m), converted to wall-local DoorNode / WindowNode, stairs become StairNode + StairSegmentNode, and furniture is resolved via catalog aliases then placed with existing layout-clearance (findValidPlacement, door keepouts, room bounds). Unmatched or unplaceable items are recorded in notes, not silently dropped.

API surface: Tool responses add doors, windows, stairs, and furniture counts alongside walls / rooms; docs and the happy-path test mock the richer vision payload and assert the new node types in the bridge.

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

@pascal

pascal Bot commented Sep 15, 2026

Copy link
Copy Markdown

I hit an error while handling your request (Model unavailable on AI Gateway free tier: Free tier users do not have access to this model. Upgrade to paid credits at https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dtop-up for unrestricted…).

Please try again, rephrase, or reach out if it keeps failing.

Error id: 79791c5c-5f59-484e-8476-c41244f630fc

@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.

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 596a8db. Configure here.

}).map((entry) => entry.aabb)
const doorKeepouts = collectDoorKeepouts(Object.values(nodes), { levelId }).map(
(entry) => entry.aabb,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Door keepouts ignored during placement

High Severity

Furniture placement calls collectDoorKeepouts with a levelId filter before the level node is inserted into nodes. resolveNodeLevelId then returns null for every wall, so the keepout list is always empty and items can sit in door swing zones.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 596a8db. Configure here.

nodes[stair.id as AnyNodeId] = linkedStair as AnyNodeT
nodes[segment.id as AnyNodeId] = segment as AnyNodeT
levelChildren.push(stair.id)
stairsAdded++

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stairs and furniture missing parentId

Medium Severity

New StairNode and ItemNode records are pushed onto levelChildren without parentId, unlike walls and zones in the same builder. Core spatial-grid lookup only walks parentId and then treats them as level default, so editor queries and elevation can miss them.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 596a8db. Configure here.

const door = DoorNode.parse({
wallId: wall.id,
parentId: wall.id,
position: [localX, height / 2, 0],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Openings can exceed host wall

Low Severity

When a detected door or window is wider than its nearest wall, localX still clamps to width / 2 and the opening is created. cut_opening rejects that case; here the opening extends past both wall ends.

Additional Locations (1)
Fix in Cursor Fix in Web

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