Skip to content

editor: add measurement tool#491

Open
sudhir9297 wants to merge 42 commits into
pascalorg:mainfrom
sudhir9297:feat/measurement-tool
Open

editor: add measurement tool#491
sudhir9297 wants to merge 42 commits into
pascalorg:mainfrom
sudhir9297:feat/measurement-tool

Conversation

@sudhir9297

@sudhir9297 sudhir9297 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds a registry-driven measurement tool for 2D and 3D editing, including distance, area, perimeter, and angle measurements; snap feedback and persisted measurement history; per-kind measurement definitions for built-in nodes; and consistent thinner measurement stroke styling across measurement overlays.

How to test

  1. Run bun run lint and bun run check; both should complete without fixes.
  2. Run bun run check-types and bun run build; both should complete successfully.
  3. In the editor, choose the Measurement tool from Build tools and verify distance, area, perimeter, and angle measurements can be created in 2D/3D, snap to registry-provided node geometry, appear in the measurement history panel, and persist after save/reload.

Screenshots / screen recording

Not added yet — this is a visual/interactive measurement tool 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

Medium Risk
Large editor/scene-graph feature with new node type and broad parent children schema updates; persistence and interaction paths need careful regression, but no auth or payment surface.

Overview
Introduces a registry-driven measurement system so editors can draw and persist length, area, perimeter, and angle readouts in 2D floorplan and 3D, instead of ad hoc dimension UI only.

Schema & core: New measurement nodes (MeasurementNode, attachments to geometry) can hang under levels, walls, slabs, stairs, cabinets, etc. NodeDefinition.measurement supplies snap anchors, direct length, area, and perimeter per node kind. The event bus and scene registry gain measurement events and a nodeIds reverse map so nested R3F picks resolve to the owning scene node.

Editor UX: Measure appears on the Build tab; the viewer menu adds a Measurements visibility toggle. A Measurement history panel controls mode, precision, chained measurements, snap kinds, and inline edits. MeasurementSceneGraphSync keeps the tool store and scene graph aligned for save/reload.

2D: FloorplanMeasurementToolLayer handles grid clicks, snapping (walls, roofs, MEP, intersections, guides), endpoint drag, hover previews, and zoom-stable HTML pill labels (shared with 3D styling via DimensionPillShell).

Misc: Group bbox helpers depend on meshEpoch without biome ignore comments; next.config adds allowedDevOrigins for 127.0.0.1.

Reviewed by Cursor Bugbot for commit ab131b0. 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>
# Conflicts:
#	packages/core/src/registry/types.ts
#	packages/editor/src/components/editor-2d/floorplan-registry-action-menu.tsx
#	packages/editor/src/components/editor-2d/renderers/floorplan-registry-layer.tsx
#	packages/editor/src/components/editor/node-arrow-handles.tsx
#	packages/editor/src/components/ui/sidebar/use-plugin-panels.tsx
#	packages/editor/src/lib/measurements.test.ts
#	packages/editor/src/lib/measurements.ts
#	packages/plugin-trees/src/index.ts
const measurement = useMeasurementTool.getState()
measurement.setPreviewArea(null)
measurement.setPreviewPerimeter(null)
measurement.setPreviewSegment(null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Grid move clears hover previews

Medium Severity

Measurement hover previews for nodes (distance, area, perimeter) don't appear in 2D. The grid:move handler clears previewArea, previewPerimeter, and previewSegment at the start, immediately wiping any previews set by pointermove events.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5d87835. Configure here.

measurement.commitAngle(point)
} else {
measurement.beginAngle('2d', point, target?.targetLine ?? null)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Collinear wall snaps zero angle

Medium Severity

When placing a 2D angle on a straight host (e.g. a wall), the second click can sit on the same ray as the reference leg. commitAngle then sets first and second to the same plan point, so angleBetweenMeasurements reports 0° instead of the corner angle users expect along that edge.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5d87835. Configure here.

measurement.commitAngle(point)
} else {
measurement.beginAngle('2d', point, target?.targetLine ?? null)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Angle start ignores guide lines

Low Severity

Starting a 2D angle passes only target?.targetLine into beginAngle. Parallel and perpendicular snaps expose their geometry on guideLine, so the reference leg falls back to the generic grid axis instead of the host edge the snap indicator already highlights.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5d87835. Configure here.

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

There are 7 total unresolved issues (including 3 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.

Reviewed by Cursor Bugbot for commit ab131b0. Configure here.

},
],
selectedId: id,
}))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Colinear angle collapses leg

Medium Severity

When committing an angle, if the second point falls on the reference line, referencePointForAngle can cause the first and second legs to align. This results in a degenerate 0° angle measurement, displaying an empty or misleading arc.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ab131b0. Configure here.

hydrateMeasurements(sceneGraph.measurements)
} else {
useScene.getState().clearScene()
hydrateMeasurements(null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Measurements dropped without field

High Severity

On load, editor state is restored only from sceneGraph.measurements, not from measurement nodes already in nodes. MeasurementSceneGraphSync then rebuilds scene nodes from an empty tool store and removes any persisted measurement_* entries, so saves missing the measurements field lose linear measurements silently.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ab131b0. Configure here.


if (!parentId) {
rootNodeIds.push(id)
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.

Orphan measurements at scene root

Medium Severity

Linear measurements without snap attachments get parentId: null and are appended to rootNodeIds. They are not parented under the active level or building, so the scene tree, outliner, and hierarchy-aware features can misplace or hide them compared to other level content.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ab131b0. Configure here.

}, [draggingSegmentEndpoint, segments])

useEffect(() => () => syncLinearMeasurementSceneNodes([]), [])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unmount strips scene measurement nodes

Medium Severity

MeasurementSceneGraphSync runs syncLinearMeasurementSceneNodes([]) on unmount, removing every measurement node from useScene while useMeasurementTool segments are unchanged. Remounts restore them, but any autosave or UI that reads the scene graph between unmount and resync can persist or show a graph missing synced measurement nodes even though the measurements store still has data.

Fix in Cursor Fix in Web

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