transforming zones to nodes

This commit is contained in:
wass08
2026-01-26 07:40:52 +09:00
parent fb3cd3e34e
commit 192a58c47e
4 changed files with 30 additions and 32 deletions
@@ -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 { useViewer } from "@pascal-app/viewer";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { import {
@@ -27,10 +27,11 @@ type DragState = {
export const ZoneBoundaryEditor: React.FC = () => { export const ZoneBoundaryEditor: React.FC = () => {
const { gl, camera } = useThree(); const { gl, camera } = useThree();
const selectedZoneId = useViewer((state) => state.selection.zoneId); const selectedZoneId = useViewer((state) => state.selection.zoneId);
const zone = useScene((state) => const zoneNode = useScene((state) =>
selectedZoneId ? state.zones[selectedZoneId] : null 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 // Local state for dragging
const [dragState, setDragState] = useState<DragState | null>(null); const [dragState, setDragState] = useState<DragState | null>(null);
@@ -89,11 +90,11 @@ export const ZoneBoundaryEditor: React.FC = () => {
// Commit polygon changes // Commit polygon changes
const commitPolygonChange = useCallback(() => { const commitPolygonChange = useCallback(() => {
if (previewPolygon && selectedZoneId) { if (previewPolygon && selectedZoneId) {
updateZone(selectedZoneId, { polygon: previewPolygon }); updateNode(selectedZoneId, { polygon: previewPolygon });
} }
setPreviewPolygon(null); setPreviewPolygon(null);
setDragState(null); setDragState(null);
}, [previewPolygon, selectedZoneId, updateZone]); }, [previewPolygon, selectedZoneId, updateNode]);
// Handle adding a new vertex at midpoint // Handle adding a new vertex at midpoint
const handleAddVertex = useCallback( const handleAddVertex = useCallback(
@@ -122,10 +123,10 @@ export const ZoneBoundaryEditor: React.FC = () => {
if (basePolygon.length <= 3) return; // Need at least 3 points if (basePolygon.length <= 3) return; // Need at least 3 points
const newPolygon = basePolygon.filter((_, i) => i !== index); const newPolygon = basePolygon.filter((_, i) => i !== index);
updateZone(selectedZoneId, { polygon: newPolygon }); updateNode(selectedZoneId, { polygon: newPolygon });
setPreviewPolygon(null); setPreviewPolygon(null);
}, },
[zone, selectedZoneId, previewPolygon, updateZone] [zone, selectedZoneId, previewPolygon, updateNode]
); );
// Set up pointer move/up listeners for dragging // Set up pointer move/up listeners for dragging
@@ -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 { useViewer } from "@pascal-app/viewer";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from "three"; 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 * Creates a zone with the given polygon points
*/ */
const commitZoneDrawing = ( const commitZoneDrawing = (
levelId: string, levelId: LevelNode["id"],
points: Array<[number, number]> points: Array<[number, number]>
) => { ) => {
const { createZone, zoneIds } = useScene.getState(); const { createNode, nodes } = useScene.getState();
// Get next zone number // Count existing zones for naming and color cycling
const zoneCount = zoneIds.length; const zoneCount = Object.values(nodes).filter((n) => n.type === "zone").length;
const name = `Zone ${zoneCount + 1}`; const name = `Zone ${zoneCount + 1}`;
// Cycle through colors // Cycle through colors
const color = ZONE_COLORS[zoneCount % ZONE_COLORS.length]; const color = ZONE_COLORS[zoneCount % ZONE_COLORS.length];
const zone = ZoneSchema.parse({ const zone = ZoneNode.parse({
levelId,
name, name,
polygon: points, polygon: points,
color, color,
}); });
createZone(zone); createNode(zone, levelId);
// Select the newly created zone // Select the newly created zone
useViewer.getState().setSelection({ zoneId: zone.id }); useViewer.getState().setSelection({ zoneId: zone.id });
@@ -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 { useViewer } from "@pascal-app/viewer";
import { Hexagon, Trash2 } from "lucide-react"; import { Hexagon, Trash2 } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@@ -21,9 +21,9 @@ const PRESET_COLORS = [
"#06b6d4", // cyan "#06b6d4", // cyan
]; ];
function ZoneItem({ zone }: { zone: Zone }) { function ZoneItem({ zone }: { zone: ZoneNode }) {
const deleteZone = useScene((state) => state.deleteZone); const deleteNode = useScene((state) => state.deleteNode);
const updateZone = useScene((state) => state.updateZone); const updateNode = useScene((state) => state.updateNode);
const selectedZoneId = useViewer((state) => state.selection.zoneId); const selectedZoneId = useViewer((state) => state.selection.zoneId);
const setSelection = useViewer((state) => state.setSelection); const setSelection = useViewer((state) => state.setSelection);
@@ -35,14 +35,14 @@ function ZoneItem({ zone }: { zone: Zone }) {
const handleDelete = (e: React.MouseEvent) => { const handleDelete = (e: React.MouseEvent) => {
e.stopPropagation(); e.stopPropagation();
deleteZone(zone.id); deleteNode(zone.id);
if (isSelected) { if (isSelected) {
setSelection({ zoneId: null }); setSelection({ zoneId: null });
} }
}; };
const handleColorChange = (color: string) => { const handleColorChange = (color: string) => {
updateZone(zone.id, { color }); updateNode(zone.id, { color });
}; };
return ( return (
@@ -96,19 +96,16 @@ function ZoneItem({ zone }: { zone: Zone }) {
} }
export function ZonePanel() { export function ZonePanel() {
const zones = useScene((state) => state.zones); const nodes = useScene((state) => state.nodes);
const zoneIds = useScene((state) => state.zoneIds);
const currentLevelId = useViewer((state) => state.selection.levelId); const currentLevelId = useViewer((state) => state.selection.levelId);
const setPhase = useEditor((state) => state.setPhase); const setPhase = useEditor((state) => state.setPhase);
const setMode = useEditor((state) => state.setMode); const setMode = useEditor((state) => state.setMode);
const setTool = useEditor((state) => state.setTool); const setTool = useEditor((state) => state.setTool);
// Filter zones to only show those for the current level // Filter nodes to get zones for the current level
const levelZones = zoneIds const levelZones = Object.values(nodes).filter(
.map((id) => zones[id]) (node): node is ZoneNode =>
.filter( node.type === "zone" && node.parentId === currentLevelId
(zone): zone is Zone =>
zone !== undefined && zone.levelId === currentLevelId
); );
const handleAddZone = () => { const handleAddZone = () => {
+2 -1
View File
@@ -10,7 +10,8 @@ export { LevelNode } from './nodes/level'
export { SiteNode } from './nodes/site' export { SiteNode } from './nodes/site'
export { WallNode } from './nodes/wall' export { WallNode } from './nodes/wall'
// Zones // 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' export type { AnyNodeId, AnyNodeType } from './types'
// Union types // Union types
export { AnyNode } from './types' export { AnyNode } from './types'