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 { 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<DragState | null>(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
@@ -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 });
@@ -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) {
+2 -1
View File
@@ -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'