remove isEditor for site-edge-labels
This commit is contained in:
@@ -1,6 +1,14 @@
|
||||
'use client'
|
||||
|
||||
import { type AnyNode, type AnyNodeId, ItemNode, WindowNode, DoorNode, sceneRegistry, useScene } from '@pascal-app/core'
|
||||
import {
|
||||
type AnyNode,
|
||||
type AnyNodeId,
|
||||
DoorNode,
|
||||
ItemNode,
|
||||
sceneRegistry,
|
||||
useScene,
|
||||
WindowNode,
|
||||
} from '@pascal-app/core'
|
||||
import { useViewer } from '@pascal-app/viewer'
|
||||
import { Html } from '@react-three/drei'
|
||||
import { useFrame } from '@react-three/fiber'
|
||||
@@ -18,7 +26,6 @@ export function FloatingActionMenu() {
|
||||
const deleteNode = useScene((s) => s.deleteNode)
|
||||
const setMovingNode = useEditor((s) => s.setMovingNode)
|
||||
const setSelection = useViewer((s) => s.setSelection)
|
||||
const isEditor = useViewer((state) => state.isEditor)
|
||||
|
||||
const groupRef = useRef<THREE.Group>(null)
|
||||
|
||||
@@ -42,7 +49,8 @@ export function FloatingActionMenu() {
|
||||
}
|
||||
})
|
||||
|
||||
const handleMove = useCallback((e: React.MouseEvent) => {
|
||||
const handleMove = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
if (!node) return
|
||||
sfxEmitter.emit('sfx:item-pick')
|
||||
@@ -50,9 +58,12 @@ export function FloatingActionMenu() {
|
||||
setMovingNode(node as any)
|
||||
}
|
||||
setSelection({ selectedIds: [] })
|
||||
}, [node, setMovingNode, setSelection])
|
||||
},
|
||||
[node, setMovingNode, setSelection],
|
||||
)
|
||||
|
||||
const handleDuplicate = useCallback((e: React.MouseEvent) => {
|
||||
const handleDuplicate = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
if (!node || !node.parentId) return
|
||||
sfxEmitter.emit('sfx:item-pick')
|
||||
@@ -85,18 +96,23 @@ export function FloatingActionMenu() {
|
||||
}
|
||||
setSelection({ selectedIds: [] })
|
||||
}
|
||||
}, [node, setMovingNode, setSelection])
|
||||
},
|
||||
[node, setMovingNode, setSelection],
|
||||
)
|
||||
|
||||
const handleDelete = useCallback((e: React.MouseEvent) => {
|
||||
const handleDelete = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
if (!selectedId || !node) return
|
||||
sfxEmitter.emit('sfx:item-delete')
|
||||
deleteNode(selectedId as AnyNodeId)
|
||||
if (node.parentId) useScene.getState().dirtyNodes.add(node.parentId as AnyNodeId)
|
||||
setSelection({ selectedIds: [] })
|
||||
}, [selectedId, node, deleteNode, setSelection])
|
||||
},
|
||||
[selectedId, node, deleteNode, setSelection],
|
||||
)
|
||||
|
||||
if (!isEditor || !selectedId || !node || !isValidType) return null
|
||||
if (!selectedId || !node || !isValidType) return null
|
||||
|
||||
return (
|
||||
<group ref={groupRef}>
|
||||
@@ -105,7 +121,7 @@ export function FloatingActionMenu() {
|
||||
zIndexRange={[100, 0]}
|
||||
style={{
|
||||
pointerEvents: 'auto',
|
||||
touchAction: 'none'
|
||||
touchAction: 'none',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -24,6 +24,7 @@ import { ExportManager } from './export-manager'
|
||||
import { FloatingActionMenu } from './floating-action-menu'
|
||||
import { Grid } from './grid'
|
||||
import { SelectionManager } from './selection-manager'
|
||||
import { SiteEdgeLabels } from './site-edge-labels'
|
||||
import { ThumbnailGenerator } from './thumbnail-generator'
|
||||
|
||||
// Load default scene initially (will be replaced when project loads)
|
||||
@@ -118,6 +119,7 @@ export default function Editor({ projectId }: EditorProps) {
|
||||
<ToolManager />
|
||||
<CustomCameraControls />
|
||||
<ThumbnailGenerator projectId={projectId} />
|
||||
<SiteEdgeLabels />
|
||||
</Viewer>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
'use client'
|
||||
|
||||
import { sceneRegistry, useScene } from '@pascal-app/core'
|
||||
import type { SiteNode } from '@pascal-app/core'
|
||||
import { Html } from '@react-three/drei'
|
||||
import { createPortal, useFrame } from '@react-three/fiber'
|
||||
import { useMemo, useRef, useState } from 'react'
|
||||
import type { Object3D } from 'three'
|
||||
|
||||
export function SiteEdgeLabels() {
|
||||
const rootNodeIds = useScene((state) => state.rootNodeIds)
|
||||
const nodes = useScene((state) => state.nodes)
|
||||
|
||||
const siteNode = rootNodeIds[0] ? (nodes[rootNodeIds[0]] as SiteNode) : null
|
||||
const siteNodeId = siteNode?.id
|
||||
|
||||
const [siteObj, setSiteObj] = useState<Object3D | null>(null)
|
||||
const prevSiteNodeIdRef = useRef<string | undefined>(undefined)
|
||||
|
||||
// Poll each frame until the site group is registered.
|
||||
// Also resets when the site node ID changes (new project loaded).
|
||||
useFrame(() => {
|
||||
if (siteNodeId !== prevSiteNodeIdRef.current) {
|
||||
prevSiteNodeIdRef.current = siteNodeId
|
||||
setSiteObj(null)
|
||||
return
|
||||
}
|
||||
if (siteObj || !siteNodeId) return
|
||||
const obj = sceneRegistry.nodes.get(siteNodeId)
|
||||
if (obj) setSiteObj(obj)
|
||||
})
|
||||
|
||||
const edges = useMemo(() => {
|
||||
const polygon = siteNode?.polygon?.points ?? []
|
||||
if (polygon.length < 2) return []
|
||||
return polygon.map(([x1, z1], i) => {
|
||||
const [x2, z2] = polygon[(i + 1) % polygon.length]!
|
||||
const midX = (x1! + x2) / 2
|
||||
const midZ = (z1! + z2) / 2
|
||||
const dist = Math.sqrt((x2 - x1!) ** 2 + (z2 - z1!) ** 2)
|
||||
return { midX, midZ, dist }
|
||||
})
|
||||
}, [siteNode?.polygon?.points])
|
||||
|
||||
if (!siteObj || edges.length === 0) return null
|
||||
|
||||
return createPortal(
|
||||
<>
|
||||
{edges.map((edge, i) => (
|
||||
<Html
|
||||
center
|
||||
key={`edge-${i}`}
|
||||
position={[edge.midX, 0.5, edge.midZ]}
|
||||
style={{ pointerEvents: 'none', userSelect: 'none' }}
|
||||
zIndexRange={[10, 0]}
|
||||
occlude
|
||||
>
|
||||
<div className="whitespace-nowrap rounded bg-black/75 px-1.5 py-0.5 font-mono text-white text-xs backdrop-blur-sm">
|
||||
{edge.dist.toFixed(2)}m
|
||||
</div>
|
||||
</Html>
|
||||
))}
|
||||
</>,
|
||||
siteObj,
|
||||
)
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
import { type SiteNode, useRegistry } from '@pascal-app/core'
|
||||
import { Html } from '@react-three/drei'
|
||||
import { useMemo, useRef } from 'react'
|
||||
import { BufferGeometry, Float32BufferAttribute, type Group, Shape } from 'three'
|
||||
import { useNodeEvents } from '../../../hooks/use-node-events'
|
||||
import useViewer from '../../../store/use-viewer'
|
||||
import { NodeRenderer } from '../node-renderer'
|
||||
|
||||
const Y_OFFSET = 0.01
|
||||
const LINE_HEIGHT = 0.5
|
||||
|
||||
/**
|
||||
* Creates simple line geometry for site boundary
|
||||
@@ -62,22 +59,6 @@ export const SiteRenderer = ({ node }: { node: SiteNode }) => {
|
||||
return createBoundaryLineGeometry(node.polygon.points)
|
||||
}, [node?.polygon?.points])
|
||||
|
||||
const isEditor = useViewer((state) => state.isEditor)
|
||||
|
||||
// Edge distances for labels
|
||||
const edges = useMemo(() => {
|
||||
if (!isEditor) return []
|
||||
const polygon = node?.polygon?.points ?? []
|
||||
if (polygon.length < 2) return []
|
||||
return polygon.map(([x1, z1], i) => {
|
||||
const [x2, z2] = polygon[(i + 1) % polygon.length]!
|
||||
const midX = (x1! + x2) / 2
|
||||
const midZ = (z1! + z2) / 2
|
||||
const dist = Math.sqrt((x2 - x1!) ** 2 + (z2 - z1!) ** 2)
|
||||
return { midX, midZ, dist }
|
||||
})
|
||||
}, [node?.polygon?.points, isEditor])
|
||||
|
||||
const handlers = useNodeEvents(node, 'site')
|
||||
|
||||
if (!node || !floorShape || !lineGeometry) {
|
||||
@@ -106,21 +87,6 @@ export const SiteRenderer = ({ node }: { node: SiteNode }) => {
|
||||
<lineBasicMaterial color="#f59e0b" linewidth={2} transparent opacity={0.6} />
|
||||
</line>
|
||||
|
||||
{/* Edge distance labels */}
|
||||
{isEditor && edges.map((edge, i) => (
|
||||
<Html
|
||||
center
|
||||
key={`edge-${i}`}
|
||||
position={[edge.midX, 0.5, edge.midZ]}
|
||||
style={{ pointerEvents: 'none', userSelect: 'none' }}
|
||||
zIndexRange={[10, 0]}
|
||||
occlude
|
||||
>
|
||||
<div className="whitespace-nowrap rounded bg-black/75 px-1.5 py-0.5 font-mono text-white text-xs backdrop-blur-sm">
|
||||
{edge.dist.toFixed(2)}m
|
||||
</div>
|
||||
</Html>
|
||||
))}
|
||||
</group>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user