diff --git a/apps/editor/components/tools/zone/zone-boundary-editor.tsx b/apps/editor/components/tools/zone/zone-boundary-editor.tsx index aa2514d7..bcf01f38 100644 --- a/apps/editor/components/tools/zone/zone-boundary-editor.tsx +++ b/apps/editor/components/tools/zone/zone-boundary-editor.tsx @@ -1,4 +1,4 @@ -import { emitter, type GridEvent, useScene } from "@pascal-app/core"; +import { useScene, type ZoneNode } from "@pascal-app/core"; import { useViewer } from "@pascal-app/viewer"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { @@ -27,10 +27,11 @@ type DragState = { export const ZoneBoundaryEditor: React.FC = () => { const { gl, camera } = useThree(); const selectedZoneId = useViewer((state) => state.selection.zoneId); - const zone = useScene((state) => - selectedZoneId ? state.zones[selectedZoneId] : null + const zoneNode = useScene((state) => + selectedZoneId ? state.nodes[selectedZoneId] : null ); - const updateZone = useScene((state) => state.updateZone); + const zone = zoneNode?.type === "zone" ? (zoneNode as ZoneNode) : null; + const updateNode = useScene((state) => state.updateNode); // Local state for dragging const [dragState, setDragState] = useState(null); @@ -89,11 +90,11 @@ export const ZoneBoundaryEditor: React.FC = () => { // Commit polygon changes const commitPolygonChange = useCallback(() => { if (previewPolygon && selectedZoneId) { - updateZone(selectedZoneId, { polygon: previewPolygon }); + updateNode(selectedZoneId, { polygon: previewPolygon }); } setPreviewPolygon(null); setDragState(null); - }, [previewPolygon, selectedZoneId, updateZone]); + }, [previewPolygon, selectedZoneId, updateNode]); // Handle adding a new vertex at midpoint const handleAddVertex = useCallback( @@ -122,10 +123,10 @@ export const ZoneBoundaryEditor: React.FC = () => { if (basePolygon.length <= 3) return; // Need at least 3 points const newPolygon = basePolygon.filter((_, i) => i !== index); - updateZone(selectedZoneId, { polygon: newPolygon }); + updateNode(selectedZoneId, { polygon: newPolygon }); setPreviewPolygon(null); }, - [zone, selectedZoneId, previewPolygon, updateZone] + [zone, selectedZoneId, previewPolygon, updateNode] ); // Set up pointer move/up listeners for dragging diff --git a/apps/editor/components/tools/zone/zone-tool.tsx b/apps/editor/components/tools/zone/zone-tool.tsx index 5ed5b9ea..a05a9cdd 100644 --- a/apps/editor/components/tools/zone/zone-tool.tsx +++ b/apps/editor/components/tools/zone/zone-tool.tsx @@ -1,4 +1,4 @@ -import { emitter, type GridEvent, useScene, ZoneSchema } from "@pascal-app/core"; +import { emitter, type GridEvent, useScene, ZoneNode, type LevelNode } from "@pascal-app/core"; import { useViewer } from "@pascal-app/viewer"; import { useEffect, useMemo, useRef, useState } from "react"; import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from "three"; @@ -61,26 +61,25 @@ const calculateSnapPoint = ( * Creates a zone with the given polygon points */ const commitZoneDrawing = ( - levelId: string, + levelId: LevelNode["id"], points: Array<[number, number]> ) => { - const { createZone, zoneIds } = useScene.getState(); + const { createNode, nodes } = useScene.getState(); - // Get next zone number - const zoneCount = zoneIds.length; + // Count existing zones for naming and color cycling + const zoneCount = Object.values(nodes).filter((n) => n.type === "zone").length; const name = `Zone ${zoneCount + 1}`; // Cycle through colors const color = ZONE_COLORS[zoneCount % ZONE_COLORS.length]; - const zone = ZoneSchema.parse({ - levelId, + const zone = ZoneNode.parse({ name, polygon: points, color, }); - createZone(zone); + createNode(zone, levelId); // Select the newly created zone useViewer.getState().setSelection({ zoneId: zone.id }); diff --git a/apps/editor/components/ui/sidebar/panels/zone-panel/index.tsx b/apps/editor/components/ui/sidebar/panels/zone-panel/index.tsx index d29288c6..94202963 100644 --- a/apps/editor/components/ui/sidebar/panels/zone-panel/index.tsx +++ b/apps/editor/components/ui/sidebar/panels/zone-panel/index.tsx @@ -1,4 +1,4 @@ -import { useScene, type Zone } from "@pascal-app/core"; +import { useScene, type ZoneNode } from "@pascal-app/core"; import { useViewer } from "@pascal-app/viewer"; import { Hexagon, Trash2 } from "lucide-react"; import { cn } from "@/lib/utils"; @@ -21,9 +21,9 @@ const PRESET_COLORS = [ "#06b6d4", // cyan ]; -function ZoneItem({ zone }: { zone: Zone }) { - const deleteZone = useScene((state) => state.deleteZone); - const updateZone = useScene((state) => state.updateZone); +function ZoneItem({ zone }: { zone: ZoneNode }) { + const deleteNode = useScene((state) => state.deleteNode); + const updateNode = useScene((state) => state.updateNode); const selectedZoneId = useViewer((state) => state.selection.zoneId); const setSelection = useViewer((state) => state.setSelection); @@ -35,14 +35,14 @@ function ZoneItem({ zone }: { zone: Zone }) { const handleDelete = (e: React.MouseEvent) => { e.stopPropagation(); - deleteZone(zone.id); + deleteNode(zone.id); if (isSelected) { setSelection({ zoneId: null }); } }; const handleColorChange = (color: string) => { - updateZone(zone.id, { color }); + updateNode(zone.id, { color }); }; return ( @@ -96,20 +96,17 @@ function ZoneItem({ zone }: { zone: Zone }) { } export function ZonePanel() { - const zones = useScene((state) => state.zones); - const zoneIds = useScene((state) => state.zoneIds); + const nodes = useScene((state) => state.nodes); const currentLevelId = useViewer((state) => state.selection.levelId); const setPhase = useEditor((state) => state.setPhase); const setMode = useEditor((state) => state.setMode); const setTool = useEditor((state) => state.setTool); - // Filter zones to only show those for the current level - const levelZones = zoneIds - .map((id) => zones[id]) - .filter( - (zone): zone is Zone => - zone !== undefined && zone.levelId === currentLevelId - ); + // Filter nodes to get zones for the current level + const levelZones = Object.values(nodes).filter( + (node): node is ZoneNode => + node.type === "zone" && node.parentId === currentLevelId + ); const handleAddZone = () => { if (currentLevelId) { diff --git a/packages/core/src/schema/index.ts b/packages/core/src/schema/index.ts index 8596ba0a..659f1381 100644 --- a/packages/core/src/schema/index.ts +++ b/packages/core/src/schema/index.ts @@ -10,7 +10,8 @@ export { LevelNode } from './nodes/level' export { SiteNode } from './nodes/site' export { WallNode } from './nodes/wall' // Zones -export type { ZoneNode, ZonePolygon } from './nodes/zone' +export { ZoneNode } from './nodes/zone' +export type { ZonePolygon } from './nodes/zone' export type { AnyNodeId, AnyNodeType } from './types' // Union types export { AnyNode } from './types'