diff options
| author | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-01-30 11:31:24 +0100 |
|---|---|---|
| committer | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-01-30 13:05:48 +0100 |
| commit | 0d4b61993cba37c180ac91e40a0fb362711de8de (patch) | |
| tree | f41dd68420b51766c1ca6fc1ee2c20dc5df830d1 /src/store/store.ts | |
Initial implementation
Diffstat (limited to 'src/store/store.ts')
| -rw-r--r-- | src/store/store.ts | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/src/store/store.ts b/src/store/store.ts new file mode 100644 index 0000000..94519e4 --- /dev/null +++ b/src/store/store.ts @@ -0,0 +1,119 @@ +import { create } from "zustand"; +import { availableModels } from "../data/availableModels.ts"; + +export interface Vector3D { + x: number; + y: number; + z: number; +} + +export interface HueShift { + red: number; + green: number; + blue: number; +} + +export interface Model3D { + id: string; + sourceId: string; + name: string; + url: string; + position: Vector3D; + rotation: Vector3D; + hueShift: HueShift; +} + +export interface AvailableModel { + id: string; + name: string; + url: string; +} + +const CAMERA_VIEWS = [ + "perspective", + "ortho-top", + "ortho-front", + "ortho-side", +] as const; +export type CameraView = (typeof CAMERA_VIEWS)[number]; + +export function isValidCameraView(value: string): value is CameraView { + return CAMERA_VIEWS.includes(value as CameraView); +} + +export interface StoreState { + models: Model3D[]; + selectedModelId: string | null; + cameraView: CameraView; + backdropUrl: string | null; + availableModels: AvailableModel[]; + addModel: (sourceId: string, position: Vector3D) => void; + removeModel: (id: string) => void; + selectModel: (id: string | null) => void; + updateModel: ( + id: string, + updates: Partial<Pick<Model3D, "position" | "rotation" | "hueShift">>, + ) => void; + setCameraView: (view: CameraView) => void; + setBackdrop: (url: string | null) => void; +} + +export const useStore = create<StoreState>((set, get) => ({ + models: [], + selectedModelId: null, + cameraView: "perspective", + backdropUrl: null, + availableModels, + + addModel: (sourceId: string, position: Vector3D) => { + const { availableModels } = get(); + const source = availableModels.find((model) => model.id === sourceId); + if (!source) return; + + const newModel: Model3D = { + id: crypto.randomUUID(), + sourceId, + name: source.name, + url: source.url, + position, + rotation: { x: 0, y: 0, z: 0 }, + hueShift: { red: 0, green: 0, blue: 0 }, + }; + + set((state) => ({ + models: [...state.models, newModel], + selectedModelId: newModel.id, + })); + }, + + removeModel: (id: string) => { + set((state) => ({ + models: state.models.filter((model) => model.id !== id), + selectedModelId: + state.selectedModelId === id ? null : state.selectedModelId, + })); + }, + + selectModel: (id: string | null) => { + set({ selectedModelId: id }); + }, + + updateModel: ( + id: string, + updates: Partial<Pick<Model3D, "position" | "rotation" | "hueShift">>, + ) => { + set((state) => ({ + models: state.models.map((model) => + model.id === id ? { ...model, ...updates } : model, + ), + })); + }, + + setCameraView: (view: CameraView) => { + set({ cameraView: view }); + }, + + setBackdrop: (url: string | null) => { + set({ backdropUrl: url }); + }, +})); |