299 lines
11 KiB
TypeScript
299 lines
11 KiB
TypeScript
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<Zone>) => 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[] };
|
|
peekRemovePoint: (pointUuid: string) => { removedZones: Zone[]; updatedZones: Zone[] };
|
|
removeZoneByPoints: (points: Point[]) => { removedZones: Zone[]; updatedZones: Zone[] };
|
|
peekRemoveZoneByPoints: (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[] | [];
|
|
getZonesByPoints: (points: Point[]) => Zone[] | [];
|
|
getZonePointById: (uuid: string) => Point | undefined;
|
|
getConnectedPoints: (uuid: string) => Point[];
|
|
}
|
|
|
|
export const createZoneStore = () => {
|
|
return create<ZoneStore>()(
|
|
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) => {
|
|
const newZones: Zone[] = [];
|
|
|
|
for (const zone of state.zones) {
|
|
const pointIndex = zone.points.findIndex((p) => p.pointUuid === pointUuid);
|
|
|
|
if (pointIndex === -1) {
|
|
newZones.push(zone);
|
|
continue;
|
|
}
|
|
|
|
const remainingPoints = zone.points.filter((p) => p.pointUuid !== pointUuid);
|
|
|
|
if (remainingPoints.length <= 2) {
|
|
removedZones.push(JSON.parse(JSON.stringify(zone)));
|
|
continue;
|
|
}
|
|
|
|
const updatedZone = { ...zone, points: remainingPoints };
|
|
updatedZones.push(JSON.parse(JSON.stringify(updatedZone)));
|
|
newZones.push(updatedZone);
|
|
}
|
|
|
|
state.zones = newZones;
|
|
});
|
|
|
|
return { removedZones, updatedZones };
|
|
},
|
|
|
|
peekRemovePoint: (pointUuid: string) => {
|
|
const removedZones: Zone[] = [];
|
|
const updatedZones: Zone[] = [];
|
|
|
|
const zones = get().zones;
|
|
|
|
for (const zone of zones) {
|
|
const pointIndex = zone.points.findIndex((p) => p.pointUuid === pointUuid);
|
|
|
|
if (pointIndex === -1) continue;
|
|
|
|
const remainingPoints = zone.points.filter((p) => p.pointUuid !== pointUuid);
|
|
|
|
if (remainingPoints.length <= 2) {
|
|
removedZones.push(JSON.parse(JSON.stringify(zone)));
|
|
} else {
|
|
const updatedZone = { ...zone, points: remainingPoints };
|
|
updatedZones.push(JSON.parse(JSON.stringify(updatedZone)));
|
|
}
|
|
}
|
|
|
|
return { removedZones, updatedZones };
|
|
},
|
|
|
|
removeZoneByPoints: ([pointA, pointB]) => {
|
|
const removedZones: Zone[] = [];
|
|
const updatedZones: Zone[] = [];
|
|
|
|
set((state) => {
|
|
const newZones: Zone[] = [];
|
|
|
|
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) {
|
|
newZones.push(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) {
|
|
newZones.push(zone);
|
|
continue;
|
|
}
|
|
|
|
const remainingPoints = zone.points.filter((p) => p.pointUuid !== pointA.pointUuid && p.pointUuid !== pointB.pointUuid);
|
|
|
|
if (remainingPoints.length > 2) {
|
|
const updatedZone = { ...zone, points: remainingPoints };
|
|
updatedZones.push(JSON.parse(JSON.stringify(updatedZone)));
|
|
newZones.push(updatedZone);
|
|
} else {
|
|
removedZones.push(JSON.parse(JSON.stringify(zone)));
|
|
}
|
|
}
|
|
|
|
state.zones = newZones;
|
|
});
|
|
|
|
return { removedZones, updatedZones };
|
|
},
|
|
|
|
peekRemoveZoneByPoints: ([pointA, pointB]) => {
|
|
const removedZones: Zone[] = [];
|
|
const updatedZones: Zone[] = [];
|
|
|
|
const zones = get().zones;
|
|
|
|
for (const zone of 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) {
|
|
continue;
|
|
}
|
|
|
|
const areAdjacent = Math.abs(idxA - idxB) === 1 || (idxA === 0 && idxB === zone.points.length - 1) || (idxB === 0 && idxA === zone.points.length - 1);
|
|
|
|
if (!areAdjacent) {
|
|
continue;
|
|
}
|
|
|
|
const remainingPoints = zone.points.filter((p) => p.pointUuid !== pointA.pointUuid && p.pointUuid !== pointB.pointUuid);
|
|
|
|
if (remainingPoints.length > 2) {
|
|
const updatedZone = { ...zone, points: remainingPoints };
|
|
updatedZones.push(JSON.parse(JSON.stringify(updatedZone)));
|
|
} else {
|
|
removedZones.push(JSON.parse(JSON.stringify(zone)));
|
|
}
|
|
}
|
|
|
|
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);
|
|
});
|
|
},
|
|
|
|
getZonesByPoints: ([pointA, pointB]) => {
|
|
const Zones: Zone[] = [];
|
|
|
|
for (const zone of get().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) {
|
|
continue;
|
|
}
|
|
|
|
const areAdjacent = Math.abs(idxA - idxB) === 1 || (idxA === 0 && idxB === zone.points.length - 1) || (idxB === 0 && idxA === zone.points.length - 1);
|
|
|
|
if (!areAdjacent) {
|
|
continue;
|
|
}
|
|
|
|
Zones.push(JSON.parse(JSON.stringify(zone)));
|
|
}
|
|
|
|
return Zones;
|
|
},
|
|
|
|
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<typeof createZoneStore>;
|