import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; interface ZoneStore { zones: Zone[]; setZones: (zones: Zone[]) => void; addZone: (zone: Zone) => void; updateZone: (uuid: string, updated: Partial) => void; setZoneName: (uuid: string, name: string) => void; setZoneHeight: (uuid: string, height: number) => void; setZoneColor: (uuid: string, color: string) => void; removeZone: (uuid: string) => void; removePoint: (pointUuid: string) => { removedZones: Zone[], updatedZones: Zone[] }; removeZoneByPoints: (points: Point[]) => { removedZones: Zone[], updatedZones: Zone[] }; clearZones: () => void; setPosition: ( pointUuid: string, position: [number, number, number] ) => Zone[] | []; setViewPort: (uuid: string, position: [number, number, number], target: [number, number, number]) => void; getZoneById: (uuid: string) => Zone | undefined; getZonesByPointId: (uuid: string) => Zone[] | []; getZoneByPoints: (points: Point[]) => Zone | undefined; getZonePointById: (uuid: string) => Point | undefined; getConnectedPoints: (uuid: string) => Point[]; } export const createZoneStore = () => { return create()( immer((set, get) => ({ zones: [], setZones: (zones) => set(state => { state.zones = zones; }), addZone: (zone) => set(state => { state.zones.push(zone); }), updateZone: (uuid, updated) => set(state => { const zone = state.zones.find(z => z.zoneUuid === uuid); if (zone) { Object.assign(zone, updated); } }), setZoneName: (uuid, name) => set(state => { const zone = state.zones.find(z => z.zoneUuid === uuid); if (zone) { zone.zoneName = name; } }), setZoneHeight: (uuid, height) => set(state => { const zone = state.zones.find(z => z.zoneUuid === uuid); if (zone) { zone.zoneHeight = height; } }), setZoneColor: (uuid, color) => set(state => { const zone = state.zones.find(z => z.zoneUuid === uuid); if (zone) { zone.zoneColor = color; } }), removeZone: (uuid) => set(state => { state.zones = state.zones.filter(z => z.zoneUuid !== uuid); }), removePoint: (pointUuid) => { const removedZones: Zone[] = []; const updatedZones: Zone[] = []; set(state => { for (const zone of state.zones) { const pointIndex = zone.points.findIndex(p => p.pointUuid === pointUuid); if (pointIndex === -1) { updatedZones.push(JSON.parse(JSON.stringify(zone))); continue; } const remainingPoints = zone.points.filter(p => p.pointUuid !== pointUuid); if (remainingPoints.length <= 2) { removedZones.push(JSON.parse(JSON.stringify(zone))); continue; } zone.points = remainingPoints; updatedZones.push(JSON.parse(JSON.stringify(zone))); } state.zones = updatedZones; }); return { removedZones, updatedZones }; }, removeZoneByPoints: ([pointA, pointB]) => { const removedZones: Zone[] = []; const updatedZones: Zone[] = []; set(state => { for (const zone of state.zones) { const indices = zone.points.map((p, i) => ({ uuid: p.pointUuid, index: i })); const idxA = indices.find(i => i.uuid === pointA.pointUuid)?.index ?? -1; const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1; if (idxA === -1 || idxB === -1) { updatedZones.push(JSON.parse(JSON.stringify(zone))); continue; } const areAdjacent = Math.abs(idxA - idxB) === 1 || (idxA === 0 && idxB === zone.points.length - 1) || (idxB === 0 && idxA === zone.points.length - 1); if (!areAdjacent) { updatedZones.push(JSON.parse(JSON.stringify(zone))); continue; } const remainingPoints = zone.points.filter( p => p.pointUuid !== pointA.pointUuid && p.pointUuid !== pointB.pointUuid ); if (remainingPoints.length > 2) { zone.points = remainingPoints; updatedZones.push(JSON.parse(JSON.stringify(zone))); } else { removedZones.push(JSON.parse(JSON.stringify(zone))); } } state.zones = updatedZones; }); return { removedZones, updatedZones }; }, clearZones: () => set(state => { state.zones = []; }), setPosition: (pointUuid, position) => { let updatedZone: Zone[] = []; set((state) => { for (const zone of state.zones) { const point = zone.points.find((p) => p.pointUuid === pointUuid); if (point) { point.position = position; updatedZone.push(JSON.parse(JSON.stringify(zone))); } } }); return updatedZone; }, setViewPort: (uuid, position, target) => set(state => { const zone = state.zones.find(z => z.zoneUuid === uuid); if (zone) { zone.viewPortPosition = position; zone.viewPortTarget = target; } }), getZoneById: (uuid) => { return get().zones.find(z => z.zoneUuid === uuid); }, getZonesByPointId: (pointUuid) => { return get().zones.filter(zone => { return zone.points.some(p => p.pointUuid === pointUuid); }); }, getZoneByPoints: (points) => { return get().zones.find(zone => { const zonePointIds = new Set(zone.points.map(p => p.pointUuid)); const givenPointIds = new Set(points.map(p => p.pointUuid)); return zonePointIds.size === givenPointIds.size && [...zonePointIds].every(id => givenPointIds.has(id)); }); }, getZonePointById: (pointUuid) => { for (const zone of get().zones) { const point = zone.points.find(p => p.pointUuid === pointUuid); if (point) return point; } return undefined; }, getConnectedPoints: (pointUuid) => { const connected: Point[] = []; for (const zone of get().zones) { if (zone.points.some(p => p.pointUuid === pointUuid)) { connected.push(...zone.points.filter(p => p.pointUuid !== pointUuid)); } } return connected; } })) ); }; export type ZoneStoreType = ReturnType;