@vizij/render 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -0
- package/dist/index.d.mts +456 -0
- package/dist/index.d.ts +456 -0
- package/dist/index.js +2723 -0
- package/dist/index.mjs +2727 -0
- package/package.json +88 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2727 @@
|
|
|
1
|
+
// src/controllers/controller.tsx
|
|
2
|
+
import { memo } from "react";
|
|
3
|
+
import { useShallow } from "zustand/react/shallow";
|
|
4
|
+
import { clsx } from "clsx";
|
|
5
|
+
import {
|
|
6
|
+
getLookup
|
|
7
|
+
} from "@vizij/utils";
|
|
8
|
+
import { ColorPickerPopover, SliderNumberField, Size } from "@semio/ui";
|
|
9
|
+
|
|
10
|
+
// src/hooks/use-vizij-store.ts
|
|
11
|
+
import { useContext } from "react";
|
|
12
|
+
import { useStore } from "zustand";
|
|
13
|
+
|
|
14
|
+
// src/context.ts
|
|
15
|
+
import { createContext } from "react";
|
|
16
|
+
var VizijContext = createContext(null);
|
|
17
|
+
|
|
18
|
+
// src/hooks/use-vizij-store.ts
|
|
19
|
+
function useVizijStore(selector) {
|
|
20
|
+
const store = useContext(VizijContext);
|
|
21
|
+
if (!store) throw new Error("Missing VizijProvider in the tree");
|
|
22
|
+
return useStore(store, selector);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/controllers/controller.tsx
|
|
26
|
+
import { jsx } from "react/jsx-runtime";
|
|
27
|
+
function InnerController({
|
|
28
|
+
animatableId,
|
|
29
|
+
namespace,
|
|
30
|
+
subfield,
|
|
31
|
+
className
|
|
32
|
+
}) {
|
|
33
|
+
const setValue = useVizijStore(useShallow((state) => state.setValue));
|
|
34
|
+
const animatable = useVizijStore(
|
|
35
|
+
useShallow((state) => state.animatables[animatableId])
|
|
36
|
+
);
|
|
37
|
+
const lookupId = getLookup(namespace ?? "default", animatableId);
|
|
38
|
+
const rawValue = useVizijStore(
|
|
39
|
+
useShallow((state) => state.values.get(lookupId))
|
|
40
|
+
);
|
|
41
|
+
if (animatable.type === "number") {
|
|
42
|
+
return /* @__PURE__ */ jsx("div", { className: clsx("flex flex-col w-full", className), children: /* @__PURE__ */ jsx(
|
|
43
|
+
SliderNumberField,
|
|
44
|
+
{
|
|
45
|
+
size: Size.Sm,
|
|
46
|
+
value: rawValue ?? animatable.default,
|
|
47
|
+
onChange: (v) => {
|
|
48
|
+
setValue(animatableId, namespace ?? "default", v);
|
|
49
|
+
},
|
|
50
|
+
min: animatable.constraints.min,
|
|
51
|
+
max: animatable.constraints.max
|
|
52
|
+
}
|
|
53
|
+
) });
|
|
54
|
+
} else if (animatable.type === "vector3" && !subfield) {
|
|
55
|
+
return /* @__PURE__ */ jsx("div", { className: clsx("flex flex-col gap-2", className), children: ["x", "y", "z"].map((axis) => {
|
|
56
|
+
return /* @__PURE__ */ jsx(
|
|
57
|
+
InnerController,
|
|
58
|
+
{
|
|
59
|
+
animatableId,
|
|
60
|
+
namespace,
|
|
61
|
+
subfield: axis
|
|
62
|
+
},
|
|
63
|
+
axis
|
|
64
|
+
);
|
|
65
|
+
}) });
|
|
66
|
+
} else if (animatable.type === "vector2" && !subfield) {
|
|
67
|
+
return /* @__PURE__ */ jsx("div", { className: clsx("flex flex-col gap-2", className), children: ["x", "y"].map((axis) => {
|
|
68
|
+
return /* @__PURE__ */ jsx(
|
|
69
|
+
InnerController,
|
|
70
|
+
{
|
|
71
|
+
animatableId,
|
|
72
|
+
namespace,
|
|
73
|
+
subfield: axis
|
|
74
|
+
},
|
|
75
|
+
axis
|
|
76
|
+
);
|
|
77
|
+
}) });
|
|
78
|
+
} else if (animatable.type === "euler" && !subfield) {
|
|
79
|
+
return /* @__PURE__ */ jsx("div", { className: clsx("flex flex-col gap-2", className), children: ["x", "y", "z"].map((axis) => {
|
|
80
|
+
return /* @__PURE__ */ jsx(
|
|
81
|
+
InnerController,
|
|
82
|
+
{
|
|
83
|
+
animatableId,
|
|
84
|
+
namespace,
|
|
85
|
+
subfield: axis
|
|
86
|
+
},
|
|
87
|
+
axis
|
|
88
|
+
);
|
|
89
|
+
}) });
|
|
90
|
+
} else if (animatable.type === "rgb" && !subfield) {
|
|
91
|
+
return /* @__PURE__ */ jsx("div", { className: clsx("flex flex-col gap-2", className), children: /* @__PURE__ */ jsx(
|
|
92
|
+
ColorPickerPopover,
|
|
93
|
+
{
|
|
94
|
+
value: rawValue ? convertRGBRange(rawValue, "255") : convertRGBRange(animatable.default, "255"),
|
|
95
|
+
onChange: (v) => {
|
|
96
|
+
setValue(
|
|
97
|
+
animatableId,
|
|
98
|
+
namespace ?? "default",
|
|
99
|
+
convertRGBRange(v, "1")
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
) });
|
|
104
|
+
} else if (animatable.type === "vector3" && subfield && ["x", "y", "z"].includes(subfield)) {
|
|
105
|
+
const axis = subfield;
|
|
106
|
+
const currentVec = rawValue ?? animatable.default;
|
|
107
|
+
return /* @__PURE__ */ jsx(
|
|
108
|
+
SliderNumberField,
|
|
109
|
+
{
|
|
110
|
+
label: axis,
|
|
111
|
+
size: Size.Sm,
|
|
112
|
+
value: currentVec[axis],
|
|
113
|
+
onChange: (v) => {
|
|
114
|
+
setValue(animatableId, namespace ?? "default", {
|
|
115
|
+
...currentVec,
|
|
116
|
+
[axis]: v
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
min: animatable.constraints.min?.[vectorIndexLookup[axis]] ?? void 0,
|
|
120
|
+
max: animatable.constraints.max?.[vectorIndexLookup[axis]] ?? void 0
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
} else if (animatable.type === "vector2" && subfield && ["x", "y"].includes(subfield)) {
|
|
124
|
+
const axis = subfield;
|
|
125
|
+
const currentVec = rawValue ?? animatable.default;
|
|
126
|
+
return /* @__PURE__ */ jsx(
|
|
127
|
+
SliderNumberField,
|
|
128
|
+
{
|
|
129
|
+
label: axis,
|
|
130
|
+
size: Size.Sm,
|
|
131
|
+
value: currentVec[axis],
|
|
132
|
+
onChange: (v) => {
|
|
133
|
+
setValue(animatableId, namespace ?? "default", {
|
|
134
|
+
...currentVec,
|
|
135
|
+
[axis]: v
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
min: animatable.constraints.min?.[vectorIndexLookup[axis]] ?? void 0,
|
|
139
|
+
max: animatable.constraints.max?.[vectorIndexLookup[axis]] ?? void 0
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
} else if (animatable.type === "euler" && subfield && ["x", "y", "z"].includes(subfield)) {
|
|
143
|
+
const axis = subfield;
|
|
144
|
+
const currentVec = rawValue ?? animatable.default;
|
|
145
|
+
return /* @__PURE__ */ jsx(
|
|
146
|
+
SliderNumberField,
|
|
147
|
+
{
|
|
148
|
+
label: axis,
|
|
149
|
+
size: Size.Sm,
|
|
150
|
+
value: currentVec[axis],
|
|
151
|
+
onChange: (v) => {
|
|
152
|
+
setValue(animatableId, namespace ?? "default", {
|
|
153
|
+
...currentVec,
|
|
154
|
+
[axis]: v
|
|
155
|
+
});
|
|
156
|
+
},
|
|
157
|
+
min: animatable.constraints.min?.[vectorIndexLookup[axis]] ?? void 0,
|
|
158
|
+
max: animatable.constraints.max?.[vectorIndexLookup[axis]] ?? void 0
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
} else if (animatable.type === "rgb" && subfield && ["r", "g", "b"].includes(subfield)) {
|
|
162
|
+
const axis = subfield;
|
|
163
|
+
const currentVec = rawValue ?? animatable.default;
|
|
164
|
+
return /* @__PURE__ */ jsx(
|
|
165
|
+
SliderNumberField,
|
|
166
|
+
{
|
|
167
|
+
label: axis,
|
|
168
|
+
size: Size.Sm,
|
|
169
|
+
value: currentVec[axis],
|
|
170
|
+
strictText: true,
|
|
171
|
+
strictSlider: true,
|
|
172
|
+
onChange: (v) => {
|
|
173
|
+
setValue(animatableId, namespace ?? "default", {
|
|
174
|
+
...currentVec,
|
|
175
|
+
[axis]: v
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
min: animatable.constraints.min?.[vectorIndexLookup[axis]] ?? void 0,
|
|
179
|
+
max: animatable.constraints.max?.[vectorIndexLookup[axis]] ?? void 0
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
var Controller = memo(InnerController);
|
|
185
|
+
var vectorIndexLookup = {
|
|
186
|
+
x: 0,
|
|
187
|
+
y: 1,
|
|
188
|
+
z: 2,
|
|
189
|
+
r: 0,
|
|
190
|
+
g: 1,
|
|
191
|
+
b: 2
|
|
192
|
+
};
|
|
193
|
+
var convertRGBRange = (color, to) => {
|
|
194
|
+
if (to === "255") {
|
|
195
|
+
return {
|
|
196
|
+
r: color.r * 255,
|
|
197
|
+
g: color.g * 255,
|
|
198
|
+
b: color.b * 255
|
|
199
|
+
};
|
|
200
|
+
} else if (to === "1") {
|
|
201
|
+
return {
|
|
202
|
+
r: color.r / 255,
|
|
203
|
+
g: color.g / 255,
|
|
204
|
+
b: color.b / 255
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return color;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// src/vizij.tsx
|
|
211
|
+
import {
|
|
212
|
+
Suspense,
|
|
213
|
+
memo as memo7,
|
|
214
|
+
useContext as useContext3,
|
|
215
|
+
useEffect as useEffect6
|
|
216
|
+
} from "react";
|
|
217
|
+
import { ErrorBoundary } from "react-error-boundary";
|
|
218
|
+
import { Object3D as Object3D4 } from "three";
|
|
219
|
+
import { Canvas, useThree } from "@react-three/fiber";
|
|
220
|
+
import { Line as Line3, OrthographicCamera, Text } from "@react-three/drei";
|
|
221
|
+
import { useShallow as useShallow7 } from "zustand/react/shallow";
|
|
222
|
+
|
|
223
|
+
// src/renderables/renderable.tsx
|
|
224
|
+
import { memo as memo6, useMemo as useMemo5 } from "react";
|
|
225
|
+
import { useShallow as useShallow6 } from "zustand/react/shallow";
|
|
226
|
+
|
|
227
|
+
// src/renderables/group.tsx
|
|
228
|
+
import {
|
|
229
|
+
memo as memo2,
|
|
230
|
+
useCallback,
|
|
231
|
+
useEffect as useEffect2,
|
|
232
|
+
useRef,
|
|
233
|
+
useMemo
|
|
234
|
+
} from "react";
|
|
235
|
+
import * as THREE from "three";
|
|
236
|
+
import { useShallow as useShallow2 } from "zustand/react/shallow";
|
|
237
|
+
import {
|
|
238
|
+
instanceOfRawEuler,
|
|
239
|
+
instanceOfRawNumber,
|
|
240
|
+
instanceOfRawVector2,
|
|
241
|
+
instanceOfRawVector3
|
|
242
|
+
} from "@vizij/utils";
|
|
243
|
+
|
|
244
|
+
// src/hooks/use-features.ts
|
|
245
|
+
import { useEffect, useContext as useContext2 } from "react";
|
|
246
|
+
import { shallow } from "zustand/shallow";
|
|
247
|
+
import { getLookup as getLookup2 } from "@vizij/utils";
|
|
248
|
+
function useFeatures(namespace, features, callbacks, debugInfo) {
|
|
249
|
+
const store = useContext2(VizijContext);
|
|
250
|
+
if (!store) throw new Error("Missing VizijProvider in the tree");
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
const unsubsribes = [];
|
|
253
|
+
Object.keys(callbacks).forEach((key) => {
|
|
254
|
+
if (!(key in features)) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const featureInfo = features[key];
|
|
258
|
+
if (!featureInfo.animated) {
|
|
259
|
+
callbacks[key](featureInfo.value);
|
|
260
|
+
} else {
|
|
261
|
+
const animatableValueInfo = store.getState().animatables[featureInfo.value];
|
|
262
|
+
if (!animatableValueInfo) {
|
|
263
|
+
console.error(
|
|
264
|
+
`Feature with id ${key} is animated but the animated value with id ${featureInfo.value} is not found`,
|
|
265
|
+
debugInfo
|
|
266
|
+
);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const cb = (value) => {
|
|
270
|
+
if (value === void 0) {
|
|
271
|
+
callbacks[key](animatableValueInfo.default);
|
|
272
|
+
} else {
|
|
273
|
+
callbacks[key](value);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const defaultValue = animatableValueInfo.default;
|
|
277
|
+
const lookupKey = getLookup2(namespace, animatableValueInfo.id);
|
|
278
|
+
const unsubscribe = store.subscribe(
|
|
279
|
+
(state) => state.values.get(lookupKey) ?? defaultValue,
|
|
280
|
+
cb,
|
|
281
|
+
{ equalityFn: shallow, fireImmediately: true }
|
|
282
|
+
);
|
|
283
|
+
unsubsribes.push(unsubscribe);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
return () => {
|
|
287
|
+
unsubsribes.forEach((unsubscribe) => unsubscribe());
|
|
288
|
+
};
|
|
289
|
+
}, [features, callbacks, store, debugInfo, namespace]);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// src/functions/create-stored-data.ts
|
|
293
|
+
import { omit } from "lodash";
|
|
294
|
+
|
|
295
|
+
// src/functions/create-stored-features.ts
|
|
296
|
+
import { mapValues } from "lodash";
|
|
297
|
+
function createStoredFeatures(objectFeatures, animatableValues) {
|
|
298
|
+
return mapValues(objectFeatures, (feat) => {
|
|
299
|
+
if (feat.animated) {
|
|
300
|
+
const storedFeat = {
|
|
301
|
+
animated: true,
|
|
302
|
+
value: animatableValues[feat.value],
|
|
303
|
+
...feat.label ? { label: feat.label } : {}
|
|
304
|
+
};
|
|
305
|
+
return storedFeat;
|
|
306
|
+
} else {
|
|
307
|
+
const staticFeature = feat;
|
|
308
|
+
return staticFeature.label ? { ...staticFeature } : staticFeature;
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/functions/create-stored-data.ts
|
|
314
|
+
function createStoredRenderable(data, animatableValues) {
|
|
315
|
+
const d = omit(data, "refs", "geometry");
|
|
316
|
+
const storedFeatures = createStoredFeatures(
|
|
317
|
+
data.features,
|
|
318
|
+
animatableValues
|
|
319
|
+
);
|
|
320
|
+
return { ...d, features: storedFeatures };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/renderables/group.tsx
|
|
324
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
325
|
+
THREE.Object3D.DEFAULT_UP.set(0, 0, 1);
|
|
326
|
+
function InnerRenderedGroup({
|
|
327
|
+
id,
|
|
328
|
+
namespace,
|
|
329
|
+
chain
|
|
330
|
+
}) {
|
|
331
|
+
const ref = useRef();
|
|
332
|
+
const group = useVizijStore(useShallow2((state) => state.world[id]));
|
|
333
|
+
const refIsNull = !group.refs[namespace]?.current;
|
|
334
|
+
const animatables = useVizijStore(useShallow2((state) => state.animatables));
|
|
335
|
+
const setHoveredElement = useVizijStore(
|
|
336
|
+
useShallow2((state) => state.setHoveredElement)
|
|
337
|
+
);
|
|
338
|
+
const animatableValues = useMemo(() => {
|
|
339
|
+
const av = {};
|
|
340
|
+
Object.values(group.features).forEach((feat) => {
|
|
341
|
+
if (feat.animated) {
|
|
342
|
+
const animatable = animatables[feat.value];
|
|
343
|
+
av[animatable.id] = animatable;
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
return av;
|
|
347
|
+
}, [group.features, animatables]);
|
|
348
|
+
const userData = {
|
|
349
|
+
gltfExtensions: {
|
|
350
|
+
RobotData: createStoredRenderable(group, animatableValues)
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
useFeatures(namespace, group.features, {
|
|
354
|
+
translation: (pos) => {
|
|
355
|
+
if (ref.current?.position && instanceOfRawVector3(pos)) {
|
|
356
|
+
ref.current.position.set(
|
|
357
|
+
pos.x,
|
|
358
|
+
pos.y,
|
|
359
|
+
pos.z
|
|
360
|
+
);
|
|
361
|
+
} else if (ref.current?.position && instanceOfRawVector2(pos)) {
|
|
362
|
+
const currentZ = ref.current.position.z;
|
|
363
|
+
ref.current.position.set(pos.x, pos.y, currentZ);
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
rotation: (rot) => {
|
|
367
|
+
if (ref.current?.rotation && instanceOfRawEuler(rot)) {
|
|
368
|
+
ref.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
|
|
369
|
+
} else if (ref.current?.rotation && instanceOfRawNumber(rot)) {
|
|
370
|
+
ref.current.rotation.set(0, 0, 0);
|
|
371
|
+
ref.current.rotateZ(rot);
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
scale: (scale) => {
|
|
375
|
+
if (ref.current?.scale && instanceOfRawVector3(scale)) {
|
|
376
|
+
if (scale.x === null || scale.y === null || scale.z === null) {
|
|
377
|
+
ref.current.scale.set(0.1, 0.1, 0.1);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
ref.current.scale.set(scale.x, scale.y, scale.z);
|
|
381
|
+
} else if (ref.current && instanceOfRawNumber(scale)) {
|
|
382
|
+
ref.current.scale.set(scale, scale, scale);
|
|
383
|
+
} else if (ref.current) {
|
|
384
|
+
ref.current.scale.set(1, 1, 1);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
const setReference = useVizijStore(
|
|
389
|
+
useShallow2((state) => state.setReference)
|
|
390
|
+
);
|
|
391
|
+
useEffect2(() => {
|
|
392
|
+
if (ref.current && refIsNull) {
|
|
393
|
+
setReference(group.id, namespace, ref);
|
|
394
|
+
}
|
|
395
|
+
}, [group.id, namespace, ref, setReference, refIsNull]);
|
|
396
|
+
const handlePointerOver = useCallback(
|
|
397
|
+
(event) => {
|
|
398
|
+
if (event.eventObject !== event.object) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
setHoveredElement({ id, namespace, type: "group" });
|
|
402
|
+
},
|
|
403
|
+
[id, namespace, setHoveredElement]
|
|
404
|
+
);
|
|
405
|
+
const handlePointerOut = useCallback(
|
|
406
|
+
(event) => {
|
|
407
|
+
if (event.eventObject !== event.object) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
setHoveredElement(null);
|
|
411
|
+
},
|
|
412
|
+
[setHoveredElement]
|
|
413
|
+
);
|
|
414
|
+
return /* @__PURE__ */ jsx2(
|
|
415
|
+
"group",
|
|
416
|
+
{
|
|
417
|
+
ref,
|
|
418
|
+
uuid: `${namespace}.${group.id}`,
|
|
419
|
+
userData,
|
|
420
|
+
onPointerOver: handlePointerOver,
|
|
421
|
+
onPointerOut: handlePointerOut,
|
|
422
|
+
children: group.children.map((child) => /* @__PURE__ */ jsx2(
|
|
423
|
+
Renderable,
|
|
424
|
+
{
|
|
425
|
+
id: child,
|
|
426
|
+
namespace,
|
|
427
|
+
chain: [...chain, id]
|
|
428
|
+
},
|
|
429
|
+
child
|
|
430
|
+
))
|
|
431
|
+
}
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
var RenderedGroup = memo2(InnerRenderedGroup);
|
|
435
|
+
|
|
436
|
+
// src/renderables/ellipse.tsx
|
|
437
|
+
import {
|
|
438
|
+
memo as memo3,
|
|
439
|
+
useCallback as useCallback2,
|
|
440
|
+
useEffect as useEffect3,
|
|
441
|
+
useRef as useRef2,
|
|
442
|
+
useMemo as useMemo2
|
|
443
|
+
} from "react";
|
|
444
|
+
import { useShallow as useShallow3 } from "zustand/react/shallow";
|
|
445
|
+
import {
|
|
446
|
+
instanceOfRawNumber as instanceOfRawNumber2,
|
|
447
|
+
instanceOfRawVector2 as instanceOfRawVector22,
|
|
448
|
+
instanceOfRawVector3 as instanceOfRawVector32,
|
|
449
|
+
instanceOfRawEuler as instanceOfRawEuler2,
|
|
450
|
+
instanceOfRawRGB,
|
|
451
|
+
instanceOfRawHSL
|
|
452
|
+
} from "@vizij/utils";
|
|
453
|
+
import { Circle, Line } from "@react-three/drei";
|
|
454
|
+
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
455
|
+
function InnerRenderedEllipse({
|
|
456
|
+
id,
|
|
457
|
+
namespace,
|
|
458
|
+
chain
|
|
459
|
+
}) {
|
|
460
|
+
const ellipseRef = useRef2();
|
|
461
|
+
const materialRef = useRef2();
|
|
462
|
+
const lineRef = useRef2();
|
|
463
|
+
const strokeOffsetRef = useRef2(0);
|
|
464
|
+
const strokeWidthRef = useRef2(0);
|
|
465
|
+
const onElementClick = useVizijStore(
|
|
466
|
+
useShallow3((state) => state.onElementClick)
|
|
467
|
+
);
|
|
468
|
+
const setHoveredElement = useVizijStore(
|
|
469
|
+
useShallow3((state) => state.setHoveredElement)
|
|
470
|
+
);
|
|
471
|
+
const ellipse = useVizijStore(
|
|
472
|
+
useShallow3((state) => state.world[id])
|
|
473
|
+
);
|
|
474
|
+
const refIsNull = !ellipse.refs[namespace]?.current;
|
|
475
|
+
const animatables = useVizijStore(useShallow3((state) => state.animatables));
|
|
476
|
+
const animatableValues = useMemo2(() => {
|
|
477
|
+
const av = {};
|
|
478
|
+
Object.values(ellipse.features).forEach((feat) => {
|
|
479
|
+
if (feat.animated) {
|
|
480
|
+
const animatable = animatables[feat.value];
|
|
481
|
+
av[animatable.id] = animatable;
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
return av;
|
|
485
|
+
}, [ellipse.features, animatables]);
|
|
486
|
+
const selectionData = useMemo2(
|
|
487
|
+
() => ({ id, namespace, type: "ellipse" }),
|
|
488
|
+
[id, namespace]
|
|
489
|
+
);
|
|
490
|
+
const userData = useMemo2(
|
|
491
|
+
() => ({
|
|
492
|
+
gltfExtensions: {
|
|
493
|
+
RobotData: createStoredRenderable(ellipse, animatableValues)
|
|
494
|
+
},
|
|
495
|
+
selection: selectionData
|
|
496
|
+
}),
|
|
497
|
+
[ellipse, animatableValues, selectionData]
|
|
498
|
+
);
|
|
499
|
+
useFeatures(namespace, ellipse.features, {
|
|
500
|
+
translation: (pos) => {
|
|
501
|
+
if (ellipseRef.current?.position && instanceOfRawVector32(pos)) {
|
|
502
|
+
ellipseRef.current.position.set(
|
|
503
|
+
pos.x,
|
|
504
|
+
pos.y,
|
|
505
|
+
pos.z
|
|
506
|
+
);
|
|
507
|
+
} else if (ellipseRef.current?.position && instanceOfRawVector22(pos)) {
|
|
508
|
+
const currentZ = ellipseRef.current.position.z;
|
|
509
|
+
ellipseRef.current.position.set(
|
|
510
|
+
pos.x,
|
|
511
|
+
pos.y,
|
|
512
|
+
currentZ
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
if (lineRef.current?.position && instanceOfRawVector32(pos)) {
|
|
516
|
+
lineRef.current.position.set(
|
|
517
|
+
pos.x,
|
|
518
|
+
pos.y,
|
|
519
|
+
pos.z
|
|
520
|
+
);
|
|
521
|
+
} else if (lineRef.current?.position && instanceOfRawVector22(pos)) {
|
|
522
|
+
const currentZ = lineRef.current.position.z;
|
|
523
|
+
lineRef.current.position.set(
|
|
524
|
+
pos.x,
|
|
525
|
+
pos.y,
|
|
526
|
+
currentZ
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
rotation: (rot) => {
|
|
531
|
+
if (ellipseRef.current?.rotation && instanceOfRawEuler2(rot)) {
|
|
532
|
+
ellipseRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
|
|
533
|
+
} else if (ellipseRef.current?.rotation && instanceOfRawNumber2(rot)) {
|
|
534
|
+
ellipseRef.current.rotation.set(0, 0, 0);
|
|
535
|
+
ellipseRef.current.rotateZ(rot);
|
|
536
|
+
}
|
|
537
|
+
if (lineRef.current?.rotation && instanceOfRawEuler2(rot)) {
|
|
538
|
+
lineRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
|
|
539
|
+
} else if (lineRef.current?.rotation && instanceOfRawNumber2(rot)) {
|
|
540
|
+
lineRef.current.rotation.set(0, 0, 0);
|
|
541
|
+
lineRef.current.rotateZ(rot);
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
fillOpacity: (op) => {
|
|
545
|
+
if (materialRef.current?.opacity !== void 0 && instanceOfRawNumber2(op)) {
|
|
546
|
+
materialRef.current.opacity = op;
|
|
547
|
+
if (op < 1) {
|
|
548
|
+
materialRef.current.transparent = true;
|
|
549
|
+
} else {
|
|
550
|
+
materialRef.current.transparent = false;
|
|
551
|
+
}
|
|
552
|
+
materialRef.current.needsUpdate = true;
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
fillColor: (color) => {
|
|
556
|
+
if (materialRef.current?.color) {
|
|
557
|
+
if (instanceOfRawRGB(color)) {
|
|
558
|
+
materialRef.current.color.setRGB(color.r, color.g, color.b);
|
|
559
|
+
materialRef.current.needsUpdate = true;
|
|
560
|
+
} else if (instanceOfRawHSL(color)) {
|
|
561
|
+
materialRef.current.color.setHSL(color.h, color.s, color.l);
|
|
562
|
+
materialRef.current.needsUpdate = true;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
height: (height) => {
|
|
567
|
+
if (ellipseRef.current && instanceOfRawNumber2(height)) {
|
|
568
|
+
ellipseRef.current.scale.set(ellipseRef.current.scale.x, height, 1);
|
|
569
|
+
}
|
|
570
|
+
if (ellipseRef.current && lineRef.current && instanceOfRawNumber2(height)) {
|
|
571
|
+
const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
|
|
572
|
+
lineRef.current.scale.set(
|
|
573
|
+
ellipseRef.current.scale.x + offset,
|
|
574
|
+
height + offset,
|
|
575
|
+
1
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
},
|
|
579
|
+
width: (width) => {
|
|
580
|
+
if (ellipseRef.current && instanceOfRawNumber2(width)) {
|
|
581
|
+
ellipseRef.current.scale.set(width, ellipseRef.current.scale.y, 1);
|
|
582
|
+
}
|
|
583
|
+
if (ellipseRef.current && lineRef.current && instanceOfRawNumber2(width)) {
|
|
584
|
+
const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
|
|
585
|
+
lineRef.current.scale.set(
|
|
586
|
+
width + offset,
|
|
587
|
+
ellipseRef.current.scale.y + offset,
|
|
588
|
+
1
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
},
|
|
592
|
+
strokeOpacity: (strokeOpacity) => {
|
|
593
|
+
if (lineRef.current?.material && instanceOfRawNumber2(strokeOpacity)) {
|
|
594
|
+
lineRef.current.material.opacity = strokeOpacity;
|
|
595
|
+
if (strokeOpacity < 1) {
|
|
596
|
+
lineRef.current.material.transparent = true;
|
|
597
|
+
} else {
|
|
598
|
+
lineRef.current.material.transparent = false;
|
|
599
|
+
}
|
|
600
|
+
lineRef.current.material.needsUpdate = true;
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
strokeColor: (strokeColor) => {
|
|
604
|
+
if (lineRef.current?.material.color) {
|
|
605
|
+
if (instanceOfRawRGB(strokeColor)) {
|
|
606
|
+
lineRef.current.material.color.setRGB(
|
|
607
|
+
strokeColor.r,
|
|
608
|
+
strokeColor.g,
|
|
609
|
+
strokeColor.b
|
|
610
|
+
);
|
|
611
|
+
lineRef.current.material.needsUpdate = true;
|
|
612
|
+
} else if (instanceOfRawHSL(strokeColor)) {
|
|
613
|
+
lineRef.current.material.color.setHSL(
|
|
614
|
+
strokeColor.h,
|
|
615
|
+
strokeColor.s,
|
|
616
|
+
strokeColor.l
|
|
617
|
+
);
|
|
618
|
+
lineRef.current.material.needsUpdate = true;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
},
|
|
622
|
+
strokeWidth: (strokeWidth) => {
|
|
623
|
+
if (lineRef.current?.material && ellipseRef.current) {
|
|
624
|
+
if (instanceOfRawNumber2(strokeWidth)) {
|
|
625
|
+
strokeWidthRef.current = strokeWidth;
|
|
626
|
+
const offset = strokeWidth * strokeOffsetRef.current / 2 + strokeOffsetRef.current * -1;
|
|
627
|
+
lineRef.current.scale.set(
|
|
628
|
+
ellipseRef.current.scale.x + offset,
|
|
629
|
+
ellipseRef.current.scale.y + offset,
|
|
630
|
+
1
|
|
631
|
+
);
|
|
632
|
+
lineRef.current.material.linewidth = strokeWidth;
|
|
633
|
+
lineRef.current.material.needsUpdate = true;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
strokeOffset: (strokeOffset) => {
|
|
638
|
+
if (lineRef.current?.material && ellipseRef.current) {
|
|
639
|
+
if (instanceOfRawNumber2(strokeOffset)) {
|
|
640
|
+
strokeOffsetRef.current = strokeOffset;
|
|
641
|
+
const offset = strokeOffset * strokeWidthRef.current / 2 + strokeOffset * -1;
|
|
642
|
+
lineRef.current.scale.set(
|
|
643
|
+
ellipseRef.current.scale.x + offset,
|
|
644
|
+
ellipseRef.current.scale.y + offset,
|
|
645
|
+
1
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
const setReference = useVizijStore(
|
|
652
|
+
useShallow3((state) => state.setReference)
|
|
653
|
+
);
|
|
654
|
+
const points = useMemo2(() => {
|
|
655
|
+
const n = 600;
|
|
656
|
+
const p = [];
|
|
657
|
+
const angleStep = 2 * Math.PI / n;
|
|
658
|
+
for (let i = 0; i < n; i++) {
|
|
659
|
+
const angle = i * angleStep;
|
|
660
|
+
const x = Math.cos(angle);
|
|
661
|
+
const y = Math.sin(angle);
|
|
662
|
+
p.push([x, y, 0]);
|
|
663
|
+
}
|
|
664
|
+
return p;
|
|
665
|
+
}, []);
|
|
666
|
+
useEffect3(() => {
|
|
667
|
+
if (ellipseRef.current && refIsNull)
|
|
668
|
+
setReference(ellipse.id, namespace, ellipseRef);
|
|
669
|
+
}, [ellipse.id, namespace, ellipseRef, setReference, refIsNull]);
|
|
670
|
+
const handlePointerOver = useCallback2(
|
|
671
|
+
(event) => {
|
|
672
|
+
event.stopPropagation();
|
|
673
|
+
setHoveredElement({ id, namespace, type: "ellipse" });
|
|
674
|
+
},
|
|
675
|
+
[id, namespace, setHoveredElement]
|
|
676
|
+
);
|
|
677
|
+
const handlePointerOut = useCallback2(
|
|
678
|
+
(event) => {
|
|
679
|
+
event.stopPropagation();
|
|
680
|
+
setHoveredElement(null);
|
|
681
|
+
},
|
|
682
|
+
[setHoveredElement]
|
|
683
|
+
);
|
|
684
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
685
|
+
/* @__PURE__ */ jsx3(
|
|
686
|
+
Circle,
|
|
687
|
+
{
|
|
688
|
+
ref: ellipseRef,
|
|
689
|
+
userData,
|
|
690
|
+
args: [1, 100],
|
|
691
|
+
onPointerOver: handlePointerOver,
|
|
692
|
+
onPointerOut: handlePointerOut,
|
|
693
|
+
onClick: (e) => {
|
|
694
|
+
onElementClick({ id, type: "ellipse", namespace }, [...chain, id], e);
|
|
695
|
+
},
|
|
696
|
+
children: /* @__PURE__ */ jsx3("meshStandardMaterial", { attach: "material", ref: materialRef })
|
|
697
|
+
}
|
|
698
|
+
),
|
|
699
|
+
showLine(ellipse) && /* @__PURE__ */ jsx3(
|
|
700
|
+
Line,
|
|
701
|
+
{
|
|
702
|
+
ref: lineRef,
|
|
703
|
+
points,
|
|
704
|
+
onPointerOver: handlePointerOver,
|
|
705
|
+
onPointerOut: handlePointerOut
|
|
706
|
+
}
|
|
707
|
+
)
|
|
708
|
+
] });
|
|
709
|
+
}
|
|
710
|
+
var RenderedEllipse = memo3(InnerRenderedEllipse);
|
|
711
|
+
var showLine = (ellipse) => {
|
|
712
|
+
if ("strokeOpacity" in ellipse.features) {
|
|
713
|
+
return true;
|
|
714
|
+
} else if ("strokeColor" in ellipse.features) {
|
|
715
|
+
return true;
|
|
716
|
+
} else if ("strokeWidth" in ellipse.features) {
|
|
717
|
+
return true;
|
|
718
|
+
} else if ("strokeOffset" in ellipse.features) {
|
|
719
|
+
return true;
|
|
720
|
+
}
|
|
721
|
+
return false;
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
// src/renderables/rectangle.tsx
|
|
725
|
+
import {
|
|
726
|
+
memo as memo4,
|
|
727
|
+
useCallback as useCallback3,
|
|
728
|
+
useEffect as useEffect4,
|
|
729
|
+
useRef as useRef3,
|
|
730
|
+
useMemo as useMemo3
|
|
731
|
+
} from "react";
|
|
732
|
+
import { useShallow as useShallow4 } from "zustand/react/shallow";
|
|
733
|
+
import {
|
|
734
|
+
instanceOfRawNumber as instanceOfRawNumber3,
|
|
735
|
+
instanceOfRawVector2 as instanceOfRawVector23,
|
|
736
|
+
instanceOfRawVector3 as instanceOfRawVector33,
|
|
737
|
+
instanceOfRawEuler as instanceOfRawEuler3,
|
|
738
|
+
instanceOfRawRGB as instanceOfRawRGB2,
|
|
739
|
+
instanceOfRawHSL as instanceOfRawHSL2
|
|
740
|
+
} from "@vizij/utils";
|
|
741
|
+
import { Plane, Line as Line2 } from "@react-three/drei";
|
|
742
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
743
|
+
function InnerRenderedRectangle({
|
|
744
|
+
id,
|
|
745
|
+
namespace,
|
|
746
|
+
chain
|
|
747
|
+
}) {
|
|
748
|
+
const rectangleRef = useRef3();
|
|
749
|
+
const materialRef = useRef3();
|
|
750
|
+
const lineRef = useRef3();
|
|
751
|
+
const strokeOffsetRef = useRef3(0);
|
|
752
|
+
const strokeWidthRef = useRef3(0);
|
|
753
|
+
const onElementClick = useVizijStore(
|
|
754
|
+
useShallow4((state) => state.onElementClick)
|
|
755
|
+
);
|
|
756
|
+
const setHoveredElement = useVizijStore(
|
|
757
|
+
useShallow4((state) => state.setHoveredElement)
|
|
758
|
+
);
|
|
759
|
+
const rectangle = useVizijStore(
|
|
760
|
+
useShallow4((state) => state.world[id])
|
|
761
|
+
);
|
|
762
|
+
const refIsNull = !rectangle.refs[namespace]?.current;
|
|
763
|
+
const animatables = useVizijStore(useShallow4((state) => state.animatables));
|
|
764
|
+
const animatableValues = useMemo3(() => {
|
|
765
|
+
const av = {};
|
|
766
|
+
Object.values(rectangle.features).forEach((feat) => {
|
|
767
|
+
if (feat.animated) {
|
|
768
|
+
const animatable = animatables[feat.value];
|
|
769
|
+
av[animatable.id] = animatable;
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
return av;
|
|
773
|
+
}, [rectangle.features, animatables]);
|
|
774
|
+
const selectionData = useMemo3(
|
|
775
|
+
() => ({ id, namespace, type: "rectangle" }),
|
|
776
|
+
[id, namespace]
|
|
777
|
+
);
|
|
778
|
+
const userData = useMemo3(
|
|
779
|
+
() => ({
|
|
780
|
+
gltfExtensions: {
|
|
781
|
+
RobotData: createStoredRenderable(rectangle, animatableValues)
|
|
782
|
+
},
|
|
783
|
+
selection: selectionData
|
|
784
|
+
}),
|
|
785
|
+
[rectangle, animatableValues, selectionData]
|
|
786
|
+
);
|
|
787
|
+
useFeatures(namespace, rectangle.features, {
|
|
788
|
+
translation: (pos) => {
|
|
789
|
+
if (rectangleRef.current?.position && instanceOfRawVector33(pos)) {
|
|
790
|
+
rectangleRef.current.position.set(
|
|
791
|
+
pos.x,
|
|
792
|
+
pos.y,
|
|
793
|
+
pos.z
|
|
794
|
+
);
|
|
795
|
+
} else if (rectangleRef.current?.position && instanceOfRawVector23(pos)) {
|
|
796
|
+
const currentZ = rectangleRef.current.position.z;
|
|
797
|
+
rectangleRef.current.position.set(
|
|
798
|
+
pos.x,
|
|
799
|
+
pos.y,
|
|
800
|
+
currentZ
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
if (lineRef.current?.position && instanceOfRawVector33(pos)) {
|
|
804
|
+
lineRef.current.position.set(
|
|
805
|
+
pos.x,
|
|
806
|
+
pos.y,
|
|
807
|
+
pos.z
|
|
808
|
+
);
|
|
809
|
+
} else if (lineRef.current?.position && instanceOfRawVector23(pos)) {
|
|
810
|
+
const currentZ = lineRef.current.position.z;
|
|
811
|
+
lineRef.current.position.set(
|
|
812
|
+
pos.x,
|
|
813
|
+
pos.y,
|
|
814
|
+
currentZ
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
},
|
|
818
|
+
rotation: (rot) => {
|
|
819
|
+
if (rectangleRef.current?.rotation && instanceOfRawEuler3(rot)) {
|
|
820
|
+
rectangleRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
|
|
821
|
+
} else if (rectangleRef.current?.rotation && instanceOfRawNumber3(rot)) {
|
|
822
|
+
rectangleRef.current.rotation.set(0, 0, 0);
|
|
823
|
+
rectangleRef.current.rotateZ(rot);
|
|
824
|
+
}
|
|
825
|
+
if (lineRef.current?.rotation && instanceOfRawEuler3(rot)) {
|
|
826
|
+
lineRef.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
|
|
827
|
+
} else if (lineRef.current?.rotation && instanceOfRawNumber3(rot)) {
|
|
828
|
+
lineRef.current.rotation.set(0, 0, 0);
|
|
829
|
+
lineRef.current.rotateZ(rot);
|
|
830
|
+
}
|
|
831
|
+
},
|
|
832
|
+
fillOpacity: (op) => {
|
|
833
|
+
if (materialRef.current?.opacity !== void 0 && instanceOfRawNumber3(op)) {
|
|
834
|
+
materialRef.current.opacity = op;
|
|
835
|
+
if (op < 1) {
|
|
836
|
+
materialRef.current.transparent = true;
|
|
837
|
+
} else {
|
|
838
|
+
materialRef.current.transparent = false;
|
|
839
|
+
}
|
|
840
|
+
materialRef.current.needsUpdate = true;
|
|
841
|
+
}
|
|
842
|
+
},
|
|
843
|
+
fillColor: (color) => {
|
|
844
|
+
if (materialRef.current?.color) {
|
|
845
|
+
if (instanceOfRawRGB2(color)) {
|
|
846
|
+
materialRef.current.color.setRGB(color.r, color.g, color.b);
|
|
847
|
+
materialRef.current.needsUpdate = true;
|
|
848
|
+
} else if (instanceOfRawHSL2(color)) {
|
|
849
|
+
materialRef.current.color.setHSL(color.h, color.s, color.l);
|
|
850
|
+
materialRef.current.needsUpdate = true;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
},
|
|
854
|
+
height: (height) => {
|
|
855
|
+
if (rectangleRef.current && instanceOfRawNumber3(height)) {
|
|
856
|
+
rectangleRef.current.scale.set(rectangleRef.current.scale.x, height, 1);
|
|
857
|
+
}
|
|
858
|
+
if (rectangleRef.current && lineRef.current && instanceOfRawNumber3(height)) {
|
|
859
|
+
const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
|
|
860
|
+
lineRef.current.scale.set(
|
|
861
|
+
rectangleRef.current.scale.x + offset,
|
|
862
|
+
height + offset,
|
|
863
|
+
1
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
},
|
|
867
|
+
width: (width) => {
|
|
868
|
+
if (rectangleRef.current && instanceOfRawNumber3(width)) {
|
|
869
|
+
rectangleRef.current.scale.set(width, rectangleRef.current.scale.y, 1);
|
|
870
|
+
}
|
|
871
|
+
if (rectangleRef.current && lineRef.current && instanceOfRawNumber3(width)) {
|
|
872
|
+
const offset = strokeOffsetRef.current * strokeWidthRef.current / 2 + strokeOffsetRef.current * -1;
|
|
873
|
+
lineRef.current.scale.set(
|
|
874
|
+
width + offset,
|
|
875
|
+
rectangleRef.current.scale.y + offset,
|
|
876
|
+
1
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
},
|
|
880
|
+
strokeOpacity: (strokeOpacity) => {
|
|
881
|
+
if (lineRef.current?.material && instanceOfRawNumber3(strokeOpacity)) {
|
|
882
|
+
lineRef.current.material.opacity = strokeOpacity;
|
|
883
|
+
if (strokeOpacity < 1) {
|
|
884
|
+
lineRef.current.material.transparent = true;
|
|
885
|
+
} else {
|
|
886
|
+
lineRef.current.material.transparent = false;
|
|
887
|
+
}
|
|
888
|
+
lineRef.current.material.needsUpdate = true;
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
strokeColor: (strokeColor) => {
|
|
892
|
+
if (lineRef.current?.material.color) {
|
|
893
|
+
if (instanceOfRawRGB2(strokeColor)) {
|
|
894
|
+
lineRef.current.material.color.setRGB(
|
|
895
|
+
strokeColor.r,
|
|
896
|
+
strokeColor.g,
|
|
897
|
+
strokeColor.b
|
|
898
|
+
);
|
|
899
|
+
lineRef.current.material.needsUpdate = true;
|
|
900
|
+
} else if (instanceOfRawHSL2(strokeColor)) {
|
|
901
|
+
lineRef.current.material.color.setHSL(
|
|
902
|
+
strokeColor.h,
|
|
903
|
+
strokeColor.s,
|
|
904
|
+
strokeColor.l
|
|
905
|
+
);
|
|
906
|
+
lineRef.current.material.needsUpdate = true;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
strokeWidth: (strokeWidth) => {
|
|
911
|
+
if (lineRef.current?.material && rectangleRef.current) {
|
|
912
|
+
if (instanceOfRawNumber3(strokeWidth)) {
|
|
913
|
+
strokeWidthRef.current = strokeWidth;
|
|
914
|
+
const offset = strokeWidth * strokeOffsetRef.current / 2 + strokeOffsetRef.current * -1;
|
|
915
|
+
lineRef.current.scale.set(
|
|
916
|
+
rectangleRef.current.scale.x + offset,
|
|
917
|
+
rectangleRef.current.scale.y + offset,
|
|
918
|
+
1
|
|
919
|
+
);
|
|
920
|
+
lineRef.current.material.linewidth = strokeWidth;
|
|
921
|
+
lineRef.current.material.needsUpdate = true;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
},
|
|
925
|
+
strokeOffset: (strokeOffset) => {
|
|
926
|
+
if (lineRef.current?.material && rectangleRef.current) {
|
|
927
|
+
if (instanceOfRawNumber3(strokeOffset)) {
|
|
928
|
+
strokeOffsetRef.current = strokeOffset;
|
|
929
|
+
const offset = strokeOffset * strokeWidthRef.current / 2 + strokeOffset * -1;
|
|
930
|
+
lineRef.current.scale.set(
|
|
931
|
+
rectangleRef.current.scale.x + offset,
|
|
932
|
+
rectangleRef.current.scale.y + offset,
|
|
933
|
+
1
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
const setReference = useVizijStore(
|
|
940
|
+
useShallow4((state) => state.setReference)
|
|
941
|
+
);
|
|
942
|
+
const points = useMemo3(() => {
|
|
943
|
+
return [
|
|
944
|
+
[-0.5, 0.5, 0],
|
|
945
|
+
[0.5, 0.5, 0],
|
|
946
|
+
[0.5, -0.5, 0],
|
|
947
|
+
[-0.5, -0.5, 0],
|
|
948
|
+
[-0.5, 0.5, 0]
|
|
949
|
+
];
|
|
950
|
+
}, []);
|
|
951
|
+
useEffect4(() => {
|
|
952
|
+
if (rectangleRef.current && refIsNull)
|
|
953
|
+
setReference(rectangle.id, namespace, rectangleRef);
|
|
954
|
+
}, [rectangle.id, namespace, rectangleRef, setReference, refIsNull]);
|
|
955
|
+
const handlePointerOver = useCallback3(
|
|
956
|
+
(event) => {
|
|
957
|
+
event.stopPropagation();
|
|
958
|
+
setHoveredElement({ id, namespace, type: "rectangle" });
|
|
959
|
+
},
|
|
960
|
+
[id, namespace, setHoveredElement]
|
|
961
|
+
);
|
|
962
|
+
const handlePointerOut = useCallback3(
|
|
963
|
+
(event) => {
|
|
964
|
+
event.stopPropagation();
|
|
965
|
+
setHoveredElement(null);
|
|
966
|
+
},
|
|
967
|
+
[setHoveredElement]
|
|
968
|
+
);
|
|
969
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
970
|
+
/* @__PURE__ */ jsx4(
|
|
971
|
+
Plane,
|
|
972
|
+
{
|
|
973
|
+
ref: rectangleRef,
|
|
974
|
+
userData,
|
|
975
|
+
args: [1, 1],
|
|
976
|
+
onPointerOver: handlePointerOver,
|
|
977
|
+
onPointerOut: handlePointerOut,
|
|
978
|
+
onClick: (e) => {
|
|
979
|
+
onElementClick(
|
|
980
|
+
{ id, type: "rectangle", namespace },
|
|
981
|
+
[...chain, id],
|
|
982
|
+
e
|
|
983
|
+
);
|
|
984
|
+
},
|
|
985
|
+
children: /* @__PURE__ */ jsx4("meshStandardMaterial", { attach: "material", ref: materialRef })
|
|
986
|
+
}
|
|
987
|
+
),
|
|
988
|
+
showLine2(rectangle) && /* @__PURE__ */ jsx4(
|
|
989
|
+
Line2,
|
|
990
|
+
{
|
|
991
|
+
ref: lineRef,
|
|
992
|
+
points,
|
|
993
|
+
onPointerOver: handlePointerOver,
|
|
994
|
+
onPointerOut: handlePointerOut
|
|
995
|
+
}
|
|
996
|
+
)
|
|
997
|
+
] });
|
|
998
|
+
}
|
|
999
|
+
var RenderedRectangle = memo4(InnerRenderedRectangle);
|
|
1000
|
+
var showLine2 = (rectangle) => {
|
|
1001
|
+
if ("strokeOpacity" in rectangle.features) {
|
|
1002
|
+
return true;
|
|
1003
|
+
} else if ("strokeColor" in rectangle.features) {
|
|
1004
|
+
return true;
|
|
1005
|
+
} else if ("strokeWidth" in rectangle.features) {
|
|
1006
|
+
return true;
|
|
1007
|
+
} else if ("strokeOffset" in rectangle.features) {
|
|
1008
|
+
return true;
|
|
1009
|
+
}
|
|
1010
|
+
return false;
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
// src/renderables/shape.tsx
|
|
1014
|
+
import {
|
|
1015
|
+
memo as memo5,
|
|
1016
|
+
useCallback as useCallback4,
|
|
1017
|
+
useRef as useRef4,
|
|
1018
|
+
useMemo as useMemo4,
|
|
1019
|
+
useEffect as useEffect5
|
|
1020
|
+
} from "react";
|
|
1021
|
+
import * as THREE2 from "three";
|
|
1022
|
+
import { useShallow as useShallow5 } from "zustand/react/shallow";
|
|
1023
|
+
import {
|
|
1024
|
+
instanceOfRawEuler as instanceOfRawEuler4,
|
|
1025
|
+
instanceOfRawHSL as instanceOfRawHSL3,
|
|
1026
|
+
instanceOfRawNumber as instanceOfRawNumber4,
|
|
1027
|
+
instanceOfRawRGB as instanceOfRawRGB3,
|
|
1028
|
+
instanceOfRawVector3 as instanceOfRawVector34
|
|
1029
|
+
} from "@vizij/utils";
|
|
1030
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1031
|
+
THREE2.Object3D.DEFAULT_UP.set(0, 0, 1);
|
|
1032
|
+
function InnerRenderedShape({
|
|
1033
|
+
id,
|
|
1034
|
+
namespace,
|
|
1035
|
+
chain
|
|
1036
|
+
}) {
|
|
1037
|
+
const refGroup = useRef4();
|
|
1038
|
+
const ref = useRef4();
|
|
1039
|
+
const shape = useVizijStore(useShallow5((state) => state.world[id]));
|
|
1040
|
+
const refs = useVizijStore(
|
|
1041
|
+
useShallow5((state) => state.world[id].refs)
|
|
1042
|
+
);
|
|
1043
|
+
const refIsNull = !refs[namespace]?.current;
|
|
1044
|
+
const animatables = useVizijStore(useShallow5((state) => state.animatables));
|
|
1045
|
+
const animatableValues = useMemo4(() => {
|
|
1046
|
+
const av = {};
|
|
1047
|
+
Object.values(shape.features).forEach((feat) => {
|
|
1048
|
+
if (feat.animated) {
|
|
1049
|
+
const animatable = animatables[feat.value];
|
|
1050
|
+
av[animatable.id] = animatable;
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
return av;
|
|
1054
|
+
}, [shape.features, animatables]);
|
|
1055
|
+
const geometry = useMemo4(() => shape.geometry.clone(), [shape.geometry]);
|
|
1056
|
+
const selectionData = useMemo4(
|
|
1057
|
+
() => ({ id, namespace, type: "shape" }),
|
|
1058
|
+
[id, namespace]
|
|
1059
|
+
);
|
|
1060
|
+
const userData = useMemo4(
|
|
1061
|
+
() => ({
|
|
1062
|
+
gltfExtensions: {
|
|
1063
|
+
RobotData: createStoredRenderable(shape, animatableValues)
|
|
1064
|
+
},
|
|
1065
|
+
selection: selectionData
|
|
1066
|
+
}),
|
|
1067
|
+
[shape, animatableValues, selectionData]
|
|
1068
|
+
);
|
|
1069
|
+
const material = useRef4();
|
|
1070
|
+
const morphTargetSettings = useMemo4(() => {
|
|
1071
|
+
if (shape.morphTargets) {
|
|
1072
|
+
const dictionary = shape.morphTargets.reduce(
|
|
1073
|
+
(acc, target, i) => ({ ...acc, [target]: i }),
|
|
1074
|
+
{}
|
|
1075
|
+
);
|
|
1076
|
+
const initialInfluences = shape.morphTargets.map(() => 0);
|
|
1077
|
+
const morphFeatureHandlers = {};
|
|
1078
|
+
shape.morphTargets.forEach((target, i) => {
|
|
1079
|
+
morphFeatureHandlers[target] = (value) => {
|
|
1080
|
+
if (ref.current?.morphTargetInfluences && instanceOfRawNumber4(value)) {
|
|
1081
|
+
ref.current.morphTargetInfluences[i] = value;
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
});
|
|
1085
|
+
return [dictionary, initialInfluences, morphFeatureHandlers];
|
|
1086
|
+
} else {
|
|
1087
|
+
return [void 0, void 0, {}];
|
|
1088
|
+
}
|
|
1089
|
+
}, [shape.morphTargets]);
|
|
1090
|
+
useFeatures(
|
|
1091
|
+
namespace,
|
|
1092
|
+
shape.features,
|
|
1093
|
+
{
|
|
1094
|
+
translation: (pos) => {
|
|
1095
|
+
if (ref.current?.position && instanceOfRawVector34(pos)) {
|
|
1096
|
+
ref.current.position.set(
|
|
1097
|
+
pos.x,
|
|
1098
|
+
pos.y,
|
|
1099
|
+
pos.z
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
if (refGroup.current?.position && instanceOfRawVector34(pos)) {
|
|
1103
|
+
refGroup.current.position.set(
|
|
1104
|
+
pos.x,
|
|
1105
|
+
pos.y,
|
|
1106
|
+
pos.z
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
},
|
|
1110
|
+
rotation: (rot) => {
|
|
1111
|
+
if (ref.current?.rotation && instanceOfRawEuler4(rot)) {
|
|
1112
|
+
ref.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
|
|
1113
|
+
}
|
|
1114
|
+
if (refGroup.current?.rotation && instanceOfRawEuler4(rot)) {
|
|
1115
|
+
refGroup.current.rotation.set(rot.x, rot.y, rot.z, "ZYX");
|
|
1116
|
+
}
|
|
1117
|
+
},
|
|
1118
|
+
scale: (scale) => {
|
|
1119
|
+
if (ref.current?.scale && instanceOfRawVector34(scale)) {
|
|
1120
|
+
ref.current.scale.set(scale.x, scale.y, scale.z);
|
|
1121
|
+
} else if (ref.current && instanceOfRawNumber4(scale)) {
|
|
1122
|
+
ref.current.scale.set(scale, scale, scale);
|
|
1123
|
+
} else if (ref.current) {
|
|
1124
|
+
ref.current.scale.set(1, 1, 1);
|
|
1125
|
+
}
|
|
1126
|
+
},
|
|
1127
|
+
opacity: (op) => {
|
|
1128
|
+
if (material.current?.opacity !== void 0 && instanceOfRawNumber4(op)) {
|
|
1129
|
+
material.current.opacity = op;
|
|
1130
|
+
if (op < 1) {
|
|
1131
|
+
material.current.transparent = true;
|
|
1132
|
+
} else {
|
|
1133
|
+
material.current.transparent = false;
|
|
1134
|
+
}
|
|
1135
|
+
material.current.needsUpdate = true;
|
|
1136
|
+
}
|
|
1137
|
+
},
|
|
1138
|
+
color: (color) => {
|
|
1139
|
+
if ((material.current || void 0)?.color && instanceOfRawRGB3(color)) {
|
|
1140
|
+
material.current.color.setRGB(
|
|
1141
|
+
color.r,
|
|
1142
|
+
color.g,
|
|
1143
|
+
color.b
|
|
1144
|
+
);
|
|
1145
|
+
if ((material.current || void 0)?.color) {
|
|
1146
|
+
material.current.needsUpdate = true;
|
|
1147
|
+
}
|
|
1148
|
+
} else if (material.current && instanceOfRawHSL3(color)) {
|
|
1149
|
+
material.current.color.setHSL(
|
|
1150
|
+
color.h,
|
|
1151
|
+
color.s,
|
|
1152
|
+
color.l
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
},
|
|
1156
|
+
...morphTargetSettings[2]
|
|
1157
|
+
},
|
|
1158
|
+
shape
|
|
1159
|
+
);
|
|
1160
|
+
const setReference = useVizijStore(useShallow5((state) => state.setReference));
|
|
1161
|
+
const onElementClick = useVizijStore(
|
|
1162
|
+
useShallow5((state) => state.onElementClick)
|
|
1163
|
+
);
|
|
1164
|
+
const setHoveredElement = useVizijStore(
|
|
1165
|
+
useShallow5((state) => state.setHoveredElement)
|
|
1166
|
+
);
|
|
1167
|
+
useEffect5(() => {
|
|
1168
|
+
if (ref.current && refIsNull) setReference(shape.id, namespace, ref);
|
|
1169
|
+
}, [shape.id, namespace, ref, setReference, refIsNull]);
|
|
1170
|
+
const handlePointerOver = useCallback4(
|
|
1171
|
+
(event) => {
|
|
1172
|
+
event.stopPropagation();
|
|
1173
|
+
setHoveredElement({ id, namespace, type: "shape" });
|
|
1174
|
+
},
|
|
1175
|
+
[id, namespace, setHoveredElement]
|
|
1176
|
+
);
|
|
1177
|
+
const handlePointerOut = useCallback4(
|
|
1178
|
+
(event) => {
|
|
1179
|
+
event.stopPropagation();
|
|
1180
|
+
setHoveredElement(null);
|
|
1181
|
+
},
|
|
1182
|
+
[setHoveredElement]
|
|
1183
|
+
);
|
|
1184
|
+
return /* @__PURE__ */ jsxs3(
|
|
1185
|
+
"mesh",
|
|
1186
|
+
{
|
|
1187
|
+
ref,
|
|
1188
|
+
userData,
|
|
1189
|
+
castShadow: true,
|
|
1190
|
+
receiveShadow: true,
|
|
1191
|
+
up: [0, 0, 1],
|
|
1192
|
+
geometry,
|
|
1193
|
+
morphTargetDictionary: morphTargetSettings[0],
|
|
1194
|
+
morphTargetInfluences: morphTargetSettings[1],
|
|
1195
|
+
onPointerOver: handlePointerOver,
|
|
1196
|
+
onPointerOut: handlePointerOut,
|
|
1197
|
+
onClick: (e) => {
|
|
1198
|
+
console.log("Clicked element", shape);
|
|
1199
|
+
onElementClick({ id, type: "shape", namespace }, [...chain, id], e);
|
|
1200
|
+
},
|
|
1201
|
+
children: [
|
|
1202
|
+
shape.material === "basic" && /* @__PURE__ */ jsx5(
|
|
1203
|
+
"meshBasicMaterial",
|
|
1204
|
+
{
|
|
1205
|
+
attach: "material",
|
|
1206
|
+
ref: material,
|
|
1207
|
+
side: THREE2.DoubleSide
|
|
1208
|
+
}
|
|
1209
|
+
),
|
|
1210
|
+
shape.material === "lambert" && /* @__PURE__ */ jsx5(
|
|
1211
|
+
"meshLambertMaterial",
|
|
1212
|
+
{
|
|
1213
|
+
attach: "material",
|
|
1214
|
+
ref: material,
|
|
1215
|
+
side: THREE2.DoubleSide
|
|
1216
|
+
}
|
|
1217
|
+
),
|
|
1218
|
+
shape.material === "phong" && /* @__PURE__ */ jsx5(
|
|
1219
|
+
"meshPhongMaterial",
|
|
1220
|
+
{
|
|
1221
|
+
attach: "material",
|
|
1222
|
+
ref: material,
|
|
1223
|
+
side: THREE2.DoubleSide
|
|
1224
|
+
}
|
|
1225
|
+
),
|
|
1226
|
+
shape.material === "standard" && /* @__PURE__ */ jsx5(
|
|
1227
|
+
"meshStandardMaterial",
|
|
1228
|
+
{
|
|
1229
|
+
attach: "material",
|
|
1230
|
+
ref: material,
|
|
1231
|
+
side: THREE2.DoubleSide
|
|
1232
|
+
}
|
|
1233
|
+
),
|
|
1234
|
+
shape.material === "normal" && /* @__PURE__ */ jsx5(
|
|
1235
|
+
"meshNormalMaterial",
|
|
1236
|
+
{
|
|
1237
|
+
attach: "material",
|
|
1238
|
+
ref: material,
|
|
1239
|
+
side: THREE2.DoubleSide
|
|
1240
|
+
}
|
|
1241
|
+
),
|
|
1242
|
+
shape.children?.map((child) => /* @__PURE__ */ jsx5(
|
|
1243
|
+
Renderable,
|
|
1244
|
+
{
|
|
1245
|
+
id: child,
|
|
1246
|
+
namespace,
|
|
1247
|
+
chain: [...chain, id]
|
|
1248
|
+
},
|
|
1249
|
+
child
|
|
1250
|
+
))
|
|
1251
|
+
]
|
|
1252
|
+
}
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
var RenderedShape = memo5(InnerRenderedShape);
|
|
1256
|
+
|
|
1257
|
+
// src/renderables/renderable.tsx
|
|
1258
|
+
import { Fragment as Fragment3, jsx as jsx6 } from "react/jsx-runtime";
|
|
1259
|
+
function InnerRenderable({
|
|
1260
|
+
id,
|
|
1261
|
+
namespace,
|
|
1262
|
+
chain
|
|
1263
|
+
}) {
|
|
1264
|
+
const type = useVizijStore(useShallow6((state) => state.world[id].type));
|
|
1265
|
+
const refs = useVizijStore(useShallow6((state) => state.world[id].refs));
|
|
1266
|
+
const resolvedNamespaces = useMemo5(() => {
|
|
1267
|
+
let namespaces = [namespace];
|
|
1268
|
+
if (namespace in refs) {
|
|
1269
|
+
namespaces = [namespace];
|
|
1270
|
+
} else {
|
|
1271
|
+
namespaces = Object.keys(refs);
|
|
1272
|
+
}
|
|
1273
|
+
return namespaces;
|
|
1274
|
+
}, [namespace, refs]);
|
|
1275
|
+
if (resolvedNamespaces.length === 0) {
|
|
1276
|
+
return null;
|
|
1277
|
+
}
|
|
1278
|
+
return /* @__PURE__ */ jsx6(Fragment3, { children: resolvedNamespaces.map((ns) => {
|
|
1279
|
+
switch (type) {
|
|
1280
|
+
case "group":
|
|
1281
|
+
return /* @__PURE__ */ jsx6(
|
|
1282
|
+
RenderedGroup,
|
|
1283
|
+
{
|
|
1284
|
+
id,
|
|
1285
|
+
namespace: ns,
|
|
1286
|
+
chain
|
|
1287
|
+
},
|
|
1288
|
+
`${ns}.${id}`
|
|
1289
|
+
);
|
|
1290
|
+
case "ellipse":
|
|
1291
|
+
return /* @__PURE__ */ jsx6(
|
|
1292
|
+
RenderedEllipse,
|
|
1293
|
+
{
|
|
1294
|
+
id,
|
|
1295
|
+
namespace: ns,
|
|
1296
|
+
chain
|
|
1297
|
+
},
|
|
1298
|
+
`${ns}.${id}`
|
|
1299
|
+
);
|
|
1300
|
+
case "rectangle":
|
|
1301
|
+
return /* @__PURE__ */ jsx6(
|
|
1302
|
+
RenderedRectangle,
|
|
1303
|
+
{
|
|
1304
|
+
id,
|
|
1305
|
+
namespace: ns,
|
|
1306
|
+
chain
|
|
1307
|
+
},
|
|
1308
|
+
`${ns}.${id}`
|
|
1309
|
+
);
|
|
1310
|
+
case "shape":
|
|
1311
|
+
return /* @__PURE__ */ jsx6(
|
|
1312
|
+
RenderedShape,
|
|
1313
|
+
{
|
|
1314
|
+
id,
|
|
1315
|
+
namespace: ns,
|
|
1316
|
+
chain
|
|
1317
|
+
},
|
|
1318
|
+
`${ns}.${id}`
|
|
1319
|
+
);
|
|
1320
|
+
default:
|
|
1321
|
+
return null;
|
|
1322
|
+
}
|
|
1323
|
+
}) });
|
|
1324
|
+
}
|
|
1325
|
+
var Renderable = memo6(InnerRenderable);
|
|
1326
|
+
|
|
1327
|
+
// src/store.ts
|
|
1328
|
+
import { create } from "zustand";
|
|
1329
|
+
import { subscribeWithSelector } from "zustand/middleware";
|
|
1330
|
+
import { produce, enableMapSet } from "immer";
|
|
1331
|
+
import * as THREE3 from "three";
|
|
1332
|
+
import { getLookup as getLookup3 } from "@vizij/utils";
|
|
1333
|
+
|
|
1334
|
+
// src/actions/create-new-element.ts
|
|
1335
|
+
import { createRef } from "react";
|
|
1336
|
+
import { mapValues as mapValues2 } from "lodash";
|
|
1337
|
+
|
|
1338
|
+
// src/functions/create-world-element.ts
|
|
1339
|
+
function createDefaultGroup(partialBody) {
|
|
1340
|
+
const translation = partialBody.features?.translation || { animated: false, value: { x: 0, y: 0 } };
|
|
1341
|
+
const rotation = partialBody.features?.rotation || { animated: false, value: { x: 0, y: 0, z: 0 } };
|
|
1342
|
+
const scale = partialBody.features?.scale || { animated: false, value: { x: 1, y: 1, z: 1 } };
|
|
1343
|
+
return {
|
|
1344
|
+
id: partialBody.id || crypto.randomUUID(),
|
|
1345
|
+
name: partialBody.name || "new-body",
|
|
1346
|
+
type: partialBody.type || "group",
|
|
1347
|
+
tags: partialBody.tags || [],
|
|
1348
|
+
refs: partialBody.refs || {},
|
|
1349
|
+
features: { translation, rotation, scale },
|
|
1350
|
+
root: partialBody.root ?? false,
|
|
1351
|
+
children: partialBody.children || []
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
// src/actions/create-new-element.ts
|
|
1356
|
+
function createNewElement(state, type, root = false) {
|
|
1357
|
+
if (type === "group") {
|
|
1358
|
+
const buildSelection = (id) => ({
|
|
1359
|
+
id,
|
|
1360
|
+
namespace: "world",
|
|
1361
|
+
type: "group"
|
|
1362
|
+
});
|
|
1363
|
+
if (Object.entries(state.world).length === 0) {
|
|
1364
|
+
const name = `New-Root`;
|
|
1365
|
+
const refs = { default: createRef() };
|
|
1366
|
+
const newElement = createDefaultGroup({
|
|
1367
|
+
name,
|
|
1368
|
+
root: true,
|
|
1369
|
+
refs
|
|
1370
|
+
});
|
|
1371
|
+
state.world[newElement.id] = newElement;
|
|
1372
|
+
state.elementSelection = [buildSelection(newElement.id)];
|
|
1373
|
+
} else {
|
|
1374
|
+
const worldRootEntry = Object.values(state.world).find(
|
|
1375
|
+
(entry) => entry.type === "group" && entry.root
|
|
1376
|
+
);
|
|
1377
|
+
if (!worldRootEntry) {
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
const name = `New-Body`;
|
|
1381
|
+
const refs = mapValues2(
|
|
1382
|
+
worldRootEntry.refs,
|
|
1383
|
+
() => createRef()
|
|
1384
|
+
);
|
|
1385
|
+
const newChild = createDefaultGroup({ name, root, refs });
|
|
1386
|
+
worldRootEntry.children.push(newChild.id);
|
|
1387
|
+
state.world[newChild.id] = newChild;
|
|
1388
|
+
state.elementSelection = [buildSelection(newChild.id)];
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
// src/actions/remove-children.ts
|
|
1394
|
+
function removeFromTree(state, nodesToRemove) {
|
|
1395
|
+
Object.entries(state.world).forEach(([, element]) => {
|
|
1396
|
+
if (element.type === "group") {
|
|
1397
|
+
element.children = element.children.filter(
|
|
1398
|
+
(c) => !nodesToRemove.includes(c)
|
|
1399
|
+
);
|
|
1400
|
+
state.world[element.id] = element;
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// src/functions/create-animatable.ts
|
|
1406
|
+
function createAnimatable(value) {
|
|
1407
|
+
if (!value.type) {
|
|
1408
|
+
return null;
|
|
1409
|
+
}
|
|
1410
|
+
if (!value.name) {
|
|
1411
|
+
value.name = "New Animatable";
|
|
1412
|
+
}
|
|
1413
|
+
if (value.type === "euler") {
|
|
1414
|
+
const newAnimatable = {
|
|
1415
|
+
id: value.id ?? crypto.randomUUID(),
|
|
1416
|
+
name: `${value.name} Rotation`,
|
|
1417
|
+
type: value.type,
|
|
1418
|
+
default: value.default ?? { x: 0, y: 0, z: 0 },
|
|
1419
|
+
constraints: value.constraints ?? {
|
|
1420
|
+
min: [0, 0, 0],
|
|
1421
|
+
max: [1, 1, 1],
|
|
1422
|
+
velocity: 1
|
|
1423
|
+
},
|
|
1424
|
+
pub: value.pub ?? {
|
|
1425
|
+
output: `${value.name}-rotation`,
|
|
1426
|
+
public: true
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
return newAnimatable;
|
|
1430
|
+
} else if (value.type === "vector3") {
|
|
1431
|
+
const newAnimatable = {
|
|
1432
|
+
id: value.id ?? crypto.randomUUID(),
|
|
1433
|
+
name: `${value.name} Vector3`,
|
|
1434
|
+
type: value.type,
|
|
1435
|
+
default: value.default ?? { x: 0, y: 0, z: 0 },
|
|
1436
|
+
constraints: value.constraints ?? {
|
|
1437
|
+
min: [0, 0, 0],
|
|
1438
|
+
max: [1, 1, 1],
|
|
1439
|
+
velocity: 1
|
|
1440
|
+
},
|
|
1441
|
+
pub: value.pub ?? {
|
|
1442
|
+
output: `${value.name}-translation`,
|
|
1443
|
+
public: true
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1446
|
+
return newAnimatable;
|
|
1447
|
+
} else if (value.type === "string") {
|
|
1448
|
+
const newAnimatable = {
|
|
1449
|
+
id: value.id ?? crypto.randomUUID(),
|
|
1450
|
+
name: value.name,
|
|
1451
|
+
type: value.type,
|
|
1452
|
+
default: value.default ?? "Hello World",
|
|
1453
|
+
constraints: value.constraints ?? { length: 25 },
|
|
1454
|
+
pub: value.pub ?? {
|
|
1455
|
+
output: `${value.name}-string`,
|
|
1456
|
+
public: true
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
return newAnimatable;
|
|
1460
|
+
} else if (value.type === "number") {
|
|
1461
|
+
const newAnimatable = {
|
|
1462
|
+
id: value.id ?? crypto.randomUUID(),
|
|
1463
|
+
name: value.name,
|
|
1464
|
+
type: value.type,
|
|
1465
|
+
default: value.default ?? 0,
|
|
1466
|
+
constraints: value.constraints ?? { min: 0, max: 255, velocity: 1 },
|
|
1467
|
+
pub: value.pub ?? {
|
|
1468
|
+
output: `${value.name}-number`,
|
|
1469
|
+
public: true
|
|
1470
|
+
}
|
|
1471
|
+
};
|
|
1472
|
+
return newAnimatable;
|
|
1473
|
+
} else if (value.type === "boolean") {
|
|
1474
|
+
const newAnimatable = {
|
|
1475
|
+
id: value.id ?? crypto.randomUUID(),
|
|
1476
|
+
name: value.name,
|
|
1477
|
+
type: value.type,
|
|
1478
|
+
default: value.default ?? false,
|
|
1479
|
+
constraints: value.constraints ?? { frequency: 1 },
|
|
1480
|
+
pub: value.pub ?? {
|
|
1481
|
+
output: `${value.name}-boolean`,
|
|
1482
|
+
public: true
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
return newAnimatable;
|
|
1486
|
+
} else if (value.type === "rgb") {
|
|
1487
|
+
const newAnimatable = {
|
|
1488
|
+
id: value.id ?? crypto.randomUUID(),
|
|
1489
|
+
name: value.name,
|
|
1490
|
+
type: value.type,
|
|
1491
|
+
default: value.default ?? { r: 0, g: 0, b: 0 },
|
|
1492
|
+
constraints: value.constraints ?? {
|
|
1493
|
+
min: [0, 0, 0],
|
|
1494
|
+
max: [1, 1, 1],
|
|
1495
|
+
velocity: 10
|
|
1496
|
+
},
|
|
1497
|
+
pub: value.pub ?? {
|
|
1498
|
+
output: `${value.name}-color`,
|
|
1499
|
+
public: true
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
return newAnimatable;
|
|
1503
|
+
} else if (value.type === "hsl") {
|
|
1504
|
+
const newAnimatable = {
|
|
1505
|
+
id: value.id ?? crypto.randomUUID(),
|
|
1506
|
+
name: value.name,
|
|
1507
|
+
type: value.type,
|
|
1508
|
+
default: value.default ?? { h: 0, s: 0, l: 0 },
|
|
1509
|
+
constraints: value.constraints ?? {
|
|
1510
|
+
min: [0, 0, 0],
|
|
1511
|
+
max: [360, 100, 100],
|
|
1512
|
+
velocity: 10
|
|
1513
|
+
},
|
|
1514
|
+
pub: value.pub ?? {
|
|
1515
|
+
output: `${value.name}-color`,
|
|
1516
|
+
public: true
|
|
1517
|
+
}
|
|
1518
|
+
};
|
|
1519
|
+
return newAnimatable;
|
|
1520
|
+
}
|
|
1521
|
+
return null;
|
|
1522
|
+
}
|
|
1523
|
+
createAnimatable({ type: "euler", name: "Rotation" });
|
|
1524
|
+
|
|
1525
|
+
// src/store.ts
|
|
1526
|
+
THREE3.Object3D.DEFAULT_UP.set(0, 0, 1);
|
|
1527
|
+
enableMapSet();
|
|
1528
|
+
var VizijSlice = (set, get) => ({
|
|
1529
|
+
// worldRef: createRef<THREE.Group>(),
|
|
1530
|
+
world: {},
|
|
1531
|
+
animatables: {},
|
|
1532
|
+
values: /* @__PURE__ */ new Map(),
|
|
1533
|
+
renderHit: false,
|
|
1534
|
+
preferences: {
|
|
1535
|
+
damping: false
|
|
1536
|
+
},
|
|
1537
|
+
elementSelection: [],
|
|
1538
|
+
hoveredElement: null,
|
|
1539
|
+
slotConfig: {},
|
|
1540
|
+
clearSelection: () => {
|
|
1541
|
+
set({ elementSelection: [] });
|
|
1542
|
+
},
|
|
1543
|
+
updateElementSelection: (selection, _chain) => {
|
|
1544
|
+
set(
|
|
1545
|
+
produce((state) => {
|
|
1546
|
+
state.elementSelection = [selection];
|
|
1547
|
+
})
|
|
1548
|
+
);
|
|
1549
|
+
},
|
|
1550
|
+
setHoveredElement: (selection) => {
|
|
1551
|
+
set({ hoveredElement: selection });
|
|
1552
|
+
},
|
|
1553
|
+
onElementClick: (selection, _chain, event) => {
|
|
1554
|
+
event.stopPropagation();
|
|
1555
|
+
const makeKey = (sel) => `${sel.namespace}__${sel.type}__${sel.id}`;
|
|
1556
|
+
const stack = [];
|
|
1557
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1558
|
+
const push = (sel) => {
|
|
1559
|
+
if (!sel) return;
|
|
1560
|
+
if (!sel.id || !sel.namespace || !sel.type) return;
|
|
1561
|
+
const key = makeKey(sel);
|
|
1562
|
+
if (seen.has(key)) return;
|
|
1563
|
+
seen.add(key);
|
|
1564
|
+
stack.push(sel);
|
|
1565
|
+
};
|
|
1566
|
+
const intersections = event.intersections ?? [];
|
|
1567
|
+
intersections.forEach((hit) => {
|
|
1568
|
+
const hitSelection = hit.object?.userData?.selection;
|
|
1569
|
+
push(hitSelection);
|
|
1570
|
+
});
|
|
1571
|
+
if (stack.length === 0) {
|
|
1572
|
+
push(selection);
|
|
1573
|
+
}
|
|
1574
|
+
const stackKeys = new Set(stack.map(makeKey));
|
|
1575
|
+
const primary = stack[0];
|
|
1576
|
+
const primaryKey = makeKey(primary);
|
|
1577
|
+
set(
|
|
1578
|
+
produce((state) => {
|
|
1579
|
+
if (event.metaKey) {
|
|
1580
|
+
const existing = state.elementSelection ?? [];
|
|
1581
|
+
const alreadySelected = existing.some(
|
|
1582
|
+
(item) => makeKey(item) === primaryKey
|
|
1583
|
+
);
|
|
1584
|
+
const existingWithoutStack = existing.filter(
|
|
1585
|
+
(item) => !stackKeys.has(makeKey(item))
|
|
1586
|
+
);
|
|
1587
|
+
if (alreadySelected) {
|
|
1588
|
+
state.elementSelection = existingWithoutStack;
|
|
1589
|
+
} else {
|
|
1590
|
+
state.elementSelection = [...stack, ...existingWithoutStack];
|
|
1591
|
+
}
|
|
1592
|
+
} else {
|
|
1593
|
+
state.elementSelection = stack;
|
|
1594
|
+
}
|
|
1595
|
+
})
|
|
1596
|
+
);
|
|
1597
|
+
},
|
|
1598
|
+
getExportableBodies: (filterIds) => {
|
|
1599
|
+
const worldData = get().world;
|
|
1600
|
+
if (!filterIds) {
|
|
1601
|
+
const bodies = Object.values(worldData).filter((entry) => entry.type === "group" && entry.rootBounds).map((entry) => {
|
|
1602
|
+
const firstNs = Object.keys(entry.refs)[0];
|
|
1603
|
+
const refGroup = entry.refs[firstNs].current;
|
|
1604
|
+
return refGroup;
|
|
1605
|
+
});
|
|
1606
|
+
return bodies;
|
|
1607
|
+
} else {
|
|
1608
|
+
const bodies = Object.values(worldData).filter(
|
|
1609
|
+
(entry) => entry.type === "group" && entry.rootBounds && filterIds.includes(entry.id)
|
|
1610
|
+
).map((entry) => {
|
|
1611
|
+
const firstNs = Object.keys(entry.refs)[0];
|
|
1612
|
+
const refGroup = entry.refs[firstNs].current;
|
|
1613
|
+
return refGroup;
|
|
1614
|
+
});
|
|
1615
|
+
return bodies;
|
|
1616
|
+
}
|
|
1617
|
+
},
|
|
1618
|
+
setGeometry: (id, geometry) => {
|
|
1619
|
+
set(
|
|
1620
|
+
produce((state) => {
|
|
1621
|
+
state.world[id].geometry = geometry;
|
|
1622
|
+
})
|
|
1623
|
+
);
|
|
1624
|
+
},
|
|
1625
|
+
setValue: (id, namespace, value) => {
|
|
1626
|
+
set(
|
|
1627
|
+
produce((state) => {
|
|
1628
|
+
const lookupId = getLookup3(namespace, id);
|
|
1629
|
+
if (typeof value === "function") {
|
|
1630
|
+
const current = state.values.get(lookupId);
|
|
1631
|
+
if (current !== void 0) {
|
|
1632
|
+
if (value(current !== void 0)) {
|
|
1633
|
+
state.values.set(lookupId, value(current));
|
|
1634
|
+
}
|
|
1635
|
+
} else {
|
|
1636
|
+
const animatableLookup = state.animatables[id];
|
|
1637
|
+
const updatedValue = value(
|
|
1638
|
+
animatableLookup !== void 0 ? animatableLookup.default : void 0
|
|
1639
|
+
);
|
|
1640
|
+
if (updatedValue !== void 0) {
|
|
1641
|
+
state.values.set(lookupId, updatedValue);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
} else {
|
|
1645
|
+
state.values.set(lookupId, value);
|
|
1646
|
+
}
|
|
1647
|
+
})
|
|
1648
|
+
);
|
|
1649
|
+
},
|
|
1650
|
+
setWorldElementName: (id, value) => {
|
|
1651
|
+
set(
|
|
1652
|
+
produce((state) => {
|
|
1653
|
+
state.world[id].name = value;
|
|
1654
|
+
})
|
|
1655
|
+
);
|
|
1656
|
+
},
|
|
1657
|
+
setParent: (id, parent) => {
|
|
1658
|
+
set(
|
|
1659
|
+
produce((state) => {
|
|
1660
|
+
state.world[id].parent = parent;
|
|
1661
|
+
})
|
|
1662
|
+
);
|
|
1663
|
+
},
|
|
1664
|
+
setChild: (id, child) => {
|
|
1665
|
+
set(
|
|
1666
|
+
produce((state) => {
|
|
1667
|
+
removeFromTree(state, [child]);
|
|
1668
|
+
state.world[id].child = child;
|
|
1669
|
+
})
|
|
1670
|
+
);
|
|
1671
|
+
},
|
|
1672
|
+
setChildren: (id, children) => {
|
|
1673
|
+
set(
|
|
1674
|
+
produce((state) => {
|
|
1675
|
+
removeFromTree(state, children);
|
|
1676
|
+
state.world[id].children = children;
|
|
1677
|
+
})
|
|
1678
|
+
);
|
|
1679
|
+
},
|
|
1680
|
+
createGroup: (root) => {
|
|
1681
|
+
set(
|
|
1682
|
+
produce((state) => {
|
|
1683
|
+
createNewElement(state, "group", root);
|
|
1684
|
+
})
|
|
1685
|
+
);
|
|
1686
|
+
},
|
|
1687
|
+
setOrigin: (id, origin) => {
|
|
1688
|
+
const { translation, rotation } = origin;
|
|
1689
|
+
set(
|
|
1690
|
+
produce((state) => {
|
|
1691
|
+
if (!state.world[id].origin) {
|
|
1692
|
+
} else {
|
|
1693
|
+
if (rotation) state.world[id].origin.rotation = rotation;
|
|
1694
|
+
if (translation) state.world[id].origin.translation = translation;
|
|
1695
|
+
}
|
|
1696
|
+
})
|
|
1697
|
+
);
|
|
1698
|
+
},
|
|
1699
|
+
setAxis: (id, axis) => {
|
|
1700
|
+
set(
|
|
1701
|
+
produce((state) => {
|
|
1702
|
+
state.world[id].axis = axis;
|
|
1703
|
+
})
|
|
1704
|
+
);
|
|
1705
|
+
},
|
|
1706
|
+
setTags: (id, tags) => {
|
|
1707
|
+
set(
|
|
1708
|
+
produce((state) => {
|
|
1709
|
+
state.world[id].tags = tags;
|
|
1710
|
+
})
|
|
1711
|
+
);
|
|
1712
|
+
},
|
|
1713
|
+
setMaterial: (id, material) => {
|
|
1714
|
+
set(
|
|
1715
|
+
produce((state) => {
|
|
1716
|
+
state.world[id].material = material;
|
|
1717
|
+
})
|
|
1718
|
+
);
|
|
1719
|
+
},
|
|
1720
|
+
setStaticFeature: (id, feature, value) => {
|
|
1721
|
+
set(
|
|
1722
|
+
produce((state) => {
|
|
1723
|
+
if (!state.world[id].features) {
|
|
1724
|
+
}
|
|
1725
|
+
const entry = state.world[id];
|
|
1726
|
+
switch (entry.type) {
|
|
1727
|
+
case "group":
|
|
1728
|
+
entry.features[feature].value = value;
|
|
1729
|
+
state.world[id] = entry;
|
|
1730
|
+
break;
|
|
1731
|
+
default:
|
|
1732
|
+
break;
|
|
1733
|
+
}
|
|
1734
|
+
})
|
|
1735
|
+
);
|
|
1736
|
+
},
|
|
1737
|
+
createAnimatable: (elementId, featureName, value) => {
|
|
1738
|
+
set(
|
|
1739
|
+
produce((state) => {
|
|
1740
|
+
console.log("Creating animatable", elementId, featureName, value);
|
|
1741
|
+
const animatable = createAnimatable(value);
|
|
1742
|
+
if (!animatable) {
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
console.log("Created animatable", animatable);
|
|
1746
|
+
state.world[elementId].features[featureName] = {
|
|
1747
|
+
animated: true,
|
|
1748
|
+
value: animatable.id
|
|
1749
|
+
};
|
|
1750
|
+
state.animatables[animatable.id] = animatable;
|
|
1751
|
+
})
|
|
1752
|
+
);
|
|
1753
|
+
},
|
|
1754
|
+
createStatic: (elementId, featureName, value) => {
|
|
1755
|
+
set(
|
|
1756
|
+
produce((state) => {
|
|
1757
|
+
state.world[elementId].features[featureName] = {
|
|
1758
|
+
animated: false,
|
|
1759
|
+
value
|
|
1760
|
+
};
|
|
1761
|
+
})
|
|
1762
|
+
);
|
|
1763
|
+
},
|
|
1764
|
+
setAnimatableValue: (id, value) => {
|
|
1765
|
+
set(
|
|
1766
|
+
produce((state) => {
|
|
1767
|
+
console.log("Setting animatable value", id, value);
|
|
1768
|
+
state.animatables[id] = value;
|
|
1769
|
+
})
|
|
1770
|
+
);
|
|
1771
|
+
},
|
|
1772
|
+
setSlot: (parentId, parentNamespace, childId, childNamespace) => {
|
|
1773
|
+
set(
|
|
1774
|
+
produce((state) => {
|
|
1775
|
+
const parentLookupId = getLookup3(parentNamespace, parentId);
|
|
1776
|
+
const childLookupId = getLookup3(childNamespace, childId);
|
|
1777
|
+
state.slotConfig[parentLookupId] = childLookupId;
|
|
1778
|
+
})
|
|
1779
|
+
);
|
|
1780
|
+
},
|
|
1781
|
+
setSlots: (slots, replace) => {
|
|
1782
|
+
set(
|
|
1783
|
+
produce((state) => {
|
|
1784
|
+
if (replace) {
|
|
1785
|
+
state.slotConfig = slots;
|
|
1786
|
+
} else {
|
|
1787
|
+
state.slotConfig = { ...state.slotConfig, ...slots };
|
|
1788
|
+
}
|
|
1789
|
+
})
|
|
1790
|
+
);
|
|
1791
|
+
},
|
|
1792
|
+
clearSlot: (parentId, parentNamespace) => {
|
|
1793
|
+
set(
|
|
1794
|
+
produce((state) => {
|
|
1795
|
+
const parentLookupId = getLookup3(parentNamespace, parentId);
|
|
1796
|
+
delete state.slotConfig[parentLookupId];
|
|
1797
|
+
})
|
|
1798
|
+
);
|
|
1799
|
+
},
|
|
1800
|
+
setVizij: (scene, animatables) => {
|
|
1801
|
+
set({
|
|
1802
|
+
world: scene,
|
|
1803
|
+
animatables
|
|
1804
|
+
});
|
|
1805
|
+
},
|
|
1806
|
+
addWorldElements(world, animatables, replace) {
|
|
1807
|
+
if (replace) {
|
|
1808
|
+
set({ world, animatables });
|
|
1809
|
+
} else {
|
|
1810
|
+
set((state) => ({
|
|
1811
|
+
world: { ...state.world, ...world },
|
|
1812
|
+
animatables: { ...state.animatables, ...animatables }
|
|
1813
|
+
}));
|
|
1814
|
+
}
|
|
1815
|
+
},
|
|
1816
|
+
setPreferences: (preferences) => {
|
|
1817
|
+
set((state) => ({
|
|
1818
|
+
preferences: { ...state.preferences, ...preferences }
|
|
1819
|
+
}));
|
|
1820
|
+
},
|
|
1821
|
+
setReference: (id, namespace, ref) => {
|
|
1822
|
+
set(
|
|
1823
|
+
produce((state) => {
|
|
1824
|
+
state.world[id].refs[namespace].current = ref.current;
|
|
1825
|
+
if (ref.current?.children && state.world[id].refs[namespace].current) {
|
|
1826
|
+
state.world[id].refs[namespace].current.children = ref.current.children;
|
|
1827
|
+
}
|
|
1828
|
+
})
|
|
1829
|
+
);
|
|
1830
|
+
}
|
|
1831
|
+
});
|
|
1832
|
+
var useDefaultVizijStore = create()(
|
|
1833
|
+
subscribeWithSelector((set, get) => ({
|
|
1834
|
+
...VizijSlice(set, get)
|
|
1835
|
+
}))
|
|
1836
|
+
);
|
|
1837
|
+
var createVizijStore = (initial) => create()(
|
|
1838
|
+
subscribeWithSelector((set, get) => ({
|
|
1839
|
+
...VizijSlice(set, get),
|
|
1840
|
+
...initial ?? {}
|
|
1841
|
+
}))
|
|
1842
|
+
);
|
|
1843
|
+
|
|
1844
|
+
// src/vizij.tsx
|
|
1845
|
+
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1846
|
+
Object3D4.DEFAULT_UP.set(0, 0, 1);
|
|
1847
|
+
function Vizij({
|
|
1848
|
+
style,
|
|
1849
|
+
className,
|
|
1850
|
+
rootId,
|
|
1851
|
+
namespace = "default",
|
|
1852
|
+
showSafeArea = false,
|
|
1853
|
+
onPointerMissed
|
|
1854
|
+
}) {
|
|
1855
|
+
const ctx = useContext3(VizijContext);
|
|
1856
|
+
if (ctx) {
|
|
1857
|
+
return /* @__PURE__ */ jsx7(
|
|
1858
|
+
Canvas,
|
|
1859
|
+
{
|
|
1860
|
+
shadows: false,
|
|
1861
|
+
style,
|
|
1862
|
+
className,
|
|
1863
|
+
onPointerMissed,
|
|
1864
|
+
children: /* @__PURE__ */ jsx7(
|
|
1865
|
+
MemoizedInnerVizij,
|
|
1866
|
+
{
|
|
1867
|
+
rootId,
|
|
1868
|
+
namespace,
|
|
1869
|
+
showSafeArea
|
|
1870
|
+
}
|
|
1871
|
+
)
|
|
1872
|
+
}
|
|
1873
|
+
);
|
|
1874
|
+
} else {
|
|
1875
|
+
return /* @__PURE__ */ jsx7(VizijContext.Provider, { value: useDefaultVizijStore, children: /* @__PURE__ */ jsx7(
|
|
1876
|
+
Canvas,
|
|
1877
|
+
{
|
|
1878
|
+
style,
|
|
1879
|
+
className,
|
|
1880
|
+
onPointerMissed,
|
|
1881
|
+
children: /* @__PURE__ */ jsx7(
|
|
1882
|
+
MemoizedInnerVizij,
|
|
1883
|
+
{
|
|
1884
|
+
rootId,
|
|
1885
|
+
namespace,
|
|
1886
|
+
showSafeArea
|
|
1887
|
+
}
|
|
1888
|
+
)
|
|
1889
|
+
}
|
|
1890
|
+
) });
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
function InnerVizij({
|
|
1894
|
+
rootId,
|
|
1895
|
+
namespace = "default",
|
|
1896
|
+
container,
|
|
1897
|
+
showSafeArea
|
|
1898
|
+
}) {
|
|
1899
|
+
const sceneParentSizing = container ? {
|
|
1900
|
+
width: container.width * container.resolution,
|
|
1901
|
+
height: container.height * container.resolution
|
|
1902
|
+
} : void 0;
|
|
1903
|
+
return /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1904
|
+
/* @__PURE__ */ jsx7("ambientLight", { intensity: Math.PI / 2 }),
|
|
1905
|
+
/* @__PURE__ */ jsx7(
|
|
1906
|
+
OrthographicCamera,
|
|
1907
|
+
{
|
|
1908
|
+
makeDefault: true,
|
|
1909
|
+
position: [0, 0, 100],
|
|
1910
|
+
near: 0.1,
|
|
1911
|
+
far: 101
|
|
1912
|
+
}
|
|
1913
|
+
),
|
|
1914
|
+
/* @__PURE__ */ jsx7(Suspense, { fallback: null, children: /* @__PURE__ */ jsx7(
|
|
1915
|
+
World,
|
|
1916
|
+
{
|
|
1917
|
+
rootId,
|
|
1918
|
+
namespace,
|
|
1919
|
+
parentSizing: sceneParentSizing
|
|
1920
|
+
}
|
|
1921
|
+
) }),
|
|
1922
|
+
showSafeArea && /* @__PURE__ */ jsx7(SafeAreaRenderer, { rootId })
|
|
1923
|
+
] });
|
|
1924
|
+
}
|
|
1925
|
+
var MemoizedInnerVizij = memo7(InnerVizij);
|
|
1926
|
+
function InnerWorld({
|
|
1927
|
+
rootId,
|
|
1928
|
+
namespace = "default",
|
|
1929
|
+
parentSizing
|
|
1930
|
+
}) {
|
|
1931
|
+
const [present, rootBounds] = useVizijStore(
|
|
1932
|
+
useShallow7((state) => {
|
|
1933
|
+
const group = state.world[rootId];
|
|
1934
|
+
const bounds = group?.rootBounds ?? defaultRootBounds;
|
|
1935
|
+
return [group !== void 0, bounds];
|
|
1936
|
+
})
|
|
1937
|
+
);
|
|
1938
|
+
const { camera, size } = useThree((state) => ({
|
|
1939
|
+
camera: state.camera,
|
|
1940
|
+
size: state.size
|
|
1941
|
+
}));
|
|
1942
|
+
useEffect6(() => {
|
|
1943
|
+
const width = rootBounds.size.x;
|
|
1944
|
+
const height = rootBounds.size.y;
|
|
1945
|
+
if (camera && parentSizing === void 0 && camera.isOrthographicCamera) {
|
|
1946
|
+
const zoom = Math.min(size.width / width, size.height / height);
|
|
1947
|
+
const center = rootBounds.center;
|
|
1948
|
+
if (camera.zoom !== zoom) {
|
|
1949
|
+
camera.zoom = zoom;
|
|
1950
|
+
camera.updateProjectionMatrix();
|
|
1951
|
+
}
|
|
1952
|
+
if (camera.position.x !== center.x || camera.position.y !== center.y) {
|
|
1953
|
+
camera.position.x = center.x;
|
|
1954
|
+
camera.position.y = center.y;
|
|
1955
|
+
camera.updateProjectionMatrix();
|
|
1956
|
+
}
|
|
1957
|
+
} else if (camera && parentSizing !== void 0 && camera.isOrthographicCamera) {
|
|
1958
|
+
const zoom = Math.min(
|
|
1959
|
+
parentSizing.width / width,
|
|
1960
|
+
parentSizing.height / height
|
|
1961
|
+
);
|
|
1962
|
+
const center = rootBounds.center;
|
|
1963
|
+
camera.left = -0.5 * parentSizing.width / zoom + center.x;
|
|
1964
|
+
camera.right = 0.5 * parentSizing.width / zoom + center.x;
|
|
1965
|
+
camera.top = 0.5 * parentSizing.height / zoom + center.y;
|
|
1966
|
+
camera.bottom = -0.5 * parentSizing.height / zoom + center.y;
|
|
1967
|
+
camera.updateProjectionMatrix();
|
|
1968
|
+
}
|
|
1969
|
+
}, [rootBounds, camera, parentSizing, size]);
|
|
1970
|
+
return /* @__PURE__ */ jsxs4(ErrorBoundary, { fallback: null, children: [
|
|
1971
|
+
present && /* @__PURE__ */ jsx7(Renderable, { id: rootId, namespace, chain: [] }),
|
|
1972
|
+
!present && /* @__PURE__ */ jsx7(
|
|
1973
|
+
Text,
|
|
1974
|
+
{
|
|
1975
|
+
position: [0, 0, 0],
|
|
1976
|
+
color: "white",
|
|
1977
|
+
anchorX: "center",
|
|
1978
|
+
anchorY: "middle",
|
|
1979
|
+
fontSize: 0.7,
|
|
1980
|
+
children: "No Output"
|
|
1981
|
+
}
|
|
1982
|
+
)
|
|
1983
|
+
] });
|
|
1984
|
+
}
|
|
1985
|
+
var World = memo7(InnerWorld);
|
|
1986
|
+
function SafeAreaRenderer({ rootId }) {
|
|
1987
|
+
const rootBounds = useVizijStore((state) => {
|
|
1988
|
+
const group = state.world[rootId];
|
|
1989
|
+
return group?.rootBounds ?? defaultRootBounds;
|
|
1990
|
+
});
|
|
1991
|
+
const left = rootBounds.center.x - rootBounds.size.x / 2;
|
|
1992
|
+
const right = rootBounds.center.x + rootBounds.size.x / 2;
|
|
1993
|
+
const top = rootBounds.center.y + rootBounds.size.y / 2;
|
|
1994
|
+
const bottom = rootBounds.center.y - rootBounds.size.y / 2;
|
|
1995
|
+
return /* @__PURE__ */ jsx7(
|
|
1996
|
+
Line3,
|
|
1997
|
+
{
|
|
1998
|
+
points: [
|
|
1999
|
+
[left, top, 99],
|
|
2000
|
+
[right, top, 99],
|
|
2001
|
+
[right, bottom, 99],
|
|
2002
|
+
[left, bottom, 99],
|
|
2003
|
+
[left, top, 99]
|
|
2004
|
+
],
|
|
2005
|
+
color: "red",
|
|
2006
|
+
lineWidth: 2
|
|
2007
|
+
}
|
|
2008
|
+
);
|
|
2009
|
+
}
|
|
2010
|
+
var defaultRootBounds = {
|
|
2011
|
+
center: { x: 0, y: 0 },
|
|
2012
|
+
size: { x: 5, y: 4 }
|
|
2013
|
+
};
|
|
2014
|
+
|
|
2015
|
+
// src/types/shape.ts
|
|
2016
|
+
var ShapeMaterial = /* @__PURE__ */ ((ShapeMaterial2) => {
|
|
2017
|
+
ShapeMaterial2["Standard"] = "standard";
|
|
2018
|
+
ShapeMaterial2["Phong"] = "phong";
|
|
2019
|
+
ShapeMaterial2["Basic"] = "basic";
|
|
2020
|
+
ShapeMaterial2["Lambert"] = "lambert";
|
|
2021
|
+
ShapeMaterial2["Normal"] = "normal";
|
|
2022
|
+
return ShapeMaterial2;
|
|
2023
|
+
})(ShapeMaterial || {});
|
|
2024
|
+
|
|
2025
|
+
// src/hooks/use-vizij-store-subscription.ts
|
|
2026
|
+
import { useContext as useContext4, useEffect as useEffect7 } from "react";
|
|
2027
|
+
function useVizijStoreSubscription(selector, listener) {
|
|
2028
|
+
const store = useContext4(VizijContext);
|
|
2029
|
+
if (!store) throw new Error("Missing VizijProvider in the tree");
|
|
2030
|
+
useEffect7(() => {
|
|
2031
|
+
const initialValue = selector(store.getState());
|
|
2032
|
+
listener(initialValue);
|
|
2033
|
+
return store.subscribe(selector, listener);
|
|
2034
|
+
}, [store, selector, listener]);
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
// src/hooks/use-vizij-store-setter.ts
|
|
2038
|
+
import { useContext as useContext5 } from "react";
|
|
2039
|
+
function useVizijStoreSetter() {
|
|
2040
|
+
const store = useContext5(VizijContext);
|
|
2041
|
+
if (!store) throw new Error("Missing VizijProvider in the tree");
|
|
2042
|
+
return store.setState;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
// src/hooks/use-vizij-store-getter.ts
|
|
2046
|
+
import { useContext as useContext6 } from "react";
|
|
2047
|
+
function useVizijStoreGetter() {
|
|
2048
|
+
const store = useContext6(VizijContext);
|
|
2049
|
+
if (!store) throw new Error("Missing VizijProvider in the tree");
|
|
2050
|
+
return store.getState;
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
// src/functions/load-gltf.ts
|
|
2054
|
+
import * as THREE5 from "three";
|
|
2055
|
+
import { GLTFLoader, DRACOLoader } from "three-stdlib";
|
|
2056
|
+
|
|
2057
|
+
// src/functions/gltf-loading/traverse-three.ts
|
|
2058
|
+
import { createRef as createRef3 } from "react";
|
|
2059
|
+
import * as THREE4 from "three";
|
|
2060
|
+
|
|
2061
|
+
// src/functions/gltf-loading/map-features.ts
|
|
2062
|
+
function mapFeatures(features) {
|
|
2063
|
+
const animatableValues = {};
|
|
2064
|
+
const mappedFeatures = {};
|
|
2065
|
+
Object.keys(features).forEach((f) => {
|
|
2066
|
+
const feature = features[f];
|
|
2067
|
+
if (feature.animated) {
|
|
2068
|
+
const animatedValue = feature.value;
|
|
2069
|
+
mappedFeatures[f] = {
|
|
2070
|
+
animated: true,
|
|
2071
|
+
value: animatedValue.id,
|
|
2072
|
+
...feature.label ? { label: feature.label } : {}
|
|
2073
|
+
};
|
|
2074
|
+
animatableValues[animatedValue.id] = animatedValue;
|
|
2075
|
+
} else {
|
|
2076
|
+
const { animated, value, label } = feature;
|
|
2077
|
+
mappedFeatures[f] = label ? { animated, value, label } : { animated, value };
|
|
2078
|
+
}
|
|
2079
|
+
});
|
|
2080
|
+
return [mappedFeatures, animatableValues];
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
// src/functions/gltf-loading/import-scene.ts
|
|
2084
|
+
import { Object3D as Object3D7 } from "three";
|
|
2085
|
+
|
|
2086
|
+
// src/functions/gltf-loading/import-group.ts
|
|
2087
|
+
import { Object3D as Object3D6 } from "three";
|
|
2088
|
+
|
|
2089
|
+
// src/functions/util.ts
|
|
2090
|
+
import { createRef as createRef2 } from "react";
|
|
2091
|
+
function namespaceArrayToRefs(namespaces) {
|
|
2092
|
+
return namespaces.reduce((acc, ns) => ({ ...acc, [ns]: createRef2() }), {});
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
// src/functions/gltf-loading/import-mesh.ts
|
|
2096
|
+
import {
|
|
2097
|
+
Object3D as Object3D5
|
|
2098
|
+
} from "three";
|
|
2099
|
+
|
|
2100
|
+
// src/functions/gltf-loading/import-geometry.ts
|
|
2101
|
+
function sanitizeMorphKey(name, fallbackIndex, used) {
|
|
2102
|
+
const baseName = name && name.trim().length > 0 ? name.trim() : `morph_${fallbackIndex + 1}`;
|
|
2103
|
+
const slug = baseName.toLowerCase().replace(/[^a-z0-9]+/gi, "_").replace(/^_+|_+$/g, "");
|
|
2104
|
+
const safeBase = slug.length > 0 ? slug : `morph_${fallbackIndex + 1}`;
|
|
2105
|
+
let candidate = safeBase;
|
|
2106
|
+
let counter = 1;
|
|
2107
|
+
while (used.has(candidate)) {
|
|
2108
|
+
candidate = `${safeBase}_${counter++}`;
|
|
2109
|
+
}
|
|
2110
|
+
used.add(candidate);
|
|
2111
|
+
return candidate;
|
|
2112
|
+
}
|
|
2113
|
+
function importGeometry(geometry, mesh) {
|
|
2114
|
+
const features = {};
|
|
2115
|
+
const animatableValues = {};
|
|
2116
|
+
const morphIds = [];
|
|
2117
|
+
const morphTargets = mesh.morphTargetDictionary;
|
|
2118
|
+
if (!morphTargets) {
|
|
2119
|
+
return [features, animatableValues, void 0];
|
|
2120
|
+
} else {
|
|
2121
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
2122
|
+
Object.entries(mesh.morphTargetDictionary ?? {}).forEach(
|
|
2123
|
+
([name, index]) => {
|
|
2124
|
+
const morphId = crypto.randomUUID();
|
|
2125
|
+
const featureKey = sanitizeMorphKey(name, index, usedKeys);
|
|
2126
|
+
morphIds.push(featureKey);
|
|
2127
|
+
features[featureKey] = {
|
|
2128
|
+
animated: true,
|
|
2129
|
+
value: morphId
|
|
2130
|
+
};
|
|
2131
|
+
const displayName = name && name.trim().length > 0 ? name.trim() : featureKey;
|
|
2132
|
+
const animatableMorphValue = {
|
|
2133
|
+
id: morphId,
|
|
2134
|
+
name: `${mesh.name ?? "Shape"} ${displayName}`,
|
|
2135
|
+
type: "number",
|
|
2136
|
+
default: mesh.morphTargetInfluences?.[index] ?? 0,
|
|
2137
|
+
constraints: {
|
|
2138
|
+
min: 0,
|
|
2139
|
+
max: 1
|
|
2140
|
+
},
|
|
2141
|
+
pub: {
|
|
2142
|
+
public: true,
|
|
2143
|
+
output: displayName
|
|
2144
|
+
}
|
|
2145
|
+
};
|
|
2146
|
+
animatableValues[morphId] = animatableMorphValue;
|
|
2147
|
+
}
|
|
2148
|
+
);
|
|
2149
|
+
return [features, animatableValues, morphIds];
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
// src/functions/gltf-loading/import-mesh.ts
|
|
2154
|
+
Object3D5.DEFAULT_UP.set(0, 0, 1);
|
|
2155
|
+
function importMesh(mesh, namespaces, colorLookup) {
|
|
2156
|
+
let world = {};
|
|
2157
|
+
let animatables = {};
|
|
2158
|
+
let newColorLookup = {};
|
|
2159
|
+
const translationAnimatable = {
|
|
2160
|
+
id: crypto.randomUUID(),
|
|
2161
|
+
name: `${mesh.name ?? "Mesh"} translation`,
|
|
2162
|
+
type: "vector3",
|
|
2163
|
+
default: { x: mesh.position.x, y: mesh.position.y, z: mesh.position.z },
|
|
2164
|
+
constraints: {},
|
|
2165
|
+
pub: {
|
|
2166
|
+
public: true,
|
|
2167
|
+
output: `${mesh.name ?? "Mesh"} translation`,
|
|
2168
|
+
units: "m"
|
|
2169
|
+
}
|
|
2170
|
+
};
|
|
2171
|
+
animatables = {
|
|
2172
|
+
...animatables,
|
|
2173
|
+
[translationAnimatable.id]: translationAnimatable
|
|
2174
|
+
};
|
|
2175
|
+
const rotationAnimatable = {
|
|
2176
|
+
id: crypto.randomUUID(),
|
|
2177
|
+
name: `${mesh.name ?? "Mesh"} rotation`,
|
|
2178
|
+
type: "euler",
|
|
2179
|
+
default: { x: mesh.rotation.x, y: mesh.rotation.y, z: mesh.rotation.z },
|
|
2180
|
+
constraints: {},
|
|
2181
|
+
pub: {
|
|
2182
|
+
public: true,
|
|
2183
|
+
output: `${mesh.name ?? "Mesh"} rotation`,
|
|
2184
|
+
units: "rad"
|
|
2185
|
+
}
|
|
2186
|
+
};
|
|
2187
|
+
animatables = { ...animatables, [rotationAnimatable.id]: rotationAnimatable };
|
|
2188
|
+
const scaleAnimatable = {
|
|
2189
|
+
id: crypto.randomUUID(),
|
|
2190
|
+
name: `${mesh.name ?? "Mesh"} scale`,
|
|
2191
|
+
type: "vector3",
|
|
2192
|
+
default: { x: mesh.scale.x, y: mesh.scale.y, z: mesh.scale.z },
|
|
2193
|
+
constraints: {},
|
|
2194
|
+
pub: {
|
|
2195
|
+
public: true,
|
|
2196
|
+
output: `${mesh.name ?? "Mesh"} scale`
|
|
2197
|
+
}
|
|
2198
|
+
};
|
|
2199
|
+
animatables = { ...animatables, [scaleAnimatable.id]: scaleAnimatable };
|
|
2200
|
+
const color = mesh.material.color;
|
|
2201
|
+
let useEmissive = false;
|
|
2202
|
+
if (color.r === 0 && color.g === 0 && color.b === 0 && mesh.material.emissive) {
|
|
2203
|
+
color.copy(mesh.material.emissive);
|
|
2204
|
+
useEmissive = true;
|
|
2205
|
+
}
|
|
2206
|
+
const colorName = mesh.material.name;
|
|
2207
|
+
const colorAnimatable = {
|
|
2208
|
+
id: crypto.randomUUID(),
|
|
2209
|
+
name: mesh.material.name ?? `${mesh.name ?? "Mesh"} color`,
|
|
2210
|
+
type: "rgb",
|
|
2211
|
+
default: { r: color.r, g: color.g, b: color.b },
|
|
2212
|
+
constraints: {
|
|
2213
|
+
min: [0, 0, 0],
|
|
2214
|
+
max: [1, 1, 1]
|
|
2215
|
+
},
|
|
2216
|
+
pub: {
|
|
2217
|
+
public: true,
|
|
2218
|
+
output: mesh.material.name ? `${mesh.material.name} color` : `${mesh.name ?? "Mesh"} color`
|
|
2219
|
+
}
|
|
2220
|
+
};
|
|
2221
|
+
const opacityAnimatable = {
|
|
2222
|
+
id: crypto.randomUUID(),
|
|
2223
|
+
name: mesh.material.name ? `${mesh.material.name} opacity` : `${mesh.name ?? "Mesh"} opacity`,
|
|
2224
|
+
type: "number",
|
|
2225
|
+
default: mesh.material.opacity,
|
|
2226
|
+
constraints: {
|
|
2227
|
+
min: 0,
|
|
2228
|
+
max: 1
|
|
2229
|
+
},
|
|
2230
|
+
pub: {
|
|
2231
|
+
public: true,
|
|
2232
|
+
output: mesh.material.name ? `${mesh.material.name} opacity` : `${mesh.name ?? "Mesh"} opacity`
|
|
2233
|
+
}
|
|
2234
|
+
};
|
|
2235
|
+
let colorId = colorAnimatable.id;
|
|
2236
|
+
let opacityId = opacityAnimatable.id;
|
|
2237
|
+
if (colorName && colorLookup[colorName]) {
|
|
2238
|
+
colorId = colorLookup[colorName][0];
|
|
2239
|
+
opacityId = colorLookup[colorName][1];
|
|
2240
|
+
useEmissive = colorLookup[colorName][2];
|
|
2241
|
+
} else {
|
|
2242
|
+
animatables = { ...animatables, [colorAnimatable.id]: colorAnimatable };
|
|
2243
|
+
animatables = { ...animatables, [opacityAnimatable.id]: opacityAnimatable };
|
|
2244
|
+
if (colorName) {
|
|
2245
|
+
newColorLookup[colorName] = [
|
|
2246
|
+
colorAnimatable.id,
|
|
2247
|
+
opacityAnimatable.id,
|
|
2248
|
+
useEmissive
|
|
2249
|
+
];
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
const [geometryFeatures, geometryAnimatables, morphTargets] = importGeometry(
|
|
2253
|
+
mesh.geometry,
|
|
2254
|
+
mesh
|
|
2255
|
+
);
|
|
2256
|
+
animatables = { ...animatables, ...geometryAnimatables };
|
|
2257
|
+
const children = [];
|
|
2258
|
+
mesh.children.forEach((child) => {
|
|
2259
|
+
if (child.isMesh) {
|
|
2260
|
+
const [newWorldItems, newAnimatables, childId, newMeshColors] = importMesh(child, namespaces, {
|
|
2261
|
+
...colorLookup,
|
|
2262
|
+
...newColorLookup
|
|
2263
|
+
});
|
|
2264
|
+
newColorLookup = { ...newColorLookup, ...newMeshColors };
|
|
2265
|
+
world = { ...world, ...newWorldItems };
|
|
2266
|
+
animatables = { ...animatables, ...newAnimatables };
|
|
2267
|
+
children.push(childId);
|
|
2268
|
+
}
|
|
2269
|
+
});
|
|
2270
|
+
const newShape = {
|
|
2271
|
+
id: mesh.uuid,
|
|
2272
|
+
name: mesh.name,
|
|
2273
|
+
geometry: mesh.geometry,
|
|
2274
|
+
material: getShapeMaterial(mesh, useEmissive),
|
|
2275
|
+
type: "shape",
|
|
2276
|
+
tags: [],
|
|
2277
|
+
morphTargets,
|
|
2278
|
+
features: {
|
|
2279
|
+
translation: { animated: true, value: translationAnimatable.id },
|
|
2280
|
+
rotation: { animated: true, value: rotationAnimatable.id },
|
|
2281
|
+
scale: { animated: true, value: scaleAnimatable.id },
|
|
2282
|
+
color: { animated: true, value: colorId },
|
|
2283
|
+
opacity: { animated: true, value: opacityId },
|
|
2284
|
+
...geometryFeatures
|
|
2285
|
+
},
|
|
2286
|
+
children: children.length > 0 ? children : void 0,
|
|
2287
|
+
refs: namespaceArrayToRefs(namespaces)
|
|
2288
|
+
};
|
|
2289
|
+
world = { ...world, [newShape.id]: newShape };
|
|
2290
|
+
return [world, animatables, newShape.id, newColorLookup];
|
|
2291
|
+
}
|
|
2292
|
+
function getShapeMaterial(mesh, useEmissive) {
|
|
2293
|
+
const material = mesh.material;
|
|
2294
|
+
if (useEmissive) {
|
|
2295
|
+
return "basic" /* Basic */;
|
|
2296
|
+
}
|
|
2297
|
+
if (material.isMeshStandardMaterial) {
|
|
2298
|
+
return "standard" /* Standard */;
|
|
2299
|
+
} else if (material.isMeshPhongMaterial) {
|
|
2300
|
+
return "phong" /* Phong */;
|
|
2301
|
+
} else if (material.isMeshBasicMaterial) {
|
|
2302
|
+
return "basic" /* Basic */;
|
|
2303
|
+
} else if (material.isMeshNormalMaterial) {
|
|
2304
|
+
return "normal" /* Normal */;
|
|
2305
|
+
} else if (material.isMeshLambertMaterial) {
|
|
2306
|
+
return "lambert" /* Lambert */;
|
|
2307
|
+
} else {
|
|
2308
|
+
return "standard" /* Standard */;
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
// src/functions/gltf-loading/import-group.ts
|
|
2313
|
+
Object3D6.DEFAULT_UP.set(0, 0, 1);
|
|
2314
|
+
function importGroup(group, namespaces, colorLookup, rootBounds) {
|
|
2315
|
+
let world = {};
|
|
2316
|
+
let animatables = {};
|
|
2317
|
+
let newColorLookup = {};
|
|
2318
|
+
const children = [];
|
|
2319
|
+
const translationAnimatable = {
|
|
2320
|
+
id: crypto.randomUUID(),
|
|
2321
|
+
name: `${group.name ?? "Group"} translation`,
|
|
2322
|
+
type: "vector3",
|
|
2323
|
+
default: { x: group.position.x, y: group.position.y, z: group.position.z },
|
|
2324
|
+
constraints: {},
|
|
2325
|
+
pub: {
|
|
2326
|
+
public: true,
|
|
2327
|
+
output: `${group.name ?? "Group"} translation`,
|
|
2328
|
+
units: "m"
|
|
2329
|
+
}
|
|
2330
|
+
};
|
|
2331
|
+
animatables = {
|
|
2332
|
+
...animatables,
|
|
2333
|
+
[translationAnimatable.id]: translationAnimatable
|
|
2334
|
+
};
|
|
2335
|
+
const rotationAnimatable = {
|
|
2336
|
+
id: crypto.randomUUID(),
|
|
2337
|
+
name: `${group.name ?? "Group"} rotation`,
|
|
2338
|
+
type: "euler",
|
|
2339
|
+
default: { x: group.rotation.x, y: group.rotation.y, z: group.rotation.z },
|
|
2340
|
+
constraints: {},
|
|
2341
|
+
pub: {
|
|
2342
|
+
public: true,
|
|
2343
|
+
output: `${group.name ?? "Group"} rotation`,
|
|
2344
|
+
units: "rad"
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
animatables = { ...animatables, [rotationAnimatable.id]: rotationAnimatable };
|
|
2348
|
+
const scaleAnimatable = {
|
|
2349
|
+
id: crypto.randomUUID(),
|
|
2350
|
+
name: `${group.name ?? "Group"} scale`,
|
|
2351
|
+
type: "vector3",
|
|
2352
|
+
default: { x: group.scale.x, y: group.scale.y, z: group.scale.z },
|
|
2353
|
+
constraints: {},
|
|
2354
|
+
pub: {
|
|
2355
|
+
public: true,
|
|
2356
|
+
output: `${group.name ?? "Group"} scale`
|
|
2357
|
+
}
|
|
2358
|
+
};
|
|
2359
|
+
animatables = { ...animatables, [scaleAnimatable.id]: scaleAnimatable };
|
|
2360
|
+
group.children.forEach((child) => {
|
|
2361
|
+
if (child.isMesh) {
|
|
2362
|
+
const [newWorldItems, newAnimatables, childId, newMeshColors] = importMesh(child, namespaces, {
|
|
2363
|
+
...colorLookup,
|
|
2364
|
+
...newColorLookup
|
|
2365
|
+
});
|
|
2366
|
+
newColorLookup = { ...newColorLookup, ...newMeshColors };
|
|
2367
|
+
world = { ...world, ...newWorldItems };
|
|
2368
|
+
animatables = { ...animatables, ...newAnimatables };
|
|
2369
|
+
children.push(childId);
|
|
2370
|
+
} else if (child.isGroup || child.isObject3D && child.children.length !== 0) {
|
|
2371
|
+
const [newWorldItems, newAnimatables, childId, newMeshColors] = importGroup(child, namespaces, {
|
|
2372
|
+
...colorLookup,
|
|
2373
|
+
...newColorLookup
|
|
2374
|
+
});
|
|
2375
|
+
newColorLookup = { ...newColorLookup, ...newMeshColors };
|
|
2376
|
+
world = { ...world, ...newWorldItems };
|
|
2377
|
+
animatables = { ...animatables, ...newAnimatables };
|
|
2378
|
+
children.push(childId);
|
|
2379
|
+
}
|
|
2380
|
+
});
|
|
2381
|
+
const newGroup = {
|
|
2382
|
+
id: group.uuid,
|
|
2383
|
+
name: group.name,
|
|
2384
|
+
type: "group",
|
|
2385
|
+
tags: [],
|
|
2386
|
+
features: {
|
|
2387
|
+
translation: { animated: true, value: translationAnimatable.id },
|
|
2388
|
+
rotation: { animated: true, value: rotationAnimatable.id },
|
|
2389
|
+
scale: { animated: true, value: scaleAnimatable.id }
|
|
2390
|
+
},
|
|
2391
|
+
root: Boolean(rootBounds),
|
|
2392
|
+
rootBounds,
|
|
2393
|
+
children,
|
|
2394
|
+
refs: namespaceArrayToRefs(namespaces)
|
|
2395
|
+
};
|
|
2396
|
+
world = { ...world, [newGroup.id]: newGroup };
|
|
2397
|
+
return [world, animatables, newGroup.id, newColorLookup];
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
// src/functions/gltf-loading/import-scene.ts
|
|
2401
|
+
Object3D7.DEFAULT_UP.set(0, 0, 1);
|
|
2402
|
+
function importScene(scene, namespaces, rootBounds) {
|
|
2403
|
+
let world = {};
|
|
2404
|
+
let animatables = {};
|
|
2405
|
+
const [newWorldItems, newAnimatables] = importGroup(
|
|
2406
|
+
scene,
|
|
2407
|
+
namespaces,
|
|
2408
|
+
{},
|
|
2409
|
+
rootBounds
|
|
2410
|
+
);
|
|
2411
|
+
world = { ...world, ...newWorldItems };
|
|
2412
|
+
animatables = { ...animatables, ...newAnimatables };
|
|
2413
|
+
return [world, animatables];
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
// src/functions/gltf-loading/traverse-three.ts
|
|
2417
|
+
THREE4.Object3D.DEFAULT_UP.set(0, 0, 1);
|
|
2418
|
+
function traverseThree(group, namespaces, aggressiveImport = false, rootBounds) {
|
|
2419
|
+
const worldData = {};
|
|
2420
|
+
const animatableData = {};
|
|
2421
|
+
let hasRobotData = false;
|
|
2422
|
+
group.traverse((child) => {
|
|
2423
|
+
if (child.userData?.gltfExtensions?.RobotData) {
|
|
2424
|
+
hasRobotData = true;
|
|
2425
|
+
}
|
|
2426
|
+
});
|
|
2427
|
+
const useRobotData = !aggressiveImport || hasRobotData;
|
|
2428
|
+
if (useRobotData) {
|
|
2429
|
+
group.traverse((child) => {
|
|
2430
|
+
if (child.userData?.gltfExtensions?.RobotData) {
|
|
2431
|
+
const data = child.userData.gltfExtensions.RobotData;
|
|
2432
|
+
let loadedData;
|
|
2433
|
+
let mappedFeatures;
|
|
2434
|
+
let animatableValues;
|
|
2435
|
+
switch (data.type) {
|
|
2436
|
+
case "group":
|
|
2437
|
+
loadedData = {
|
|
2438
|
+
...data,
|
|
2439
|
+
refs: namespaces.reduce(
|
|
2440
|
+
(acc, ns) => ({ ...acc, [ns]: createRef3() }),
|
|
2441
|
+
{}
|
|
2442
|
+
)
|
|
2443
|
+
};
|
|
2444
|
+
[mappedFeatures, animatableValues] = mapFeatures(data.features);
|
|
2445
|
+
isGroupFeatures(mappedFeatures);
|
|
2446
|
+
loadedData.features = mappedFeatures;
|
|
2447
|
+
Object.assign(animatableData, animatableValues);
|
|
2448
|
+
worldData[loadedData.id] = loadedData;
|
|
2449
|
+
break;
|
|
2450
|
+
case "shape":
|
|
2451
|
+
loadedData = {
|
|
2452
|
+
...data,
|
|
2453
|
+
geometry: child.geometry,
|
|
2454
|
+
refs: namespaces.reduce(
|
|
2455
|
+
(acc, ns) => ({ ...acc, [ns]: createRef3() }),
|
|
2456
|
+
{}
|
|
2457
|
+
)
|
|
2458
|
+
};
|
|
2459
|
+
[mappedFeatures, animatableValues] = mapFeatures(data.features);
|
|
2460
|
+
isShapeFeatures(mappedFeatures);
|
|
2461
|
+
loadedData.features = mappedFeatures;
|
|
2462
|
+
Object.assign(animatableData, animatableValues);
|
|
2463
|
+
worldData[loadedData.id] = loadedData;
|
|
2464
|
+
break;
|
|
2465
|
+
case "ellipse":
|
|
2466
|
+
loadedData = {
|
|
2467
|
+
...data,
|
|
2468
|
+
refs: namespaces.reduce(
|
|
2469
|
+
(acc, ns) => ({ ...acc, [ns]: createRef3() }),
|
|
2470
|
+
{}
|
|
2471
|
+
)
|
|
2472
|
+
};
|
|
2473
|
+
[mappedFeatures, animatableValues] = mapFeatures(data.features);
|
|
2474
|
+
isEllipseFeatures(mappedFeatures);
|
|
2475
|
+
loadedData.features = mappedFeatures;
|
|
2476
|
+
Object.assign(animatableData, animatableValues);
|
|
2477
|
+
worldData[loadedData.id] = loadedData;
|
|
2478
|
+
break;
|
|
2479
|
+
case "rectangle":
|
|
2480
|
+
loadedData = {
|
|
2481
|
+
...data,
|
|
2482
|
+
refs: namespaces.reduce(
|
|
2483
|
+
(acc, ns) => ({ ...acc, [ns]: createRef3() }),
|
|
2484
|
+
{}
|
|
2485
|
+
)
|
|
2486
|
+
};
|
|
2487
|
+
[mappedFeatures, animatableValues] = mapFeatures(data.features);
|
|
2488
|
+
isRectangleFeatures(mappedFeatures);
|
|
2489
|
+
loadedData.features = mappedFeatures;
|
|
2490
|
+
Object.assign(animatableData, animatableValues);
|
|
2491
|
+
worldData[loadedData.id] = loadedData;
|
|
2492
|
+
break;
|
|
2493
|
+
default:
|
|
2494
|
+
throw new Error(`Unhandled type`);
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
});
|
|
2498
|
+
} else {
|
|
2499
|
+
const derivedRootBounds = rootBounds ?? deriveRootBounds(group);
|
|
2500
|
+
if (!derivedRootBounds) {
|
|
2501
|
+
throw new Error("Root bounds are expected if using an aggressive import");
|
|
2502
|
+
}
|
|
2503
|
+
const [newWorldData, newAnimatableData] = importScene(
|
|
2504
|
+
group,
|
|
2505
|
+
namespaces,
|
|
2506
|
+
derivedRootBounds
|
|
2507
|
+
);
|
|
2508
|
+
Object.assign(worldData, newWorldData);
|
|
2509
|
+
Object.assign(animatableData, newAnimatableData);
|
|
2510
|
+
}
|
|
2511
|
+
return [worldData, animatableData];
|
|
2512
|
+
}
|
|
2513
|
+
function isGroupFeatures(value) {
|
|
2514
|
+
if (!value || typeof value !== "object") {
|
|
2515
|
+
throw new Error("Expected object");
|
|
2516
|
+
}
|
|
2517
|
+
if (!["translation", "rotation"].every((key) => key in value)) {
|
|
2518
|
+
throw new Error("Expected translation and rotation keys in features");
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
function isShapeFeatures(value) {
|
|
2522
|
+
if (!value || typeof value !== "object") {
|
|
2523
|
+
throw new Error("Expected object");
|
|
2524
|
+
}
|
|
2525
|
+
if (!["translation", "rotation"].every((key) => key in value)) {
|
|
2526
|
+
throw new Error("Expected translation and rotation keys in features");
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
function isEllipseFeatures(value) {
|
|
2530
|
+
if (!value || typeof value !== "object") {
|
|
2531
|
+
throw new Error("Expected object");
|
|
2532
|
+
}
|
|
2533
|
+
if (!["translation", "rotation", "height", "width"].every((key) => key in value)) {
|
|
2534
|
+
throw new Error(
|
|
2535
|
+
"Expected translation, rotation, width, and height keys in features"
|
|
2536
|
+
);
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
function isRectangleFeatures(value) {
|
|
2540
|
+
if (!value || typeof value !== "object") {
|
|
2541
|
+
throw new Error("Expected object");
|
|
2542
|
+
}
|
|
2543
|
+
if (!["translation", "rotation", "height", "width"].every((key) => key in value)) {
|
|
2544
|
+
throw new Error(
|
|
2545
|
+
"Expected translation, rotation, width, and height keys in features"
|
|
2546
|
+
);
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
function deriveRootBounds(group) {
|
|
2550
|
+
const boundingBox = new THREE4.Box3().setFromObject(group);
|
|
2551
|
+
if (boundingBox.isEmpty()) {
|
|
2552
|
+
return null;
|
|
2553
|
+
}
|
|
2554
|
+
const { min, max } = boundingBox;
|
|
2555
|
+
if (!Number.isFinite(min.x) || !Number.isFinite(min.y) || !Number.isFinite(max.x) || !Number.isFinite(max.y)) {
|
|
2556
|
+
return null;
|
|
2557
|
+
}
|
|
2558
|
+
const width = max.x - min.x;
|
|
2559
|
+
const height = max.y - min.y;
|
|
2560
|
+
if (!Number.isFinite(width) || !Number.isFinite(height)) {
|
|
2561
|
+
return null;
|
|
2562
|
+
}
|
|
2563
|
+
const safeWidth = Math.max(Math.abs(width), 1e-3);
|
|
2564
|
+
const safeHeight = Math.max(Math.abs(height), 1e-3);
|
|
2565
|
+
return {
|
|
2566
|
+
center: {
|
|
2567
|
+
x: (min.x + max.x) / 2,
|
|
2568
|
+
y: (min.y + max.y) / 2
|
|
2569
|
+
},
|
|
2570
|
+
size: {
|
|
2571
|
+
x: safeWidth,
|
|
2572
|
+
y: safeHeight
|
|
2573
|
+
}
|
|
2574
|
+
};
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
// src/functions/load-gltf.ts
|
|
2578
|
+
THREE5.Object3D.DEFAULT_UP.set(0, 0, 1);
|
|
2579
|
+
var EmptyModelError = class extends Error {
|
|
2580
|
+
constructor(message) {
|
|
2581
|
+
super(message);
|
|
2582
|
+
this.name = "EmptyModelError";
|
|
2583
|
+
}
|
|
2584
|
+
};
|
|
2585
|
+
async function loadGLTF(url, namespaces, aggressiveImport = false, rootBounds) {
|
|
2586
|
+
const modelLoader = new GLTFLoader();
|
|
2587
|
+
modelLoader.setDRACOLoader(new DRACOLoader());
|
|
2588
|
+
const modelData = await modelLoader.loadAsync(url);
|
|
2589
|
+
const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
|
|
2590
|
+
return traverseThree(
|
|
2591
|
+
modelData.scene,
|
|
2592
|
+
actualizedNamespaces,
|
|
2593
|
+
aggressiveImport,
|
|
2594
|
+
rootBounds
|
|
2595
|
+
);
|
|
2596
|
+
}
|
|
2597
|
+
async function loadGLTFFromBlob(blob, namespaces, aggressiveImport = false, rootBounds) {
|
|
2598
|
+
const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
|
|
2599
|
+
if (typeof URL !== "undefined" && typeof URL.createObjectURL === "function") {
|
|
2600
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
2601
|
+
try {
|
|
2602
|
+
return await loadGLTF(
|
|
2603
|
+
objectUrl,
|
|
2604
|
+
actualizedNamespaces,
|
|
2605
|
+
aggressiveImport,
|
|
2606
|
+
rootBounds
|
|
2607
|
+
);
|
|
2608
|
+
} finally {
|
|
2609
|
+
URL.revokeObjectURL(objectUrl);
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
const arrayBuffer = typeof blob.arrayBuffer === "function" ? await blob.arrayBuffer() : await new Response(blob).arrayBuffer();
|
|
2613
|
+
return new Promise((resolve, reject) => {
|
|
2614
|
+
const loader = new GLTFLoader();
|
|
2615
|
+
loader.setDRACOLoader(new DRACOLoader());
|
|
2616
|
+
loader.parse(
|
|
2617
|
+
arrayBuffer,
|
|
2618
|
+
"",
|
|
2619
|
+
(gltf) => {
|
|
2620
|
+
try {
|
|
2621
|
+
resolve(
|
|
2622
|
+
traverseThree(
|
|
2623
|
+
gltf.scene,
|
|
2624
|
+
actualizedNamespaces,
|
|
2625
|
+
aggressiveImport,
|
|
2626
|
+
rootBounds
|
|
2627
|
+
)
|
|
2628
|
+
);
|
|
2629
|
+
} catch (error) {
|
|
2630
|
+
if (error instanceof Error) {
|
|
2631
|
+
reject(error);
|
|
2632
|
+
} else {
|
|
2633
|
+
reject(new Error(String(error)));
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
},
|
|
2637
|
+
(error) => {
|
|
2638
|
+
reject(new Error(`Error loading GLTF: ${error.message}`));
|
|
2639
|
+
}
|
|
2640
|
+
);
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2643
|
+
|
|
2644
|
+
// src/functions/load-gltf-blob.ts
|
|
2645
|
+
import { DRACOLoader as DRACOLoader2, GLTFLoader as GLTFLoader2 } from "three-stdlib";
|
|
2646
|
+
var loadGltfFromBlob = (blob, namespaces) => {
|
|
2647
|
+
return new Promise((resolve, reject) => {
|
|
2648
|
+
const loader = new GLTFLoader2();
|
|
2649
|
+
loader.setDRACOLoader(new DRACOLoader2());
|
|
2650
|
+
const reader = new FileReader();
|
|
2651
|
+
reader.onload = () => {
|
|
2652
|
+
const arrayBuffer = reader.result;
|
|
2653
|
+
loader.parse(
|
|
2654
|
+
arrayBuffer,
|
|
2655
|
+
"",
|
|
2656
|
+
// Base path for resolving external resources
|
|
2657
|
+
(gltf) => {
|
|
2658
|
+
const actualizedNamespaces = namespaces.length > 0 ? namespaces : ["default"];
|
|
2659
|
+
resolve(traverseThree(gltf.scene, actualizedNamespaces));
|
|
2660
|
+
},
|
|
2661
|
+
(error) => {
|
|
2662
|
+
reject(new Error(`Error loading GLTF: ${error.message}`));
|
|
2663
|
+
}
|
|
2664
|
+
);
|
|
2665
|
+
};
|
|
2666
|
+
reader.onerror = () => {
|
|
2667
|
+
reject(new Error("Failed to read Blob as ArrayBuffer."));
|
|
2668
|
+
};
|
|
2669
|
+
reader.readAsArrayBuffer(blob);
|
|
2670
|
+
});
|
|
2671
|
+
};
|
|
2672
|
+
|
|
2673
|
+
// src/functions/export.ts
|
|
2674
|
+
import { GLTFExporter } from "three-stdlib";
|
|
2675
|
+
import * as THREE6 from "three";
|
|
2676
|
+
THREE6.Object3D.DEFAULT_UP.set(0, 0, 1);
|
|
2677
|
+
function exportScene(data, fileName = "scene.glb") {
|
|
2678
|
+
const exporter = new GLTFExporter();
|
|
2679
|
+
exporter.parse(
|
|
2680
|
+
data,
|
|
2681
|
+
(gltf) => {
|
|
2682
|
+
if (!(gltf instanceof ArrayBuffer)) {
|
|
2683
|
+
throw new Error("Failed to export scene!");
|
|
2684
|
+
}
|
|
2685
|
+
const link = document.createElement("a");
|
|
2686
|
+
link.href = URL.createObjectURL(
|
|
2687
|
+
new Blob([gltf], {
|
|
2688
|
+
type: "application/octet-stream"
|
|
2689
|
+
})
|
|
2690
|
+
);
|
|
2691
|
+
const trimmed = fileName.trim();
|
|
2692
|
+
const safeFileName = trimmed.length > 0 ? trimmed : "scene.glb";
|
|
2693
|
+
const downloadName = safeFileName.toLowerCase().endsWith(".glb") ? safeFileName : `${safeFileName}.glb`;
|
|
2694
|
+
link.download = downloadName;
|
|
2695
|
+
link.click();
|
|
2696
|
+
URL.revokeObjectURL(link.href);
|
|
2697
|
+
},
|
|
2698
|
+
() => {
|
|
2699
|
+
},
|
|
2700
|
+
{
|
|
2701
|
+
trs: true,
|
|
2702
|
+
onlyVisible: false,
|
|
2703
|
+
binary: true,
|
|
2704
|
+
includeCustomExtensions: true
|
|
2705
|
+
}
|
|
2706
|
+
);
|
|
2707
|
+
}
|
|
2708
|
+
export {
|
|
2709
|
+
Controller,
|
|
2710
|
+
EmptyModelError,
|
|
2711
|
+
InnerVizij,
|
|
2712
|
+
ShapeMaterial,
|
|
2713
|
+
Vizij,
|
|
2714
|
+
VizijContext,
|
|
2715
|
+
VizijSlice,
|
|
2716
|
+
createVizijStore,
|
|
2717
|
+
exportScene,
|
|
2718
|
+
loadGLTF,
|
|
2719
|
+
loadGLTFFromBlob,
|
|
2720
|
+
loadGltfFromBlob,
|
|
2721
|
+
useDefaultVizijStore,
|
|
2722
|
+
useFeatures,
|
|
2723
|
+
useVizijStore,
|
|
2724
|
+
useVizijStoreGetter,
|
|
2725
|
+
useVizijStoreSetter,
|
|
2726
|
+
useVizijStoreSubscription
|
|
2727
|
+
};
|