@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/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
+ };