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 });
},
}));
|