aboutsummaryrefslogtreecommitdiff
path: root/src/store/store.ts
blob: 94519e4b9a3ffa185ab949b2c7151314c6e14f12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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 });
  },
}));