ai-chatbot/apps/editor/components/editor/site-edge-labels.tsx
2026-03-02 08:35:16 +09:00

67 lines
2.1 KiB
TypeScript

'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,
)
}