fix: harden measurement geometry (#507)

This commit is contained in:
Aymeric Rabot
2026-07-18 09:44:34 +02:00
committed by GitHub
parent 99cc537e25
commit cddb73b232
9 changed files with 592 additions and 117 deletions
+3 -3
View File
@@ -28,9 +28,9 @@ Smart pointer handling is latest-event-wins. One shared animation-frame schedule
Zone visuals intentionally unmount outside zone presentation, so a 3D Smart floor hit cannot depend only on a zone mesh. When the visible hit is an upward/downward slab face, Smart checks active-level zone polygons in level-local plan space and resolves the smallest containing zone. It never replaces a wall hit. When zone geometry is mounted, a nearly coplanar zone may also win over its slab within the bounded surface tolerance.
`deriveZoneQuantityReport` is a pure, conservative Core report. Footprint area, perimeter, and individual edge lengths are always available. Enclosed-room classification requires a matching detected wall loop; gross wall face surface comes only from that boundary. Floor surface requires exactly one matching slab and subtracts its holes. Volume additionally requires one matching flat upper surface and a positive clear height. Missing or ambiguous evidence returns a user-facing unavailable reason rather than a plausible estimate.
`deriveZoneQuantityReport` is a pure, conservative Core report. Footprint area, perimeter, and individual edge lengths are always available. Enclosed-room classification requires either a matching detected wall loop or complete wall coverage of the closed zone boundary within the modeling tolerance. Detected loops contribute their ordered boundary-face polylines, so concave rooms, curved segments, T-junction spans, and unequal per-wall heights do not have to be reconstructed from a simplified polygon. Floor surface requires exactly one slab that covers the zone and subtracts holes contained by the zone; the slab may serve a larger level footprint. Volume additionally requires one covering flat upper surface and a positive clear height. Missing or ambiguous evidence returns a user-facing unavailable reason rather than a plausible estimate.
The zone parametric inspector derives this report from the current zone polygon and scene nodes, then renders a compact top-view SVG with every edge dimension plus wall, floor, and volume rows. None of these values are persisted in `ZoneNode.metadata`, represented by hidden measurement nodes, or written during rendering. Net opening subtraction, retained partial wall spans, and sloped upper surfaces remain future topology work.
The zone parametric inspector derives this report from the current zone polygon and scene nodes, then renders a compact top-view SVG with every edge dimension plus wall, floor, and volume rows. None of these values are persisted in `ZoneNode.metadata`, represented by hidden measurement nodes, or written during rendering. Net wall-opening subtraction, normalized persisted span parameters, and sloped upper surfaces remain future topology work.
An exact room-footprint zone may be procedural. Space detection retains the wall IDs traversed by the proving half-edge cycle; the zone stores those IDs in `boundaryWallIds` with `autoFromWalls`. Its 2D/3D geometry, Smart report, semantic features, and quantity report resolve the current polygon from those effective walls, including per-wall live overrides. Pointer movement does not write the scene. The wall commit refreshes the stored polygon as a fallback while scene history is paused. Moving or editing the zone itself clears the association, so a deliberate manual boundary cannot be snapped back by room reconciliation. Site/lawn zones that do not exactly match a detected enclosure remain manual.
@@ -103,7 +103,7 @@ Measurement snapping is always magnetic. The construction snapping-mode chip (`g
The registered 3D tool raycasts visible geometry under registered scene roots and converts the winning world-space point and normal into the active level frame. A system-rendered mesh outside those roots must opt in with `userData.measurementSurface = true`; this is the contract used by collective instanced plant meshes. An editor-helper root nested under registered geometry must opt out with `userData.measurementSurface = false`, which is inherited by its descendants. Measurement activation clears object selection, and measurement/guide/scan nodes, invisible objects, zero-opacity or non-depth-tested materials, and `colorWrite: false` colliders are excluded. Instanced hits must include the intersected instance matrix when their normals are transformed.
Area, perimeter, and volume drafting treat the first surface as intent. Before the first point, a horizontal slab, ceiling, or site surface may outrank a wall that occludes it by no more than 0.45 metres along the pointer ray, which makes floor corners stable without turning a mid-wall hover into a floor pick. The first committed contact then supplies a preferred plane; nearby hits with a matching normal and plane outrank incidental wall edges for the rest of that polygon. Holding Alt bypasses this preference and restores the raw nearest visible surface. Distance and angle remain nearest-surface tools.
Area, perimeter, and volume drafting treat the first surface as a hard reference plane. Before the first point, a horizontal slab, ceiling, or site surface may outrank a wall that occludes it by no more than 0.45 metres along the pointer ray, which makes floor corners stable without turning a mid-wall hover into a floor pick. The first committed contact then supplies the plane for every later vertex and draft edit. Pointer queries accept only hits on that plane, even through a nearer occluder; when the pointer ray has no matching surface hit, no candidate is offered. Alt still releases magnetic axes and feature attraction, but never releases the captured polygon plane. The shared draft store projects accepted points and feature fallbacks onto the plane as a final invariant. Distance and angle remain nearest-surface tools.
Axis assistance starts from the previous vertex. X, Y, or Z becomes a snap only when the projected candidate is verified on the hit surface within the screen-space threshold. Otherwise the raw surface hit remains authoritative and the nearest axis is shown as a passive guide. A magnetic lock enters at 16 screen pixels and retains the same axis and anchor until 24 pixels; it must release immediately if that candidate no longer verifies on the surface.