scene starting with site and not with building anymore

This commit is contained in:
wass08
2026-02-05 14:42:46 +09:00
parent 875cf277e0
commit 39f9f595d8
8 changed files with 84 additions and 15 deletions
@@ -49,8 +49,10 @@ function SitePhaseView() {
const selectedBuildingId = useViewer((state) => state.selection.buildingId);
const setSelection = useViewer((state) => state.setSelection);
const buildings = rootNodeIds
.map((id) => nodes[id])
// Get site node and its building children
const siteNode = rootNodeIds[0] ? nodes[rootNodeIds[0]] : null;
const buildings = (siteNode?.type === 'site' ? siteNode.children : [])
.map((child) => typeof child === 'string' ? nodes[child] : child)
.filter((node): node is BuildingNode => node?.type === "building");
if (buildings.length === 0) {
@@ -92,8 +94,10 @@ function BuildingSelector() {
const selectedBuildingId = useViewer((state) => state.selection.buildingId);
const setSelection = useViewer((state) => state.setSelection);
const buildings = rootNodeIds
.map((id) => nodes[id])
// Get site node and its building children
const siteNode = rootNodeIds[0] ? nodes[rootNodeIds[0]] : null;
const buildings = (siteNode?.type === 'site' ? siteNode.children : [])
.map((child) => typeof child === 'string' ? nodes[child] : child)
.filter((node): node is BuildingNode => node?.type === "building");
const selectedBuilding = selectedBuildingId
+3 -1
View File
@@ -1,6 +1,6 @@
import type { ThreeEvent } from '@react-three/fiber'
import mitt from 'mitt'
import type { BuildingNode, CeilingNode, ItemNode, LevelNode, RoofNode, SlabNode, WallNode, ZoneNode } from '../schema'
import type { BuildingNode, CeilingNode, ItemNode, LevelNode, RoofNode, SiteNode, SlabNode, WallNode, ZoneNode } from '../schema'
import type { AnyNode } from '../schema/types'
// Base event interfaces
@@ -20,6 +20,7 @@ export interface NodeEvent<T extends AnyNode = AnyNode> {
export type WallEvent = NodeEvent<WallNode>
export type ItemEvent = NodeEvent<ItemNode>
export type SiteEvent = NodeEvent<SiteNode>
export type BuildingEvent = NodeEvent<BuildingNode>
export type LevelEvent = NodeEvent<LevelNode>
export type ZoneEvent = NodeEvent<ZoneNode>
@@ -64,6 +65,7 @@ type CameraControlEvents = {
type EditorEvents = GridEvents &
NodeEvents<'wall', WallEvent> &
NodeEvents<'item', ItemEvent> &
NodeEvents<'site', SiteEvent> &
NodeEvents<'building', BuildingEvent> &
NodeEvents<'level', LevelEvent> &
NodeEvents<'zone', ZoneEvent> &
@@ -8,6 +8,7 @@ export const sceneRegistry = {
// Categorized lookups: Type -> Set of IDs
// Using a Set is faster for adding/deleting than an Array
byType: {
site: new Set<string>(),
building: new Set<string>(),
ceiling: new Set<string>(),
level: new Set<string>(),
+1
View File
@@ -8,6 +8,7 @@ export type {
ItemEvent,
LevelEvent,
NodeEvent,
SiteEvent,
SlabEvent,
WallEvent,
ZoneEvent,
+46 -10
View File
@@ -6,6 +6,7 @@ import { create, type StoreApi, type UseBoundStore } from 'zustand'
import { persist } from 'zustand/middleware'
import { BuildingNode } from '../schema'
import { LevelNode } from '../schema/nodes/level'
import { SiteNode } from '../schema/nodes/site'
import type { AnyNode, AnyNodeId } from '../schema/types'
import { isObject } from '../utils/types'
import * as nodeActions from './actions/node-actions'
@@ -87,25 +88,29 @@ const useScene: UseSceneStore = create<SceneState>()(
return // Scene already loaded
}
const building = BuildingNode.parse({
children: [],
})
// Create hierarchy: Site → Building → Level
const level0 = LevelNode.parse({
level: 0,
children: [],
})
building.children.push(level0.id)
const building = BuildingNode.parse({
children: [level0.id],
})
const site = SiteNode.parse({
children: [building],
})
// Define all nodes flat
const nodes: Record<AnyNodeId, AnyNode> = {
[site.id]: site,
[building.id]: building,
[level0.id]: level0,
}
// Root nodes are the levels
const rootNodeIds = [building.id]
// Site is the root
const rootNodeIds = [site.id]
set({ nodes, rootNodeIds })
},
@@ -155,13 +160,44 @@ const useScene: UseSceneStore = create<SceneState>()(
onRehydrateStorage: (state) => {
console.log('hydrating...')
// optional
return (state, error) => {
if (error) {
console.log('an error happened during hydration', error)
} else {
console.log('hydration finished')
return
}
if (!state) {
console.log('hydration finished - no state')
return
}
// Migration: Wrap old scenes (where root is not a SiteNode) in a SiteNode
const rootId = state.rootNodeIds?.[0]
const rootNode = rootId ? state.nodes[rootId] : null
if (rootNode && rootNode.type !== 'site') {
console.log('Migrating old scene: wrapping in SiteNode')
// Collect existing root nodes (should be BuildingNode or ItemNode)
const existingRoots = (state.rootNodeIds || [])
.map(id => state.nodes[id])
.filter(node => node?.type === 'building' || node?.type === 'item')
// Create a new SiteNode with existing roots as children
const site = SiteNode.parse({
children: existingRoots,
})
// Add site to nodes
state.nodes[site.id] = site
// Update root to be the site
state.rootNodeIds = [site.id]
console.log('Migration complete: scene now has SiteNode as root')
}
console.log('hydration finished')
}
},
},
@@ -8,6 +8,7 @@ import { ItemRenderer } from './item/item-renderer'
import { LevelRenderer } from './level/level-renderer'
import { RoofRenderer } from './roof/roof-renderer'
import { ScanRenderer } from './scan/scan-renderer'
import { SiteRenderer } from './site/site-renderer'
import { SlabRenderer } from './slab/slab-renderer'
import { WallRenderer } from './wall/wall-renderer'
import { ZoneRenderer } from './zone/zone-renderer'
@@ -19,6 +20,7 @@ export const NodeRenderer = ({ nodeId }: { nodeId: AnyNode['id'] }) => {
return (
<>
{node.type === 'site' && <SiteRenderer node={node} />}
{node.type === 'building' && <BuildingRenderer node={node} />}
{node.type === 'ceiling' && <CeilingRenderer node={node} />}
{node.type === 'level' && <LevelRenderer node={node} />}
@@ -0,0 +1,20 @@
import { type SiteNode, useRegistry } from '@pascal-app/core'
import { useRef } from 'react'
import type { Group } from 'three'
import { useNodeEvents } from '../../../hooks/use-node-events'
import { NodeRenderer } from '../node-renderer'
export const SiteRenderer = ({ node }: { node: SiteNode }) => {
const ref = useRef<Group>(null!)
useRegistry(node.id, node.type, ref)
const handlers = useNodeEvents(node, 'site')
return (
<group ref={ref} {...handlers}>
{node.children.map((child) => (
<NodeRenderer key={child.id} nodeId={child.id} />
))}
</group>
)
}
@@ -11,6 +11,8 @@ import {
type LevelNode,
type RoofEvent,
type RoofNode,
type SiteEvent,
type SiteNode,
type SlabEvent,
type SlabNode,
type WallEvent,
@@ -21,6 +23,7 @@ import {
import type { ThreeEvent } from '@react-three/fiber'
type NodeConfig = {
site: { node: SiteNode; event: SiteEvent }
item: { node: ItemNode; event: ItemEvent }
wall: { node: WallNode; event: WallEvent }
building: { node: BuildingNode; event: BuildingEvent }