Move item dimensions into local item overlays

This commit is contained in:
sudhir
2026-04-27 10:57:50 +05:30
parent e4f42e0ee5
commit a796c0fd0a
2 changed files with 40 additions and 27 deletions
@@ -4137,6 +4137,7 @@ const FloorplanNodeLayer = memo(function FloorplanNodeLayer({
palette, palette,
selectedIdSet, selectedIdSet,
stairEntries, stairEntries,
unit,
}: { }: {
canFocusItems: boolean canFocusItems: boolean
canFocusStairs: boolean canFocusStairs: boolean
@@ -4161,12 +4162,15 @@ const FloorplanNodeLayer = memo(function FloorplanNodeLayer({
palette: FloorplanPalette palette: FloorplanPalette
selectedIdSet: ReadonlySet<string> selectedIdSet: ReadonlySet<string>
stairEntries: FloorplanStairEntry[] stairEntries: FloorplanStairEntry[]
unit: 'metric' | 'imperial'
}) { }) {
if (itemEntries.length === 0 && stairEntries.length === 0) { if (itemEntries.length === 0 && stairEntries.length === 0) {
return null return null
} }
const itemNodes = itemEntries.map(({ item, points, polygon }) => { const itemNodes = itemEntries.map((itemEntry) => {
const { item, points, polygon } = itemEntry
const itemDimensionMeasurements = getItemDimensionMeasurementOverlays(itemEntry, unit)
const isSelected = selectedIdSet.has(item.id) const isSelected = selectedIdSet.has(item.id)
const isHighlighted = highlightedIdSet.has(item.id) const isHighlighted = highlightedIdSet.has(item.id)
const isHovered = hoveredItemId === item.id const isHovered = hoveredItemId === item.id
@@ -4309,6 +4313,13 @@ const FloorplanNodeLayer = memo(function FloorplanNodeLayer({
y2={toSvgY(diagonalBEnd.y)} y2={toSvgY(diagonalBEnd.y)}
/> />
)} )}
{itemDimensionMeasurements.length > 0 ? (
<FloorplanMeasurementsLayer
className="item-dimension-measurement"
measurements={itemDimensionMeasurements}
palette={palette}
/>
) : null}
</g> </g>
) )
}) })
@@ -6776,15 +6787,6 @@ export function FloorplanPanel() {
isFenceEndpointMoveActive || isFenceEndpointMoveActive ||
isFloorItemBuildActive || isFloorItemBuildActive ||
isFloorItemMoveActive isFloorItemMoveActive
const itemPlacementDimensionMeasurements = useMemo(() => {
if (!isItemPlacementPreviewActive) {
return [] as LinearMeasurementOverlay[]
}
return floorplanItemEntries.flatMap((itemEntry) =>
getItemDimensionMeasurementOverlays(itemEntry, unit),
)
}, [floorplanItemEntries, isItemPlacementPreviewActive, unit])
const floorplanPreviewStairSegment = useMemo( const floorplanPreviewStairSegment = useMemo(
() => () =>
StairSegmentNodeSchema.parse({ StairSegmentNodeSchema.parse({
@@ -11988,6 +11990,7 @@ export function FloorplanPanel() {
palette={palette} palette={palette}
selectedIdSet={selectedIdSet} selectedIdSet={selectedIdSet}
stairEntries={renderedFloorplanStairEntries} stairEntries={renderedFloorplanStairEntries}
unit={unit}
/> />
<FloorplanRoofLayer <FloorplanRoofLayer
@@ -11996,12 +11999,6 @@ export function FloorplanPanel() {
selectedIdSet={selectedIdSet} selectedIdSet={selectedIdSet}
/> />
<FloorplanMeasurementsLayer
className="item-dimension-measurement"
measurements={itemPlacementDimensionMeasurements}
palette={palette}
/>
<FloorplanMeasurementsLayer <FloorplanMeasurementsLayer
className="opening-placement-dimension" className="opening-placement-dimension"
measurements={movingOpeningPlacementMeasurements} measurements={movingOpeningPlacementMeasurements}
@@ -17,8 +17,8 @@ import {
} from '@pascal-app/core' } from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer' import { useViewer } from '@pascal-app/viewer'
import { Html } from '@react-three/drei' import { Html } from '@react-three/drei'
import { useFrame } from '@react-three/fiber' import { createPortal, useFrame } from '@react-three/fiber'
import { useEffect, useRef } from 'react' import { useEffect, useRef, useState } from 'react'
import { import {
BoxGeometry, BoxGeometry,
Box3, Box3,
@@ -221,6 +221,10 @@ export function usePlacementCoordinator(config: PlacementCoordinatorConfig): Rea
const previewBoundsSignatureRef = useRef<string | null>(null) const previewBoundsSignatureRef = useRef<string | null>(null)
const meshPreviewAppliedRef = useRef(false) const meshPreviewAppliedRef = useRef(false)
const dimensionBoundsRef = useRef<PreviewBounds | null>(null) const dimensionBoundsRef = useRef<PreviewBounds | null>(null)
const [measurementTargetState, setMeasurementTargetState] = useState<{
id: string
object: Object3D
} | null>(null)
// Store config callbacks in refs to avoid re-running effect when they change // Store config callbacks in refs to avoid re-running effect when they change
const configRef = useRef(config) const configRef = useRef(config)
@@ -1172,6 +1176,12 @@ export function usePlacementCoordinator(config: PlacementCoordinatorConfig): Rea
if (!draftNode.current) return if (!draftNode.current) return
const mesh = sceneRegistry.nodes.get(draftNode.current.id) const mesh = sceneRegistry.nodes.get(draftNode.current.id)
if (!mesh) return if (!mesh) return
if (
measurementTargetState?.id !== draftNode.current.id ||
measurementTargetState.object !== mesh
) {
setMeasurementTargetState({ id: draftNode.current.id, object: mesh })
}
if (!meshPreviewAppliedRef.current) { if (!meshPreviewAppliedRef.current) {
const previewBounds = getPreviewBoundsFromObject(mesh) const previewBounds = getPreviewBoundsFromObject(mesh)
@@ -1207,10 +1217,6 @@ export function usePlacementCoordinator(config: PlacementCoordinatorConfig): Rea
draftNode.current.rotation, draftNode.current.rotation,
) )
mesh.position.y = slabElevation mesh.position.y = slabElevation
// Cursor group is at the world root (not inside a level group), so add the
// level group's current world Y to convert from level-local to world space.
const levelGroup = sceneRegistry.nodes.get(levelId as AnyNodeId)
cursorGroupRef.current.position.y = slabElevation + (levelGroup?.position.y ?? 0)
} }
} }
}) })
@@ -1247,11 +1253,12 @@ export function usePlacementCoordinator(config: PlacementCoordinatorConfig): Rea
initialDimensionBounds.center[2] - initialDimensionBounds.dimensions[2] / 2, initialDimensionBounds.center[2] - initialDimensionBounds.dimensions[2] / 2,
] ]
return ( const measurementTarget =
<group ref={cursorGroupRef}> draftNode.current && measurementTargetState?.id === draftNode.current.id
<lineSegments layers={EDITOR_LAYER} material={edgeMaterial} ref={edgesRef} renderOrder={999}> ? measurementTargetState.object
<edgesGeometry args={[initialBoxGeometry]} /> : null
</lineSegments> const measurementContent = (
<>
<lineSegments <lineSegments
layers={EDITOR_LAYER} layers={EDITOR_LAYER}
material={measurementMaterial} material={measurementMaterial}
@@ -1330,6 +1337,15 @@ export function usePlacementCoordinator(config: PlacementCoordinatorConfig): Rea
{heightLabel} {heightLabel}
</div> </div>
</Html> </Html>
</>
)
return (
<group ref={cursorGroupRef}>
<lineSegments layers={EDITOR_LAYER} material={edgeMaterial} ref={edgesRef} renderOrder={999}>
<edgesGeometry args={[initialBoxGeometry]} />
</lineSegments>
{measurementTarget ? createPortal(measurementContent, measurementTarget) : measurementContent}
<mesh <mesh
geometry={basePlaneGeometry} geometry={basePlaneGeometry}
layers={EDITOR_LAYER} layers={EDITOR_LAYER}