@liveroom-tech/react-immersive 2.2.0 → 3.0.0
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 +193 -28
- package/dist/index.d.mts +386 -76
- package/dist/index.d.ts +386 -76
- package/dist/index.js +1793 -490
- package/dist/index.mjs +1616 -313
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -48,13 +48,17 @@ var init_bvhWorkerCode = __esm({
|
|
|
48
48
|
var index_exports = {};
|
|
49
49
|
__export(index_exports, {
|
|
50
50
|
BindingBuilder: () => BindingBuilder,
|
|
51
|
+
DEFAULT_SCENE_CONFIG: () => DEFAULT_SCENE_CONFIG,
|
|
51
52
|
MATERIAL_BLENDING_MODES: () => MATERIAL_BLENDING_MODES,
|
|
52
53
|
MATERIAL_SIDES: () => MATERIAL_SIDES,
|
|
53
54
|
ModelViewer: () => ModelViewer,
|
|
54
55
|
SimpleModelViewer: () => SimpleModelViewer,
|
|
56
|
+
defineObjectBindings: () => defineObjectBindings,
|
|
55
57
|
material: () => material,
|
|
56
58
|
patchBindings: () => patchBindings,
|
|
57
59
|
patchSceneConfig: () => patchSceneConfig,
|
|
60
|
+
selectBindingKeys: () => selectBindingKeys,
|
|
61
|
+
selectBindings: () => selectBindings,
|
|
58
62
|
useObjectBinding: () => useObjectBinding,
|
|
59
63
|
useObjectBindingIds: () => useObjectBindingIds,
|
|
60
64
|
useObjectBindings: () => useObjectBindings,
|
|
@@ -63,6 +67,8 @@ __export(index_exports, {
|
|
|
63
67
|
useViewerActions: () => useViewerActions,
|
|
64
68
|
useViewerAnimations: () => useViewerAnimations,
|
|
65
69
|
useViewerCamera: () => useViewerCamera,
|
|
70
|
+
useViewerConnection: () => useViewerConnection,
|
|
71
|
+
useViewerEffects: () => useViewerEffects,
|
|
66
72
|
useViewerHover: () => useViewerHover,
|
|
67
73
|
useViewerModel: () => useViewerModel,
|
|
68
74
|
useViewerSelection: () => useViewerSelection
|
|
@@ -221,7 +227,7 @@ function disposeSceneBVH(root) {
|
|
|
221
227
|
var import_fiber12 = require("@react-three/fiber");
|
|
222
228
|
var import_xr4 = require("@react-three/xr");
|
|
223
229
|
var import_qrcode = require("qrcode.react");
|
|
224
|
-
var
|
|
230
|
+
var import_three21 = require("three");
|
|
225
231
|
|
|
226
232
|
// #style-inject:#style-inject
|
|
227
233
|
function styleInject(css, { insertAt } = {}) {
|
|
@@ -2063,6 +2069,26 @@ function Controls({ controlsRef, onCameraChange }) {
|
|
|
2063
2069
|
// src/components/Lights.tsx
|
|
2064
2070
|
var import_react9 = require("react");
|
|
2065
2071
|
var import_fiber5 = require("@react-three/fiber");
|
|
2072
|
+
var import_three10 = require("three");
|
|
2073
|
+
|
|
2074
|
+
// src/utils/resolveBindingKey.ts
|
|
2075
|
+
function resolveBindingKey(objectBindings, objectId) {
|
|
2076
|
+
if (objectBindings[objectId]) {
|
|
2077
|
+
return objectId;
|
|
2078
|
+
}
|
|
2079
|
+
const idMatch = Object.entries(objectBindings).find(
|
|
2080
|
+
([, binding]) => binding.id === objectId
|
|
2081
|
+
)?.[0];
|
|
2082
|
+
if (idMatch) {
|
|
2083
|
+
return idMatch;
|
|
2084
|
+
}
|
|
2085
|
+
const modelObjectMatch = Object.entries(objectBindings).find(
|
|
2086
|
+
([, binding]) => binding.modelObjectId === objectId
|
|
2087
|
+
)?.[0];
|
|
2088
|
+
return modelObjectMatch ?? null;
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
// src/components/Lights.tsx
|
|
2066
2092
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
2067
2093
|
function rotatePositionY(pos, deg) {
|
|
2068
2094
|
if (!deg) return pos;
|
|
@@ -2142,21 +2168,42 @@ function LightInstance({
|
|
|
2142
2168
|
shadows,
|
|
2143
2169
|
shadowsEnabled,
|
|
2144
2170
|
environmentRotation,
|
|
2145
|
-
modelBounds
|
|
2171
|
+
modelBounds,
|
|
2172
|
+
objectBindings,
|
|
2173
|
+
nodeRefs
|
|
2146
2174
|
}) {
|
|
2147
2175
|
const dirRef = (0, import_react9.useRef)(null);
|
|
2148
2176
|
const pointRef = (0, import_react9.useRef)(null);
|
|
2149
2177
|
const spotRef = (0, import_react9.useRef)(null);
|
|
2150
2178
|
const camera = (0, import_fiber5.useThree)((s) => s.camera);
|
|
2179
|
+
const rootScene = (0, import_fiber5.useThree)((s) => s.scene);
|
|
2180
|
+
const attachedPosition = (0, import_react9.useMemo)(() => new import_three10.Vector3(), []);
|
|
2151
2181
|
const position = (0, import_react9.useMemo)(
|
|
2152
|
-
() => light.attachedToCamera ? light.position : rotatePositionY(light.position, environmentRotation),
|
|
2153
|
-
[
|
|
2182
|
+
() => light.attachedToCamera || light.attachment ? light.position : rotatePositionY(light.position, environmentRotation),
|
|
2183
|
+
[
|
|
2184
|
+
light.position,
|
|
2185
|
+
light.attachedToCamera,
|
|
2186
|
+
light.attachment,
|
|
2187
|
+
environmentRotation
|
|
2188
|
+
]
|
|
2154
2189
|
);
|
|
2155
2190
|
(0, import_fiber5.useFrame)(() => {
|
|
2156
|
-
|
|
2157
|
-
if (
|
|
2158
|
-
if (
|
|
2159
|
-
|
|
2191
|
+
const instance = dirRef.current ?? pointRef.current ?? spotRef.current;
|
|
2192
|
+
if (!instance) return;
|
|
2193
|
+
if (light.attachedToCamera) {
|
|
2194
|
+
instance.position.copy(camera.position);
|
|
2195
|
+
return;
|
|
2196
|
+
}
|
|
2197
|
+
const attachment = light.attachment;
|
|
2198
|
+
if (!attachment) return;
|
|
2199
|
+
const bindingKey = objectBindings ? resolveBindingKey(objectBindings, attachment.objectId) : null;
|
|
2200
|
+
const binding = bindingKey ? objectBindings?.[bindingKey] : void 0;
|
|
2201
|
+
const modelObjectId = binding?.modelObjectId ?? attachment.objectId;
|
|
2202
|
+
const target = (bindingKey ? nodeRefs?.current[bindingKey] : void 0) ?? rootScene.getObjectByName(modelObjectId);
|
|
2203
|
+
if (!target) return;
|
|
2204
|
+
attachedPosition.fromArray(attachment.offset);
|
|
2205
|
+
target.localToWorld(attachedPosition);
|
|
2206
|
+
instance.position.copy(attachedPosition);
|
|
2160
2207
|
});
|
|
2161
2208
|
const castShadow = shadowsEnabled && shadows.enabled && light.castShadow;
|
|
2162
2209
|
const fit = (0, import_react9.useMemo)(
|
|
@@ -2202,6 +2249,8 @@ function LightInstance({
|
|
|
2202
2249
|
color: light.color,
|
|
2203
2250
|
intensity: light.intensity,
|
|
2204
2251
|
position,
|
|
2252
|
+
distance: light.distance,
|
|
2253
|
+
decay: light.decay,
|
|
2205
2254
|
castShadow,
|
|
2206
2255
|
"shadow-mapSize-width": shadows.mapSize,
|
|
2207
2256
|
"shadow-mapSize-height": shadows.mapSize,
|
|
@@ -2227,7 +2276,9 @@ function Lights({
|
|
|
2227
2276
|
shadowsEnabled = true,
|
|
2228
2277
|
environmentRotation = 0,
|
|
2229
2278
|
environmentConfig,
|
|
2230
|
-
modelBounds
|
|
2279
|
+
modelBounds,
|
|
2280
|
+
objectBindings,
|
|
2281
|
+
nodeRefs
|
|
2231
2282
|
}) {
|
|
2232
2283
|
const lighting = lightingConfig ?? DEFAULT_LIGHTING;
|
|
2233
2284
|
const shadows = shadowConfig ?? DEFAULT_SHADOWS;
|
|
@@ -2248,7 +2299,9 @@ function Lights({
|
|
|
2248
2299
|
shadows,
|
|
2249
2300
|
shadowsEnabled,
|
|
2250
2301
|
environmentRotation,
|
|
2251
|
-
modelBounds
|
|
2302
|
+
modelBounds,
|
|
2303
|
+
objectBindings,
|
|
2304
|
+
nodeRefs
|
|
2252
2305
|
},
|
|
2253
2306
|
light.id
|
|
2254
2307
|
)),
|
|
@@ -2269,7 +2322,9 @@ function Lights({
|
|
|
2269
2322
|
shadows,
|
|
2270
2323
|
shadowsEnabled,
|
|
2271
2324
|
environmentRotation: 0,
|
|
2272
|
-
modelBounds
|
|
2325
|
+
modelBounds,
|
|
2326
|
+
objectBindings,
|
|
2327
|
+
nodeRefs
|
|
2273
2328
|
}
|
|
2274
2329
|
)
|
|
2275
2330
|
] });
|
|
@@ -2283,7 +2338,7 @@ var import_fiber7 = require("@react-three/fiber");
|
|
|
2283
2338
|
var import_drei3 = require("@react-three/drei");
|
|
2284
2339
|
var import_fiber6 = require("@react-three/fiber");
|
|
2285
2340
|
var import_react10 = require("react");
|
|
2286
|
-
var
|
|
2341
|
+
var import_three11 = require("three");
|
|
2287
2342
|
var import_three_stdlib2 = require("three-stdlib");
|
|
2288
2343
|
function createUnlitEmissiveTexturePlugin(parser) {
|
|
2289
2344
|
return {
|
|
@@ -2309,7 +2364,7 @@ function createUnlitEmissiveTexturePlugin(parser) {
|
|
|
2309
2364
|
[...wanted].map(async ([name, index]) => {
|
|
2310
2365
|
const texture = await parser.getDependency("texture", index);
|
|
2311
2366
|
if (texture) {
|
|
2312
|
-
texture.colorSpace =
|
|
2367
|
+
texture.colorSpace = import_three11.SRGBColorSpace;
|
|
2313
2368
|
textureByName.set(name, texture);
|
|
2314
2369
|
}
|
|
2315
2370
|
})
|
|
@@ -2367,7 +2422,7 @@ function useConfiguredGLTF(url, config = {}) {
|
|
|
2367
2422
|
}
|
|
2368
2423
|
|
|
2369
2424
|
// src/components/Model.tsx
|
|
2370
|
-
var
|
|
2425
|
+
var import_three15 = require("three");
|
|
2371
2426
|
var import_three_stdlib3 = require("three-stdlib");
|
|
2372
2427
|
|
|
2373
2428
|
// src/utils/animationClips.ts
|
|
@@ -2387,7 +2442,7 @@ function resolveAnimationClips(clips) {
|
|
|
2387
2442
|
}
|
|
2388
2443
|
|
|
2389
2444
|
// src/components/model/materialUtils.ts
|
|
2390
|
-
var
|
|
2445
|
+
var import_three12 = require("three");
|
|
2391
2446
|
function isMeshStandardMaterial(material2) {
|
|
2392
2447
|
return Boolean(
|
|
2393
2448
|
material2 && typeof material2 === "object" && "clone" in material2 && "color" in material2 && "metalness" in material2 && "roughness" in material2
|
|
@@ -2429,28 +2484,28 @@ var SRGB_TEXTURE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2429
2484
|
"specularColorMap"
|
|
2430
2485
|
]);
|
|
2431
2486
|
function colorSpaceForTextureKey(key) {
|
|
2432
|
-
return SRGB_TEXTURE_KEYS.has(key) ?
|
|
2487
|
+
return SRGB_TEXTURE_KEYS.has(key) ? import_three12.SRGBColorSpace : import_three12.NoColorSpace;
|
|
2433
2488
|
}
|
|
2434
2489
|
function getMaterialTexturePath(material2, key) {
|
|
2435
2490
|
return material2?.[key]?.path;
|
|
2436
2491
|
}
|
|
2437
2492
|
function sideFromConfig(side) {
|
|
2438
|
-
if (side === "back") return
|
|
2439
|
-
if (side === "double") return
|
|
2440
|
-
return
|
|
2493
|
+
if (side === "back") return import_three12.BackSide;
|
|
2494
|
+
if (side === "double") return import_three12.DoubleSide;
|
|
2495
|
+
return import_three12.FrontSide;
|
|
2441
2496
|
}
|
|
2442
2497
|
function blendingFromConfig(blendingMode) {
|
|
2443
|
-
if (blendingMode === "additive") return
|
|
2444
|
-
return
|
|
2498
|
+
if (blendingMode === "additive") return import_three12.AdditiveBlending;
|
|
2499
|
+
return import_three12.NormalBlending;
|
|
2445
2500
|
}
|
|
2446
2501
|
function toSafeVector2(value, fallbackX = 1, fallbackY = fallbackX) {
|
|
2447
2502
|
if (value && typeof value === "object") {
|
|
2448
2503
|
const vectorLike = value;
|
|
2449
2504
|
if (typeof vectorLike.x === "number" && Number.isFinite(vectorLike.x) && typeof vectorLike.y === "number" && Number.isFinite(vectorLike.y)) {
|
|
2450
|
-
return new
|
|
2505
|
+
return new import_three12.Vector2(vectorLike.x, vectorLike.y);
|
|
2451
2506
|
}
|
|
2452
2507
|
}
|
|
2453
|
-
return new
|
|
2508
|
+
return new import_three12.Vector2(fallbackX, fallbackY);
|
|
2454
2509
|
}
|
|
2455
2510
|
function clampAnimationSpeed(speed) {
|
|
2456
2511
|
return Number.isFinite(speed) && speed > 0 ? speed : 1;
|
|
@@ -2465,12 +2520,12 @@ function getClipLoopMode(clipConfig) {
|
|
|
2465
2520
|
}
|
|
2466
2521
|
|
|
2467
2522
|
// src/components/model/materialFactory.ts
|
|
2468
|
-
var
|
|
2523
|
+
var import_three13 = require("three");
|
|
2469
2524
|
function toStandardMaterial(material2) {
|
|
2470
2525
|
if (isMeshStandardMaterial(material2)) return material2;
|
|
2471
2526
|
if (!material2 || typeof material2 !== "object") return null;
|
|
2472
2527
|
const src = material2;
|
|
2473
|
-
const std = new
|
|
2528
|
+
const std = new import_three13.MeshStandardMaterial();
|
|
2474
2529
|
if (typeof src.name === "string") std.name = src.name;
|
|
2475
2530
|
std.metalness = 0;
|
|
2476
2531
|
std.roughness = 1;
|
|
@@ -2478,17 +2533,17 @@ function toStandardMaterial(material2) {
|
|
|
2478
2533
|
if (isUnlit) {
|
|
2479
2534
|
std.color.set(0);
|
|
2480
2535
|
std.emissiveIntensity = 1;
|
|
2481
|
-
if (src.map instanceof
|
|
2536
|
+
if (src.map instanceof import_three13.Texture) {
|
|
2482
2537
|
std.emissiveMap = src.map;
|
|
2483
2538
|
std.emissive.set(16777215);
|
|
2484
|
-
} else if (src.color instanceof
|
|
2539
|
+
} else if (src.color instanceof import_three13.Color) {
|
|
2485
2540
|
std.emissive.copy(src.color);
|
|
2486
2541
|
}
|
|
2487
2542
|
} else {
|
|
2488
|
-
if (src.color instanceof
|
|
2489
|
-
if (src.map instanceof
|
|
2490
|
-
if (src.emissive instanceof
|
|
2491
|
-
if (src.emissiveMap instanceof
|
|
2543
|
+
if (src.color instanceof import_three13.Color) std.color.copy(src.color);
|
|
2544
|
+
if (src.map instanceof import_three13.Texture) std.map = src.map;
|
|
2545
|
+
if (src.emissive instanceof import_three13.Color) std.emissive.copy(src.emissive);
|
|
2546
|
+
if (src.emissiveMap instanceof import_three13.Texture) std.emissiveMap = src.emissiveMap;
|
|
2492
2547
|
}
|
|
2493
2548
|
const shared = [
|
|
2494
2549
|
"aoMap",
|
|
@@ -2520,7 +2575,7 @@ function createPhysicalMaterialFromStandard(original) {
|
|
|
2520
2575
|
if (isMeshPhysicalMaterial(original)) {
|
|
2521
2576
|
return original.clone();
|
|
2522
2577
|
}
|
|
2523
|
-
const cloned = new
|
|
2578
|
+
const cloned = new import_three13.MeshPhysicalMaterial();
|
|
2524
2579
|
cloned.name = original.name;
|
|
2525
2580
|
cloned.color.copy(original.color);
|
|
2526
2581
|
cloned.map = original.map;
|
|
@@ -2649,7 +2704,7 @@ function snapshotBaseMaterial(cloned) {
|
|
|
2649
2704
|
}
|
|
2650
2705
|
|
|
2651
2706
|
// src/components/model/applyMaterialConfig.ts
|
|
2652
|
-
var
|
|
2707
|
+
var import_three14 = require("three");
|
|
2653
2708
|
function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
|
|
2654
2709
|
material2.map = material2.userData.baseMap ?? null;
|
|
2655
2710
|
material2.color.copy(material2.userData.baseColor);
|
|
@@ -2657,7 +2712,7 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
|
|
|
2657
2712
|
material2.metalnessMap = material2.userData.baseMetalnessMap ?? null;
|
|
2658
2713
|
material2.roughness = material2.userData.baseRoughness ?? material2.roughness;
|
|
2659
2714
|
material2.roughnessMap = material2.userData.baseRoughnessMap ?? null;
|
|
2660
|
-
material2.emissive.copy(material2.userData.baseEmissive ?? new
|
|
2715
|
+
material2.emissive.copy(material2.userData.baseEmissive ?? new import_three14.Color(0, 0, 0));
|
|
2661
2716
|
material2.emissiveIntensity = material2.userData.baseEmissiveIntensity ?? material2.emissiveIntensity;
|
|
2662
2717
|
material2.emissiveMap = material2.userData.baseEmissiveMap ?? null;
|
|
2663
2718
|
material2.opacity = material2.userData.baseOpacity ?? 1;
|
|
@@ -2666,9 +2721,9 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
|
|
|
2666
2721
|
material2.alphaTest = material2.userData.baseAlphaTest ?? 0;
|
|
2667
2722
|
material2.alphaHash = material2.userData.baseAlphaHash ?? false;
|
|
2668
2723
|
material2.dithering = material2.userData.baseDithering ?? false;
|
|
2669
|
-
material2.blending = material2.userData.baseBlending ??
|
|
2724
|
+
material2.blending = material2.userData.baseBlending ?? import_three14.NormalBlending;
|
|
2670
2725
|
material2.depthWrite = material2.userData.baseDepthWrite ?? true;
|
|
2671
|
-
material2.side = material2.userData.baseSide ??
|
|
2726
|
+
material2.side = material2.userData.baseSide ?? import_three14.FrontSide;
|
|
2672
2727
|
material2.normalMap = material2.userData.baseNormalMap ?? null;
|
|
2673
2728
|
material2.normalScale.copy(toSafeVector2(material2.userData.baseNormalScale));
|
|
2674
2729
|
material2.bumpMap = material2.userData.baseBumpMap ?? null;
|
|
@@ -2688,7 +2743,7 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
|
|
|
2688
2743
|
);
|
|
2689
2744
|
material2.sheen = material2.userData.baseSheen ?? material2.sheen;
|
|
2690
2745
|
material2.sheenColor.copy(
|
|
2691
|
-
material2.userData.baseSheenColor ?? new
|
|
2746
|
+
material2.userData.baseSheenColor ?? new import_three14.Color(0, 0, 0)
|
|
2692
2747
|
);
|
|
2693
2748
|
material2.sheenColorMap = material2.userData.baseSheenColorMap ?? null;
|
|
2694
2749
|
material2.sheenRoughness = material2.userData.baseSheenRoughness ?? material2.sheenRoughness;
|
|
@@ -2701,14 +2756,14 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
|
|
|
2701
2756
|
material2.thickness = material2.userData.baseThickness ?? material2.thickness;
|
|
2702
2757
|
material2.thicknessMap = material2.userData.baseThicknessMap ?? null;
|
|
2703
2758
|
material2.attenuationColor.copy(
|
|
2704
|
-
material2.userData.baseAttenuationColor ?? new
|
|
2759
|
+
material2.userData.baseAttenuationColor ?? new import_three14.Color("#ffffff")
|
|
2705
2760
|
);
|
|
2706
2761
|
material2.attenuationDistance = material2.userData.baseAttenuationDistance ?? material2.attenuationDistance;
|
|
2707
2762
|
material2.reflectivity = material2.userData.baseReflectivity ?? material2.reflectivity;
|
|
2708
2763
|
material2.specularIntensity = material2.userData.baseSpecularIntensity ?? material2.specularIntensity;
|
|
2709
2764
|
material2.specularIntensityMap = material2.userData.baseSpecularIntensityMap ?? null;
|
|
2710
2765
|
material2.specularColor.copy(
|
|
2711
|
-
material2.userData.baseSpecularColor ?? new
|
|
2766
|
+
material2.userData.baseSpecularColor ?? new import_three14.Color("#ffffff")
|
|
2712
2767
|
);
|
|
2713
2768
|
material2.specularColorMap = material2.userData.baseSpecularColorMap ?? null;
|
|
2714
2769
|
material2.flatShading = material2.userData.baseFlatShading ?? material2.flatShading;
|
|
@@ -2775,7 +2830,7 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
|
|
|
2775
2830
|
"specularColorMap"
|
|
2776
2831
|
);
|
|
2777
2832
|
if (texturePath) {
|
|
2778
|
-
material2.map = getOrCreateTexture(texturePath,
|
|
2833
|
+
material2.map = getOrCreateTexture(texturePath, import_three14.SRGBColorSpace);
|
|
2779
2834
|
material2.color.set(resolvedBaseColor ?? "#ffffff");
|
|
2780
2835
|
} else if (resolvedBaseColor) {
|
|
2781
2836
|
material2.map = null;
|
|
@@ -2802,7 +2857,7 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
|
|
|
2802
2857
|
material2.emissiveIntensity = configuredMaterial.emissiveIntensity;
|
|
2803
2858
|
}
|
|
2804
2859
|
if (emissiveMapPath) {
|
|
2805
|
-
material2.emissiveMap = getOrCreateTexture(emissiveMapPath,
|
|
2860
|
+
material2.emissiveMap = getOrCreateTexture(emissiveMapPath, import_three14.SRGBColorSpace);
|
|
2806
2861
|
}
|
|
2807
2862
|
if (configuredMaterial?.opacity !== void 0) {
|
|
2808
2863
|
material2.opacity = configuredMaterial.opacity;
|
|
@@ -2890,7 +2945,7 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
|
|
|
2890
2945
|
if (sheenColorMapPath) {
|
|
2891
2946
|
material2.sheenColorMap = getOrCreateTexture(
|
|
2892
2947
|
sheenColorMapPath,
|
|
2893
|
-
|
|
2948
|
+
import_three14.SRGBColorSpace
|
|
2894
2949
|
);
|
|
2895
2950
|
}
|
|
2896
2951
|
if (configuredMaterial?.sheenRoughness !== void 0) {
|
|
@@ -2943,10 +2998,10 @@ function applyMaterialConfig(material2, configuredMaterial, resolvedBaseColor) {
|
|
|
2943
2998
|
if (specularColorMapPath) {
|
|
2944
2999
|
material2.specularColorMap = getOrCreateTexture(
|
|
2945
3000
|
specularColorMapPath,
|
|
2946
|
-
|
|
3001
|
+
import_three14.SRGBColorSpace
|
|
2947
3002
|
);
|
|
2948
3003
|
}
|
|
2949
|
-
material2.transparent = material2.opacity < 1 || material2.transmission > 0 || material2.blending !==
|
|
3004
|
+
material2.transparent = material2.opacity < 1 || material2.transmission > 0 || material2.blending !== import_three14.NormalBlending || Boolean((alphaMapPath || material2.alphaMap) && material2.alphaTest <= 0);
|
|
2950
3005
|
if (!material2.transparent) {
|
|
2951
3006
|
material2.depthWrite = true;
|
|
2952
3007
|
}
|
|
@@ -2966,6 +3021,24 @@ function refreshSkinnedBounds(root) {
|
|
|
2966
3021
|
|
|
2967
3022
|
// src/components/Model.tsx
|
|
2968
3023
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
3024
|
+
function applyBindingTransform(node, transform, original) {
|
|
3025
|
+
if (!original) return;
|
|
3026
|
+
if (transform?.position) node.position.fromArray(transform.position);
|
|
3027
|
+
else node.position.copy(original.position);
|
|
3028
|
+
if (transform?.rotation) {
|
|
3029
|
+
node.rotation.set(
|
|
3030
|
+
transform.rotation[0],
|
|
3031
|
+
transform.rotation[1],
|
|
3032
|
+
transform.rotation[2],
|
|
3033
|
+
original.rotation.order
|
|
3034
|
+
);
|
|
3035
|
+
} else {
|
|
3036
|
+
node.rotation.copy(original.rotation);
|
|
3037
|
+
}
|
|
3038
|
+
if (transform?.scale) node.scale.fromArray(transform.scale);
|
|
3039
|
+
else node.scale.copy(original.scale);
|
|
3040
|
+
node.updateMatrix();
|
|
3041
|
+
}
|
|
2969
3042
|
function firstMaterial(node) {
|
|
2970
3043
|
const mat = node.material;
|
|
2971
3044
|
if (!mat) return void 0;
|
|
@@ -3019,19 +3092,30 @@ function Model({
|
|
|
3019
3092
|
});
|
|
3020
3093
|
return map;
|
|
3021
3094
|
}, [scene]);
|
|
3095
|
+
const originalNodeTransforms = (0, import_react11.useMemo)(() => {
|
|
3096
|
+
const transforms = /* @__PURE__ */ new WeakMap();
|
|
3097
|
+
scene.traverse((node) => {
|
|
3098
|
+
transforms.set(node, {
|
|
3099
|
+
position: node.position.clone(),
|
|
3100
|
+
rotation: node.rotation.clone(),
|
|
3101
|
+
scale: node.scale.clone()
|
|
3102
|
+
});
|
|
3103
|
+
});
|
|
3104
|
+
return transforms;
|
|
3105
|
+
}, [scene]);
|
|
3022
3106
|
const shellNodeNames = (0, import_react11.useMemo)(() => {
|
|
3023
3107
|
const shell = /* @__PURE__ */ new Set();
|
|
3024
|
-
const sceneBox = new
|
|
3108
|
+
const sceneBox = new import_three15.Box3();
|
|
3025
3109
|
const meshNodes = [];
|
|
3026
3110
|
for (const [name, node] of Object.entries(nodeMap)) {
|
|
3027
3111
|
if (!isMeshNode(node)) continue;
|
|
3028
|
-
const box = new
|
|
3112
|
+
const box = new import_three15.Box3().setFromObject(node);
|
|
3029
3113
|
if (box.isEmpty()) continue;
|
|
3030
3114
|
meshNodes.push({ name, box });
|
|
3031
3115
|
sceneBox.union(box);
|
|
3032
3116
|
}
|
|
3033
|
-
const sceneSize = sceneBox.getSize(new
|
|
3034
|
-
const size = new
|
|
3117
|
+
const sceneSize = sceneBox.getSize(new import_three15.Vector3());
|
|
3118
|
+
const size = new import_three15.Vector3();
|
|
3035
3119
|
for (const { name, box } of meshNodes) {
|
|
3036
3120
|
box.getSize(size);
|
|
3037
3121
|
const isShell = size.x > sceneSize.x * 0.7 && size.z > sceneSize.z * 0.7 && size.y > sceneSize.y * 0.4;
|
|
@@ -3071,6 +3155,43 @@ function Model({
|
|
|
3071
3155
|
).sort().join(""),
|
|
3072
3156
|
[objectBindings]
|
|
3073
3157
|
);
|
|
3158
|
+
const bindingTransformSignature = (0, import_react11.useMemo)(
|
|
3159
|
+
() => Object.entries(objectBindings).map(
|
|
3160
|
+
([nodeName, binding]) => JSON.stringify([
|
|
3161
|
+
nodeName,
|
|
3162
|
+
binding.modelObjectId ?? nodeName,
|
|
3163
|
+
binding.transform ?? null
|
|
3164
|
+
])
|
|
3165
|
+
).sort().join(""),
|
|
3166
|
+
[objectBindings]
|
|
3167
|
+
);
|
|
3168
|
+
const transformedNodesRef = (0, import_react11.useRef)(/* @__PURE__ */ new Set());
|
|
3169
|
+
(0, import_react11.useEffect)(() => {
|
|
3170
|
+
for (const node of transformedNodesRef.current) {
|
|
3171
|
+
applyBindingTransform(node, void 0, originalNodeTransforms.get(node));
|
|
3172
|
+
}
|
|
3173
|
+
const nextTransformedNodes = /* @__PURE__ */ new Set();
|
|
3174
|
+
for (const [nodeName, binding] of Object.entries(objectBindings)) {
|
|
3175
|
+
if (!binding.transform) continue;
|
|
3176
|
+
const node = nodeMap[binding.modelObjectId ?? nodeName];
|
|
3177
|
+
if (!node) continue;
|
|
3178
|
+
applyBindingTransform(
|
|
3179
|
+
node,
|
|
3180
|
+
binding.transform,
|
|
3181
|
+
originalNodeTransforms.get(node)
|
|
3182
|
+
);
|
|
3183
|
+
nextTransformedNodes.add(node);
|
|
3184
|
+
}
|
|
3185
|
+
transformedNodesRef.current = nextTransformedNodes;
|
|
3186
|
+
scene.updateMatrixWorld(true);
|
|
3187
|
+
invalidate();
|
|
3188
|
+
}, [
|
|
3189
|
+
scene,
|
|
3190
|
+
nodeMap,
|
|
3191
|
+
originalNodeTransforms,
|
|
3192
|
+
bindingTransformSignature,
|
|
3193
|
+
invalidate
|
|
3194
|
+
]);
|
|
3074
3195
|
const perNodeMaterials = (0, import_react11.useMemo)(() => {
|
|
3075
3196
|
const map = {};
|
|
3076
3197
|
for (const [nodeName, binding] of Object.entries(objectBindings)) {
|
|
@@ -3105,7 +3226,7 @@ function Model({
|
|
|
3105
3226
|
const map = {};
|
|
3106
3227
|
if (shadingMode !== "unlit") return map;
|
|
3107
3228
|
for (const [nodeName, material2] of Object.entries(perNodeMaterials)) {
|
|
3108
|
-
const basic = new
|
|
3229
|
+
const basic = new import_three15.MeshBasicMaterial();
|
|
3109
3230
|
basic.color.copy(material2.color);
|
|
3110
3231
|
basic.map = material2.map;
|
|
3111
3232
|
basic.alphaMap = material2.alphaMap;
|
|
@@ -3121,7 +3242,7 @@ function Model({
|
|
|
3121
3242
|
const map = {};
|
|
3122
3243
|
if (shadingMode !== "normals") return map;
|
|
3123
3244
|
for (const nodeName of Object.keys(perNodeMaterials)) {
|
|
3124
|
-
map[nodeName] = new
|
|
3245
|
+
map[nodeName] = new import_three15.MeshNormalMaterial();
|
|
3125
3246
|
}
|
|
3126
3247
|
return map;
|
|
3127
3248
|
}, [perNodeMaterials, shadingMode]);
|
|
@@ -3130,7 +3251,7 @@ function Model({
|
|
|
3130
3251
|
if (shadingMode !== "lit" || rendererMode !== "matcap") return map;
|
|
3131
3252
|
const matcapTexture = getMatcapTexture();
|
|
3132
3253
|
for (const [nodeName, material2] of Object.entries(perNodeMaterials)) {
|
|
3133
|
-
const matcap = new
|
|
3254
|
+
const matcap = new import_three15.MeshMatcapMaterial();
|
|
3134
3255
|
matcap.matcap = matcapTexture;
|
|
3135
3256
|
matcap.color.copy(material2.color);
|
|
3136
3257
|
matcap.alphaMap = material2.alphaMap;
|
|
@@ -3304,7 +3425,7 @@ function Model({
|
|
|
3304
3425
|
const sourceMaterials = Array.isArray(original) ? original : [original];
|
|
3305
3426
|
const hasUv0 = Boolean(object.geometry.getAttribute("uv"));
|
|
3306
3427
|
const diagnosticMaterials = sourceMaterials.map((source, index) => {
|
|
3307
|
-
const material2 = new
|
|
3428
|
+
const material2 = new import_three15.MeshBasicMaterial({
|
|
3308
3429
|
color: hasUv0 ? "#ffffff" : "#ff00ff",
|
|
3309
3430
|
map: hasUv0 ? checkerTexture : null,
|
|
3310
3431
|
side: source.side
|
|
@@ -3334,16 +3455,16 @@ function Model({
|
|
|
3334
3455
|
const glbNodeName = binding.modelObjectId ?? nodeName;
|
|
3335
3456
|
const node = nodeMap[glbNodeName];
|
|
3336
3457
|
if (!isMeshNode(node)) continue;
|
|
3337
|
-
const geometry = new
|
|
3458
|
+
const geometry = new import_three15.WireframeGeometry(
|
|
3338
3459
|
node.geometry
|
|
3339
3460
|
);
|
|
3340
|
-
const lineMaterial = new
|
|
3461
|
+
const lineMaterial = new import_three15.LineBasicMaterial({
|
|
3341
3462
|
color: wireframeColor,
|
|
3342
3463
|
transparent: true,
|
|
3343
3464
|
opacity: wireframeOpacity,
|
|
3344
3465
|
depthTest: false
|
|
3345
3466
|
});
|
|
3346
|
-
const line = new
|
|
3467
|
+
const line = new import_three15.LineSegments(geometry, lineMaterial);
|
|
3347
3468
|
line.renderOrder = 1;
|
|
3348
3469
|
node.add(line);
|
|
3349
3470
|
added.push({ node, line });
|
|
@@ -3395,7 +3516,7 @@ function Model({
|
|
|
3395
3516
|
const applyClipConfigToAction = (action, clipConfig) => {
|
|
3396
3517
|
const loopMode = getClipLoopMode(clipConfig);
|
|
3397
3518
|
action.setLoop(
|
|
3398
|
-
loopMode === "once" ?
|
|
3519
|
+
loopMode === "once" ? import_three15.LoopOnce : import_three15.LoopRepeat,
|
|
3399
3520
|
loopMode === "once" ? 1 : Infinity
|
|
3400
3521
|
);
|
|
3401
3522
|
action.clampWhenFinished = loopMode === "once";
|
|
@@ -3462,7 +3583,7 @@ function Model({
|
|
|
3462
3583
|
invalidate();
|
|
3463
3584
|
};
|
|
3464
3585
|
(0, import_react11.useEffect)(() => {
|
|
3465
|
-
const mixer = new
|
|
3586
|
+
const mixer = new import_three15.AnimationMixer(scene);
|
|
3466
3587
|
mixerRef.current = mixer;
|
|
3467
3588
|
const actions = {};
|
|
3468
3589
|
for (const { clip, sourceName } of resolvedAnimationClips) {
|
|
@@ -3553,17 +3674,17 @@ function Model({
|
|
|
3553
3674
|
(0, import_fiber7.useFrame)((_, delta) => {
|
|
3554
3675
|
if (!groupRef.current) return;
|
|
3555
3676
|
const rotation = groupRef.current.rotation;
|
|
3556
|
-
rotation.x =
|
|
3677
|
+
rotation.x = import_three15.MathUtils.lerp(
|
|
3557
3678
|
rotation.x,
|
|
3558
3679
|
targetRotation[0],
|
|
3559
3680
|
1 - Math.exp(-5 * delta)
|
|
3560
3681
|
);
|
|
3561
|
-
rotation.y =
|
|
3682
|
+
rotation.y = import_three15.MathUtils.lerp(
|
|
3562
3683
|
rotation.y,
|
|
3563
3684
|
targetRotation[1],
|
|
3564
3685
|
1 - Math.exp(-5 * delta)
|
|
3565
3686
|
);
|
|
3566
|
-
rotation.z =
|
|
3687
|
+
rotation.z = import_three15.MathUtils.lerp(
|
|
3567
3688
|
rotation.z,
|
|
3568
3689
|
targetRotation[2],
|
|
3569
3690
|
1 - Math.exp(-5 * delta)
|
|
@@ -3574,27 +3695,30 @@ function Model({
|
|
|
3574
3695
|
const explodePartsRef = (0, import_react11.useRef)([]);
|
|
3575
3696
|
const explodeStrengthRef = (0, import_react11.useRef)(0);
|
|
3576
3697
|
const currentExplodeRef = (0, import_react11.useRef)(0);
|
|
3577
|
-
const explodeTmp = (0, import_react11.useMemo)(() => new
|
|
3698
|
+
const explodeTmp = (0, import_react11.useMemo)(() => new import_three15.Vector3(), []);
|
|
3578
3699
|
(0, import_react11.useEffect)(() => {
|
|
3700
|
+
const bindingByNode = /* @__PURE__ */ new Map();
|
|
3701
|
+
for (const [nodeName, binding] of Object.entries(objectBindings)) {
|
|
3702
|
+
const node = nodeMap[binding.modelObjectId ?? nodeName];
|
|
3703
|
+
if (node) bindingByNode.set(node, binding);
|
|
3704
|
+
}
|
|
3579
3705
|
for (const part of explodePartsRef.current) {
|
|
3580
|
-
|
|
3706
|
+
applyBindingTransform(
|
|
3707
|
+
part.node,
|
|
3708
|
+
bindingByNode.get(part.node)?.transform,
|
|
3709
|
+
originalNodeTransforms.get(part.node)
|
|
3710
|
+
);
|
|
3581
3711
|
}
|
|
3582
3712
|
currentExplodeRef.current = 0;
|
|
3583
3713
|
scene.updateWorldMatrix(true, true);
|
|
3584
|
-
const sceneBox = new
|
|
3585
|
-
const modelCenter = sceneBox.getCenter(new
|
|
3586
|
-
const radius = sceneBox.getSize(new
|
|
3714
|
+
const sceneBox = new import_three15.Box3().setFromObject(scene);
|
|
3715
|
+
const modelCenter = sceneBox.getCenter(new import_three15.Vector3());
|
|
3716
|
+
const radius = sceneBox.getSize(new import_three15.Vector3()).length() * 0.5;
|
|
3587
3717
|
explodeStrengthRef.current = Number.isFinite(radius) ? radius : 0;
|
|
3588
|
-
const targets = [];
|
|
3589
|
-
const targetSet =
|
|
3590
|
-
for (const [nodeName, binding] of Object.entries(objectBindings)) {
|
|
3591
|
-
const node = nodeMap[binding.modelObjectId ?? nodeName];
|
|
3592
|
-
if (!node || targetSet.has(node)) continue;
|
|
3593
|
-
targetSet.add(node);
|
|
3594
|
-
targets.push(node);
|
|
3595
|
-
}
|
|
3718
|
+
const targets = [...bindingByNode.keys()];
|
|
3719
|
+
const targetSet = new Set(targets);
|
|
3596
3720
|
const parts = [];
|
|
3597
|
-
const partCenter = new
|
|
3721
|
+
const partCenter = new import_three15.Vector3();
|
|
3598
3722
|
for (const node of targets) {
|
|
3599
3723
|
if (!node.parent) continue;
|
|
3600
3724
|
let ancestor = node.parent;
|
|
@@ -3607,7 +3731,7 @@ function Model({
|
|
|
3607
3731
|
ancestor = ancestor.parent;
|
|
3608
3732
|
}
|
|
3609
3733
|
if (nested) continue;
|
|
3610
|
-
const box = new
|
|
3734
|
+
const box = new import_three15.Box3().setFromObject(node);
|
|
3611
3735
|
if (box.isEmpty()) continue;
|
|
3612
3736
|
box.getCenter(partCenter);
|
|
3613
3737
|
const worldDir = partCenter.clone().sub(modelCenter);
|
|
@@ -3616,14 +3740,21 @@ function Model({
|
|
|
3616
3740
|
parts.push({
|
|
3617
3741
|
node,
|
|
3618
3742
|
restLocalPos: node.position.clone(),
|
|
3619
|
-
restWorldPos: node.getWorldPosition(new
|
|
3743
|
+
restWorldPos: node.getWorldPosition(new import_three15.Vector3()),
|
|
3620
3744
|
worldDir,
|
|
3621
|
-
parentInverse: new
|
|
3745
|
+
parentInverse: new import_three15.Matrix4().copy(node.parent.matrixWorld).invert()
|
|
3622
3746
|
});
|
|
3623
3747
|
}
|
|
3624
3748
|
explodePartsRef.current = parts;
|
|
3625
3749
|
invalidate();
|
|
3626
|
-
}, [
|
|
3750
|
+
}, [
|
|
3751
|
+
scene,
|
|
3752
|
+
nodeMap,
|
|
3753
|
+
nodeBindingSignature,
|
|
3754
|
+
bindingTransformSignature,
|
|
3755
|
+
originalNodeTransforms,
|
|
3756
|
+
invalidate
|
|
3757
|
+
]);
|
|
3627
3758
|
(0, import_react11.useEffect)(() => {
|
|
3628
3759
|
invalidate();
|
|
3629
3760
|
}, [explodeFactor, invalidate]);
|
|
@@ -3636,7 +3767,7 @@ function Model({
|
|
|
3636
3767
|
if (current === target) return;
|
|
3637
3768
|
currentExplodeRef.current = target;
|
|
3638
3769
|
} else {
|
|
3639
|
-
currentExplodeRef.current =
|
|
3770
|
+
currentExplodeRef.current = import_three15.MathUtils.lerp(
|
|
3640
3771
|
current,
|
|
3641
3772
|
target,
|
|
3642
3773
|
1 - Math.exp(-6 * delta)
|
|
@@ -3709,7 +3840,7 @@ function Model({
|
|
|
3709
3840
|
var import_react12 = require("react");
|
|
3710
3841
|
var import_drei4 = require("@react-three/drei");
|
|
3711
3842
|
var import_fiber8 = require("@react-three/fiber");
|
|
3712
|
-
var
|
|
3843
|
+
var import_three16 = require("three");
|
|
3713
3844
|
|
|
3714
3845
|
// src/styles/AnnotationsLayer.css
|
|
3715
3846
|
styleInject(".ri-annotationMarker--hovered {\n transform: scale(1.08);\n box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);\n background: rgba(40, 40, 40, 0.92);\n}\n.ri-viewAnnotationHeader {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n.ri-viewAnnotationTitle {\n margin: 0;\n color: #e2e8f0;\n font-size: 14px;\n font-weight: 600;\n}\n");
|
|
@@ -3722,7 +3853,7 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3722
3853
|
const scene = (0, import_fiber8.useThree)((state) => state.scene);
|
|
3723
3854
|
const anchorNodeRef = (0, import_react12.useRef)(null);
|
|
3724
3855
|
const baseLocalPosition = (0, import_react12.useMemo)(
|
|
3725
|
-
() => new
|
|
3856
|
+
() => new import_three16.Vector3(
|
|
3726
3857
|
...annotation.anchorLocalPosition ?? annotation.localPosition ?? annotation.worldPosition
|
|
3727
3858
|
),
|
|
3728
3859
|
[
|
|
@@ -3731,9 +3862,9 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3731
3862
|
annotation.worldPosition
|
|
3732
3863
|
]
|
|
3733
3864
|
);
|
|
3734
|
-
const worldPosition = (0, import_react12.useMemo)(() => new
|
|
3735
|
-
const biasedWorldPosition = (0, import_react12.useMemo)(() => new
|
|
3736
|
-
const toCamera = (0, import_react12.useMemo)(() => new
|
|
3865
|
+
const worldPosition = (0, import_react12.useMemo)(() => new import_three16.Vector3(), []);
|
|
3866
|
+
const biasedWorldPosition = (0, import_react12.useMemo)(() => new import_three16.Vector3(), []);
|
|
3867
|
+
const toCamera = (0, import_react12.useMemo)(() => new import_three16.Vector3(), []);
|
|
3737
3868
|
(0, import_react12.useEffect)(() => {
|
|
3738
3869
|
anchorNodeRef.current = null;
|
|
3739
3870
|
}, [annotation.anchorNodeName]);
|
|
@@ -3756,7 +3887,7 @@ function useCameraBiasedAnchor(annotation, worldPositionsRef) {
|
|
|
3756
3887
|
if (worldPositionsRef) {
|
|
3757
3888
|
let stored = worldPositionsRef.current.get(annotation.id);
|
|
3758
3889
|
if (!stored) {
|
|
3759
|
-
stored = new
|
|
3890
|
+
stored = new import_three16.Vector3();
|
|
3760
3891
|
worldPositionsRef.current.set(annotation.id, stored);
|
|
3761
3892
|
}
|
|
3762
3893
|
stored.copy(worldPosition);
|
|
@@ -4105,17 +4236,17 @@ function AnnotationsLayer({
|
|
|
4105
4236
|
);
|
|
4106
4237
|
(0, import_fiber8.useFrame)((_, delta) => {
|
|
4107
4238
|
if (!groupRef.current) return;
|
|
4108
|
-
groupRef.current.rotation.x =
|
|
4239
|
+
groupRef.current.rotation.x = import_three16.MathUtils.lerp(
|
|
4109
4240
|
groupRef.current.rotation.x,
|
|
4110
4241
|
targetRotation[0],
|
|
4111
4242
|
1 - Math.exp(-5 * delta)
|
|
4112
4243
|
);
|
|
4113
|
-
groupRef.current.rotation.y =
|
|
4244
|
+
groupRef.current.rotation.y = import_three16.MathUtils.lerp(
|
|
4114
4245
|
groupRef.current.rotation.y,
|
|
4115
4246
|
targetRotation[1],
|
|
4116
4247
|
1 - Math.exp(-5 * delta)
|
|
4117
4248
|
);
|
|
4118
|
-
groupRef.current.rotation.z =
|
|
4249
|
+
groupRef.current.rotation.z = import_three16.MathUtils.lerp(
|
|
4119
4250
|
groupRef.current.rotation.z,
|
|
4120
4251
|
targetRotation[2],
|
|
4121
4252
|
1 - Math.exp(-5 * delta)
|
|
@@ -4154,7 +4285,7 @@ function AnnotationsLayer({
|
|
|
4154
4285
|
// src/components/ModelViewer/MeasurementLayer.tsx
|
|
4155
4286
|
var import_react13 = require("react");
|
|
4156
4287
|
var import_drei5 = require("@react-three/drei");
|
|
4157
|
-
var
|
|
4288
|
+
var import_three17 = require("three");
|
|
4158
4289
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
4159
4290
|
var LINE_COLOR = "#22d3ee";
|
|
4160
4291
|
var BOX_COLOR = "#facc15";
|
|
@@ -4182,8 +4313,8 @@ function DimensionsBox({ box, unit }) {
|
|
|
4182
4313
|
);
|
|
4183
4314
|
const edges = (0, import_react13.useMemo)(() => {
|
|
4184
4315
|
const [w2, h2, d2] = box.size;
|
|
4185
|
-
const geometry = new
|
|
4186
|
-
new
|
|
4316
|
+
const geometry = new import_three17.EdgesGeometry(
|
|
4317
|
+
new import_three17.BoxGeometry(w2 || 1e-4, h2 || 1e-4, d2 || 1e-4)
|
|
4187
4318
|
);
|
|
4188
4319
|
return geometry;
|
|
4189
4320
|
}, [box.size]);
|
|
@@ -4229,7 +4360,7 @@ function MeasurementLayer({
|
|
|
4229
4360
|
points.slice(1).map((point, index) => {
|
|
4230
4361
|
const a = points[index].position;
|
|
4231
4362
|
const b = point.position;
|
|
4232
|
-
const distance = new
|
|
4363
|
+
const distance = new import_three17.Vector3(...a).distanceTo(new import_three17.Vector3(...b));
|
|
4233
4364
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("group", { children: [
|
|
4234
4365
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_drei5.Line, { points: [a, b], color: LINE_COLOR, lineWidth: 2 }),
|
|
4235
4366
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MeasureLabel, { position: midpoint(a, b), children: formatMeasurement(distance, unit) })
|
|
@@ -4243,7 +4374,7 @@ function MeasurementLayer({
|
|
|
4243
4374
|
var import_react14 = require("react");
|
|
4244
4375
|
var import_xr3 = require("@react-three/xr");
|
|
4245
4376
|
var import_fiber9 = require("@react-three/fiber");
|
|
4246
|
-
var
|
|
4377
|
+
var import_three18 = require("three");
|
|
4247
4378
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
4248
4379
|
var TARGET_SIZE = 1;
|
|
4249
4380
|
var FORWARD_DISTANCE = 1;
|
|
@@ -4280,13 +4411,13 @@ var XRModelPresenter = (0, import_react14.forwardRef)(
|
|
|
4280
4411
|
() => box ? computeNormalization(box, scaleMode) : null,
|
|
4281
4412
|
[box, scaleMode]
|
|
4282
4413
|
);
|
|
4283
|
-
const placedPosition = (0, import_react14.useRef)(new
|
|
4414
|
+
const placedPosition = (0, import_react14.useRef)(new import_three18.Vector3(0, 0, -FORWARD_DISTANCE));
|
|
4284
4415
|
const userScale = (0, import_react14.useRef)(1);
|
|
4285
4416
|
const rotationY = (0, import_react14.useRef)(0);
|
|
4286
4417
|
const placementMode = (0, import_react14.useRef)(true);
|
|
4287
4418
|
const modelPlaced = (0, import_react14.useRef)(false);
|
|
4288
|
-
const hitMatrix = (0, import_react14.useMemo)(() => new
|
|
4289
|
-
const hitPosition = (0, import_react14.useRef)(new
|
|
4419
|
+
const hitMatrix = (0, import_react14.useMemo)(() => new import_three18.Matrix4(), []);
|
|
4420
|
+
const hitPosition = (0, import_react14.useRef)(new import_three18.Vector3());
|
|
4290
4421
|
const hasHit = (0, import_react14.useRef)(false);
|
|
4291
4422
|
const lastARState = (0, import_react14.useRef)(null);
|
|
4292
4423
|
const gesturing = (0, import_react14.useRef)(false);
|
|
@@ -4479,7 +4610,7 @@ var XRModelPresenter_default = XRModelPresenter;
|
|
|
4479
4610
|
// src/components/ModelViewer/CinematicCamera.tsx
|
|
4480
4611
|
var import_react15 = require("react");
|
|
4481
4612
|
var import_fiber10 = require("@react-three/fiber");
|
|
4482
|
-
var
|
|
4613
|
+
var import_three19 = require("three");
|
|
4483
4614
|
var ENTER_BLEND_SECONDS = 1.2;
|
|
4484
4615
|
function CinematicCamera({
|
|
4485
4616
|
controlsRef,
|
|
@@ -4558,14 +4689,14 @@ function CinematicCamera({
|
|
|
4558
4689
|
const curves = (0, import_react15.useMemo)(() => {
|
|
4559
4690
|
if (!hasPath) return null;
|
|
4560
4691
|
return {
|
|
4561
|
-
position: new
|
|
4562
|
-
waypoints.map((w) => new
|
|
4692
|
+
position: new import_three19.CatmullRomCurve3(
|
|
4693
|
+
waypoints.map((w) => new import_three19.Vector3(...w.position)),
|
|
4563
4694
|
loop,
|
|
4564
4695
|
"catmullrom",
|
|
4565
4696
|
0.5
|
|
4566
4697
|
),
|
|
4567
|
-
target: new
|
|
4568
|
-
waypoints.map((w) => new
|
|
4698
|
+
target: new import_three19.CatmullRomCurve3(
|
|
4699
|
+
waypoints.map((w) => new import_three19.Vector3(...w.target)),
|
|
4569
4700
|
loop,
|
|
4570
4701
|
"catmullrom",
|
|
4571
4702
|
0.5
|
|
@@ -4581,8 +4712,8 @@ function CinematicCamera({
|
|
|
4581
4712
|
const points = waypoints.map((waypoint) => ({
|
|
4582
4713
|
azimuth: waypoint.azimuthAngle,
|
|
4583
4714
|
polar: waypoint.polarAngle,
|
|
4584
|
-
distance: new
|
|
4585
|
-
new
|
|
4715
|
+
distance: new import_three19.Vector3(...waypoint.position).distanceTo(
|
|
4716
|
+
new import_three19.Vector3(...waypoint.target)
|
|
4586
4717
|
)
|
|
4587
4718
|
}));
|
|
4588
4719
|
for (let index = 1; index < points.length; index += 1) {
|
|
@@ -4594,15 +4725,15 @@ function CinematicCamera({
|
|
|
4594
4725
|
}, [signature, hasPath]);
|
|
4595
4726
|
const elapsedRef = (0, import_react15.useRef)(0);
|
|
4596
4727
|
const enterElapsedRef = (0, import_react15.useRef)(0);
|
|
4597
|
-
const enterPos = (0, import_react15.useRef)(new
|
|
4598
|
-
const enterTarget = (0, import_react15.useRef)(new
|
|
4728
|
+
const enterPos = (0, import_react15.useRef)(new import_three19.Vector3());
|
|
4729
|
+
const enterTarget = (0, import_react15.useRef)(new import_three19.Vector3());
|
|
4599
4730
|
const startedRef = (0, import_react15.useRef)(false);
|
|
4600
4731
|
const pausedElapsedRef = (0, import_react15.useRef)(0);
|
|
4601
4732
|
const hasFinishedRef = (0, import_react15.useRef)(false);
|
|
4602
4733
|
const orbitRef = (0, import_react15.useRef)(null);
|
|
4603
|
-
const tmpPos = (0, import_react15.useMemo)(() => new
|
|
4604
|
-
const tmpTarget = (0, import_react15.useMemo)(() => new
|
|
4605
|
-
const tmpSpherical = (0, import_react15.useMemo)(() => new
|
|
4734
|
+
const tmpPos = (0, import_react15.useMemo)(() => new import_three19.Vector3(), []);
|
|
4735
|
+
const tmpTarget = (0, import_react15.useMemo)(() => new import_three19.Vector3(), []);
|
|
4736
|
+
const tmpSpherical = (0, import_react15.useMemo)(() => new import_three19.Spherical(), []);
|
|
4606
4737
|
(0, import_react15.useEffect)(() => {
|
|
4607
4738
|
elapsedRef.current = 0;
|
|
4608
4739
|
pausedElapsedRef.current = 0;
|
|
@@ -4653,7 +4784,7 @@ function CinematicCamera({
|
|
|
4653
4784
|
if (!hasPath) {
|
|
4654
4785
|
const scene = sceneRef.current;
|
|
4655
4786
|
if (scene) {
|
|
4656
|
-
const sphere = new
|
|
4787
|
+
const sphere = new import_three19.Box3().setFromObject(scene).getBoundingSphere(new import_three19.Sphere());
|
|
4657
4788
|
orbitRef.current = {
|
|
4658
4789
|
center: sphere.center.clone(),
|
|
4659
4790
|
radius: sphere.radius > 0 ? sphere.radius : 1
|
|
@@ -5817,22 +5948,30 @@ function consumeInitialAutoFitOverride(controls) {
|
|
|
5817
5948
|
var import_react22 = require("react");
|
|
5818
5949
|
var import_fiber11 = require("@react-three/fiber");
|
|
5819
5950
|
var import_drei6 = require("@react-three/drei");
|
|
5820
|
-
var
|
|
5951
|
+
var import_three20 = require("three");
|
|
5952
|
+
|
|
5953
|
+
// src/assets/light-gizmos/sun.png
|
|
5954
|
+
var sun_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic7N15mFxllfjx76nqTmclewJJSEIWsgBh3xcNi0mqExHUjMO46wwz/lSYcQHFJTqKqCgoKu4CMipBBQzpEAJE9kWQnSydzr7va3d6qTq/P97ukD1Vdd9b997q83mefgKm6r1Huuq+577LecEYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYY00qnk446BhOcRB2AMcaY+NP76EEVnwX+BTgeqAAWocyggltkAiuijdAUyhIAY4wxh6UPch4p7gUGHOIluxD+n0zizlLGZYKxBMAYY8wh6SzejXIP0PFIL0X4mCUByWEJgDHGmIPSGj4M/AY33J+PXWQZK1NYHmJYxpNU1AEYY4yJH63hWuAO8u/8AbqQ5rPhRGR8sxGAGNMahgP/hXIpwgBgO/APhF/LJB6NODxjTJnSmfwvwleKfPsbkuEkrwGZUFgCEFNawxeBbwJVh3jJX2jkk3IFW0sYljGmjKkizOKHwLUBmtkmGXr4ismEx6YAYkhruAn4Lofu/AHeSxXP6GyOK1FYxpgypi9SySz+QLDOH6DeRzwmfJYAxIw+xKXAdXm+fAxZntOZnB1mTMaY8qZz6ch6/gJ8IHBjwqvBIzKlYAlA3GT5WoHv6IfwuM7y8MU1xrQ7Opsu1DMDmOKnQf7qpR0TOlsDECP6ID1JsYnifi854DrJcLPnsIwxZUrn0J1mZgLne2pyG2kGygR2eWrPhMhGAOKkguMoPilLAd/Xmfxc5xa0bccY0w7pg/Skmdn46/wB7rDOPzksAYiTFpoCtyFczW5m6Fy6eojIGFOG9AH6k+Lv4HX9kCLc7rE9EzJLAOKkC4uA3YHbUSaym7/rA/QPHpQxppzoDAZTyZPAOM9Nz5FJLPDcpgmRrQGIGa1hOvB+T80tJstEmUKtp/ZMQukcutPCxcAolBTCcoRHZSJroo7NlI7OYijKo8Aw740Ll8sk/ua9XRMaSwBiRh/iQnI84bHJzcC7JcPTHts0CaE1VAFfA64Buuz31y3A7+nAF+RSNpU8OFNSOpvRZHkEGBhC88vZyTCZSjaEtk1IbAogZmQiTyI847HJXsDDWuNpi49JDJ1BZ2A28GUO7PzB1Xj/GE08qw9xTEmDMyWlNZxKlicIp/MH+Jl1/sljCUA8+d7K1xm4T2fyn57bNXGW5hbgHXm8ciRZ7lG1EcFypLM4F3gM6BvSJRrI8puQ2jYhsgQgjiZyPzDPc6tphNu1hpvsRl/+dAYjgU/k/QbhQmYxObyITBR0JhehzIZQa/PfLVPYGGL7JiSWAMSQCArcElLz1zGL3+mLVIbUvomDCt4LpAt6j/Kv4QRjoqA1TEJ4COgW5mXI8eMQ2zchsgQgvu6C0FZof4T1zNQHQr0xmCjluKDg9wjv9B+IiYLOZCLwV6BTyJeaI5N5I+RrmJBYAhBTkqER5bYQL3EZlTyqs+kX4jVMVIRTinjXMTqDwd5jMSWlM5mIcB/QMfSLCT8K/RomNJYAxFkTtwPbQ7zCmWR5WmsYHuI1TInpI/Sm2NXeKc7xG40ppZJ2/rCQ53moBNcxIbEEIMbkCrZC6KU1RwDP64OcF/J1TKk0cXLR703Z0dJJVeLOH+BWmUauRNcyIbAEIO6auQVoCPkqvUkxWx/i0pCvY0ojSIlXGwFIoAg6/y2kuatE1zIhsQQg5uRy1gG/LsGlupJjptbwvhJcy4RJA4wAKKe3Vg80CRFB5w/wSzv1L/ksAUiG74GHkwKPrAPwJ51VwP5xEz8SaASgilSABMKUVESdfwtpflrC65mQWAKQAJJhJcrvS3S5NMqvtIbPl+h6xiOdSwUwNlAjOVsHkAQRdf4Af5YJrCjxNU0ILAFICuE7uMNbSnM1+L7WcFOJrmd8aWQUwTsESwBiLsLOH7Ctf+XCEoCEkAx1KPeW+LLX6Ux+qtPsc5IYWS/D97YQMMYi7vyflQzPRXBdEwK7sSeJ8C0o8bYb4VOcxe+tdHBCBJv/bzNcH6C/h3aMZzqLCRF2/mBP/2XFEoAEkQxvIcyI4NJXsZ6/6vTQy4qaoMTTAr5KzvLSjvFGZzEB5X6i6/xX0Y+/RnRtEwJLAJJG+CagEVx5Ml2ZpTUcFcG1Tb6CbAHcVyLXAegSOupCxuj88jrnQmt4F8oDRNf5A9wmZ9Ac4fWNZ5YAJIxM5J/AnIgu/w6Ex7QmtHPFTQA6gz7AMX4aS1YCoCvopIu4lSzbSfEWFWzVOqbr4uRPZWgN7wDug0jrM+wkxa8ivL4JgSUASaR8O8Jrn47yhNYwKLIYQqRLOFoXcorWJnCkIxVw+9/ehNNVEW/thW03PwGugT1rVVIo7yfHY7oiuVNXWsM5wAygc7SB8CuZyOZIYzDeWQKQQFLNEyhPRhcAo4GndAYjI4vBM13IGF3E38myhhQvI6zXRXxPlXTUseUtxRiPrfXkIYZ4bC80uoiLET5+iL8eSxMfKWlAnuhDnAzMhMinM5rJcWvEMZgQWAKQXDdGfP0hpHlcazw+dUZE5zOAFI8B79jrf64CvsBivhdRWIVTRntu8TTP7YVD+Pph/145tUSReKOzGEWO2UCvqGNB+T+ZwvKowzD+WQKQUFLNQ8CLEYdxDPD31ieV5Krg/wFHH/TvlP/WxQlZES+eE4Bc/DtOrWUCykVHeFmizjbQGYxEmQuxWL+gpLk56iBMOCwBSDKJfBQAoC85HtOZnBF1IAGccpi/E3Lcmoj5cPU6BQDEPwEgxTfyeNXK0OPwRGcwmDRz8LWYM7gZMpE3ow7ChMMSgCSbyP3AP6IOA+iF8Ig+yHlRB1KkI22rPJfFXFGSSIqkM+gMHOu1UYl3Uqe1TMlzt8Ky0IPxQGsYRJq5EKu1F8mZAjMFswQgwURQ4H+jjqNVd1I8rDVcHHUgRag74iuU72ttjIeS04zC//e5vz4UmyfRfagiSF5P/6DxTwB0Nv2Ah4FhUceyh/KkZHg66jBMeCwBSDjJMAN4Puo4WnUBZuhMLos6kIIoC/J41TBS/GfosRTL//B/m3guBFzEu8l3iiLF0lBjCUhn0Icsj0Jov8PipPlu1CGYcFkCUA6UaVGHsJfOCDN0JpdHHUjehHl5vU75qi6hR8jRFMf3AsA22ZiuAxBuyPOVSof4Hl2rc+hOmlnAiVHHsp95PMesqIMw4bIEoAxINQ9FWhfgQFUI03UWV0YdSF5a8hoBAOhNji+GGkvxwkkAYrgQUOuYDJyZ58vXy7E0hBlPsXQ2XWhiBsRwrYVwo0wr8cFjpuQsASgX+a2GLqUOKNN1Fh+MOpAjkdGsBrbm9WLlWq2NZRXEcIaPJYZTADm+WsCrj7y+IwI6g85kmYlwYdSxHMRy+nJP1EGY8FkCUCZkEo8Cf486jv2kUe7QWXw06kCOSPIeBeiE8LVQYymQTicNjAip+aGtZwzEgtaRQQqoyyAsDjGcouiLVJLmXvYtPBUfwg/s0J/2wRKAciIFPRmVShrltzqT/xd1IIeVK2iv88e1NkYVEI/iOMI8Ja4iRoWelC8X9PpcvEYAdBop1nMXkIk6lkPYTEd+G3UQpjQsASgjMomnUB6JOo6DEITbdCbXRB3IYRSSAKSR2Gy/BBgVaut62EJJJaO1TADOL+hNcRsBOIsfAh+IOoxDEn4s49kZdRimNCwBKDepeA1P70UQbtEaro06kEMotNrZlbooJoWPcqEfynRCyO3nR/hKEe+KzQiAzuJGiHUSXE8LP406CFM6lgCUGZnEsxDb7TsC3KI1XBd1IAfI8UYR77rJexzFGR5y+yeF3P4R6WIuAy4o+I0V8RgB0Bo+h/KlqOM4gl/JFDZGHYQpHUsAypFbJX2k8rZRuiluSYCMYhVa8HnnF2od1aEEVJiwE4CxOi3ie4UW9fRfzxDWeo+lQFrDh4HvRx3HETSSjX2MxjNLAMqQTOYlYEbUcRzBd3QWn406iH0IbxX8HuU7qqRDiKYQYe0AaNOZc6IrUauLuDiPE/8OZnFruezIaA3vAX4DMT9MSvmNTGFV1GGY0rIEoFy5J6Y4F/IQlFtjlgQUc+rZSdRFV+ugdQtg+IfHaKTTAMU8/UPE8/+t52L8CaiIMo48NJOyp//2yBKAMiXVvA7cF3UcR+CSgBo+E3UgrYpZBwDwjcgOCurOYKBD6NfRaErVah1nAuOLenOEOwD0Ic4CHoAYHyD1tt/JpHifl2DCYQlAefsakI06iCMQ4EcxqRNQ7LnnQxD+3Wsk+WoJffi/TTQjAIXu+9/3vZEkAPogJ5JjFtA1iusXqJl0bBazmhKzBKCMSYa3gLuijiMPrk5A1CMBWV4P8O4v62o6e4slf2EvAGxT8hEAXcJo4N0Bmij5FIA+yDBSzAZ6lfraRbpbJrAk6iBMNCwBKHdZvgrxPAxlP24koIZPRxbAKDYC64t8+zHU8ymf8eRFSpYAjNS5IVYbPJgs1xPkHiUs8hfMkels+pFiFjCglNcNIIva0397ZglAmZMprEK4Peo48iTAj6NMAih+HQDAdTqfbt4iyU+ppgAqqA/txMED6CKOBf41QBNNLC/dk60+SE+yPAIcX6prBqb8UapZGHUYJjqWALQHWb4FbIk6jDy5JCC6NQFBpgH6UFHySm+lSgAgVdJpgC8QbHFjnYynxVcwh6Nz6YjwADEomFSAHMp3ow7CRMsSgHZAJrMF+EHUcRSg7eyAKJKAVwK+//O6ojTzv6oIcFwpruUuWJoEQOfRG+HjAZvJ93THQHQ6aRq4O6bH+h7OdJkcaLTLlAFLANqLNLdC9FXRCuCSgFn8R0mvmuLlgC10p5H/9hLLkczhGKBLSa7llOYJt5Jr0YD/v5T5nqI5vK7cAry3JNfyR1FujDoIEz1LANoJmcCumJ1glw9B+bnWlHCL3WbeAnYHbOVaXUQ/H+EcVksJn/4BlDGhX8KtoQg+8iPhjwDoLL4OsalhUYi/ttYJMe2cJQDtSV9+BaVdGe2BALfrTK4qycXOoBmKKAm8r664OeywDS3BNd4mDNEZIW91rOA/gZ6B29FwEwCt4d9RpoV5jZAoOb4TdRAmHiwBaEfkDJoRvhp1HEVII9ylM5lakqtp4HUAAJ/WBQz00M7hhF8CeF8pKsM7eri1mqKf46JbwlvdrjOZDPwsrPZDNqP1rBBjLAFodyZyD5LIG0Aa4fetN99wSeB1AAAdqQj9+NdSJwCQC3ErYIp/w88e+g0yhk0e2jmAzuRsJBH1/Q9O+FbUIZj4SOaH2BRNBNVZ3AA8FHUsReiAMF1rmCwZHgvtKsIrXs6QUz6ptdwkI1npobWDGQICVcdAp8FQ2evtnw69oLL32/+eqgCpgHRrmYJUB0jvtc6uZTtoa9XoltYdo9nd0Lx5358Ox7xf9YatwDpgpYh4OT9eFaHO2xbKUIb/tYaxQA2lXXjpjzJTMvwj6jBMfMT7iEoTGq3hEeCSqOMoUj2QkQyPh9G4vklXqtiGnxGy22REsBMPVbUbMAY4AffUPxQYTMOyC6g6ppJU+GcBHUY9sBRYDixr/XMxbh3FfBFpyqcRrSODMtNLRMJvZDif9NJWK53BQNI8TRSjLn4owtkyyRIA8zZLANopncWZKM+T3M/AdlJcJhN5IYzGdRELwct8926yjJBRRz5rXVUrcDX3x+E6+xOBsbhOJ4m/pxagFnfIUtvPqyJywPy8LuJR4GJP1/2ijPB3vK3OoTtNPI5wsq82I3CfZLgy6iBMvCTxpmI80Rr+TPL2MO9tGzkuCWNRk9YxHeX9fhrjVhl5YG0AVT0GOAM4HTgfOA8iOVCo1LbjKi4+BTzN2ndvZ9eMv3trXblcRvI3L03NpSP1PJzAQj97U1KcKhN5NepATLxYAtCO6SxGobxBsteCbADe2XryoTday5cRvu2pud20MJxRWgFc2vpzAXCsp/YTTpWmecLuJ6F+DjTMhdzmAM0xQkYGPwlQp5OmK/eQ7CQZ4B7J8IGogzDxYwlAO6cz+SkSwSl2fq0nzTtkgr/qb7qISbgFX8VLdYGqc6HzpdD1A+uoGNIP+87lIQeNL0PDI1D/COx+ErQx3zfXM5xuIuSCRqE13A78Z9B2IpYlx0kymXlRB2Lix25G7ZzW0Bc3T9s96lgCWkmai3ydba619EWKOBq4YiB0eQ90uRI6XuRW3ptgcrug4SHY+Veonwm5bYd79YsygjODXlJruA7K4Khc5W6p5kNRh2HiyRIAg87ierQsqoMtR7lIqlnmozFdxFLyWfVdMcR1+l3fDx3PxcprhCkLu5+DnffCzj9Bdt3+L7hDRvCxIFfQWbwf5U8k/xeZRThBJpXmYCSTPEn/gBsfdvAj3PatpBuMMFtne6rDL4fZMpXuBz3+Gwa9BEOWQp9boeP52FcqbGn337nPrTBkJQyYA90+BLJn7eSbQVrXWVyAchfl8Yu80zp/czjl8CE3AclUGhBuiDoOT0aRZY4+6KWe/H4JQBo6XQr9p7vOp/cPoeq0wJcxRZIK9/vodxcMXeP+7HVLpaoWNbKpNQxH+SvQ0XOkUWgm520RqylTNgVggNZKbLN4HoLPn8bEs6S5TCawq9gGdBHjgcfocAJ0+zh0+6B78jdxtxC4E7hDRFbn8wadQR/SPIOf2g9x8HPJ8F9RB2HizRIAs4fO5CIknOp6kVAeQZgsGfJeQr7nraopsksm0/jW/XTOiH1VEqkZuB/4kYg8fagX6XQ60ZVHgXNLFlm4GkkzUiawIupATLzZFIDZQ6p5AuGBqOPwRrgU+KPOzb/Ogap2U9X/AN4gfdwDdK62zj+5KoH3A0+p6ouq+mFVrdz7BTqNFF24m/Lp/EH5hXX+Jh+WAJh95fgi7smpXFxBA79WPXwvrqpDVPVHwCrgF7ja+6Z8nI6bFliiql9SVbft9Sx+iJRVidzd5Phe1EGYZLAEwOxDqlmI6wDLyUeo4daD/cVeHf8C4LNAt5JGFrmWqAMotYHAjcAK3ThnNpW9fJ1AGBc/kSlHPnfCGLCxTXMQ+hC9yFEL9Io6Fs9ukAw3AqjqUOC/gauBqiiDCqYF2NX60wA0tf407vfPOd7u7Fta/31/6dYfcNWh00CH1p+qvf6swi2U79L6kz6gpcTI7oSVv4W6G6HxgJoCSbMTGCYZNkQdiEkGSwDMQZVNJbT99Z5wI2c9NAi4isScgZDDnZ+zrfXPnbhTeHdB4esbQ7B3MtAFV1SyO24wJSG3mJYdsOwnsOQH0Lwp6miKo3xDqpkWdRgmORLy7TSlptPpQFfeAoZHHYsXlb1g2Bdh6LWQivMDfwuwufVnW+vPDg7+xB53Kd5OBrrjBpR6EusRg+atsPgmWPojyO2OOppCbASGS4btUQdiksMSAHNIOpOrEP4v6jgCkUoY8ikYMQ0qe0QdzUHsxnX2m3D38M0ks7PPlwA9gD64ZKAvsTwBuWE51H4VVv0e0KijOTLhf2QSt0QdhkkWSwDMIaki1PAcwllRx1KUflNgzC3QOU6DGC3AemBt60/RdYrKyFHA0a0/fYjVCMG2F2De52HLk1FHcjjLgFHF1Lsw7ZslAOaw9EHOI8VTJOmz0m0cnHA79Dwv6kha7QDW4Dr8DZT3E35QaaAfLhk4BremIGoKq/8I8z8HjWujDuZAwsdkEndEHYZJnuTc1E1ktIa7gX+LOo4jSneCYdfBsC9BqkPEwewCVgMrcUP7pjhHAYNwhzJ2jTaU5q2waBosuw00NkncAjpxooxvf/s5TXCWAJgj0hkMJM0C4vE4dnC9x8MJP4cux0cYxE5gBa7T3xphHOWqNy4ZOBboFF0YW56BN6+GHW9EF8PbrpAM90cdhEkmSwBMXrSGrwLfjDqOA1QdDaO+BwM/FFEAOdyT/mIg8fvIE0Jw0wTDgAFEUs9MW2D5z2DBDa6WQBSUF8hwjkgSVimaOLIEwORF59KRBuYBQ6OOZY8BV8HYn0a0un8HsBRYQjz24rdXHXEjAsNw0wUlVr8IXv0wbH229NeGSyTDY1Fc2JQHSwBM3nQmUxHuiToOKrrD2NsieOpX3FEBC3Hb9ky89AOOxy0eLCHNwpKbofZrkGsq1VVnSYZMqS5mypMlAKYgWsPfgXdEFkDvS2Hc76DjoBJetAVYjuv4d5TwuqY4XYERuFGBEm4p3PEavPoh92e4lBRnyET+GfaFTHmzBMAURGdyCsKLlHqzdqojjJwGx30BpFRzvg3AIqCO8jogsb3oiEsEhuPOMSiB3G6onQZLvh/eTgHlj1LNVeE0btoTSwBMwbSGXwD/UbILdh0Lp97r/iyJRtzTfi2QLdE1TXgqcInAKEqWCGyY5UYD/J8r0EyKsTKRRb4bNu2PJQCmYFpDX1wPGf7qu/7vgXF3uHn/0DXhOv1a7Im/HLUlAqOByvAvt3sFvDwVtj7ns9XbJcOnfDZo2i9LAExRdCb/g/CD0C4gFXD8t1xhn9A1AwtwHb/VUyl/VbjRgBGEPpOVa4QF17nDhYJrIMtImcIqH40ZYwmAKYq+SCXreR13J/Wr40A45R7oeb73pveluMV9r+EO5Um4bD00bnAV61p2tP7sfPufybnXaA5Q989t0q01nqQC0h1B0lDRDSq6QsVRb/9Z2QOq+ro1GYnXCTgJV2UwZKvvhjf+E7KBzn74tmT4iq+QjLEEwBRNZ3IZwsNeG+31TjjlT1DV32uzB9oAvELiKvZlG6BhBTQsg4bV0LTBdfpN66GlhAcLVXSDqn7up0Nf6DgAOg+BToNiftzywfQCTsFVGgzRjlfhn++F+rpi3r0BGBGn4351ESeifBDhdJR1CL+VEVaXIEksATCBaA01wCQvjQ36GJz4C3eEb2h24Z74V4Z4DU9adsGuhbBzIdQvd0fUNq4n1sfTSsolb52GuISg6/HQZYQ7pyHWBFfj6kTc7oGQNG+Gf14Jmx8v9J2fkgy3hxFSobSWsQhfB97HgWUYvyoj+FYEYZkiWAJgAtHZjCbLawRaVSUw8usw4uve4jqQ4rb0vUFs5/kb18OOebBzvuv0G1YS684+X5KCToOh62iXEBx1AlT2jDqqQ6gETsCtDwjp9phrhNc/6aYF8rOAfpwkZ0S7MlXncxwVXA98gsMtnlDOlZF4XflowmEJgAlMa/g+8Pmi3pyqgpN+68r6hmYb8CKwOcRrFCHX7Dr77a/B9tdh1+KoIyqdqv7Q4zTocQZ0G+PWHsRKH+AMoFtI7Sss+ibUfoMjJnnC5TKJv4UUyBHpQoaRYhpwFfmtmrxNRvDZcKMyPlgCYALTB+hGJfOAgQW9sbI3nH4f9LwwnMDIAvNbf2JyfGvLLtj6Amx5Hra/6Z4G27t0JzhqHPQ6xyUEsVlDkMatcR1DaAcOrbkHXvuoKyB0cE9IJprKmzqfAaT5CsInKWSET/mLjOR94UVmfLEEIAF0Hr2p5BPACJQ1pPmDDGNB1HHtTWfxAZQ/5v2GLiPhjIeg87CQItoEvIA7ojdiuUbY+k/Y9ARse9WdJGcOLlXpkoGe50Cvs2Oy26AHcDahHTa0aS788wpo2bb/3yjC+TKJkp401Hq/uQ74NMWcuyx8XYbH8ORQcwBLAGJOF3I2KR5i36I7OeBvKN+TkaW9ORyO1vAocPERX9h1LJw1B6oGhBEFbk//m0T71K+w/Q3Y8AhseRHUCgsVLFUFvc6FvpdAV/+7TQuTBsbiRgRCuG1u/yf8Y6Lb1dGmxCV/9VW60JVPo1xP8UW+NpJljIxio8/YTDgsAYgxXUEnGqnj8MebPYnwXYZRE/W54FrDWNzeukMPF3Y/3T35d+gTQgT1wPMQ5b2neStsfNx1/I3roouj3HQcAH3GQ9/xrh5BZPoDZ1LMg/ER7ZoPL1wKu1eBK0s5VjIUtWewELqEjrTwKYTrgb4BmtoNVNtWwOSwBCDGtI5/RflDni9/A+EmlnOPjI9umbvWcDPwuYP+Zc8L4YwHQ7qBLwNeJrISvjsXwtoHYes/3PGwJhxSCb3Pg/6T3TbDSFThkoAQjh1uWOqSgPq6myXDF/xf4G06lwqO5SqEaSjHBWwui/IBGcmfvQRnSsISgBjTWr6EcGOBb6sDvksjd8oJlOxw8jaHXBDYezyc/jdId/V8xRzwT2CJ53bzoW5uf12NW8VvSqvrKDjmPW43QSS3spHAOLwvEGxal2PnmxdK70ue8duwo4qwiPcjfAv3fyJwk8DHZAR3emjLlJAlADGmdVyJ8pci374O4RY6cZsMoP7IL/dHZ+03ctH/clfa1/vq7nrgWUq+vU9bYMNcWPs3G+aPg06D4ZjLofcFlP6W1hc4hxCKB60DLhMRr5ml1nIOwveBCzw2+3kZEeK5ICY0lgDEmNZShfAirjxZsTai/JQKbpXjSlf3VmfxGMp4+lwGp88IofPfgOv8S7mNTmHzc7DyD9F3/Cnc4XZtu7KFfb/Nbesfs7jnsxbKoqbQYXUaCEe/B3pf6IoPle7CwLmEUEp4A/AOEZkXtCFdwmiy3ARcHjysffyvjOBrnts0JWIJQMzpYsaRZS5Cr4BNbQVuo5kfyRi8H1K+P61hLD0veJUzH6rYc9CMN/Nwq/xL1aO1dvyr/gS715Tmkmnc0fUdcFPOHXBLKytaf4rp31pwCUFz60/jXj/ltEmh07Ew4H2urkDJD2brhgAAIABJREFUbnEp4FTA+7bWVcCFIlLUHJcuoA9pvgp8CvfJ8enHMoJrPLdpSsgSgATQWk5DeATwUT91J/ALKviBDCW03kxVzybX+ASpqg7+Ws3hKvot89fkkWx/E1bcAfUhXjOFG0Hu3PpnJ1yHX0o53BruBtxxCfW4ZCHJugyHwR8t8RbCkcDJeL611gEXicjqfN+gq+lMA59B+TJhFDBQfscIPhH1ziMTjCUACaELOYUUj+BvnLEJuIMWviGjyfvGkg9VHQfMhcCjFntpAp7BjYqWQONaWPlH2BxSmYVOQBdcpdlOxPOb2IRLF3fgkoJE3urFjQQc+6GQtp4ezEBc4aB8qubmbSEuCTjs3JMqKer4IHAToWxTAIS7GcZHROJSXtMUK463HXMIISQB4AaA7/SVCKjqSOAJ4OjAke2xC3gKSnESarYBVv/Frez3WbFPcB1+d1yn77VvKIEcLhnYjksIknbrT3WEAVfA0ZNDPm2yTS/gfDwvDnwVGC8iWw72l1pHNcr3cBWLwqH8hZV8IMqtxsYfSwASRhdxOjAHP9MBe6sHbge+JyNYX0wDqjoQeA4Y5C+szbjOvwSL/ba9DEt/BU0eCwm1dfpHkbxO/1ByuERgKy43S5Kqo2Ho1e5EwtB1wS229zoC/yRud8CeL0TrAr+bgWqfFzqA8DA53i0jS7ry1oTIEoAEah0JmIM7sswvYRfKb0jzHTmOtXnHpNoVd3M6xV8wG3Cdf8gPGy073cr+DY/4aS+NK6Tai9LP5ZdaE7AFl6clZlRA3E6BwR+FCt91KfbXAbgQr7NhMB34ACulJ418nXAW+O3vKXYxUU5OXMpnDsMSgITShYwhxaOEN8/nEoEUN8owjjDvqGngL3jdYrQGt80v5JVoGx+HFXdBy47gbXXCTc50I7TD42Irh6vAvInkJAIdesGQf4cep4d8oQrcdEA/f03Wz36QNRPPIYyHgAM9RwvvktF4+JKYOLEEIMG0lrGtuwPCSQKcHcBtVHKzDOHgc4+qPwY+4++SK3An+YXYk2R3wdJfw+ang7fVBXcbDvthMgmagNUka2qg90Uw9JMhnzyYxiUB/T21p7D+Y7Aj5OJ7yqt05GI5ttTVtkwpWAKQcFrH8SiP4nXe/aB2oPwMuFFGvr0aT1WvBW7xd5llwD8Idcn59tdhyc+gKWA5hC64QnC+yxyUg43AepKzc6CqHwz7TMhbBlO4qoEDj/TC/GgzrJkEDY/6ae9Ab5LlnXayX/myBKAM6BKGkuMxDwd65GMjws2k+BFD9TLgPrwtb1sMvOSnqYPRZre1b+1MAvVMHXCjud09xVWudgHLSc6UgKRh4FQ4+vIQKwl6TgJyW2DludC8wE97b5tHivFHmv4zyWYJQJnQOgajPAYML8kFO4xZw6BXeiAdPJ2LGvKTf9MmWPRD2FVbfBsVuCf+ntg3J1+7gaUkq6hQtxNg+LVQGVaGlwLOw9vMXfNCWHkm5Lxtk11IC+N91wcx8WO3sTKiizgWeBQ/J3wdWqoLDHweOvjaSrUKt+AvpM5/x1tQdws0byu+jZ64ygbtbXGfD424wxqTlAR06A0j/ge6hPVVSuO2CHpaGLjrflh7JR6+Q7W08E7r/NsHSwDKjC6mPzkeBcLb6NzvTuj2YU+NrcNt9QtjnFhh3SxY8XvQInufDsAAbJ4/qHrcSEBS1gSAKxg05OPQ95KQLpAGLsLbQv6N/w3bbg3SwqLWOf9VfgIycWcJQBlqTQIeIdgpggfX/TPQ58eeGluP6/xDeDTMNcGSnxZfyldwW7f7YU/9vmyFRHYt/Sa4mgESRiWnSuCduMIRAWkLrL4Ydj9ZzLuXkeadchxLgwdiksISgDKly+hJMzNx55T60fFsGPAEiI/qNtuBxwjlGLqWHVD7fdg5v7j321N/eFYCAWZiInPUOBjxOUh7WvKyj47AJbjToALKroUVp0G2oHO+rPNvp+zZpkzJELawi8sAP+Xt0n2g/589df67cUUDQ+j8G9fCvK8U3/n3wi2jtM4/HMeQzJLI21+DeV/1WyZ6D4/fh/TR0P//KOA/8nJaGG+df/tkCUAZk5PZhTIZuD9wY31+BhU+Sg204G529R7a2s+Ot+CtL8PuIk45TgNDcB2UfSvCk8ZrQbySaljukoBQjobeDjyNl7UwncZDj8/n88plpHmHjGZJ8IuaJLJbXZmTkTSygvcDdxXdSLePQdf3e4hGgedxk8Gebf0nLPy2q+tfqM64p36r5FcaPXFT30nUtAnmfx12et93jzv7wlMdjF7/C1VnHu4V9uRvbA1Ae6FKmjpuB/69oDdWHgeDXoGUjxPNXgYWeWhnP5ufhcU/Lm6lf29cdVb7JpTWRkh0iZlUFYz4PHQ/OYTGTwTGBG+maR6sPB20Yf+/WYZyiYykLvhFTJLZCEA7IUKW4VwN/CD/d6Wg7+88df7LCKXz3/RkcZ1/CleM7Wis84+Ch0Xvkco1Qu13YcsLITT+Ju4wrIA6jIHeN+7/vy6lhfHW+RuwBKBdEUFlBJ9HuD6vN/T4InR6h4crbyWUEr/rZ8PinxTe+VcCQ0l+J5RkFbjTE5NMW1yBqc3P+G4YdxhWEdNZ++t+DXSe2PZvC8hygc35mzb27NNOaS2fQriNQyWBHcbBoBdAqgJeqQm3EcHz8XAb58KSn1NwZZnOwGCSuRK93KzFHR+cdJKCYddAL387bp3uuO2BAT+sLSth+Sm1NG+yCn9mHxVB3qw1TAI+DJyGq5K+GVfdZSOwEWUdKdaTYyPCRmAtKdaynfUyNVGFQcuOjORnuohdwK854HOQgr4/99D5ty3689z5b3qyuM7/KNywv417xUPQj1dcaA4W3+bWBfQ4zWPD24AXgbODNVMxCIYsfloqulvnb/ZR1AiA1nAUcDcwpcjrZoH1CKtR1gBrUFaTav1TWEuaVVSyXsbTUuQ1TB60jsko97B3FZLun4Y+t3lo/Q1gnod29rLlBTfsWuiwf2/cfL+Jj524pSHlItUBjv+SO0zIq1OBEUEbyQEXiojv+QqTYAUnADqdDnTlUdxJFmHL4UYUXGIAaxFWIawClpFiGR1YKuPZXYJYypYu5GxSPAj0IX0MDH4LUkEnyDcCf8dr8fdtL7sKf1pgTng0LgEw8bILym4TWroTjPoqdAncYe8lBVyKh/On3wBOE5EQKnCZJCo8AajhOuCmEGIJYi2wDGEZOZZB60+aJVSxTMb7WE1T3rSWExBmc/SfB9LlvQFbawYexmuxn/olMO/rkCsg1xNcSV9b7BdP24EVUQcRgopuMOZb0NHTcb+Am7+6FA+LV64Xke8Gj8eUg4ISAH2RStazArdzOkk20ZYUKEtb/3kpaRZRRZ2NIDja8NRH6Hj+HcFbeg6vd/amzTDvBleEJV/W+cdf0msBHE5Vfxj7bajwsYW2zUjglKCNNAAnisji4PGUD51NF5o5DmEoMAShP4qQYhEpZskE1kcdYxgKSwBmcS5KOc4hbUFYDLyF8ibCYrIspgsL2svogap2wW1AHhKspaXAP4IH1CbbAPO/Vlj5VQGOBbr5C8OEYAVuFKBcdRsDo77ijhX25gJcvepAZolIxkMwiaIPM4Asw4DhKMNQhiEMB4Zx+IfaZuBndOL6cntYLCwBqOHDwJ0hxRJX7SI5UNVpwNeDtbILN/Tvad2mZmHhjbD99fzfY51/MuSABXgpfR9rvS+AYZ/B347rjsC78LCFolpEaoLHEx86nQ50ZxjKcHKtHb3r3Nv+uWPASzxFlgkyJYyDTKJRWAIwkw8hAWrKlxcFVqIsQJgHzCPHfLK8JZcna2BTVQcACwl8Bt4TeB3TXfYbV+wnXwIMwk2XmnjbjJdid4kw6Co45j0eGxwCnBW0kfnASSKSuF1Weh896MAoYCzCaGA0rnbycQTc2p6HX0qGq0O+RskUmgCcjfBcWMGUkS0I88gxD2E+wltkmc+LLJVp8XvmUdXfAh8L1soS3J5lTzY96fZWF2IQHhZKm9DlgFq8DRTFn7jtgd0Dz9/v5XzcIpdA/ktEfu4hmFBoDYNIMZoco4GxwChcR+9zdWWhcihjpJqFEcbgTWEJwHTSdGUJbpDVFG43LvOeD8xrTRLms4sFMpWmKAJS1ZNxdXoDLC/eDcwGX/8X6pfBvK+4euv5GoA7Zc7EX7lUACxERVcY+x23ONCLzsAEAj7wrgdGikikKzF0Nv1QxqGchHIS7jSk0cR1Ik/5hlQzLeowfCh8G+AsPoHy6zCCaceacUPwr6O8hvAGwusyKfxd0qr6MHBZsFaeBVb6CAdadsFb10NjAVMJfUnuGfPtzQ5gedRBRKTzELc9MOWrBKKXXQE3isgNHoI5Ip1LRxoZu1dHP671J2m7yh6UTNFF8GKluEqAM/k1wid8B2MOsB3hDXK8TorXyPEGTbwmV7DVR+OqmgFmBmtlFXjbGKJQ+z3YWsDBQT1w5X1N/O3Cdf6xmwQrob6XwdDCTuQ+NAEuBnoFaWQ3MFpEvNZk1FkMBcahnIhwcmuHP5Lw5+hL4QnJ4OOUtMgV98v4B//B2SxA+SbBV1aaQzsK5TyE81Dc970KtIYVuKperwGvk+IN+vCWnEHeFb5UVYBvBgsvC7wSrIm9rZ9dWOffFQ/ToKYktgGrad+dP8CGOXDUiZ4ODlLc7N2lBNhl0BG4AfiPoiKYRoozGUGK08hxGsJpwGnoXhNyHouBxkSiFnkfTqC9KTqbY8nxbZQPBm3LBNYCLER4iRwvkeIlUrwsEw5+Eo+qXg7cH+ySb+FKB3jQsNIN/efyXEdQhdvcYwf7xFsLbs5/W9SBxEhFNzjxZqj0tWjlDNwC+KI1A6NE5LDHBOt00hzFaHKcjjug4LTWP+M5Vx8W5dNSzU+jDsMHL522zuJMcvwA4UIf7Rmv1gAvobyE8BJpnpcJrFfVF4Azi2+2AXgIL0u5tRneugHql+b3+hSu8y+X0+TKUQtuq98m7Kn/YI4aB6NuwM8tuAqYBAQqOPRzEfmvtn/RuVRQz4mkOH2vJ/tx7H1oWPu0iUqGy2XlkdJ6fWrXGqYAt+CKLpi4OvrKzZz6l0ATh+6YX0+ruVb8HtbOyO+1AgzGDf+beMnhTvjbhlvsV35Dv34N/gj0r/bU2Chc/1wk1RZemnwtG2uGoZyFe7pv7539/nLAv0iGP0cdiC9eB1AlwwzgBOAL4GehmgnBsOsCdv6b8Nb571oE6wpYh9gP6/zjIodb2LcBVwF6Pm+X97XO/8hW/hEa13pqrBaXdRVJpIK+mZ+g/A+u3rB1/vuqR/loOXX+EMIMqmRolAw304ERCD8ECtjMbULXdxJ0D1pF7FUvoaAtsOR20DzHiLsCffxc2hShBdfHrMPVfZqP6/jX4xIB6/QLk2uCJb/Az3+4HFBAyeyDOfaT0HGQh1jKjPIIyqlSze+jDsW30JZQyaVskkl8jjQjgV/iloybqA0PuuV3Nd6quKy5DxryPDWwAtvuV2otuOH8tcBiXO3+5bhT/OqxDt+HHW/Cxr97amwVbuFFkVJVcNznPMVSFlYiTJVqLiuXyn/7K9nKfZ3FOJTvAO3uFKrY6H46nBe0XO8cvMzu7F4Nb34BcnnuXBxK4JMKzBE04Z7w61t/2k2p3oilO8NJt3jaFXAMbgS/SC07YO6x0FIWa9yK1YTyc1r4ilweZF4l/kq2iUom8ZpkqG7d0/5kqa5r9jL0fwI2sBI/SzsUlvw8/86/D9b5hyGH6/DX4KaQa3FP+9uxzr+UsvWw3NcZa2twQzRFqugGgwIeC5JsD5JjjFRzTbl3/hDBLmqZxLNSzUWkuIzAk1Ymb1XHwNHvC9CA4vb9e7DpGdg5P7/XdsTK/PrUgpvBWYqbw1+OGzWO5CQKs8fmp2HHPE+NBazNMfQakABHgyTTWyjvkgxTZDKLow6mVCIroyITeYQXOAX4CG5i2YRp8Kcg1SFAAyvwUs0l1wQr/5DfawVX6c9KTAWTA7bgFu4twD3l26K9+Fl+R/4LYg9rPW5rRpE6DYW+vrYnxt5W4Hp2cqpUMyfqYEotFrdWnU0XslyL2z5oB7r6lqqCdy4LeBLZbNzYcECr74VV9+b32j4k75iQOGnAdfzbsGI8SdAC9LoERvk4br4fBClXv+kxeOESD3HEliLcTRNfkMvLp7RvoWJRSFUmsEsyfJsUw4Dv4g6oML4c868BO/81eOn8mzbDmr/l99oOuFP+TOF24Ib4F+MSAOv8k6ERWDkXmn2czrse98svUu+LodtJHuKIIeEZcpwpk/hwe+78ISYJQBuZyGbJcD3CGOBObCmSH4ODPlEs8BIGq+6BXJ5lIY4hZp/OmFPck/4i3Lz+QU+AMLHWDGRzMP8nnhoMuHPt2KLOB4qzNQhX8zwXymQKOHWsfMXyFiuTWCoZPkqa47EaAsF0OR56nB2ggS0Emk9ss3sNbHoiv9cehVX7y1cOt+h7IW6ThpXdSqZm3l6Tse5VaPDxYLoCt5+zSAOuctOHydcA3EgnjpdJ/FKm2ZhYm1gmAG1kAkskw9XAOITp2GBm4QZ9gmBLPTzVv1g1HTSPPE6wVf/52obburcOGytLur0Tt5zCgp95aFRxH5AiVfaCfu/2EEdkFLgXYaxkuEHGszPqgOIm1glAG8nwlkziX0gxDrgXW7+cH6mAgR8M0EA97rEyoIYVsPmZ/F7bGzvl70h2AXW4X411/MmnHPh73DAP6j1891iCG14o0qCPeoghEo8Bp0uGqTKJpVEHE1eJSADayETelAxTUU7GEoEj6zMBqgYEaGAxXgZdVk0nr19VGlv4dzgNuPv5UmyZbDnZe/i/jQLzfBw530ygg7sC30NKbgHCVMlwiWR4Oepg4i5RCUAbqeZ1yTCVFKfiEgFzMIEqeimwLHgMDStgywv5vbYPCf1EhizH2wfwBJjSNTF1qHUbm+ugwcdpgXXFv1XSAUcRS2YjyrV04kSZZH1CvhJ9u5WJvCoZpuKKXz8WdTyxUtkL+k0O0MAavPQ2ax8k76f/gIcUl6UduJX9G7HxrnKU49DTOArU/sbDRbYRaEvgwI94iCE09bit48Olmh/JeJsUK0SiE4A2kuFpyXAJlgi8rf/lAVfweqiG2bwNNj+V32vt6X9fLbjD3ZYTaArXxNyRSjBveM2dFRBYgO9z17HQ9QQPMXiVA35HluMlw/WS8VGopP0pq1vunkRAeVe7P3Co/5UB3tyAqxcb0Lqa/A78saf/fW3FLd72ce6SibcjbdvMKtTd6eFCywm0YvTo93qIwZs5pDhNMnxcprAq6mCSrKwSgDZSzRyp5iKEC4EHo46n5Cq6QZ9LAzSwhMDjzblG2PBIfq/tQZl+EguUw+Vdq7ANr+1BM/n9nlc95eGMgBZcXYAiHR3kgcIT5QWESyXDu2Qir0YdTjko69uuTOIpyTAF3bNYsH3MovadDKmOARoIcKNos+lJd7Z4Puzp3z0JLsGd1Gfah3yLNjU3w8oZHi4YYDdAt5Oh83APMRRlHsJUMpwjk3g0qiDKUVknAG2kmldatw+eBvyFcn++CpStb8VL3f98n/674er+t2fbcVO0trWv/chR2NqOZTUeLroBN71XpEDTikVZivBRdnKSTOJekXbyAFdC7SIBaNOaCLwP9pQYLr8Vo+lO0HdSgAY8PP3XL4NdeS46as9P/4rbbLGCck9Jzf4KLdlcvwV2LQ14UYUgU+alWwewEbieToyRSdwpU60UfFjaVQLQRjLUSYarSXM8yo8pp2ev3pdCukuABjxUH9vwcH6vq6D91vzP4Tr+zVEHYiJxpNX/B7P4Tx4uHCDB73EWVB3jIYZD2knblr4M35XxZXRfjql2mQC0kQkskWquIcuo1kQgwPhYTPS5LMCbt0DQctm5JtiUZ9nf7sEulVgtuPn+PJdImDLTRHEjPhtfI/gypk0Uf5sT6H1JwOsfVBPwS9IMty19pdWuE4A2MoXlUs01pBiO8EOSfJhqoATAw/D/5mchm+d/vqOCXy5xdmPz/e1dsSc2NrfA2qBr4JRAo3yB7i8HaAZ+SZZhkuFqmcB6n42bI7MEYC8ykTUyic+RZSjKN0jamuyOg6DL6AANrA4eQ76H/lQCnYNfLlF24+r4W2Gf9itLsJVHKx7yEESA73mfdxHsdFHAjX/cS5YTJMPVtpc/OpYAHIRMYaNUMw0YCHyEQGdqllCfdwV48y4Cj0m37ILtr+f32vb29N+A6/xtOVP7VuzTf5ttK1yNjUA2UnQWUnW0qwxYHNfxK2Mkw1SZkpD7ahmzBOAwJEOjZLiLFxgNvBt4MeqYDivQ8JyHyn9bngPN88bSnhb/7cadq2Sdf/uWo7jFf/u0obAiaG2zHAQZbS/8PuM6/jQnSIapUs3C4i9ufLIEIA8yjZxkmCEZzoxvdUGB3hcHeP+a4CFsfja/1wntZ/h/N27Bn3X+phE/pcjWPuGjkeLfmn8CoLh75RmSYapMYH7xFzVhsASgQHtVFzwHuI+47ODuOhY69CvyzTlckZAAWnbAjjfze20n2scnr+0o9nh8QkyUlODD/212rAUNupAkQMLf80J3TPChKXA/KU6VDFMkw8vFX8yEqT3chkMh1TwvGa6EPbUEot1C2PPcAG/eQOCaSNteAc3zMTdImYKkyOKG/W3BnwF/T//gpgHWBR0FqKfoip8V3Q59OqDyCMpZkuEKq9cff5YABCQZ6qSaa0jv2TkQ4ODtALqfHeDNHnbfbCsgye8U/HKxlsM9+ft64jPJ5/uzsPZxD40E+N73OGf//6WGFGdLNZdJdczXSpk9LAHwRCawXqqZRiXHAdfhpaReAQ78QhZgY8CLa/6r/wGqAl4u7tbgHrCMAbcOxPc00NY8S20fVoDvfY+zoW2OP8XZkqFaJvKCh6BMCVkC4JlcxjbJ8D12MhS3cyDPlXEBVHSDrmOKfHOOwIMWuxZB87b8XpuivA//2YQ7T8mYNkFX/h9Mc1P+520cUoAEoM+l21FOlgxTrONPLksAQiJTybbuHDgP5R3A/YS1HKz7WUdalHMYWwi8RH3rK/m/tpyf/uuBdVEHYWKliXB2gCiwcnbARhooeqiq4+BuZLS0o5zGO0sASkCqeUIyXEGOkbjDLvJ8XM5TjyDz/0GH/4Edb+T/2nJNAFpwlZTtwFLTRgm35POmAr53h1T091+AMz0EYCJkCUAJyWQWS4brgcEo1+KWigV31KkB3hyw2rE2uymAfFUGu1xsraQcD5c2QTQTbv2HBh+VygM9AJziIQATIUsAIiAZtks1P6IfIxCmAs8FavBQW3LyEvAmsmsx5ArY61bsTEWcbSTJx0eZsIR94FM2BzvrAjYS6Psf5MZjYsASgAjJGTTLJO6VDOe2Vhi8l0KfGVIdoMvIIiNoJPBdameBVT3LLQHYjZddlKbMNFKa6o/rngrYwHYCLE2yBCDhLAGIidYKg1PJMgb4CfmezNNlNEhFkVf1sFx9Z4HVPYsNNY5yuKF/m/c3+yvVcc9b3grYQA7YWeybx6iq9SEJZr+8mJEp1EqGz9DMQISrgcPX1w00/O9hLWKhIwDl9IlbjxX7MQcKY9//oez0cIZH8feBzsBQDwGYiJTT7bisyOXskEn8UjKceNjpgW4RJgBNm/Pf/19udgObow7CxE7YK//317zbw/HAgb7DNg2QYJYAJMCe6QEYhdtG+HbXE+UIQMOyYO9PKgVWY0P/5kC7Ke3nQoFNLwVsJNBU4IkBL24iZAlAgkiGOslwPZ0YCHwEeJ3OI4psTSn6MJA29UXsYiyHTnMzUR/9ZOLI54l/hdgStB5AoAeBYQEvbiJUTkuy2g0Zz27gLlV+j47eiZuLK1A9gZcpNxSRACT9aNxmbNW/ObhSP/232Rm0nEjbvaCoLTpDAl7cRMhGABJNuyMVRXT+4OW0mvoipgCSPgKwjuQnMca/HNEtCK33kZEWfT+wBCDBLAFItgBfvoCVazQHu1cX/r4kV8urx3cRZ1MudhFdctuc347hwyv6fjBYVcVDACYClgAkW3QJQPNm0CJ68wKKBsbO2qgDMLHURLSJbXMWckGPHCz6ftAR6Bfw4iYilgAk2+Di3xowAWjcUNz7kjoCsA1b+GcOpMTjcxG4JHCgKUGbBkgoSwCSLcIEoMh5xyQmAIot/DMH10A81oRsqw3YQKD7gSUACWUJQLL1L/6tAR9bmtpRArAFN8xrzN5aiE8lyJ1LAzYQaAQgwH3IRMm2ASZbr+LfGrBcWXuZAsgR8MRUU5YULxtpvGko8vu4R4BMZutN39Ra/g1hCTnqSDOPHM/LSILOS5iQWQKQbEUmAFkC1wBo3lLc+1pwnWpSxp62kOyFiyYcuynNaX/5agm6EyBAAiAdeyKcA5xDCpccCWgditKIKzW4BliKMg94mSzPyGiK2EZkfLIEINl6F/c2D+PZzQGqCDbh1g7HXQ4I+mBlyk8Lpa33n4/moMMRzRSdmacOcRtSBPdNP7r151SEKwCoAK1FERpwtTXXAHWkeJ0sT9PEs3KCTbyFzRKAZCtyBMDDxGWQJ45GkpEAbCJeT3kmekrg9bOhyPpYjFBkZp4u8jYkCK6KaWdgEHAmOT6AAFWgi2hG2YGwGmEhyssoTzKCp0USN5kYS5YAJFRr8Y2exb3bQ2IdJAHYDXQPHkKocrgEwJi9lfKo30K0+JinKjIBSAVYinR4lQi9gF4oJwJXIkAd6CIaUDYByxFeR3mKNHNkGOvCCqYcWQKQIFrLUSgXIZzH0r6nMnRDkb+/gE8LuaZgR5DGYd/0kdjTv9lfM/Eb+m+T8/FhLfLBoNgRgGA6IQzCjRych3A1OdBFtODWHCxHeIBO3CwDYrVcM1YsAYghreN4clwGnAucgDAIpQdCBXuKbgb51QUcPQu64KiBPQuFYsme/s3+csRz6L+NqqvMKUHuC0WOIkinANf0rgLoA/R+Yh0YAAAgAElEQVRBOY0GvqQLeaccz/NRBxZHlgBESJdyHDkuRjmfHOOAIQg9UdIHdI4H/HuHAFcOOIYZ5Om/7fK7gVjdN/ayEXv6N/vaSbwPslKgaTtUBXkaL/K+IFUBrhkypSMpZqvSSySWkzeRsgSgBHQRxwKXI1xEjpMQBgDdaNmrWy/0aTjSBMDDfONO4pkAZLGnf7OvBpKREGaDzq0VmwAEuReVRHdqmQTMjDqQuLEEwCNV0szjYiqZgHAWMBI3HOX+O/sc9o4yASjmEKD9bQX6Bm/Guw3Ec5GXiUac5/33FzgxL/aDH/sEANKcgSUAB7AEoEhaS18quIIs4xHGkWMwi+hasu9CoAQg4OOMjwSgCbcWMU6jh024HcnGgPuaxHnef38aNAEo8r4Q/xEAaOG1qEOII0sA8qBv0osq/g24GGUcMBChas/3JZIFbQkfAQB3wl6cDhJdR7zneU3ptO33T9LnIfCRwMVOAVTiCgjFduhsJ8fzQNRBxJElAIehtVSR4nGUs2jr4uOycl0qA7w54F0t5ykB2IKbIIlDWeCdQIDihqbM7CIZ8/57C/y9DNCBSwfQGM6VKM1UkbEFgAcXh1tvfKV4FeVs4tPt7yXCz7N4+ti07diNWg5XiNQYcIv+2uX5D0Fuc7HKlnYDdeT4JRX0k8E8GXVAcWUjAIegC7gEZVTUcRySBhnuC9iBB9prvJ+NuHqGUaZY67Hjfo3TSHIW/e1P0gEbKPa+oB7WHxR3WYR6YCXwCvAIWe6TUXZ+Z74sATiUCjKxnv+LMgFIefzYNOOmAiIpJoYb+rdtfwbcZzHJNeOiSgDUxzkER5TFLdGtQ3mJCuZQwcNybCLqisaWJQCHojFf/xsoAQh4o/A5AgDuCfwoSv9pbAFWlfiaJp5acMlgkqWDnrBV5H0h0L3ooBoR1gK1ZHmONA/IcF70fRFjCcChCXcDX2k90jKGymQKAFxuvwoY4rfZw8oBKwhcFdmUgSzJ7/wBKroGbKDYEYCi70UNuL03ixBeQnmcNHPluMROwiSOJQCHIMNZqHX8Ebgq6lgOKtIpgBD2/e7ErQfo47/pg1pNsod7jR854l/mN1+VUSUAh50CUIR6lLXAQuAlKpjLEp6Q8ZZ+R80SgMOQ4fyb1rIQ+GzrsZTxEWjeLeAUQLpLsPcfynpcYaBu4TS/x1pcDQLTvrV1/uWwQUyAdNDa2gGmAJQmhC3ASoQFKC+T40lG8qJIvLYImLdZAnAEMpJvAN9QpYJFnA+8kxRnoBwPHAN0JYo17LltFF+BKOATfEUXtxVQPd85FTcsfyzhJQFrsUV/5u3Ov1y6Ji9bc4u8L1QOfUVGcqqHAEyJWQKQJxFagMdbf/ah6zmanVxIlnMRTgKG4QazuxFWcqDNkNsJqWJ6yqBD+ALprtASQuWctiRgANDDc7ursCd/U36dP0Aq6A4AKP6+IJZSJ5QlAB5IP9YC97b+7KFKikWcjHAhcDopRpFjMG7nexVBk4Pc5iITAA8F+Cu6hZMAwNud9S7cGEvQh5sm3E5h2zBkcsAOymPYf29pH+tyir4v2AkaCWUJQIhay0++3PpzAF3P0WzjLIQzEE4AhqH0B7r///buPEyuomr8+Pd0z54ZErIBSSA7JOyyLwIGgslMgryigiLugguKvqAGccM94E8FUV5xQcE9CopkJkFAVGQPOyGBbIQQCAkJk3XW7vP7o7qTSZjp6e5bt293z/k8Tz8zkL5VlUnPrXNrOYU7LDdzgJDYBBX5LJ33cLOoCHuiHpclcBvuvIAh5BcubcEFE+V2wze5K9fOH6Ay6Px/jADdgQUAJcoCgAilRg7+nnrtRpU4z3MocY4DDgcmAqOB4SiDEWpJbszz2bgS15sGWPpcuVf+1+aiG7difwNu3GQw/ccvirvRv4Y99RunnDt/gKqgOwACjQraFECJsgCgSKVWzj6ZevVKk6feApyTR+m4XjTAToKqEflfm48u3C6B9bim16W+VuAeXrpxc7ptuO195XqjN7lLJ/kph61+fakKukkp0Kjg6wErNxGxAKCUSdX6/C+uIVAAUF3gAKCnTix3v8nOQOj8Aer2DVhAoCyCNgVQouw0wNIWIJFtwL38UQYAxmSjEzfsXy6df6agt/6AgIUHuh+8FLByExELAErb6vwvrQtWc9XIYNcbE6Z2KPLTPHKTIHPa6sFTA1YQKAAIcB8yUbIAoLS9mP+lQUcALAAwRWo75bf4czt9361FoHa/gBXkfT9IZ+4wJcgCgNIWIPIOGADEa6FycLAyjPEpvdK/3NaHtOGe/vvK9VPpIa9H/veDDSJip2qUKAsAStta8s5n5iGff23QeUdjPOnGdf7ldrxMAnaejddXAFDjI2VmnlOCmrSn/xJmAUAJE5Eu3C75PHgIAOoKeX6vMX3ooHwO9dnTdtwge4y+79Z1owJWUkHeuwDW33a4NnOtNhf0MG/jiQUApe+F/C6rxCUbDMBGAEyUFNdB7qB8Vvr3lB76h8wH9e09JWBFAZJ67VhZiXAJwkpt4XZdyHTVCA5HM3mxAKD0Lc3/0oBz+DYCYKKSoDzn+9O62X0hY6aMLcOODVhZgPvAtiXp72LAbJLcyQKWaDOf0Tt8DDOaMFkAUPoW539pwLnDmjEgPk4hMyYHHbgzHsrpNL+eFDel0VNfAUA8BnWjA1YYJADo9fZzEMI1JFirzVyrdzA+/wpMmCwAKH0BAoCAIwCxSpsGMIWTPsa33Nec95bKus8FgD524uRbhvYcAei9YOESEizXFm6z6YHiYwFA6YsuAABoCDr/aEwWunBP/V1RNyRknbwxQ3emBYANPqbh8rwPtL8E3ZuzeWcMeFtqemCpNvMZnRd0AZLxwQKAEicir+DOvctDA4E/AvUHBbvemEzSw+EDIZ9/kt6zF1ZmuGb4UQErrSHvkwC35vXscSDCNdTzgrYwVxcSNIORCcACgPLwbH6XxQg8ClBvIwAmJAPlqT9tC70HOZkCgJGnBqx07/wv7X3+P1sjgTkkWaULuFkXckiQwkx+LAAoD8/kf+nwYDVXDYWqgGUY01OCXU/95bi3vzeZ/q59LQCsqYOKgGd6BPn9DxYApFWjvI8kT2szd2oLZ9k6gcKxAKA8PJz/pcOC194Q9CASY1LaGVhP/eC2+/W1nTFO33fpvXzM/wcIAFof8lD/ToIwHfg7C3hCF3CRrRMInwUA5WD9X5blf7GHp/fBRwYvwwxs6eH+cjvEpz9dZP47Zxr+H3FSwMpj5D0F0L0ZtgdIQZLZ4Sg3UM8L2syVeruPm5TpjQUAJUzvYKS2MJdHz72Tro15llJL4KOBBx8JNmpn8tFzuL9c9/X3pZs37vffU18BgAD7nRawAXuTOcVgBq0PgYY+PzMS4WvEeVGb+akuwFYce2YBQAnS2zlAW7ieBC8Cc0DraH0kQIkBA+yKBhg0IVgZZmBR3H73gTbcn5Y+uTDTzgah7wBg0FCI5Zm/f6cgw/8PBqw7J7UIH0N5Vlu4XecTdOjDpFgAUEJ2PvHHeQ74BD3372wOMh9n0wCmQBQ3z7+ZN+53HyiS9L3iv6dMw//DjvDQkJIJANJcuuEY92kL/7UFg8FZAFACtIUR2sJcErwAzKG3o7sC/ULuG+DalMFB9yObsqa4Dn8zbs673Pf09yWd1yCb0fOqDH82pilgQ2K4nXj5UGgNsO7Yj5NxCwaf1AVcoIsyhkumDxYAFDG9neHawlzY2fH3vSq29UHQfCdRB+GSAgVQP8ltCTSmp55P/OV6al8utrPrhL/+9NWlVdfCoKA7AIaT+YShDLY/R/5rjrw7DOU3rGeZHUCUOwsAipDexTBt5krirMB1/P2v0utqhS2PBag16CiAwN4nBizDlA3FPemnV/YP9I4f3JN/tqcXVtH3utphPnLmBPh9f+0uD/V7N3a3A4gsw2BWLAAoIrqQodrMlXSyAuFr5HpQ92t3Bqjdw+/LUAsABrwErsPfjHvyHyiJfPqzndyOLu4rO68AY98evD1Bft8D3WdC5w4gSmcYvJ3JUTeomFkAUAT0Nhq0hTkkd3b8+eXnDfSLOYK8hwTT6idD1YhgZZjS1I3r5LbgOn574t9lO7kteIzR969idR00BO3Tasn12WIn7YZN/w5Yf0G4DINxlmoLt2szx0TdoGJkAUCE9B7qtZkvU8lqYC4wJFCBrfdDor+NxX2J4YKAIASGnhCwDFMy0gv7tuC2tOXyhDtQbCP33Q6Zhv9H+lhsG+Dpv/WhbE8ALBZu54DwsLbQrAuwYcoeLACIgC6iUhdwEW08j/BNAp3I0UOyEzbdG6CAMcHbUHdwbgudTOlJ4Bb0pRf2DbQEPtlIr/bPJyjqa3u/AGPflXeTdgnwe76xKOf/syFAE8r96S2EUTeoGFgAUEB6JTFdwLtYzxKUG/Ay8b6HQNMAo8k7M1jasKOBGvdEaE+F5aPn0/6W1Pc2zN87Jfc5/7RK+r4r1w2F2qC3jGry3/5Hsc//Z+tk4O+6gPsGei4BCwAKRBcyneN4FGUeMDG0ijYsCHBxJbBP8Dbse7z7mp4XbsUWhJUixWXp24Y97WcrneEv38C3r8V/APvPzLPQnsaQd9rurk3FsP/fH+Uk4O+08Lgu4F0DMRCwACBk2sJp2sL9JLkTCD9d3valsO3ZAAXsH7wNEy/Y/R6T3hK2GdeZdGFPj8VKcZ1XutO3f6/spTv/fKe/YvS9978iDvv7GLUO8Pu9/u+gZZi3WTgCZR4LeEpbeL/OCzoMWjosAAiJzudQbWEe8C8o8MKTdbcEuHgUgacBKgfD4D7mGXs+UW7HOpdi0LPTb8X+XfLRjZsaCTJCUkPfD+fDDwMJ2i/VECj9b6D7Skk4FLiJQTyjzbxP7wm6Lar4WQDgmS5gnLZwAzGeBHys2Mndq7cGuLgCL6mBJ5yX+c/3fNLcgbuJWqdTGAnctMxWdu/0Te66cD/HoFNcmfb+T3pfwMIh0PB/YluxJgDyT5iCcDNtqeyC9/S5LLPkWQDgiTazrzbzU5TngYuI8me75QnYsSJAAeODt2H48VBbn9170wvMtrJrZKATWzPgUwL3M06vyUhn6LPdGsGkg6iggWump/+GfaHOw9Qc4/K/dP18SLZ7aENJGYdwDW08py18qhwDAQsAAtI7GKQtfBVhGcLHyHyGV+EEGgXYl2yyD/frgMbcr0mPDGzHBQOtuFGCdmyEIBfduJ9Zzw5/B+5naz9DP7bhfqY+ZOpaJvt4+h9CoN3G5T/8n8kBwHW0sVpbmFNOgYAFAHnauaUvwWLg60CWj7sFsi5IACAEelpIG/sOqAgYD6VXorexa7g63ZlZUOAkcB37DtzPphX3s2rDOvwwJHHBqa8trtVk2Po3GIYd66GSCflfmmiDDQs9tKHkjQTmpkYELiyHEwgtAMjDHlv6gh7LFY7WhzxMAwTdFROD0acGLKMX6eHsnkFBesV6W+rPyjEwSHf06Sf7dGe/hV3pZhOU39+7mHTiPmu+tkMKmc74hAnneKgkjnuIzdP62wJkGC1LBwA/Yz3LdAEXlfJiQQsAcqDzmaotzCvYlr5AFNbeFOD6OrzkBJj0QYgX4GOWxI0UtOOehNOBQc8Ocgeuk+xiV0dZLJ2l4v4O3ezq5NN/j83A6+z6e6Sf7K2zLxzF/Xtsw+/PPNPTf3UtjPKx939/As1MBrqPlLWxKDewg+d1AReV4vbBAZf4IB+6kP1QvoHyIQLvkSugmjHwlhcCbB9aC9wfvB1LroU19wUvJwzp34BY6vv0154v9vi+t/9O6xlUCLs6dnp8Tf8/3eN7U5wS+FnlvyfBHfvVVwAw5d1wgI8RgNOBYfld2r4W/jUW1DJAZWEJMJdt/E7OLY2UWTYCkIHeTl3qlL6lKB+llDp/gPaXYNM9AQoYhZelDQd+DOJF+qNLd8IJdj19d+CewNtwT307cE/e23q8trIrLW7P19Y93rOtRxntqVd6FKIbe4ovdukh/zB2pNSS+en/AB/H/g4l784fYO3N1vlnbypwE/U8pi38TylkFrQAoBc6j7g281HiLMed0pfn2ZlF4KVfB7hYgEnB2xCvgTEhrAUwJizprH5hTX3HyJz2d9K5+BmgPSjY5Wtv9tCGAedw4K8s4MliTzFsAcAedCHHUc99CD8njMN6Cu3VW6GrNUABE3DnkwY0+UKoLNm1MmYg6cCtHQkzMVIdfffvtfUwuslDJYNwB3zl6fX7XWpxk6/DUimGH9AW3hp1Y3pjAUCK3s5obeF3JHkQOD7q9niTaIN1fwpQQJxAW4jSYhUw1seCJmNCkmDXQsswVZA5pj7wA/h5+j8wWDlrf+2hDQbXn9yROn3w5Kgb09OADwB0EZXazGeIswQ4n3JcGPnCdQSbaJ6Ml+UPE97n5jaNKTbpw6oKkRkxU46thpGwz2keKqkiUC6Prtfh5d97aIfZyZ0+eK+28Edd6GNuNbgBHQDoQqaznicRrgEaom5PaLYtho3/DFBADYH2Ee8kMPUjHsoxxpNO3HB/W4Hqq4E+d43HgMM+66miSRkqysKaGyAR9lDIgCTAeSR5Vlu4QZt9HLySvwEZAOjtTNZm5qf280+Nuj0F8cK1AQs4GC8fl5Gnwl6lv7TClLhudi3yK9SZEzEyJ/3Z5wio9/FgWIkbtcuTdsPq6z20w2RQCVyEsFxbmKst0Sw0H1ABgN5GgzZzNXGeQZgVdXsKav182P5cgALq8JIeGODQS8txosWUgiSu099C4U8/zLTwryIOU309/R9EoIW7626B9jWe2mL6MQiYA6zQFuZoS8a9Id4NiABAFdEFvItKFiN8Hi/L2kuNeojqD8bLWoD6sbDfccHLMSZb6Ux+PnP456KKzHediW+HikGeKgo4ihB4tNDkYThuy/kz2sy5hdo6WPbPYTqfo4lxHXBi1G2JXEUDTFsDFYMDFPIEsCx4W5Ld8J8PQ+eAO2LUFFo6AVNUx0vHcBn/+jzudxic+H+eKjucQHv/Nz8K9x/jqS0mgEXAHGkiyOKtfpXtCIDOZ29t5ifEeAjr/J3urbDmZwELmYKXUYBYBRx2yQAIQU1k0gv8dhBd5w+Zh/5jAofN8VRRNTAxWBErr/LSEhPYMcDd2syd2sKbwqqkLAMAXcDbiLEU4ZOUWvresK28OuDJXjW4/cUeDDsGhgfMVGbMnjrYdTpklB0/uD4509D/2DOgfpynyg4h0Mr/bc/Cq7d4aovxQpgOLNIWfq0tjPFdfNkFANrChSh/w53dbPbU+dpa1t22PFghU8m8mTkHh18BlSV/rLaJmuKG+VtxiXyKIX19nMy/JnUNLkOmF3sROGHX8m+CRh0xmV7EgA8Az2kL39A78LFYBCizAVhdyBEkeYRAZ1+WlY0ojwOLiPEw3TwsZ7FWVfcFVhCoF18NPOynlRvuhyeusUNxTO7SHX87xfX5EVyf3Nf4Y0zghG9BfYDters5lUDHd2vXMu5q+BxdHUchHIwbTjjYU+OMX68gXMlWfhn01MHyCgBa+AvwjqjbEZFXgMdRHiPGYyR5TGaxuq83q+o1wGeCVXkP8FqwItKe+hase8pPWab8JdnV8Rejevoe+hdg4lkuM6YXo4GTghZygYj8ruf/0L8yhBoOJcnRxDga5WjcIqCyGzkuUc8ifF4aacm3gLIJAHQeVdTzOt7GpovaK8CjKI8iPEqSxTKblbkUoKr74UYBAuTmfR24Gy+PXpqEez8C7ZZ9zGTQhZvjj2IrX7ZqyHwXGrIfHOdrq10MmEHAY7uXA1NFpN9EyHong+niSOBNwGG4bQcHMzDuu8XqduJcLDPIOXlD+QQALUzEfZDLSRewFHgaeArlMap5TKaz0UfhqnotcEmwUh6GvgcacrN9FTxwOSSLaSzXRC6J+01opzjm9jOpIPPh4RUV8OafQNXeniqcguuHA3mfiPw234t1HnFqmUCMw4HDEA4FjsAtSrDRgsJoBWZLE/flclH5BADzmUCMFVG3I4C1CE+jPAU8hfA0W1kq54b3rKOqQ3Gb+ofmX0onsBD3WObBC3+EZbcW13yuiUY3u572S+HzEMN1/n11eQIceQmMeLOnCgcBbyXQyn+X2ONoEfG++k9vp44KDgEORzmUXSMGI3zXZQDYjHJEpqnfPZVPAFA6UwDbURYT4yngaRI8TQVPykw2RdEYVf08cHWwUjwuCAR49Euw0UOyIVN6krgOP8rEPfkQ3HFimfricafDgR/3WGnAhX/OdBG520Njsqa3sQ/VHEaCw4kxFWUqbihjWCHbUab+KE28J9s3l00AAKAt3Aq8Pep2pGwClqAsRVia+rqEh1klVxbPrU1Vq4BnCZxB5D7gZQ8twmUJvO9j0LbVT3mm+HX2eJWiTIv+APYeDcf+AH+33PG4XDGB3CYi/+OhMV5oCyOAgxGmpIKCdGDg4yjSgaKLLobJ2WR18yy3AOAEXE9UqHmnJMpqYjyH60SfI+k6emliQ4HaEJiqngf8MVgpO4A78Hagets6eOB/oTvDpG8Hbmi4mjL7JA8AipvX70x9LYUh/r7U4Rb+9aW6Gk7+qadc/6Qqm0HAI026gSNE5FkvTQqR3kM92zmIeGq0QJmCMBV36IFt+d6TcIo08t/s3lpmtIVvA1d4LvZlYCVu1fxK4DmU59jOc3JuwU4SD42qCnAvcHKwkpbhphQ9ySY/wDZcJ1KFCwTsdlC80p1+B677KeVOP62WzPto4jE4/jtQHzBJz25OhOBJ4X4kIgG3AUdLF1HJRibSzUEIk3ABweTU1/0ZuAsQm6SJBdm8sfwCAEVo4WsIXyL71THtuI59JZr6KqkOv5aVMq1odxt7o6onAf8l0GdCgf8A6/00CmDl72H53zJXuZ1dQ8cxXBBQTcC1UcaLBK6zTz/tl5NqyJiTTYAjPgkj3+Kx0jF4ONqkFZgsIp6SeBSf1LG6E1Em7wwOlEmp7w+gnIODGEfLTB7L5q1lFwCk6XymInwc4c24Ve6duCf51SgvEGMVwkq6WMlsXhYpi+eRQFT1RuBDwUppA/6B17v94v8Ha/tZZLiFN84+pIOBKlwwULaf9iKiuH+HztTXYt+2l6/+On+Ag86Bse/2WGkdcCbBTzNPflIk7uv4wZKjLVSTZAJxJpPcGRRMBMamXpkmdIpdKyMZKcfQlc2b7ZZodkptC1xC4HMU1gL3e2hRD4vmwKZVff95EthK3x2O4IKB9Kt84//C62b3Tr/cVdF/3p0xx8PBl3msVIDTCLyDruNheOmkm1iT+KhMGxD/WjnTZvYlHQwIB6S+H8euACFTpodoCT+QRrL+4FkAYHajqh8Afh28pEVAhg47V9oN918M21/P8B5cEJDNbS2Gu5FXYqMDuUiya1g//RpIY2fZdP7DJ8BR38Xvhyqdnj8A7Ya1x0LHE6DMZxDnySh2eGneAKLz2RthLDEOAMaRTAUHLlgYjXuAiuIU2hdJcqTMJsNNcnd22zNvoKp3AWcEK6UbuAuy242SncQOuP/TmbcHKm5hYFYDYD3EU6+KHi/jOvsE7ufZRWntzfetGjcKn+muOXgfOO4aEJ/3/6HANAIPW7VeBRsv3/XfwkN0Mkum+sksahydR5y9GAnsR4JRwL5I6qsLEPbBLeYYib9lyy8T460yk8W5XGQBgHkDVZ0MPEXgubDNuLMCPE4Ed22G+y+BjgybL/INAnqKpV7pYCCe+u9y/I1RXMeeZNe8fSL13wPp6T6TbOb8G4bB8T+CmM+tKFXA9Cwq70f3alhzCCTfcNbGsygzZDIvBavA5EoV4e+MpKpHQJBkDMJIYATCPijDcfM+w+n9sWQrym8Qrsxn63k53s5MQKrE2fyzRQy+8Mjgpb0IPBS8mJ46NsIDn4XODOmH99wd4IOkXhXsCg5iPV7FLt3JJ9h9KF+xjj6T/g73ARg0GE78EcQCnK31BgK8GffgGNArs2BHn4fGrUY4UyZi6TeLmN7FMBKMIMlwhBq6eZ0Yz0hT/nnYLQAwu1Elzkp+DRUXMPp+qD7WQ6mP4/2cprZX4MHPQ1c/PfwOCndkbAw3UiDsHhhIj/+X/j6odIedfkpPv5J7/L90R28dfH76S/IDUNvgOn9viX7SDsMlwgto642w/iP9vWsdMWbIBOxM7gHEAgCzky6ikiH8ETgHgKqpMOZRkKBPNUlcfgDPyRHb18NDn4OOfnr4dii6pU7S46vgOuhMowjpzjz9vQmX4Ebd+9txV7cXnPBDqGjw3IBRBM7LBdC1Cl46EpJbsnn3RoRGmcgjwSs2paAUBi5NAehiqhjCPNKdP0DnEtj4RQ+lx4AT8L69tmYkHP9DqOonQKnBrdwupnB3zyf09Px7X68ENlRfKOmDffrr/BuGwknXhdD5NwDHeSgnCRs+lG3nDzAM5Z+6gukeKjclwAIAgy6jmmr+CrzxYJDNP4IdWWWV7EcNbj7T8/L6mmFw8nVQ189NuAp3X7VPvMmkAhhM/x/TIfvC8dd5nvMH90E9GS+Lw1+fC23/zvWqepT5uryXe4EpO8X0TGQioIupopo/A2/r800Vo2D/pyE21EONr+DOa/L8KJvYAQ9dCtv6OVU5iVscGGSHgClP2az0Bxg2Ho76juetfuCi01MInIcLoONxWHsCaN6rYLtQLpDJzAveGFOs7HloANPFVFHDX8jU+QN0vwwbLvZU637AEZ7K6iFeByf+BPYel/l9MdxIQCkn+zT+1dF/5y/AqCPh6LkhdP7gjvf10PlrG6y/IEjnD1CJ8HtdxkeDN8gUKwsABihdRCXV/AnlrKwu2PZH2HKDp9onp16eSRyOvRrGnNT/e+voP6ObKX8xXGLX/gJCASbNhkOvIJyB08NwWWY92PAp6PRyym8c4We6PPvUsqa02BTAAKRKnBX8DjgvpwulGkbf62lroAIP4M4NCMHqP8Hzt/Q/05DEJQ2yrOgDTxXuqb+/u6/E06cAACAASURBVGBc4JALYd+w1sZNAI72U9SWX8CGC/2U1ZNwpUzk6/4LNlGyAGCASXX+vwXyO6as4gC3NTA+3ENrkrj1AOs8lNWLDffB0z+G7iwyEbbhtgvaKvvyJ7gRoOos3ltVA0d/BRpCGLECXAK4E/ByK+58El460U0BhON7MokvhFW4KTwLAAYQVeIs52aE8wMVVHsGjLoDP+ddJIB78Z4jIK19PSy6AnZksRWqGzcaMJDz3Ze7CtxTfzYf3SH7usV+FWHNFY0CTsTLTGzydXjpGOhaGbysTITrmcCn7Pj08mBrAAaIVOd/U+DOH6Dtbnj9Wx5aBe5OfDLuwJMQ1IyEk2+AkQf3/94KYAi2QLAcpRP77EX/nb8A+58Ix10bYuc/Evfk7+MWrLD+w+F3/q6qT7KC/1O1vqMc2AjAAJAa9v8N8B5/pcZgv/lQ1+ipvC7gX0Crp/J6sfpP8PytoFk8vCRw2wVtbUDpqwJqye6pvzIOB38c9jktxAYNA07FX06M5LdZHn8SYS5uQUEh/JyJfFzExstKmQUAZU6VGCu5CeUC74XHGmD0f6HqcE8FtuNSBm/2VF4vtr0Aj38D2rZl9/5OXBphu82Vnhhurr+/jH5pew2HN30dqkeE2KhhuL3+3k4M/AtwnogkUzk9Pgh8HS8nCPVD+T2TeL+Iz+M+TSFZAFDGVBFW8H/Ax0KrpGI0jH4QKsZ4KrAT+C+EekR5Ehb/AF5+OLtFf0ncIsG8z9wyBSW4BX79neCXFgPGTYdJFxLuLXEEnrNhLgJOE5HdTrrQxdRTzWeBy3CTWmH6DRP5kAUBpckCgDKmy7ka+HzoFVUfBaP/m0RqPc0LduN2B6z3U1xfNtwHz1wPXVmmBUzgAgGfRwwbv6pwHX+2n8TaQXDE5bDXQSE2ClwCrBPxs3AWgBeAE0Tk1b7eoEtpoJJPolxOuIHAPFq5QI6x/JqlxgKAMqXL+Sbw5QJU9TrCVUzYshQabsHbHS6BCwL6vL95qqYdnvkerH86+y2AXbhpAXvmKR4VuI4/24drERhzAkz5NIjn8yneYH/c4T7e1s1tAU4WkWeyeXNBAgHhdpK8SybbOFkpsQCgDOkKPovyw5Cr6QZuRPmyTHZ7+FT1UuD7/qpIAg8SWrKgnl57GJ75MXT2c7RwT524ZQu2UDA6lbhdG7lMqQ8aDEfMgfpJITWqpwnAUXi81XYBTSJyV64X6hqG0sGlwCW4hNi+3UYH58ohNkZWKiwAKDO6nE8B14VcTQtxLpPxLH1D/arXAJ/xW91iwEtq08w0AUuug7UPZLdTIK0bNyJggUDhVOBW9ufS8cdjMH4WTLiAwtz6DgYO8VlgEvigiPwmSCG6hqG0cwnCZ3FnH/q0AOXtNhJQGiwAKCO6nPcDvyK8/A6LEebIRJr7bIOqAD8FLvJb9UrgMQqSqm/HS/D092HL2tyq68KNCNhMaHiqU69cRu0FGH4gHPo5qAx7TRy4X79jgQN8F3ypiHgb2dMlDKOCT4cQCLSgnGNBQPGzAKBM6ArOQfkTHpcY97AG+BYT+WU2q31VNQb8Fq95B8ClDH6Agj1qb7gflvwc2rfndl0SFwh0YtsHfUiv6q8ht9BWgLq94dDPwOAsEkF5UQWchFvx79UXRWSu70IB9DmGE+NzCJ/E39TAX2nlPFsYWNwsACgDuowZCLeRXXbzXLgFfjGulfHkMDkOqloJ3AJZnjaYQ5Pc4sDQ8p3vQWHV72FVM3TnGHgoLgjowKYH8lGJ+0RXkvudqqYWJp8P+83w364+NeC2+XnOHrjqh6+x6dJRYXemuoy9iPEJlM/jEhYEdSutvNuCgOJlAUCJ0xVMR7kdvwlsO4DrqOQ7MpbX8y1EVauA24CZ3loGuOY9SOjbBHvSJKz8DbywEBJ5LP9P4JptowKZxdg1zJ/PRFZVFYxtgnHvBilkttr9gOPxmODHWf1jePbToFwms/iB38J7l8oj8HHgUtxfLH/C9TKRi700zHhnAUAJ05UcR5K78ffIoQh/IcHlciBeEour6iDgDlzCf48UeAbeuA4xXMlOWP4rWPOv/AIBcKMBHbi1AhYMuI2jlbjR83wnsCorYeyZMP79Be74AaYAh+L9drrmF/DMRaQWomwlwVQ5qxBbYhxdRQ3dfBjh88C4fItBOF4m8ojHphlPLAAoUbqMQxD+jZ+hOoC7Ub4gk3nMU3k7qeogurf8g4q9TvJdtluesIiCj7Enu9zUwJo7oTPArqdu3KhANwNrmqCCXR1+kFUrVTVwwFth/HtAvCXZybZy3FN/CFl3X/wpPHuxG3na5ffSxHv9V5aZLqKSIbwXuBzIPWOScoVM5rveG2YCswCgBOly9sfly/WxzPhplG/KZP7soaw+6T/q5nP472axz/+EUPpm3OLArSGUnYWXmmHVX6EtiyOHM0myezBQTqMDMdxTfkXqa5CHdAHqhsD4c2CU59mlrA3FZfbLNt9wDlZeBc9d3vufJTldZnOP/0r7p0qM5cxC+Apum0O2viCT+F5Y7TL5swCgxOgyRiDcSz6R+O6WoHyNSfwl7LO9tYXTgH8hFXDYjTD6fSHUkgCeBpaFUHaWNi6CVX+C11/MLY9AX5LsCga6KJ3Mg4Lr4NOdfQV+NqbGBIZOgskfgIYDPRSYDwEmAYcTym7bTJ2/s5iRvCnKhXWpM0ZmAl8A3pLFJcfIJB4Nt1UmHxYAlJDUaV8P4FKL5UdYhXIlE/ldIQ7wUEVYwMPAMa7+OBx6A4z5SEg1rgMegdw2LfjVtd1ND7xyH3Ts6P/92VJcEJBMfU0Q7UhBuqOP9/hagcd09yk1dTDqVBh/PsR9rnXNVR0upW8YpwUqLLkMXshim79wqTSGnukzK/o8xxPjC8D/0FtEpPxKJvPhgjfMZMUCgBKiy/giwnfyvPwVlG/Tyc8LmapTF3AByh6ZywQOvhbGfjqkWjtwQcArIZWfg9an4IVbYdNz0B1ivJXs8Ur0+F7ZlcxIyS5YEHZ17vT4Pv0S/D3V96WyAoZOhYnvhvrJIVaUrTHA0WR/tnAONOEW+710Y7ZXbKWCKfJWXvbfmPzoCg5E+Rzwbtx+yFaEa5jAt+ykwOJlAUAJ0RU8iHJ8bhexCbiOBN+XKYWdJNd7qKGNpcDYXt8w7jMw5QchrtpegZsWKIZtyArr74OX7oDXl+e/g6CcVVTAkPFwwCwYHsJ60bxUA0cSQlY/J7Ednjgf1v89t+uU38oswphLC0TvoYJJ7CX7synqtpj+WQBQQnQ5TwGHZfn2bcA1xPm+jKc1xGb1SVuYA2TOXrbPOXDEbyAewmIqwE0FPA68FFL5+VB49T/wyj3QuhK62guS4bjoCFA9yD3pjzoThr4p6hbtYQxuts13fq2Ujlfg0bNgc57T48I0aeRfXttkBhQLAEqILuenwMf6edsO4Cco30uf0hcFXchQkizDLZfObMjxcNRtUL1PiC16GXeWQKEyCOag7RVYuwA2PA47NkCinJb/9yBAvBIa9oXhR8OoGVDtaxerT4Nww/0hfh63Pg2Pzoa2F4OU8jS1HCXTBtQGUuORBQAlRFcyliSPA3v38sdtCD9FuEom8Gqh27YnbeFHQPaT/LXj4Zj5UB9mzvZO3JTAKor6kXv7Knj1v7DxGdj+SumOEAhQWQ2D9oEhh8Co02FQ77NBxSEGTMad4BdiToHX/gGPvwu6A24bBRAulkauD16QGYgsACgx+jxHEuNGID1euhm4iQrmyrhiWPUGOp8JxFhCriumKofAm/4Cw84Ip2E7tQJPQHQDJLlJtMOmx2HjE7D1BWh/DTq2Q7JIRgoEt0Wvsg7qRkDDeNj7UBh2bMSr9nOxD26uf69wq1n9E1jyGbfwz4/XqWKyTGejrwLNwGEBQAlSRVjJJJLU8xJPF9sQoLbwF+AdeV0scZj0FZj4lQKkdH0ZFwjkeNpfsWjfAJuXwNaV0LYO2jdCx2boboNElwsQfI0cxOMQr4KqWqjcC2qHQd1oaJgEQ6ZCpe9j5QulATiCoCnv+5Vsdzn91/wijNKvkyYuCaNgU94sADBe6QJORLmPoJ+tkbPh8JuhsrfZDp8SwPO4MwWKKo7yI9EG7etcsNDd4Tqi7jZIdrsV6JqEijqIpRa6xWsgXgtVDVA93L1K5ik+F1W4of6JhH4bbFvthvw3h5YOvxvlKJnF02FVYMqTBQDGK13AP1GmeSmsbhIcdQs0HO6luMw6cVkEn6csAwGTUoHL5DcF7yf39Wb9fHjq/dCV96Ga2bpbmpgediWmvFgAYLzRBTShNHstNFYDB18H+3/Ua7F968AFAcsondy7pn/pjv8gQknmsydNwIpvwfJv7HmgT3iEs6WRHBMKmIHMAgDjRSrl76PsWpzo15gPw9RroKIhlOLfqA03LbAKCwRKWSVumL9AHT+4If+n3g+b/lOY+nZZARwiTXQUumJTmiwAMF5oM+cj/C7USmrHweE3wdBTQ61mdx24IGA5RZlDwPShBpiA29ZXoI4fYN2f4ZmPFWLIvy9zpImro6rclBYLAExguohK1vMsbow1XBJzZwgcdDXECnhjJwmswY0KeNi/bUIyBDgQ2J9wDyvYQ1crPPspeDncGDgLW4lxkMwsji3BprhZAGAC0wV8EuUnBa204TA44rcFWiDYk+JOHFyJO2yoFDP0lJsYMBr3xD+y8NVvvAue+hC0F0266RulibCO2zRlxAIAE4jOo5Z6luHuwIUVr4XJ34Jxl4BUFLx6d87AC7gpgm0R1D/QNQDjgPGElq8/k+4t8PwVsPp6iiwQTCKcII2Etu/QlAcLAEwg2sIVwLcjbUTDEXDoDe5MgUgosB4XDLyMbSMMUyXukJ5xwPDomrF+Piz+JLSvia4NmT1AIyeLFFdkYoqLBQAmbzqfvYmxgt7PJigsicGYj8KU70FFyOlcM0oAr+JOH1yLBQM+xHFD++OAURR0bn9P7WtdKt91t0TXhmwJ50sjf4i6GaZ4WQBg8qbNXI3w+ajbsZvq/eCgq2B0MRyV3o0bEXgJFxRYMJC9KmBf3NP+voR6OE82tBtevB6e/zJ0b422LdlbQ4IpchY7om6IKU4WAJi86D8YRTfLgLqo29KrEbPcaED91KhbkqK4Q4hexi0ejGybWJES3Ar+fXBP+yOI9Em/p413wZLLYOtTUbckd8rXZRZXRt0MU5wsADB50RZuAC6Kuh0ZSYVLIDT5G1Ad4tnueWnD7SZ4LfUaiIsI98LN44/AdfwRLOTLZPtz8PxX3N7+0tVGnINkBkW7WMFExwIAkzNt5kCExbj8qsUvXg8TPgfjv+B2DhSldmAjLhjYiBshKJLjfr2I45aKDE+9hlHQBD256NoIy7/pju7Vspi2sW2BplcWAJicaQvzgHdF3Y6c1Y6FA78N+72nAEcNB6XADmAzLhjY0uNV7Gpwnf1g3FP+3rgte8V+u9E2Vl2TYPmV9XSXws85a0liHCszeSzqhpjiUuy/kabI6HyOJsYjlPJnp24CTJjjpgciyR8QRBewHRccbN/j1Q6hp4EX3JN7LTBoj1cdUE/kC/Zytx34JTCXZhlGjH8DQyNuk2//lCbOiLoRpriU7k3cREJbaAaaom6HF7XjYeLlsP9HkxAr9iGBHHTgjjfu7PF9Ehc8aOqVHtpOsmuxXQXulhBLfR/HdfZVuPn59PdlYxtwI/BdEVmX/p+6kONIchdu2KJ8KI0yi4VRN8MUDwsATNZ0AceiPEQ5fW6Ehczc8FkYPge4gIIcEm8ithm4DrhGRDb29gZt4XSgGTefUS6WUMvhMs32oxqnjJ56TOiSfIdy6vzhGSp4t8iI50Tkw8ABwNdxK/FM+VkJXA6MF5Gv9NX5A0gT/0Q4j/JK3jCVNj4UdSNM8Sinm7kJkS7gzSj3Rt0Oj14mzgm9bY9S1WrgPOAyoNCnDRn/7gOuBW4VkUQuF+oCLkC5ifJ5WFpPF5PkbEomm5EJT7l8qE3YlG9G3QSPthKjqa+90SLSISI3i8gRwHTg75TXk2D5S26DLb+ANYfDC/JREflzrp0/gDTyW5RLwmhiREZSyeeiboQpDjYCYPqlC5lOkjujbocnXcBsaeIfuVykqvsC55Hc9FliQ8eF0jITXMejsOVnsO0PkEw95Crvk8n8NkixuoCvoWWTUc+SAxnARgBMNhJ8NeomeKIIF+Xa+QOIyDoRuZaVw25izaHQehUkbKlAUehe6/49XpwMLx3jAoBkjxFu4digVUgjXwe+H7ScIlFLomyCGROAjQCYjLSFRqAl6nZ44SEvuq7gzyjvBEBqoG4W1L/DfY1FeQrhANP9Mmz/K2y/Bdr+TT9ZEx+QSZwUtEpVhAX8Avhw0LKKQIIYR8tMnoy6ISY6FgCYjLSZhxCOi7odgSl/oIn3Bj0fXZexBGHKG/5AqqHmFBh0FtSfB/FiO3ugDHS/CNv/Btv+DO33k0Oq5HZa2UuOoStoE/RKYhzHzcB7g5YVOeEeaeT0qJthomMBgOmTNnM2wt+ibocHd7ONJjmXziCF6DKqEbbR3xkIUgE1p7lRgbrpUHVYkGoHLu2Gjodgx52w43boCJDJNsmb5ECe8NKsecSp53e4nSKlrkmaWBB1I0w0LAAwvUoNdz4GHBl1WwJ6mg5OlbfTGrQgXcnhJPMYMo2PhNrToHY61DVCxf5Bm1LOVgJ3seW6LjZ+9WKSgf/ZHOEjMpEb/RQGOo8q6rkFmO2rzIg8wzaOlHPJeYeEKX2llgjdFMpC3kmpd/7KKuLM8NH5p8o7JK/rEuvdsPW21LGyVYdAzZthr4v/Q/VhI4ApDMxgvAt4AngQeAj4j4isAVAlzgbeCkz2UpNyhJdyUuRcOnUe76Cev1LaqbEPZRDvB34VdUNM4Q3Em47pR2qe8wmglMeuX0N4szTynK8CdRlfR7ztiHiJVibIMXSp6hDgBOD41OtQoNyGCRK4p/sncR3+g8BjItLW1wW6gg+j/NJT/ffKJE71VNZOOo9a6mkB3uK77AJayzYmy7n0+W9hypMFAOYNtJnzEX4XdTsC2IEwXRp5wGehupw/Aef6KYzLZDI/6POPVQcDB+OCgYOBQ4CpwGiK+/e2C1gNLAae7fF1iYi051KQLqKSISwFJnho1xYmMiToItDe6B0MopsFCKf4LrtglMtkVt+fR1OeivlGYiKg84gziGd6XeleGrqAs8NY2KTLeALxMpS8BeEAmcjmnNugWoUbHRiLO7tgXOr70cAIOl45hIqGSuL1Hpq5h65W6NoEXRuhcwO0r4G21aCxf3LQt76K6/hfySfjXl90ORcCP/NTGJNkMiu8lLVn0XcymC7uAo4Jo/wCeJ0kE2U2r0fdEFM4tgbA7K6e90LJdv6KcKE0htD5K8IKT/PRcEM+nT+AiHQCK1KvN9AWbgXeTqwKKoemXntDrNa9oaLB7VIQgYohqYsS0L3FfZ9sh0RqJDixtUenv8m9r9dKuUumfPu+fP4+/Wrl1wzhClygE0yMI+nj5xaUnMlm/StnUs0/gTeFUUfI9ka4FPhK1A0xhWMBgNlJ5xEHroi6HQF8URq5KZSSV7I/UOehpC7cUbRhWQK8nWQndKxzr7DFeCGsouUYunQZ30L4ReDClCOBW4K3qnfydlq1hRnAv3DTNqVF+F9t5icyiwJ8aEwxsFTAZpcGzgUOiroZeVGulyauCq38pLdRkd/LpFBzsHtb9Ji1BKtDLX8Sv8bH30vD39UiTWygi9OJ4t8huEHYCMCAYgGAAVJD3MoXo25Hnv7EI3w61BrES2CkKN/zUE7fYiwNtfzeaLgBgAgJxMNplFKYba1yNq8S50wIb2QkNMKFupBJUTfDFIYFAMZp4RxKc9vfv4APyJXZ54XNi58A4G8ymcUeyulbkqXgf6V7Bh08yiuh1zKBPwBPBSxljC5npI/m9EdmsIYYZwIvF6I+jypJ8I2oG2EKwwIA457+YyX59P80HbxdmugIvSb1EACE/fQPSBNboIDHvCqrQg++ABGSiIeOKeY3IVAmMpPlJHgLsLZQdXohvFsXclTUzTDhswDAwELOQjk66mbkRFlFzGOWv/4FXQNwt0z2m5egT8rTBanHCWVVfa8mcCvKw4HKSBY2u6WcxTISTKO0ggAhyXeiboQJnwUABpQvR92EHG0AZsrMAgw9A/okg3D77PMnfNdPa7Kq65kC1rW8YFUJSoyvByuEwz01J/sqdwUBpTQdMENb7KTAcmcBwACnLTQCx0bdjhxsIUmjzOL5gtVYx4EES5r1iEzkbl/NyUJ5jgAAMpEWhP/kXUC+5zkEJGexDC25IGCuqiWLK2cWAJhS2vffBrxNZvNoQWsVDgx4/VxPLcm2vsIFAFLYACBVZ5ARq6mqxL21JQcyi+dLLAg4loW8PepGmPBYADCA6QLOAN4cdTuylEC4QJr4d8FrViYGuHYpE/ibx9b0bytLcQmHwpcs3BRAmkzgXuCfeV5ew8oA/54BlVwQoHxX77GEceXKAoCBTEsm6YeiXCiN3BpJ7cL4ANfOFQl/lfxuVZ5LJ7CsAFUlkZCTAPUtyGf3UG+tyIPM4nninAElkXHvQHbwvqgbYcJhAcAApfM5CTgt6nZkRbhMZkV6Xnm+T4xr6OAPXluSvUJMA7xYkC2YvZBJ3A/8Pa+LI1oH0JPMYCnCWyiFkQDhazqPqqibYfyzAGCgkoCrqQvnq9LIDyNuQ77H0X5PDqHTa0uyV4gAoPDz/z3FmQN053Fl5AEAgDTyHMLpFH8QMJYGPhh1I4x/FgAMQNrCCQjTo25HFn4sTR5SwAagi6gExuRx6TqqPRxgky8twFZALfz8f08ynqUoN+dxaaRTAD31CAIKsqU1b8qXtIXqqJth/LIAYGAqhbn/m2jkkqgbwd6MgzxWjQtzZX/avLcnW4VIBhSLeAQAIMlXgR05XnWgLi6eIe1UEDCN4g4CDkD4SNSNMH5ZADDA6EKOABqjbkc//k4tHxUpaE773uW3A+BVavm597bkYhargK0h1xJ5ACAHsRbN+XjlSqoDbu30rCSCAOXLOo/aqJth/LEAYKBJcjnBktqES7iHWs6TaXnN7fqnecz/K1fLqJyfSr1KBU/hHjwURQ6A3lQwF9iY41VFsQ6gJ2nkOWA6xRsE7McgLoq6EcYfCwAGEL2D8cA7o25HBo/QydkyjfaoG7KT5BwAvEYnPwulLbkLenpeJkm6CrLVsF8ynlbgqhwvK7oAAECaeJY4J6OsirotvRIu19upi7oZxg8LAAaSbj4HRZvU43nizJazQx+2zlWuAcDVcgjbQmlJrpTHQyx7tZwV7SjHbuJcR26nIBbt0dcyg1XANIpgiqUX+xLnE1E3wvhhAcAAoXcwEinSrTzKKhKcLjNYH3VT3iC3KYCNdPPT0NqSqziPhVa2sDS0svMg42lH+FrWF2jg0x1DJbNYTZxpFCahU64u19toiLoRJjgLAAaKBJdAUQ7dvYQyXc4q0uNSc5kCEP6fTCmiEYxqniKslMDCklDKDWICN6E8mdV7hYmpLZ5FS2awhi5OgQKe7pid4VTyyagbYYKzAGAA0DsYBHw86nb0Yj1J3iqzWRl1Q3qjSxgGWT/pbKSLn4TZnlyl1lKE86SeLL4AIJVyOdstrpUMi+5MgGzJ2bxKnDOyDmwK5/Pawl5RN8IEYwHAQNDNx4BhUTdjD6+R5AyZXXwdyU4VjM36vcoPiurpf5dwpgFixTUFkCaTuR2yPDAqUdzTAGkyg/Uo01AejrotPQxDiyBPhwnEAoAyp4uoRPhM1O3Yw2aURplddEObuxMOyOp9yibgx+E2Jk9hLQTsLs4AAIAkn4UsDmASDgq/MX7IbF6nircCD0Tdlp2Ey3Q+e0fdDJM/CwDK3atcAFl2ZIWxnRhnySwWRd2QfknWIwDfl8lsCbUt+QpnIeAGOYvXQijXCzmQJ4Df9fvGZOkEAAByJpuJcyb5H4Xs25AifLgwObAAoIypIgiXRd2OHtoQZstM7o26IVnKJgB4jUTOmegKp5rHyeZpODfFO22zy5egn1TMMYYXpin+yAy2E+dtwN1RtwUA4bO6kKFRN8PkxwKActbC2yiehCedCO+URv4VdUOylsxq5GRukc79AyDT2IbvrWRa/AGATGIN8IOMb1JaC9Mav2QG24FZ5Hscsl+DUT4ddSNMfiwAKGcxvhB1E1K6EN4ljbRE3ZCcCIP7eccr1PF/BWlLMH7XARRZDoA+dXMVZNxe+odCNcU3aaKDbbwL+GvUbUG5xPIClCYLAMqUNnMqyklRtwNIIHxAGoviaSVX/c1zfzfqnP9Z8h0AFP0IAIBMYSsxzkHY3ssff08msaDgjfJIzqWTkZwH/DHipgylqii3GZt+WABQroQ5UTcBUJSPS2OJPmkJv83wZ6vQosn5n1nM80LA7tIIAABkAg+TZArw/4Bm4Oco02RS0YyOBSLH0EUj5wPXRtoQ5TI7KbD0FO+pcCZvOp9DifEU0f77KsInpJEbImxDYLqcq4HP7/4/2YTSJAfyUDStyo0uZChJXsPP52EbjexVFEc1m91oC3OAuZE1QLhYGrk+svpNzmwEoBzF+F+iDu6EOaXe+QOknhRPBq4D/ojwNZIcVCqdP4DMZBP+DpZ52jr/4iRNXJXalhfNv49yuc6jKpK6TV6K9WQ4kydtYQRwfsTNmCONfC/iNngjk7gfuD/qdgSiPIgwyUNJxZaS1vQgjfxIW2gFfknh7+/708D5wK8LXK/Jk40AlJ9PADUR1v9laeLqCOs3vXvQUzlPeSrHhESauBnlnUB7wStXvqTziBe8XpMXCwDKSGr4LcrVuF+SJr4dYf2mL+opAEjaCEApkFncRpImKHiOikkM4h0FrtPkyQKAclLPu4H9Iqr9S9LEdyKq2/RnX56CwFsWlViRn99gdpLZ3INwBrCxwFVfoWoLzEuBBQDl5VMR1WudhvXiewAABXBJREFUf5GTY+hCeTRgMSulqUjPPDC9kkYeAU4lc0Ikz5VyBHfQVLD6TN4sACgT2sypwLERVG2df+kIOg1g8/8lSJp4ljinAMsLVmmSrxSsLpM3CwDKRxSnclnnX0piAQMAsfn/UiUzWIVyCoUL4o7X+UwrUF0mTxYAlAFtZizC2YWtlCus8y8xEvAsebUAoJTJLNaR5C0Q8HOQrRhfKkg9Jm8WAJSDGJdAAbfeKFfILL5bsPqMFzKTV4AX8y4gaVMApU5m8zq1vBX4RwGqO0NbOKEA9Zg8WQBQ4vQ2GlA+UrgKrfMvaZL3NMBWZrHKa1tMJGQa2xjJbODmAlR3RQHqMHmyAKDUVfEh6PfYWj+s8y99ybxTGD9lKYDLR+oQoQ+ifD3kqmbrQg4JuQ6TJwsASpgqgvLJwlRmnX9ZiOU5/2vz/2VHBJVZXAl8BOgOqxqSXBZS2SYgCwBK2QJmAweFXIsiXGqdf5mo4XHySRHr+0hhUzSkiRtTqYODJorqywV6B/uHVLYJwAKA0vbZkMtX4DPSyA9DrscUiEyjHbgrx8uSKHeE0R5THGQWt6GcDmwIofhKuvl0COWagCwAKFHazGEQ6j7bbpQPSBPXhViHiYJwTY5X/FmaeCmUtpiiIbN4CDiRMBIGCR/TvzLEe7kmEAsASpXwaQgt33Yn8B6ZxW9CKt9ESBq5G/hZlm9/GQ19pMkUCWliBcopSOC00Xvai2ou8lymCcgCgBKUiqTPD6n4DpRzpYm/hFS+KQa1XAxcBxlX9j9DjNNkFusK1CpTBGQW66jhLcACz0X/r94T6VHlZg8WAJSiaj4IDAqh5O3EmC2zuC2Esk0RkWl0SxOXoBwH/ApYCbQBrwJ3Ah9hJEfJzALmjzdFI5Ur4Gz85grYl3bO81ieCciObCwxqggLWIL/1f+tCE3SWKA0ocaYoqeKsJBvo3zRS4HCPdLI6V7KMoFZAFBidAEzUBZ6LnYTwszU0aHGGLMbbeHDwA1ARcCi2tlGvZxLwkOzTEA2BVBqlIs9l7iOJKdZ52+M6Ys0cSNwDrA9YFE1VNLgoUnGAxsBKCF6OwcQZyX+Dv5ZTYzpNs9rjMmGLuQoktwOjMqziA5G0iDH0OWzXSY/NgJQSmJ8An+d//PEOcU6f2NMtmQmjxHnBMj7ZMhHrPMvHhYAlAhVBOG9nop7lgqmyQzWeCrPGDNApO4bpyB5rEUSrvffIpMvCwBKxUIOBC/5tB8hxinyVl72UJYxZgCSJrZQw1nAT7O/iIXM5I/htcrkygKAUpHIe86tp7vp4gyZySYPZRljBrBULolPAJ+j/9ME/4Vynh0pXVwsACgVwtZA1yu3ArPk7IDlGGNMD9LE94FTgH/38scvI1xKLWdKE1sK3DTTD9sFUCK0hWpgE1CX88XCL9jKx23vrTEmTNrCGJQjiVGFsoqHeVKuJBl1u0zvLAAoIdrCz4ALc7zsKhr5og29GWOM6cmmAEpJF18B1mf5bkX4gjRxuXX+xhhj9mQBQAmRs3kVZRb9BwEdKB+RRr5XiHYZY4wpPRYAlBiZxSLgaOCP0MucvnA/yptlFr8qdNuMMcaUDlsDUMK0mX0RTkMYRZJWlAdlNkuibpcxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHGGGOMMaZo/X/BUewfDO2dLgAAAABJRU5ErkJggg==";
|
|
5955
|
+
|
|
5956
|
+
// src/assets/light-gizmos/bulb.png
|
|
5957
|
+
var bulb_default = "data:image/png;base64,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";
|
|
5958
|
+
|
|
5959
|
+
// src/components/BindingBuilder/components/LightHelpers.tsx
|
|
5821
5960
|
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
5822
5961
|
var POSITION_MIN = -100;
|
|
5823
5962
|
var POSITION_MAX = 100;
|
|
5824
5963
|
var SUN_SCREEN_SIZE = 0.03;
|
|
5825
5964
|
var BULB_SCREEN_SIZE = 0.03;
|
|
5826
5965
|
var MIN_LINE_DISTANCE = 0.5;
|
|
5827
|
-
var SUN_ICON_PATH =
|
|
5828
|
-
var BULB_ICON_PATH =
|
|
5966
|
+
var SUN_ICON_PATH = sun_default;
|
|
5967
|
+
var BULB_ICON_PATH = bulb_default;
|
|
5829
5968
|
var RING_RADIUS = 0.4;
|
|
5830
5969
|
var RING_SEGMENTS = 64;
|
|
5831
5970
|
var RING_SCREEN_SIZE = SUN_SCREEN_SIZE * 3;
|
|
5832
5971
|
var RING_COLOR = "#4d7dff";
|
|
5833
5972
|
var LINE_COLOR2 = "#ffcc00";
|
|
5834
5973
|
var LINE_GLOW_RADIUS = 0.02;
|
|
5835
|
-
var UP_AXIS = new
|
|
5974
|
+
var UP_AXIS = new import_three20.Vector3(0, 1, 0);
|
|
5836
5975
|
var AXIS_ARROW_LENGTH = 1.4;
|
|
5837
5976
|
var AXIS_CONE_LENGTH = 0.35;
|
|
5838
5977
|
var AXIS_CONE_RADIUS = 0.13;
|
|
@@ -5844,17 +5983,17 @@ var AXIS_COLORS = {
|
|
|
5844
5983
|
z: "#4444ff"
|
|
5845
5984
|
};
|
|
5846
5985
|
function axisVector(axis) {
|
|
5847
|
-
return new
|
|
5986
|
+
return new import_three20.Vector3(axis === "x" ? 1 : 0, axis === "y" ? 1 : 0, axis === "z" ? 1 : 0);
|
|
5848
5987
|
}
|
|
5849
5988
|
function computeAxisDragPlaneNormal(axis, eye) {
|
|
5850
5989
|
const axisVec = axisVector(axis);
|
|
5851
5990
|
if (eye.lengthSq() < 1e-8) {
|
|
5852
5991
|
eye = axisVec.clone();
|
|
5853
5992
|
}
|
|
5854
|
-
let normal = new
|
|
5993
|
+
let normal = new import_three20.Vector3().crossVectors(axisVec, new import_three20.Vector3().crossVectors(eye, axisVec));
|
|
5855
5994
|
if (normal.lengthSq() < 1e-8) {
|
|
5856
|
-
const helper = Math.abs(axisVec.y) < 0.9 ? UP_AXIS : new
|
|
5857
|
-
normal = new
|
|
5995
|
+
const helper = Math.abs(axisVec.y) < 0.9 ? UP_AXIS : new import_three20.Vector3(1, 0, 0);
|
|
5996
|
+
normal = new import_three20.Vector3().crossVectors(axisVec, helper);
|
|
5858
5997
|
}
|
|
5859
5998
|
return normal.normalize();
|
|
5860
5999
|
}
|
|
@@ -5926,18 +6065,18 @@ function LightHelpers({
|
|
|
5926
6065
|
const shaftYRef = (0, import_react22.useRef)(null);
|
|
5927
6066
|
const shaftZRef = (0, import_react22.useRef)(null);
|
|
5928
6067
|
const axisDragRef = (0, import_react22.useRef)({ dragging: false, axis: null });
|
|
5929
|
-
const axisDragPlaneRef = (0, import_react22.useRef)(new
|
|
5930
|
-
const dragStartPosRef = (0, import_react22.useRef)(new
|
|
6068
|
+
const axisDragPlaneRef = (0, import_react22.useRef)(new import_three20.Plane());
|
|
6069
|
+
const dragStartPosRef = (0, import_react22.useRef)(new import_three20.Vector3());
|
|
5931
6070
|
const dragLineRef = (0, import_react22.useRef)(null);
|
|
5932
6071
|
const dragLineGeomRef = (0, import_react22.useRef)(null);
|
|
5933
|
-
const currentPosRef = (0, import_react22.useRef)(new
|
|
6072
|
+
const currentPosRef = (0, import_react22.useRef)(new import_three20.Vector3());
|
|
5934
6073
|
const isDraggingRef = (0, import_react22.useRef)(false);
|
|
5935
|
-
const dragPlaneRef = (0, import_react22.useRef)(new
|
|
6074
|
+
const dragPlaneRef = (0, import_react22.useRef)(new import_three20.Plane());
|
|
5936
6075
|
const onDraggingChangeRef = (0, import_react22.useRef)(onDraggingChange);
|
|
5937
6076
|
(0, import_react22.useEffect)(() => {
|
|
5938
6077
|
onDraggingChangeRef.current = onDraggingChange;
|
|
5939
6078
|
}, [onDraggingChange]);
|
|
5940
|
-
const lightPos = (0, import_react22.useMemo)(() => new
|
|
6079
|
+
const lightPos = (0, import_react22.useMemo)(() => new import_three20.Vector3(position[0], position[1], position[2]), [position]);
|
|
5941
6080
|
const linePositions = (0, import_react22.useMemo)(() => new Float32Array(6), []);
|
|
5942
6081
|
const dragLinePositions = (0, import_react22.useMemo)(() => new Float32Array(6), []);
|
|
5943
6082
|
const ringXPositions = (0, import_react22.useMemo)(() => makeRingPositions("x"), []);
|
|
@@ -5947,7 +6086,7 @@ function LightHelpers({
|
|
|
5947
6086
|
const iconPath = isPointLight ? BULB_ICON_PATH : SUN_ICON_PATH;
|
|
5948
6087
|
const iconScreenSize = isPointLight ? BULB_SCREEN_SIZE : SUN_SCREEN_SIZE;
|
|
5949
6088
|
const sunTexture = (0, import_react22.useMemo)(() => {
|
|
5950
|
-
const loader = new
|
|
6089
|
+
const loader = new import_three20.TextureLoader();
|
|
5951
6090
|
return loader.load(iconPath);
|
|
5952
6091
|
}, [iconPath]);
|
|
5953
6092
|
const activeGizmoId = (0, import_react22.useSyncExternalStore)(subscribeActiveGizmo, getActiveGizmoSnapshot, getActiveGizmoSnapshot);
|
|
@@ -6054,7 +6193,7 @@ function LightHelpers({
|
|
|
6054
6193
|
(0, import_react22.useEffect)(() => {
|
|
6055
6194
|
const getMouseNdc = (event) => {
|
|
6056
6195
|
const rect = gl.domElement.getBoundingClientRect();
|
|
6057
|
-
return new
|
|
6196
|
+
return new import_three20.Vector2(
|
|
6058
6197
|
(event.clientX - rect.left) / rect.width * 2 - 1,
|
|
6059
6198
|
-((event.clientY - rect.top) / rect.height) * 2 + 1
|
|
6060
6199
|
);
|
|
@@ -6096,12 +6235,12 @@ function LightHelpers({
|
|
|
6096
6235
|
const axis = axisDragRef.current.axis;
|
|
6097
6236
|
const currentPos = currentPosRef.current;
|
|
6098
6237
|
const axisVec = axisVector(axis);
|
|
6099
|
-
const eye = new
|
|
6238
|
+
const eye = new import_three20.Vector3().subVectors(camera.position, currentPos);
|
|
6100
6239
|
const planeNormal = computeAxisDragPlaneNormal(axis, eye);
|
|
6101
6240
|
axisDragPlaneRef.current.setFromNormalAndCoplanarPoint(planeNormal, currentPos);
|
|
6102
6241
|
const mouse2 = getMouseNdc(event);
|
|
6103
6242
|
raycaster.setFromCamera(mouse2, camera);
|
|
6104
|
-
const intersection2 = new
|
|
6243
|
+
const intersection2 = new import_three20.Vector3();
|
|
6105
6244
|
const hitPoint2 = raycaster.ray.intersectPlane(axisDragPlaneRef.current, intersection2);
|
|
6106
6245
|
if (!hitPoint2) return;
|
|
6107
6246
|
const toPoint = hitPoint2.clone().sub(currentPos);
|
|
@@ -6120,7 +6259,7 @@ function LightHelpers({
|
|
|
6120
6259
|
if (!isDraggingRef.current) return;
|
|
6121
6260
|
const mouse = getMouseNdc(event);
|
|
6122
6261
|
raycaster.setFromCamera(mouse, camera);
|
|
6123
|
-
const intersection = new
|
|
6262
|
+
const intersection = new import_three20.Vector3();
|
|
6124
6263
|
const hitPoint = raycaster.ray.intersectPlane(dragPlaneRef.current, intersection);
|
|
6125
6264
|
if (!hitPoint) return;
|
|
6126
6265
|
const newPos = [
|
|
@@ -6167,7 +6306,7 @@ function LightHelpers({
|
|
|
6167
6306
|
setActiveTab?.("scene");
|
|
6168
6307
|
setActiveSubTab?.("Lighting");
|
|
6169
6308
|
}
|
|
6170
|
-
const cameraDir = new
|
|
6309
|
+
const cameraDir = new import_three20.Vector3();
|
|
6171
6310
|
camera.getWorldDirection(cameraDir);
|
|
6172
6311
|
dragPlaneRef.current.setFromNormalAndCoplanarPoint(cameraDir, currentPosRef.current);
|
|
6173
6312
|
isDraggingRef.current = true;
|
|
@@ -6230,7 +6369,7 @@ function LightHelpers({
|
|
|
6230
6369
|
map: sunTexture,
|
|
6231
6370
|
transparent: true,
|
|
6232
6371
|
opacity: 0.9,
|
|
6233
|
-
blending:
|
|
6372
|
+
blending: import_three20.AdditiveBlending,
|
|
6234
6373
|
depthWrite: false
|
|
6235
6374
|
},
|
|
6236
6375
|
iconPath
|
|
@@ -6300,17 +6439,17 @@ function LightHelpers({
|
|
|
6300
6439
|
] }),
|
|
6301
6440
|
gizmoVisible && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("group", { ref: axisGroupRef, children: axisArrows.map((axis) => {
|
|
6302
6441
|
const axisColor = AXIS_COLORS[axis];
|
|
6303
|
-
const end = new
|
|
6442
|
+
const end = new import_three20.Vector3(
|
|
6304
6443
|
axis === "x" ? AXIS_ARROW_LENGTH : 0,
|
|
6305
6444
|
axis === "y" ? AXIS_ARROW_LENGTH : 0,
|
|
6306
6445
|
axis === "z" ? AXIS_ARROW_LENGTH : 0
|
|
6307
6446
|
);
|
|
6308
|
-
const conePos = new
|
|
6447
|
+
const conePos = new import_three20.Vector3(
|
|
6309
6448
|
axis === "x" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0,
|
|
6310
6449
|
axis === "y" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0,
|
|
6311
6450
|
axis === "z" ? AXIS_ARROW_LENGTH - AXIS_CONE_LENGTH / 2 : 0
|
|
6312
6451
|
);
|
|
6313
|
-
const shaftPos = new
|
|
6452
|
+
const shaftPos = new import_three20.Vector3(
|
|
6314
6453
|
axis === "x" ? AXIS_ARROW_LENGTH / 2 : 0,
|
|
6315
6454
|
axis === "y" ? AXIS_ARROW_LENGTH / 2 : 0,
|
|
6316
6455
|
axis === "z" ? AXIS_ARROW_LENGTH / 2 : 0
|
|
@@ -6322,7 +6461,7 @@ function LightHelpers({
|
|
|
6322
6461
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
6323
6462
|
import_drei6.Line,
|
|
6324
6463
|
{
|
|
6325
|
-
points: [new
|
|
6464
|
+
points: [new import_three20.Vector3(0, 0, 0), end],
|
|
6326
6465
|
color: axisColor,
|
|
6327
6466
|
transparent: true,
|
|
6328
6467
|
opacity: 0.9,
|
|
@@ -6350,7 +6489,7 @@ function LightHelpers({
|
|
|
6350
6489
|
color: LINE_COLOR2,
|
|
6351
6490
|
transparent: true,
|
|
6352
6491
|
opacity: 0.05,
|
|
6353
|
-
blending:
|
|
6492
|
+
blending: import_three20.AdditiveBlending,
|
|
6354
6493
|
depthWrite: false,
|
|
6355
6494
|
depthTest: false
|
|
6356
6495
|
}
|
|
@@ -6371,7 +6510,7 @@ function LightHelpers({
|
|
|
6371
6510
|
color: LINE_COLOR2,
|
|
6372
6511
|
transparent: true,
|
|
6373
6512
|
opacity: 0.7,
|
|
6374
|
-
blending:
|
|
6513
|
+
blending: import_three20.AdditiveBlending
|
|
6375
6514
|
}
|
|
6376
6515
|
)
|
|
6377
6516
|
] })
|
|
@@ -6411,7 +6550,7 @@ function findInfluencingBone(hit) {
|
|
|
6411
6550
|
if (!skinIndex || !skinWeight || !position) return null;
|
|
6412
6551
|
const localHit = mesh.worldToLocal(hit.point.clone());
|
|
6413
6552
|
const vertices = [hit.face.a, hit.face.b, hit.face.c];
|
|
6414
|
-
const vertex = new
|
|
6553
|
+
const vertex = new import_three21.Vector3();
|
|
6415
6554
|
let nearest = vertices[0];
|
|
6416
6555
|
let nearestDist = Infinity;
|
|
6417
6556
|
for (const vi of vertices) {
|
|
@@ -6461,9 +6600,9 @@ function GuidedTourCameraFollow({
|
|
|
6461
6600
|
const scene = (0, import_fiber12.useThree)((state) => state.scene);
|
|
6462
6601
|
const nodeRef = (0, import_react23.useRef)(null);
|
|
6463
6602
|
const hasPrev = (0, import_react23.useRef)(false);
|
|
6464
|
-
const live = (0, import_react23.useMemo)(() => new
|
|
6465
|
-
const prev = (0, import_react23.useMemo)(() => new
|
|
6466
|
-
const goal = (0, import_react23.useMemo)(() => new
|
|
6603
|
+
const live = (0, import_react23.useMemo)(() => new import_three21.Vector3(), []);
|
|
6604
|
+
const prev = (0, import_react23.useMemo)(() => new import_three21.Vector3(), []);
|
|
6605
|
+
const goal = (0, import_react23.useMemo)(() => new import_three21.Vector3(), []);
|
|
6467
6606
|
const nodeName = annotation?.anchorNodeName;
|
|
6468
6607
|
const localPosition = annotation?.anchorLocalPosition;
|
|
6469
6608
|
(0, import_react23.useEffect)(() => {
|
|
@@ -6502,8 +6641,8 @@ var DEFAULT_AR_OVERLAY_STATE = {
|
|
|
6502
6641
|
isModelPlaced: false
|
|
6503
6642
|
};
|
|
6504
6643
|
function computeMeasureBox(scene) {
|
|
6505
|
-
const box = new
|
|
6506
|
-
const size = new
|
|
6644
|
+
const box = new import_three21.Box3().setFromObject(scene);
|
|
6645
|
+
const size = new import_three21.Vector3();
|
|
6507
6646
|
box.getSize(size);
|
|
6508
6647
|
return {
|
|
6509
6648
|
min: [box.min.x, box.min.y, box.min.z],
|
|
@@ -6522,18 +6661,18 @@ function computeUvMeshStats(scene) {
|
|
|
6522
6661
|
return stats;
|
|
6523
6662
|
}
|
|
6524
6663
|
function computeModelBounds(scene) {
|
|
6525
|
-
const box = new
|
|
6664
|
+
const box = new import_three21.Box3().setFromObject(scene);
|
|
6526
6665
|
if (box.isEmpty()) return null;
|
|
6527
|
-
const center = box.getCenter(new
|
|
6528
|
-
const radius = box.getSize(new
|
|
6666
|
+
const center = box.getCenter(new import_three21.Vector3());
|
|
6667
|
+
const radius = box.getSize(new import_three21.Vector3()).length() / 2;
|
|
6529
6668
|
if (!Number.isFinite(radius) || radius <= 0) return null;
|
|
6530
6669
|
return { center: [center.x, center.y, center.z], radius };
|
|
6531
6670
|
}
|
|
6532
6671
|
function fitClippingPlanesToScene(scene, controls) {
|
|
6533
6672
|
const camera = controls?.camera;
|
|
6534
|
-
if (!(camera instanceof
|
|
6535
|
-
const box = new
|
|
6536
|
-
const size = box.getSize(new
|
|
6673
|
+
if (!(camera instanceof import_three21.PerspectiveCamera)) return;
|
|
6674
|
+
const box = new import_three21.Box3().setFromObject(scene);
|
|
6675
|
+
const size = box.getSize(new import_three21.Vector3()).length();
|
|
6537
6676
|
if (!Number.isFinite(size) || size <= 0) return;
|
|
6538
6677
|
camera.near = size / 100;
|
|
6539
6678
|
camera.far = size * 100;
|
|
@@ -6611,7 +6750,8 @@ function ModelViewer({
|
|
|
6611
6750
|
showAnnotationNavigation = true,
|
|
6612
6751
|
showAnnotationOnHover = false
|
|
6613
6752
|
}) {
|
|
6614
|
-
const { activeTab, activeSubTab } = useBindingBuilderContext();
|
|
6753
|
+
const { isInsideEditor, activeTab, activeSubTab } = useBindingBuilderContext();
|
|
6754
|
+
const showLightHelpers = isInsideEditor && activeTab === "scene" && activeSubTab === "Lighting";
|
|
6615
6755
|
const license = useLicense(licenseKey);
|
|
6616
6756
|
const resolvedSceneConfig = (0, import_react23.useMemo)(
|
|
6617
6757
|
() => sceneConfig ?? DEFAULT_SCENE_CONFIG,
|
|
@@ -6717,8 +6857,8 @@ function ModelViewer({
|
|
|
6717
6857
|
if (!controls || initialCameraStateRef.current) {
|
|
6718
6858
|
return;
|
|
6719
6859
|
}
|
|
6720
|
-
const position = controls.getPosition(new
|
|
6721
|
-
const target = controls.getTarget(new
|
|
6860
|
+
const position = controls.getPosition(new import_three21.Vector3());
|
|
6861
|
+
const target = controls.getTarget(new import_three21.Vector3());
|
|
6722
6862
|
initialCameraStateRef.current = {
|
|
6723
6863
|
position: [position.x, position.y, position.z],
|
|
6724
6864
|
target: [target.x, target.y, target.z]
|
|
@@ -6976,7 +7116,7 @@ function ModelViewer({
|
|
|
6976
7116
|
const controls = controlsRef.current;
|
|
6977
7117
|
const scene = sceneRef.current;
|
|
6978
7118
|
if (!controls) return;
|
|
6979
|
-
const target = new
|
|
7119
|
+
const target = new import_three21.Vector3();
|
|
6980
7120
|
const livePosition = annotationWorldPositionsRef.current.get(
|
|
6981
7121
|
annotation.id
|
|
6982
7122
|
);
|
|
@@ -6994,15 +7134,15 @@ function ModelViewer({
|
|
|
6994
7134
|
controls.smoothTime = 0.6;
|
|
6995
7135
|
const storedNormal = annotation.worldNormal;
|
|
6996
7136
|
if (scene) {
|
|
6997
|
-
const box = new
|
|
6998
|
-
const modelCenter = new
|
|
7137
|
+
const box = new import_three21.Box3().setFromObject(scene);
|
|
7138
|
+
const modelCenter = new import_three21.Vector3();
|
|
6999
7139
|
box.getCenter(modelCenter);
|
|
7000
|
-
const modelSize = new
|
|
7140
|
+
const modelSize = new import_three21.Vector3();
|
|
7001
7141
|
box.getSize(modelSize);
|
|
7002
7142
|
const modelRadius = modelSize.length() * 0.2;
|
|
7003
7143
|
let outwardDir;
|
|
7004
7144
|
if (storedNormal) {
|
|
7005
|
-
outwardDir = new
|
|
7145
|
+
outwardDir = new import_three21.Vector3(
|
|
7006
7146
|
storedNormal[0],
|
|
7007
7147
|
storedNormal[1],
|
|
7008
7148
|
storedNormal[2]
|
|
@@ -7153,6 +7293,16 @@ function ModelViewer({
|
|
|
7153
7293
|
const isControlledBindings = onObjectBindingsChange !== void 0;
|
|
7154
7294
|
const currentObjectBindings = isControlledBindings ? objectBindings : internalObjectBindings;
|
|
7155
7295
|
currentObjectBindingsRef.current = currentObjectBindings;
|
|
7296
|
+
const currentBindingTransformSignature = (0, import_react23.useMemo)(
|
|
7297
|
+
() => Object.entries(currentObjectBindings).map(
|
|
7298
|
+
([bindingKey, binding]) => JSON.stringify([
|
|
7299
|
+
bindingKey,
|
|
7300
|
+
binding.modelObjectId,
|
|
7301
|
+
binding.transform ?? null
|
|
7302
|
+
])
|
|
7303
|
+
).sort().join(""),
|
|
7304
|
+
[currentObjectBindings]
|
|
7305
|
+
);
|
|
7156
7306
|
const currentHiddenObjects = (0, import_react23.useMemo)(
|
|
7157
7307
|
() => Object.fromEntries(
|
|
7158
7308
|
Object.entries(currentObjectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
|
|
@@ -7292,7 +7442,7 @@ function ModelViewer({
|
|
|
7292
7442
|
const fallbackY = Math.max(minOffset, (rect.height - height) / 2);
|
|
7293
7443
|
const objectKey = resolveObjectKey(objectId, binding);
|
|
7294
7444
|
const object = objectKey ? nodeRefsMap.current[objectKey] : null;
|
|
7295
|
-
const
|
|
7445
|
+
const clamp3 = (value, max) => {
|
|
7296
7446
|
if (max <= minOffset) {
|
|
7297
7447
|
return minOffset;
|
|
7298
7448
|
}
|
|
@@ -7300,23 +7450,23 @@ function ModelViewer({
|
|
|
7300
7450
|
};
|
|
7301
7451
|
if (canvasContainer && cameraControls && object) {
|
|
7302
7452
|
const canvasRect = canvasContainer.getBoundingClientRect();
|
|
7303
|
-
const objectCenter = new
|
|
7304
|
-
new
|
|
7453
|
+
const objectCenter = new import_three21.Vector3();
|
|
7454
|
+
new import_three21.Box3().setFromObject(object).getCenter(objectCenter);
|
|
7305
7455
|
objectCenter.project(cameraControls);
|
|
7306
7456
|
const projectedX = (objectCenter.x + 1) / 2 * canvasRect.width + (canvasRect.left - rect.left);
|
|
7307
7457
|
const projectedY = (1 - objectCenter.y) / 2 * canvasRect.height + (canvasRect.top - rect.top);
|
|
7308
7458
|
return {
|
|
7309
7459
|
objectId,
|
|
7310
|
-
x:
|
|
7311
|
-
y:
|
|
7460
|
+
x: clamp3(projectedX + 24, maxX),
|
|
7461
|
+
y: clamp3(projectedY - height / 2, maxY)
|
|
7312
7462
|
};
|
|
7313
7463
|
}
|
|
7314
7464
|
const rawX = typeof fallbackCoords?.screenX === "number" ? fallbackCoords.screenX - rect.left : fallbackX;
|
|
7315
7465
|
const rawY = typeof fallbackCoords?.screenY === "number" ? fallbackCoords.screenY - rect.top : fallbackY;
|
|
7316
7466
|
return {
|
|
7317
7467
|
objectId,
|
|
7318
|
-
x:
|
|
7319
|
-
y:
|
|
7468
|
+
x: clamp3(rawX, maxX),
|
|
7469
|
+
y: clamp3(rawY, maxY)
|
|
7320
7470
|
};
|
|
7321
7471
|
},
|
|
7322
7472
|
[resolveObjectKey]
|
|
@@ -7325,7 +7475,6 @@ function ModelViewer({
|
|
|
7325
7475
|
(event) => {
|
|
7326
7476
|
const { objectId, action, binding } = event;
|
|
7327
7477
|
setCurrentAction(event);
|
|
7328
|
-
onAction?.(event);
|
|
7329
7478
|
switch (action.id) {
|
|
7330
7479
|
case "toggle-visibility": {
|
|
7331
7480
|
let targetKey = objectId;
|
|
@@ -7373,6 +7522,7 @@ function ModelViewer({
|
|
|
7373
7522
|
default:
|
|
7374
7523
|
break;
|
|
7375
7524
|
}
|
|
7525
|
+
onAction?.(event);
|
|
7376
7526
|
},
|
|
7377
7527
|
[
|
|
7378
7528
|
applyObjectBindings,
|
|
@@ -7513,7 +7663,8 @@ function ModelViewer({
|
|
|
7513
7663
|
objectBindings: currentObjectBindingsRef.current,
|
|
7514
7664
|
nodeRefs: nodeRefsMap.current,
|
|
7515
7665
|
homeCameraState: initialCameraStateRef.current,
|
|
7516
|
-
captureImage: (options) => captureImage(rendererCaptureRef.current, options)
|
|
7666
|
+
captureImage: (options) => captureImage(rendererCaptureRef.current, options),
|
|
7667
|
+
invalidate: () => rendererCaptureRef.current?.invalidate()
|
|
7517
7668
|
});
|
|
7518
7669
|
}, []);
|
|
7519
7670
|
const fitScene = (0, import_react23.useCallback)(
|
|
@@ -7525,14 +7676,14 @@ function ModelViewer({
|
|
|
7525
7676
|
}
|
|
7526
7677
|
try {
|
|
7527
7678
|
if (flyIn) {
|
|
7528
|
-
const target = controls.getTarget(new
|
|
7529
|
-
const position = controls.getPosition(new
|
|
7679
|
+
const target = controls.getTarget(new import_three21.Vector3());
|
|
7680
|
+
const position = controls.getPosition(new import_three21.Vector3());
|
|
7530
7681
|
const direction = position.clone().sub(target);
|
|
7531
7682
|
if (direction.lengthSq() < 1e-6) {
|
|
7532
7683
|
direction.set(0, 0, 1);
|
|
7533
7684
|
}
|
|
7534
7685
|
direction.normalize();
|
|
7535
|
-
const sphere = new
|
|
7686
|
+
const sphere = new import_three21.Box3().setFromObject(scene).getBoundingSphere(new import_three21.Sphere());
|
|
7536
7687
|
const farDistance = Math.max(sphere.radius * 10, 20);
|
|
7537
7688
|
const farPosition = target.clone().add(direction.multiplyScalar(farDistance));
|
|
7538
7689
|
await controls.setLookAt(
|
|
@@ -7844,6 +7995,13 @@ function ModelViewer({
|
|
|
7844
7995
|
);
|
|
7845
7996
|
setBindingsMismatch(!hasAnyMatch);
|
|
7846
7997
|
}, [modelLoaded, currentObjectBindings]);
|
|
7998
|
+
(0, import_react23.useEffect)(() => {
|
|
7999
|
+
const scene = sceneRef.current;
|
|
8000
|
+
if (!modelLoaded || !scene) return;
|
|
8001
|
+
scene.updateMatrixWorld(true);
|
|
8002
|
+
setMeasureBox(computeMeasureBox(scene));
|
|
8003
|
+
setModelBounds(computeModelBounds(scene));
|
|
8004
|
+
}, [modelLoaded, currentBindingTransformSignature]);
|
|
7847
8005
|
const clearMeasurements = (0, import_react23.useCallback)(() => setMeasurePoints([]), []);
|
|
7848
8006
|
const handleMeasurePointerDown = (0, import_react23.useCallback)(
|
|
7849
8007
|
(event) => {
|
|
@@ -7869,8 +8027,8 @@ function ModelViewer({
|
|
|
7869
8027
|
const rect = canvas.getBoundingClientRect();
|
|
7870
8028
|
const ndcX = (event.clientX - rect.left) / rect.width * 2 - 1;
|
|
7871
8029
|
const ndcY = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
7872
|
-
const raycaster = new
|
|
7873
|
-
raycaster.setFromCamera(new
|
|
8030
|
+
const raycaster = new import_three21.Raycaster();
|
|
8031
|
+
raycaster.setFromCamera(new import_three21.Vector2(ndcX, ndcY), cam);
|
|
7874
8032
|
const hits = raycaster.intersectObject(scene, true);
|
|
7875
8033
|
if (hits.length === 0) return;
|
|
7876
8034
|
const { point } = hits[0];
|
|
@@ -7887,7 +8045,11 @@ function ModelViewer({
|
|
|
7887
8045
|
);
|
|
7888
8046
|
(0, import_react23.useEffect)(() => {
|
|
7889
8047
|
setMeasurePoints([]);
|
|
7890
|
-
}, [
|
|
8048
|
+
}, [
|
|
8049
|
+
modelUrl,
|
|
8050
|
+
resolvedSceneConfig.model.rotation,
|
|
8051
|
+
currentBindingTransformSignature
|
|
8052
|
+
]);
|
|
7891
8053
|
const handleDownloadMesh = (0, import_react23.useCallback)(() => {
|
|
7892
8054
|
const matRefs = materialRefsOut.current;
|
|
7893
8055
|
const scene = sceneRef.current;
|
|
@@ -7991,8 +8153,8 @@ function ModelViewer({
|
|
|
7991
8153
|
const rect = canvas.getBoundingClientRect();
|
|
7992
8154
|
const ndcX = (e.clientX - rect.left) / rect.width * 2 - 1;
|
|
7993
8155
|
const ndcY = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
|
7994
|
-
const raycaster = new
|
|
7995
|
-
raycaster.setFromCamera(new
|
|
8156
|
+
const raycaster = new import_three21.Raycaster();
|
|
8157
|
+
raycaster.setFromCamera(new import_three21.Vector2(ndcX, ndcY), cam);
|
|
7996
8158
|
const hits = raycaster.intersectObject(scene, true);
|
|
7997
8159
|
if (hits.length === 0) return;
|
|
7998
8160
|
const hit = hits[0];
|
|
@@ -8230,33 +8392,28 @@ function ModelViewer({
|
|
|
8230
8392
|
shadowsEnabled: shadows,
|
|
8231
8393
|
environmentRotation: resolvedSceneConfig.environment.rotation,
|
|
8232
8394
|
environmentConfig: resolvedSceneConfig.environment,
|
|
8233
|
-
modelBounds
|
|
8395
|
+
modelBounds,
|
|
8396
|
+
objectBindings: currentObjectBindings,
|
|
8397
|
+
nodeRefs: nodeRefsMap
|
|
8234
8398
|
}
|
|
8235
8399
|
),
|
|
8236
|
-
(() => {
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8400
|
+
showLightHelpers && resolvedSceneConfig.lighting.lights.map((light) => {
|
|
8401
|
+
if (!light.visible || light.type === "none" || light.type === "hemisphere" || light.attachment) {
|
|
8402
|
+
return null;
|
|
8403
|
+
}
|
|
8404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
8405
|
+
LightHelpers,
|
|
8406
|
+
{
|
|
8407
|
+
light,
|
|
8408
|
+
position: light.position,
|
|
8409
|
+
color: light.color,
|
|
8410
|
+
intensity: light.intensity,
|
|
8411
|
+
visible: light.visible,
|
|
8412
|
+
onPositionChange: (newPosition) => handleLightPositionChange(light.id, newPosition)
|
|
8413
|
+
},
|
|
8414
|
+
light.id
|
|
8240
8415
|
);
|
|
8241
|
-
|
|
8242
|
-
if (light.type === "none" || light.type === "hemisphere") {
|
|
8243
|
-
return null;
|
|
8244
|
-
}
|
|
8245
|
-
const uniqueKey = light.id;
|
|
8246
|
-
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
8247
|
-
LightHelpers,
|
|
8248
|
-
{
|
|
8249
|
-
light,
|
|
8250
|
-
position: light.position,
|
|
8251
|
-
color: light.color,
|
|
8252
|
-
intensity: light.intensity,
|
|
8253
|
-
visible: light.visible,
|
|
8254
|
-
onPositionChange: (newPos) => handleLightPositionChange(light.id, newPos)
|
|
8255
|
-
},
|
|
8256
|
-
uniqueKey
|
|
8257
|
-
);
|
|
8258
|
-
});
|
|
8259
|
-
})()
|
|
8416
|
+
})
|
|
8260
8417
|
] }),
|
|
8261
8418
|
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
8262
8419
|
Controls,
|
|
@@ -8883,7 +9040,7 @@ function ModelViewer({
|
|
|
8883
9040
|
}
|
|
8884
9041
|
|
|
8885
9042
|
// src/components/BindingBuilder/BindingBuilder.tsx
|
|
8886
|
-
var
|
|
9043
|
+
var import_react42 = require("react");
|
|
8887
9044
|
var import_lucide_react16 = require("lucide-react");
|
|
8888
9045
|
|
|
8889
9046
|
// src/styles/BindingBuilder.css
|
|
@@ -8891,32 +9048,13 @@ styleInject('.bb-root {\n --bb-font-family:\n Inter,\n "Segoe UI",\n H
|
|
|
8891
9048
|
|
|
8892
9049
|
// src/hooks/useViewerCamera.ts
|
|
8893
9050
|
var import_react24 = require("react");
|
|
8894
|
-
var
|
|
8895
|
-
|
|
8896
|
-
// src/utils/resolveBindingKey.ts
|
|
8897
|
-
function resolveBindingKey(objectBindings, objectId) {
|
|
8898
|
-
if (objectBindings[objectId]) {
|
|
8899
|
-
return objectId;
|
|
8900
|
-
}
|
|
8901
|
-
const idMatch = Object.entries(objectBindings).find(
|
|
8902
|
-
([, binding]) => binding.id === objectId
|
|
8903
|
-
)?.[0];
|
|
8904
|
-
if (idMatch) {
|
|
8905
|
-
return idMatch;
|
|
8906
|
-
}
|
|
8907
|
-
const modelObjectMatch = Object.entries(objectBindings).find(
|
|
8908
|
-
([, binding]) => binding.modelObjectId === objectId
|
|
8909
|
-
)?.[0];
|
|
8910
|
-
return modelObjectMatch ?? null;
|
|
8911
|
-
}
|
|
8912
|
-
|
|
8913
|
-
// src/hooks/useViewerCamera.ts
|
|
9051
|
+
var import_three22 = require("three");
|
|
8914
9052
|
function toTuple(vector) {
|
|
8915
9053
|
return [vector.x, vector.y, vector.z];
|
|
8916
9054
|
}
|
|
8917
9055
|
function buildCameraState(camera, controls) {
|
|
8918
|
-
const position = new
|
|
8919
|
-
const target = new
|
|
9056
|
+
const position = new import_three22.Vector3();
|
|
9057
|
+
const target = new import_three22.Vector3();
|
|
8920
9058
|
controls.getPosition(position);
|
|
8921
9059
|
controls.getTarget(target);
|
|
8922
9060
|
return {
|
|
@@ -8943,11 +9081,16 @@ async function runSmoothCameraTransition2(controls, action, smoothTime = 0.6) {
|
|
|
8943
9081
|
controls.smoothTime = originalSmoothTime;
|
|
8944
9082
|
}
|
|
8945
9083
|
}
|
|
8946
|
-
function useViewerCamera() {
|
|
9084
|
+
function useViewerCamera(options = {}) {
|
|
9085
|
+
const optionsRef = (0, import_react24.useRef)(options);
|
|
9086
|
+
optionsRef.current = options;
|
|
8947
9087
|
const viewerRef = (0, import_react24.useRef)(null);
|
|
8948
9088
|
const [cameraState, setCameraStateValue] = (0, import_react24.useState)(
|
|
8949
9089
|
null
|
|
8950
9090
|
);
|
|
9091
|
+
const [cameraPresets, setCameraPresets] = (0, import_react24.useState)(() => ({ ...options.presets }));
|
|
9092
|
+
const cameraPresetsRef = (0, import_react24.useRef)(cameraPresets);
|
|
9093
|
+
cameraPresetsRef.current = cameraPresets;
|
|
8951
9094
|
const updateCameraState = (0, import_react24.useCallback)((nextState) => {
|
|
8952
9095
|
setCameraStateValue(
|
|
8953
9096
|
(previousState) => isSameCameraState(previousState, nextState) ? previousState : nextState
|
|
@@ -8961,16 +9104,56 @@ function useViewerCamera() {
|
|
|
8961
9104
|
);
|
|
8962
9105
|
const homeCameraStateRef = (0, import_react24.useRef)(null);
|
|
8963
9106
|
const homeSceneRef = (0, import_react24.useRef)(null);
|
|
9107
|
+
const initializedControlsRef = (0, import_react24.useRef)(null);
|
|
8964
9108
|
const handleViewerReady = (0, import_react24.useCallback)((viewer) => {
|
|
8965
9109
|
viewerRef.current = viewer;
|
|
8966
|
-
|
|
9110
|
+
const currentState = buildCameraState(
|
|
9111
|
+
viewer.controls.camera,
|
|
9112
|
+
viewer.controls
|
|
9113
|
+
);
|
|
9114
|
+
updateCameraState(currentState);
|
|
8967
9115
|
if (viewer.scene !== homeSceneRef.current) {
|
|
8968
9116
|
homeSceneRef.current = viewer.scene;
|
|
8969
9117
|
homeCameraStateRef.current = null;
|
|
9118
|
+
initializedControlsRef.current = null;
|
|
8970
9119
|
}
|
|
8971
9120
|
if (viewer.homeCameraState && !homeCameraStateRef.current) {
|
|
8972
9121
|
homeCameraStateRef.current = viewer.homeCameraState;
|
|
8973
9122
|
}
|
|
9123
|
+
const { initialPosition, initialTarget, initialTransition = false } = optionsRef.current;
|
|
9124
|
+
if ((initialPosition || initialTarget) && viewer.scene && // A target-only setup should preserve ModelViewer's fitted or authored
|
|
9125
|
+
// camera position. Wait until that initial framing has settled instead
|
|
9126
|
+
// of cancelling auto-fit while the camera is still at its fallback.
|
|
9127
|
+
(initialPosition || viewer.homeCameraState) && initializedControlsRef.current !== viewer.controls) {
|
|
9128
|
+
initializedControlsRef.current = viewer.controls;
|
|
9129
|
+
if (!viewer.homeCameraState) {
|
|
9130
|
+
markInitialAutoFitOverride(viewer.controls);
|
|
9131
|
+
}
|
|
9132
|
+
const position = initialPosition ?? currentState.position;
|
|
9133
|
+
const target = initialTarget ?? currentState.target;
|
|
9134
|
+
void viewer.controls.setLookAt(
|
|
9135
|
+
position[0],
|
|
9136
|
+
position[1],
|
|
9137
|
+
position[2],
|
|
9138
|
+
target[0],
|
|
9139
|
+
target[1],
|
|
9140
|
+
target[2],
|
|
9141
|
+
initialTransition
|
|
9142
|
+
).then(() => {
|
|
9143
|
+
if (viewerRef.current?.controls !== viewer.controls) {
|
|
9144
|
+
return;
|
|
9145
|
+
}
|
|
9146
|
+
const nextState = buildCameraState(
|
|
9147
|
+
viewer.controls.camera,
|
|
9148
|
+
viewer.controls
|
|
9149
|
+
);
|
|
9150
|
+
homeCameraStateRef.current = {
|
|
9151
|
+
position: nextState.position,
|
|
9152
|
+
target: nextState.target
|
|
9153
|
+
};
|
|
9154
|
+
updateCameraState(nextState);
|
|
9155
|
+
}).catch(() => void 0);
|
|
9156
|
+
}
|
|
8974
9157
|
}, [updateCameraState]);
|
|
8975
9158
|
const resetView = (0, import_react24.useCallback)(async () => {
|
|
8976
9159
|
const viewer = viewerRef.current;
|
|
@@ -9066,27 +9249,33 @@ function useViewerCamera() {
|
|
|
9066
9249
|
return false;
|
|
9067
9250
|
}
|
|
9068
9251
|
try {
|
|
9252
|
+
if (!viewerRef.current?.homeCameraState) {
|
|
9253
|
+
markInitialAutoFitOverride(controls);
|
|
9254
|
+
}
|
|
9069
9255
|
await controls.setTarget(
|
|
9070
9256
|
target[0],
|
|
9071
9257
|
target[1],
|
|
9072
9258
|
target[2],
|
|
9073
9259
|
enableTransition
|
|
9074
9260
|
);
|
|
9261
|
+
updateCameraState(buildCameraState(controls.camera, controls));
|
|
9075
9262
|
return true;
|
|
9076
9263
|
} catch {
|
|
9077
9264
|
return false;
|
|
9078
9265
|
}
|
|
9079
9266
|
},
|
|
9080
|
-
[]
|
|
9267
|
+
[updateCameraState]
|
|
9081
9268
|
);
|
|
9082
|
-
const
|
|
9269
|
+
const applyCameraPreset = (0, import_react24.useCallback)(
|
|
9083
9270
|
async (state, enableTransition = true) => {
|
|
9084
9271
|
const controls = viewerRef.current?.controls;
|
|
9085
9272
|
if (!controls) {
|
|
9086
9273
|
return false;
|
|
9087
9274
|
}
|
|
9088
9275
|
try {
|
|
9089
|
-
|
|
9276
|
+
if (!viewerRef.current?.homeCameraState) {
|
|
9277
|
+
markInitialAutoFitOverride(controls);
|
|
9278
|
+
}
|
|
9090
9279
|
const transitions = [
|
|
9091
9280
|
controls.setLookAt(
|
|
9092
9281
|
state.position[0],
|
|
@@ -9110,11 +9299,11 @@ function useViewerCamera() {
|
|
|
9110
9299
|
await Promise.all(transitions);
|
|
9111
9300
|
const camera = controls.camera;
|
|
9112
9301
|
let projectionChanged = false;
|
|
9113
|
-
if (state.fov
|
|
9302
|
+
if (state.fov != null && "fov" in camera && typeof camera.fov === "number") {
|
|
9114
9303
|
camera.fov = state.fov;
|
|
9115
9304
|
projectionChanged = true;
|
|
9116
9305
|
}
|
|
9117
|
-
if ("zoom" in camera && typeof camera.zoom === "number") {
|
|
9306
|
+
if (state.zoom != null && "zoom" in camera && typeof camera.zoom === "number") {
|
|
9118
9307
|
camera.zoom = state.zoom;
|
|
9119
9308
|
projectionChanged = true;
|
|
9120
9309
|
}
|
|
@@ -9129,13 +9318,94 @@ function useViewerCamera() {
|
|
|
9129
9318
|
},
|
|
9130
9319
|
[updateCameraState]
|
|
9131
9320
|
);
|
|
9321
|
+
const setCameraState = (0, import_react24.useCallback)(
|
|
9322
|
+
(state, enableTransition = true) => applyCameraPreset(state, enableTransition),
|
|
9323
|
+
[applyCameraPreset]
|
|
9324
|
+
);
|
|
9325
|
+
const lookAt = (0, import_react24.useCallback)(
|
|
9326
|
+
(position, target, enableTransition = true) => applyCameraPreset({ position, target }, enableTransition),
|
|
9327
|
+
[applyCameraPreset]
|
|
9328
|
+
);
|
|
9329
|
+
const orbitTo = (0, import_react24.useCallback)(
|
|
9330
|
+
async (azimuthAngle, polarAngle, enableTransition = true) => {
|
|
9331
|
+
const controls = viewerRef.current?.controls;
|
|
9332
|
+
if (!controls) {
|
|
9333
|
+
return false;
|
|
9334
|
+
}
|
|
9335
|
+
try {
|
|
9336
|
+
if (!viewerRef.current?.homeCameraState) {
|
|
9337
|
+
markInitialAutoFitOverride(controls);
|
|
9338
|
+
}
|
|
9339
|
+
await controls.rotateTo(
|
|
9340
|
+
azimuthAngle,
|
|
9341
|
+
polarAngle,
|
|
9342
|
+
enableTransition
|
|
9343
|
+
);
|
|
9344
|
+
updateCameraState(buildCameraState(controls.camera, controls));
|
|
9345
|
+
return true;
|
|
9346
|
+
} catch {
|
|
9347
|
+
return false;
|
|
9348
|
+
}
|
|
9349
|
+
},
|
|
9350
|
+
[updateCameraState]
|
|
9351
|
+
);
|
|
9352
|
+
const dollyTo = (0, import_react24.useCallback)(
|
|
9353
|
+
async (distance, enableTransition = true) => {
|
|
9354
|
+
const controls = viewerRef.current?.controls;
|
|
9355
|
+
if (!controls) {
|
|
9356
|
+
return false;
|
|
9357
|
+
}
|
|
9358
|
+
try {
|
|
9359
|
+
if (!viewerRef.current?.homeCameraState) {
|
|
9360
|
+
markInitialAutoFitOverride(controls);
|
|
9361
|
+
}
|
|
9362
|
+
await controls.dollyTo(distance, enableTransition);
|
|
9363
|
+
updateCameraState(buildCameraState(controls.camera, controls));
|
|
9364
|
+
return true;
|
|
9365
|
+
} catch {
|
|
9366
|
+
return false;
|
|
9367
|
+
}
|
|
9368
|
+
},
|
|
9369
|
+
[updateCameraState]
|
|
9370
|
+
);
|
|
9371
|
+
const savePreset = (0, import_react24.useCallback)((name) => {
|
|
9372
|
+
const controls = viewerRef.current?.controls;
|
|
9373
|
+
const trimmedName = name.trim();
|
|
9374
|
+
if (!controls || !trimmedName) {
|
|
9375
|
+
return null;
|
|
9376
|
+
}
|
|
9377
|
+
const nextState = buildCameraState(controls.camera, controls);
|
|
9378
|
+
cameraPresetsRef.current = {
|
|
9379
|
+
...cameraPresetsRef.current,
|
|
9380
|
+
[trimmedName]: nextState
|
|
9381
|
+
};
|
|
9382
|
+
setCameraPresets((current) => ({
|
|
9383
|
+
...current,
|
|
9384
|
+
[trimmedName]: nextState
|
|
9385
|
+
}));
|
|
9386
|
+
updateCameraState(nextState);
|
|
9387
|
+
return nextState;
|
|
9388
|
+
}, [updateCameraState]);
|
|
9389
|
+
const goToPreset = (0, import_react24.useCallback)(
|
|
9390
|
+
(name, enableTransition = true) => {
|
|
9391
|
+
const preset = cameraPresetsRef.current[name];
|
|
9392
|
+
return preset ? applyCameraPreset(preset, enableTransition) : Promise.resolve(false);
|
|
9393
|
+
},
|
|
9394
|
+
[applyCameraPreset]
|
|
9395
|
+
);
|
|
9132
9396
|
return {
|
|
9133
9397
|
cameraState,
|
|
9398
|
+
cameraPresets,
|
|
9134
9399
|
resetView,
|
|
9135
9400
|
focusObject,
|
|
9136
9401
|
fitScene,
|
|
9137
9402
|
setCameraTarget,
|
|
9138
9403
|
setCameraState,
|
|
9404
|
+
lookAt,
|
|
9405
|
+
orbitTo,
|
|
9406
|
+
dollyTo,
|
|
9407
|
+
savePreset,
|
|
9408
|
+
goToPreset,
|
|
9139
9409
|
handleCameraChange,
|
|
9140
9410
|
handleViewerReady
|
|
9141
9411
|
};
|
|
@@ -14992,6 +15262,9 @@ function IdentityPanel({
|
|
|
14992
15262
|
] }) });
|
|
14993
15263
|
}
|
|
14994
15264
|
|
|
15265
|
+
// src/components/BindingBuilder/components/BasicsPanel.tsx
|
|
15266
|
+
var import_react38 = require("react");
|
|
15267
|
+
|
|
14995
15268
|
// src/components/BindingBuilder/utils/constants.ts
|
|
14996
15269
|
var typeSuggestions = [
|
|
14997
15270
|
"other",
|
|
@@ -15031,6 +15304,31 @@ function BasicsPanel({
|
|
|
15031
15304
|
onToggle,
|
|
15032
15305
|
updateBinding
|
|
15033
15306
|
}) {
|
|
15307
|
+
const [groupDraft, setGroupDraft] = (0, import_react38.useState)(selectedBinding.group ?? "");
|
|
15308
|
+
const [tagsDraft, setTagsDraft] = (0, import_react38.useState)(
|
|
15309
|
+
selectedBinding.tags?.join(", ") ?? ""
|
|
15310
|
+
);
|
|
15311
|
+
(0, import_react38.useEffect)(() => {
|
|
15312
|
+
setGroupDraft(selectedBinding.group ?? "");
|
|
15313
|
+
setTagsDraft(selectedBinding.tags?.join(", ") ?? "");
|
|
15314
|
+
}, [selectedItem, selectedBinding.group, selectedBinding.tags]);
|
|
15315
|
+
const commitGroup = () => {
|
|
15316
|
+
updateBinding(selectedItem, (binding) => ({
|
|
15317
|
+
...binding,
|
|
15318
|
+
group: groupDraft.trim() || void 0
|
|
15319
|
+
}));
|
|
15320
|
+
};
|
|
15321
|
+
const commitTags = () => {
|
|
15322
|
+
const tags = [
|
|
15323
|
+
...new Set(
|
|
15324
|
+
tagsDraft.split(",").map((tag) => tag.trim()).filter(Boolean)
|
|
15325
|
+
)
|
|
15326
|
+
];
|
|
15327
|
+
updateBinding(selectedItem, (binding) => ({
|
|
15328
|
+
...binding,
|
|
15329
|
+
tags: tags.length > 0 ? tags : void 0
|
|
15330
|
+
}));
|
|
15331
|
+
};
|
|
15034
15332
|
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(BBSection, { title: "Basics", open, onToggle, children: [
|
|
15035
15333
|
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-grid", children: [
|
|
15036
15334
|
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
|
|
@@ -15077,6 +15375,38 @@ function BasicsPanel({
|
|
|
15077
15375
|
children: statusOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("option", { value: option, children: option }, option))
|
|
15078
15376
|
}
|
|
15079
15377
|
)
|
|
15378
|
+
] }),
|
|
15379
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
|
|
15380
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: "bb-label", children: "Group" }),
|
|
15381
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
15382
|
+
"input",
|
|
15383
|
+
{
|
|
15384
|
+
className: "bb-input",
|
|
15385
|
+
placeholder: "paint",
|
|
15386
|
+
value: groupDraft,
|
|
15387
|
+
onChange: (event) => setGroupDraft(event.target.value),
|
|
15388
|
+
onBlur: commitGroup,
|
|
15389
|
+
onKeyDown: (event) => {
|
|
15390
|
+
if (event.key === "Enter") event.currentTarget.blur();
|
|
15391
|
+
}
|
|
15392
|
+
}
|
|
15393
|
+
)
|
|
15394
|
+
] }),
|
|
15395
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-field", children: [
|
|
15396
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("label", { className: "bb-label", children: "Tags" }),
|
|
15397
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
15398
|
+
"input",
|
|
15399
|
+
{
|
|
15400
|
+
className: "bb-input",
|
|
15401
|
+
placeholder: "exterior, configurable",
|
|
15402
|
+
value: tagsDraft,
|
|
15403
|
+
onChange: (event) => setTagsDraft(event.target.value),
|
|
15404
|
+
onBlur: commitTags,
|
|
15405
|
+
onKeyDown: (event) => {
|
|
15406
|
+
if (event.key === "Enter") event.currentTarget.blur();
|
|
15407
|
+
}
|
|
15408
|
+
}
|
|
15409
|
+
)
|
|
15080
15410
|
] })
|
|
15081
15411
|
] }),
|
|
15082
15412
|
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "bb-checkboxRow", style: { marginTop: 12 }, children: [
|
|
@@ -15127,7 +15457,7 @@ function BasicsPanel({
|
|
|
15127
15457
|
}
|
|
15128
15458
|
|
|
15129
15459
|
// src/components/BindingBuilder/components/ActionsPanel.tsx
|
|
15130
|
-
var
|
|
15460
|
+
var import_react39 = require("react");
|
|
15131
15461
|
|
|
15132
15462
|
// src/components/BindingBuilder/utils/actionCatalog.ts
|
|
15133
15463
|
var ACTION_CATALOG = [
|
|
@@ -15153,19 +15483,19 @@ var ACTION_CATALOG = [
|
|
|
15153
15483
|
id: "toggle-light",
|
|
15154
15484
|
label: "Toggle Light",
|
|
15155
15485
|
handling: "host",
|
|
15156
|
-
description: "Seeded on light bindings.
|
|
15486
|
+
description: "Seeded on light bindings. Add declarative effects for useViewerActions, or handle it in onAction."
|
|
15157
15487
|
},
|
|
15158
15488
|
{
|
|
15159
15489
|
id: "change-light-color",
|
|
15160
15490
|
label: "Change Light Color",
|
|
15161
15491
|
handling: "host",
|
|
15162
|
-
description: "Seeded on light bindings.
|
|
15492
|
+
description: "Seeded on light bindings. Add declarative effects for useViewerActions, or handle it in onAction."
|
|
15163
15493
|
},
|
|
15164
15494
|
{
|
|
15165
15495
|
id: "set-brightness",
|
|
15166
15496
|
label: "Set Brightness",
|
|
15167
15497
|
handling: "host",
|
|
15168
|
-
description: "Seeded on light bindings.
|
|
15498
|
+
description: "Seeded on light bindings. Add declarative effects for useViewerActions, or handle it in onAction."
|
|
15169
15499
|
}
|
|
15170
15500
|
];
|
|
15171
15501
|
var CUSTOM_ACTION_ID = "__custom__";
|
|
@@ -15226,8 +15556,8 @@ function ActionsPanel({
|
|
|
15226
15556
|
onToggle,
|
|
15227
15557
|
updateBinding
|
|
15228
15558
|
}) {
|
|
15229
|
-
const [customRows, setCustomRows] = (0,
|
|
15230
|
-
(0,
|
|
15559
|
+
const [customRows, setCustomRows] = (0, import_react39.useState)(/* @__PURE__ */ new Set());
|
|
15560
|
+
(0, import_react39.useEffect)(() => {
|
|
15231
15561
|
setCustomRows(/* @__PURE__ */ new Set());
|
|
15232
15562
|
}, [selectedItem]);
|
|
15233
15563
|
const actions = selectedBinding.actions ?? [];
|
|
@@ -15343,8 +15673,8 @@ function ActionsPanel({
|
|
|
15343
15673
|
}
|
|
15344
15674
|
)
|
|
15345
15675
|
] }),
|
|
15346
|
-
catalogEntry ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "bb-note", children: catalogEntry.description }) : trimmedId.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "bb-note", style: { color: "#fca5a5" }, children: "An action ID is required
|
|
15347
|
-
"Unrecognised ID
|
|
15676
|
+
catalogEntry ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "bb-note", children: catalogEntry.description }) : trimmedId.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "bb-note", style: { color: "#fca5a5" }, children: "An action ID is required, the button renders but can never be identified in onAction." }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "bb-note", style: { color: "#fcd34d" }, children: [
|
|
15677
|
+
"Unrecognised ID, the viewer has no built-in behavior for it. Add declarative effects and execute them with useViewerActions, or handle it in onAction.",
|
|
15348
15678
|
suggestion ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
15349
15679
|
" Did you mean ",
|
|
15350
15680
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
@@ -15367,7 +15697,13 @@ function ActionsPanel({
|
|
|
15367
15697
|
"?"
|
|
15368
15698
|
] }) : null
|
|
15369
15699
|
] }),
|
|
15370
|
-
|
|
15700
|
+
action.effects?.length ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "bb-note", children: [
|
|
15701
|
+
action.effects.length,
|
|
15702
|
+
" declarative effect",
|
|
15703
|
+
action.effects.length === 1 ? "" : "s",
|
|
15704
|
+
", execute with useViewerActions."
|
|
15705
|
+
] }) : null,
|
|
15706
|
+
isDuplicate ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "bb-note", style: { color: "#fcd34d" }, children: "Another action on this binding uses the same ID, they will render as two identical buttons." }) : null
|
|
15371
15707
|
] }),
|
|
15372
15708
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "bb-actionFooter", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
15373
15709
|
"button",
|
|
@@ -15516,7 +15852,7 @@ function CameraPositionPanel({
|
|
|
15516
15852
|
}
|
|
15517
15853
|
|
|
15518
15854
|
// src/components/BindingBuilder/components/SceneNodesPanel.tsx
|
|
15519
|
-
var
|
|
15855
|
+
var import_react40 = require("react");
|
|
15520
15856
|
|
|
15521
15857
|
// src/components/BindingBuilder/utils/bindingFactory.ts
|
|
15522
15858
|
function slugify2(value) {
|
|
@@ -15605,9 +15941,9 @@ function SceneNodesPanel({
|
|
|
15605
15941
|
isOpen = true,
|
|
15606
15942
|
onToggle
|
|
15607
15943
|
}) {
|
|
15608
|
-
const [search, setSearch] = (0,
|
|
15609
|
-
const nodeKeys = (0,
|
|
15610
|
-
const filteredNodeKeys = (0,
|
|
15944
|
+
const [search, setSearch] = (0, import_react40.useState)("");
|
|
15945
|
+
const nodeKeys = (0, import_react40.useMemo)(() => Object.keys(bindings), [bindings]);
|
|
15946
|
+
const filteredNodeKeys = (0, import_react40.useMemo)(() => {
|
|
15611
15947
|
const query = search.trim().toLowerCase();
|
|
15612
15948
|
if (!query) return nodeKeys;
|
|
15613
15949
|
return nodeKeys.filter((nodeKey) => {
|
|
@@ -15750,7 +16086,7 @@ function mergeAnimationConfig(sceneConfig, animationDetails) {
|
|
|
15750
16086
|
}
|
|
15751
16087
|
|
|
15752
16088
|
// src/components/BindingBuilder/hooks/useUndoableState.ts
|
|
15753
|
-
var
|
|
16089
|
+
var import_react41 = require("react");
|
|
15754
16090
|
var MAX_HISTORY = 100;
|
|
15755
16091
|
function historyReducer(state, action) {
|
|
15756
16092
|
switch (action.type) {
|
|
@@ -15789,13 +16125,13 @@ function historyReducer(state, action) {
|
|
|
15789
16125
|
}
|
|
15790
16126
|
}
|
|
15791
16127
|
function useUndoableState(initial, coalesceWindowMs = 600) {
|
|
15792
|
-
const [state, dispatch] = (0,
|
|
16128
|
+
const [state, dispatch] = (0, import_react41.useReducer)(historyReducer, {
|
|
15793
16129
|
past: [],
|
|
15794
16130
|
present: initial,
|
|
15795
16131
|
future: []
|
|
15796
16132
|
});
|
|
15797
|
-
const lastSetAt = (0,
|
|
15798
|
-
const setValue = (0,
|
|
16133
|
+
const lastSetAt = (0, import_react41.useRef)(0);
|
|
16134
|
+
const setValue = (0, import_react41.useCallback)(
|
|
15799
16135
|
(updater) => {
|
|
15800
16136
|
const now = Date.now();
|
|
15801
16137
|
const coalesce = now - lastSetAt.current < coalesceWindowMs;
|
|
@@ -15804,15 +16140,15 @@ function useUndoableState(initial, coalesceWindowMs = 600) {
|
|
|
15804
16140
|
},
|
|
15805
16141
|
[coalesceWindowMs]
|
|
15806
16142
|
);
|
|
15807
|
-
const undo = (0,
|
|
16143
|
+
const undo = (0, import_react41.useCallback)(() => {
|
|
15808
16144
|
lastSetAt.current = 0;
|
|
15809
16145
|
dispatch({ type: "undo" });
|
|
15810
16146
|
}, []);
|
|
15811
|
-
const redo = (0,
|
|
16147
|
+
const redo = (0, import_react41.useCallback)(() => {
|
|
15812
16148
|
lastSetAt.current = 0;
|
|
15813
16149
|
dispatch({ type: "redo" });
|
|
15814
16150
|
}, []);
|
|
15815
|
-
const reset = (0,
|
|
16151
|
+
const reset = (0, import_react41.useCallback)((value) => {
|
|
15816
16152
|
lastSetAt.current = 0;
|
|
15817
16153
|
dispatch({ type: "reset", value });
|
|
15818
16154
|
}, []);
|
|
@@ -15836,9 +16172,9 @@ function toBindingsJson(bindings) {
|
|
|
15836
16172
|
}
|
|
15837
16173
|
function toBindingsTypeScript(bindings) {
|
|
15838
16174
|
return [
|
|
15839
|
-
'import
|
|
16175
|
+
'import { defineObjectBindings } from "@liveroom-tech/react-immersive";',
|
|
15840
16176
|
"",
|
|
15841
|
-
`export const objectBindings
|
|
16177
|
+
`export const objectBindings = defineObjectBindings(${JSON.stringify(bindings, null, 2)});`,
|
|
15842
16178
|
""
|
|
15843
16179
|
].join("\n");
|
|
15844
16180
|
}
|
|
@@ -16058,30 +16394,30 @@ function mergeSceneConfigSection(current, incoming) {
|
|
|
16058
16394
|
return next;
|
|
16059
16395
|
}
|
|
16060
16396
|
function BindingBuilder({ licenseKey }) {
|
|
16061
|
-
const modelInputRef = (0,
|
|
16062
|
-
const bindingsInputRef = (0,
|
|
16063
|
-
const sceneConfigInputRef = (0,
|
|
16064
|
-
const [modelUrl, setModelUrl] = (0,
|
|
16065
|
-
const [modelName, setModelName] = (0,
|
|
16066
|
-
const [selectedItem, setSelectedItem] = (0,
|
|
16397
|
+
const modelInputRef = (0, import_react42.useRef)(null);
|
|
16398
|
+
const bindingsInputRef = (0, import_react42.useRef)(null);
|
|
16399
|
+
const sceneConfigInputRef = (0, import_react42.useRef)(null);
|
|
16400
|
+
const [modelUrl, setModelUrl] = (0, import_react42.useState)(null);
|
|
16401
|
+
const [modelName, setModelName] = (0, import_react42.useState)("");
|
|
16402
|
+
const [selectedItem, setSelectedItem] = (0, import_react42.useState)(null);
|
|
16067
16403
|
const bindingsHistory = useUndoableState({});
|
|
16068
16404
|
const bindings = bindingsHistory.value;
|
|
16069
16405
|
const setBindings = bindingsHistory.setValue;
|
|
16070
|
-
const [loading, setLoading] = (0,
|
|
16071
|
-
const [isExporting, setIsExporting] = (0,
|
|
16072
|
-
const [statusMessage, setStatusMessage] = (0,
|
|
16406
|
+
const [loading, setLoading] = (0, import_react42.useState)(false);
|
|
16407
|
+
const [isExporting, setIsExporting] = (0, import_react42.useState)(false);
|
|
16408
|
+
const [statusMessage, setStatusMessage] = (0, import_react42.useState)(
|
|
16073
16409
|
"Load the demo model or upload a GLB/GLTF model to generate bindings."
|
|
16074
16410
|
);
|
|
16075
|
-
const [statusTone, setStatusTone] = (0,
|
|
16076
|
-
const [statusBannerVisible, setStatusBannerVisible] = (0,
|
|
16077
|
-
const [metricsDraft, setMetricsDraft] = (0,
|
|
16078
|
-
const [metadataDraft, setMetadataDraft] = (0,
|
|
16079
|
-
const [metricsError, setMetricsError] = (0,
|
|
16080
|
-
const [metadataError, setMetadataError] = (0,
|
|
16081
|
-
const [isPreviewDragActive, setIsPreviewDragActive] = (0,
|
|
16082
|
-
const [isEditorCollapsed, setIsEditorCollapsed] = (0,
|
|
16083
|
-
const [isNodesCollapsed, setIsNodesCollapsed] = (0,
|
|
16084
|
-
const [openObjectSections, setOpenObjectSections] = (0,
|
|
16411
|
+
const [statusTone, setStatusTone] = (0, import_react42.useState)("default");
|
|
16412
|
+
const [statusBannerVisible, setStatusBannerVisible] = (0, import_react42.useState)(false);
|
|
16413
|
+
const [metricsDraft, setMetricsDraft] = (0, import_react42.useState)("{}");
|
|
16414
|
+
const [metadataDraft, setMetadataDraft] = (0, import_react42.useState)("{}");
|
|
16415
|
+
const [metricsError, setMetricsError] = (0, import_react42.useState)(null);
|
|
16416
|
+
const [metadataError, setMetadataError] = (0, import_react42.useState)(null);
|
|
16417
|
+
const [isPreviewDragActive, setIsPreviewDragActive] = (0, import_react42.useState)(false);
|
|
16418
|
+
const [isEditorCollapsed, setIsEditorCollapsed] = (0, import_react42.useState)(false);
|
|
16419
|
+
const [isNodesCollapsed, setIsNodesCollapsed] = (0, import_react42.useState)(false);
|
|
16420
|
+
const [openObjectSections, setOpenObjectSections] = (0, import_react42.useState)({
|
|
16085
16421
|
identity: true,
|
|
16086
16422
|
basics: true,
|
|
16087
16423
|
material: false,
|
|
@@ -16090,7 +16426,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16090
16426
|
metrics: false,
|
|
16091
16427
|
metadata: false
|
|
16092
16428
|
});
|
|
16093
|
-
const [openMaterialPanels, setOpenMaterialPanels] = (0,
|
|
16429
|
+
const [openMaterialPanels, setOpenMaterialPanels] = (0, import_react42.useState)({
|
|
16094
16430
|
pbr: true,
|
|
16095
16431
|
roughness: false,
|
|
16096
16432
|
emission: false,
|
|
@@ -16106,27 +16442,27 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16106
16442
|
});
|
|
16107
16443
|
const toggleObjectSection = (key) => setOpenObjectSections((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
16108
16444
|
const toggleMaterialPanel = (key) => setOpenMaterialPanels((prev) => ({ ...prev, [key]: !prev[key] }));
|
|
16109
|
-
const [activeEditorTab, setActiveEditorTab] = (0,
|
|
16445
|
+
const [activeEditorTab, setActiveEditorTab] = (0, import_react42.useState)(
|
|
16110
16446
|
"object"
|
|
16111
16447
|
);
|
|
16112
|
-
const [activeSubTab, setActiveSubTab] = (0,
|
|
16113
|
-
const [pbrWorkflow, setPbrWorkflow] = (0,
|
|
16114
|
-
const [materialPanelEnabled, setMaterialPanelEnabled] = (0,
|
|
16115
|
-
const materialPanelItemRef = (0,
|
|
16116
|
-
const [zoomOnSelected, setZoomOnSelected] = (0,
|
|
16117
|
-
const [highlightOnHover, setHighlightOnHover] = (0,
|
|
16118
|
-
const [resetDialogOpen, setResetDialogOpen] = (0,
|
|
16119
|
-
const [resetTarget, setResetTarget] = (0,
|
|
16448
|
+
const [activeSubTab, setActiveSubTab] = (0, import_react42.useState)("General");
|
|
16449
|
+
const [pbrWorkflow, setPbrWorkflow] = (0, import_react42.useState)("metalness");
|
|
16450
|
+
const [materialPanelEnabled, setMaterialPanelEnabled] = (0, import_react42.useState)(createMaterialPanelEnabledState());
|
|
16451
|
+
const materialPanelItemRef = (0, import_react42.useRef)(null);
|
|
16452
|
+
const [zoomOnSelected, setZoomOnSelected] = (0, import_react42.useState)(false);
|
|
16453
|
+
const [highlightOnHover, setHighlightOnHover] = (0, import_react42.useState)(true);
|
|
16454
|
+
const [resetDialogOpen, setResetDialogOpen] = (0, import_react42.useState)(false);
|
|
16455
|
+
const [resetTarget, setResetTarget] = (0, import_react42.useState)(
|
|
16120
16456
|
null
|
|
16121
16457
|
);
|
|
16122
16458
|
const sceneConfigHistory = useUndoableState(DEFAULT_SCENE_CONFIG);
|
|
16123
16459
|
const sceneConfig = sceneConfigHistory.value;
|
|
16124
16460
|
const setSceneConfig = sceneConfigHistory.setValue;
|
|
16125
|
-
const [activeAnnotation, setActiveAnnotation] = (0,
|
|
16126
|
-
const [panelHoveredBinding, setPanelHoveredBinding] = (0,
|
|
16461
|
+
const [activeAnnotation, setActiveAnnotation] = (0, import_react42.useState)(null);
|
|
16462
|
+
const [panelHoveredBinding, setPanelHoveredBinding] = (0, import_react42.useState)(null);
|
|
16127
16463
|
const license = useLicense(licenseKey);
|
|
16128
16464
|
const tier = normalizeTier(license.tier);
|
|
16129
|
-
const bindingBuilderContextValue = (0,
|
|
16465
|
+
const bindingBuilderContextValue = (0, import_react42.useMemo)(
|
|
16130
16466
|
() => ({
|
|
16131
16467
|
isInsideEditor: true,
|
|
16132
16468
|
activeTab: activeEditorTab,
|
|
@@ -16137,11 +16473,11 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16137
16473
|
}),
|
|
16138
16474
|
[activeEditorTab, activeSubTab, tier]
|
|
16139
16475
|
);
|
|
16140
|
-
const uploadedModelRef = (0,
|
|
16141
|
-
const previewDragDepthRef = (0,
|
|
16142
|
-
const loadRequestRef = (0,
|
|
16143
|
-
const pendingLoadRef = (0,
|
|
16144
|
-
const loadedSceneRef = (0,
|
|
16476
|
+
const uploadedModelRef = (0, import_react42.useRef)(null);
|
|
16477
|
+
const previewDragDepthRef = (0, import_react42.useRef)(0);
|
|
16478
|
+
const loadRequestRef = (0, import_react42.useRef)(0);
|
|
16479
|
+
const pendingLoadRef = (0, import_react42.useRef)(null);
|
|
16480
|
+
const loadedSceneRef = (0, import_react42.useRef)(null);
|
|
16145
16481
|
const {
|
|
16146
16482
|
cameraState,
|
|
16147
16483
|
resetView,
|
|
@@ -16151,7 +16487,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16151
16487
|
handleCameraChange,
|
|
16152
16488
|
handleViewerReady
|
|
16153
16489
|
} = useViewerCamera();
|
|
16154
|
-
const handlePreviewWaypoint = (0,
|
|
16490
|
+
const handlePreviewWaypoint = (0, import_react42.useCallback)(
|
|
16155
16491
|
(waypoint) => {
|
|
16156
16492
|
setCameraState(
|
|
16157
16493
|
{
|
|
@@ -16175,7 +16511,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16175
16511
|
position: formatVector3(cameraState.position),
|
|
16176
16512
|
target: formatVector3(cameraState.target)
|
|
16177
16513
|
} : null;
|
|
16178
|
-
const handleAnnotationsChange = (0,
|
|
16514
|
+
const handleAnnotationsChange = (0, import_react42.useCallback)(
|
|
16179
16515
|
(newAnnotations) => {
|
|
16180
16516
|
setSceneConfig((prev) => {
|
|
16181
16517
|
const currentIds = prev.annotations.map((a) => a.id).join(",");
|
|
@@ -16191,7 +16527,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16191
16527
|
},
|
|
16192
16528
|
[]
|
|
16193
16529
|
);
|
|
16194
|
-
const handleActiveAnnotationChange = (0,
|
|
16530
|
+
const handleActiveAnnotationChange = (0, import_react42.useCallback)(
|
|
16195
16531
|
(annotation) => {
|
|
16196
16532
|
setActiveAnnotation(
|
|
16197
16533
|
annotation ? {
|
|
@@ -16201,7 +16537,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16201
16537
|
},
|
|
16202
16538
|
[]
|
|
16203
16539
|
);
|
|
16204
|
-
(0,
|
|
16540
|
+
(0, import_react42.useEffect)(() => {
|
|
16205
16541
|
if (!selectedBinding) {
|
|
16206
16542
|
setMetricsDraft("{}");
|
|
16207
16543
|
setMetadataDraft("{}");
|
|
@@ -16214,7 +16550,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16214
16550
|
setMetricsError(null);
|
|
16215
16551
|
setMetadataError(null);
|
|
16216
16552
|
}, [selectedBinding]);
|
|
16217
|
-
(0,
|
|
16553
|
+
(0, import_react42.useEffect)(() => {
|
|
16218
16554
|
const derived = createMaterialPanelEnabledState(selectedMaterial);
|
|
16219
16555
|
if (materialPanelItemRef.current !== selectedItem) {
|
|
16220
16556
|
materialPanelItemRef.current = selectedItem;
|
|
@@ -16233,12 +16569,12 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16233
16569
|
return changed ? next : prev;
|
|
16234
16570
|
});
|
|
16235
16571
|
}, [selectedItem, selectedMaterial]);
|
|
16236
|
-
(0,
|
|
16572
|
+
(0, import_react42.useEffect)(() => {
|
|
16237
16573
|
return () => {
|
|
16238
16574
|
uploadedModelRef.current?.cleanup();
|
|
16239
16575
|
};
|
|
16240
16576
|
}, []);
|
|
16241
|
-
(0,
|
|
16577
|
+
(0, import_react42.useEffect)(() => {
|
|
16242
16578
|
const handleKeyDown = (event) => {
|
|
16243
16579
|
if (event.key.toLowerCase() !== "z" || !(event.metaKey || event.ctrlKey)) {
|
|
16244
16580
|
return;
|
|
@@ -16259,7 +16595,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16259
16595
|
window.addEventListener("keydown", handleKeyDown);
|
|
16260
16596
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
16261
16597
|
}, [activeEditorTab, bindingsHistory, sceneConfigHistory]);
|
|
16262
|
-
(0,
|
|
16598
|
+
(0, import_react42.useEffect)(() => {
|
|
16263
16599
|
if (loading || statusTone === "default" || statusTone === "success") return;
|
|
16264
16600
|
setStatusBannerVisible(true);
|
|
16265
16601
|
const timeout = window.setTimeout(
|
|
@@ -16268,8 +16604,8 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16268
16604
|
);
|
|
16269
16605
|
return () => window.clearTimeout(timeout);
|
|
16270
16606
|
}, [statusMessage, statusTone, loading]);
|
|
16271
|
-
const nodeKeys = (0,
|
|
16272
|
-
const idError = (0,
|
|
16607
|
+
const nodeKeys = (0, import_react42.useMemo)(() => Object.keys(bindings), [bindings]);
|
|
16608
|
+
const idError = (0, import_react42.useMemo)(() => {
|
|
16273
16609
|
if (!selectedItem || !selectedBinding) return null;
|
|
16274
16610
|
const trimmed = selectedBinding.id.trim();
|
|
16275
16611
|
if (!trimmed) return "ID is required.";
|
|
@@ -16312,7 +16648,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16312
16648
|
});
|
|
16313
16649
|
}
|
|
16314
16650
|
};
|
|
16315
|
-
const applyBindingsFromScene = (0,
|
|
16651
|
+
const applyBindingsFromScene = (0, import_react42.useCallback)(
|
|
16316
16652
|
(scene, nextModelName) => {
|
|
16317
16653
|
const nodeNames = collectNodeNames(scene);
|
|
16318
16654
|
bindingsHistory.reset(buildBindingsFromNodes(nodeNames));
|
|
@@ -16351,7 +16687,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16351
16687
|
bindingsHistory.reset({});
|
|
16352
16688
|
setSelectedItem(null);
|
|
16353
16689
|
};
|
|
16354
|
-
const handleViewerModelLoaded = (0,
|
|
16690
|
+
const handleViewerModelLoaded = (0, import_react42.useCallback)(
|
|
16355
16691
|
(scene) => {
|
|
16356
16692
|
const pending = pendingLoadRef.current;
|
|
16357
16693
|
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
@@ -16361,7 +16697,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16361
16697
|
},
|
|
16362
16698
|
[applyBindingsFromScene]
|
|
16363
16699
|
);
|
|
16364
|
-
const handleViewerLoadError = (0,
|
|
16700
|
+
const handleViewerLoadError = (0, import_react42.useCallback)((error) => {
|
|
16365
16701
|
const pending = pendingLoadRef.current;
|
|
16366
16702
|
if (!pending || pending.requestId !== loadRequestRef.current) return;
|
|
16367
16703
|
pendingLoadRef.current = null;
|
|
@@ -16372,7 +16708,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16372
16708
|
setStatusTone("error");
|
|
16373
16709
|
setLoading(false);
|
|
16374
16710
|
}, []);
|
|
16375
|
-
const handleAnimationsReady = (0,
|
|
16711
|
+
const handleAnimationsReady = (0, import_react42.useCallback)(
|
|
16376
16712
|
(controls) => {
|
|
16377
16713
|
viewerAnimations.handleAnimationsReady(controls);
|
|
16378
16714
|
const details = controls.clipDetails ?? controls.clips.map((sourceName) => ({ sourceName, duration: 0 }));
|
|
@@ -16673,7 +17009,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16673
17009
|
}));
|
|
16674
17010
|
setError(null);
|
|
16675
17011
|
};
|
|
16676
|
-
const handleViewerSelect = (0,
|
|
17012
|
+
const handleViewerSelect = (0, import_react42.useCallback)(
|
|
16677
17013
|
(binding) => {
|
|
16678
17014
|
if (!binding) {
|
|
16679
17015
|
setSelectedItem(null);
|
|
@@ -16686,7 +17022,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16686
17022
|
},
|
|
16687
17023
|
[bindings]
|
|
16688
17024
|
);
|
|
16689
|
-
const handleSceneNodeSelect = (0,
|
|
17025
|
+
const handleSceneNodeSelect = (0, import_react42.useCallback)(
|
|
16690
17026
|
async (nodeKey) => {
|
|
16691
17027
|
setSelectedItem(nodeKey);
|
|
16692
17028
|
setPanelHoveredBinding(null);
|
|
@@ -16694,7 +17030,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16694
17030
|
},
|
|
16695
17031
|
[focusObject]
|
|
16696
17032
|
);
|
|
16697
|
-
const saveCameraPositionForSelectedObject = (0,
|
|
17033
|
+
const saveCameraPositionForSelectedObject = (0, import_react42.useCallback)(() => {
|
|
16698
17034
|
if (!selectedItem || !cameraState) {
|
|
16699
17035
|
return;
|
|
16700
17036
|
}
|
|
@@ -16709,7 +17045,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16709
17045
|
cameraState: currentCameraState
|
|
16710
17046
|
}));
|
|
16711
17047
|
}, [selectedItem, cameraState]);
|
|
16712
|
-
const goToSavedCameraPosition = (0,
|
|
17048
|
+
const goToSavedCameraPosition = (0, import_react42.useCallback)(() => {
|
|
16713
17049
|
if (!savedCameraPosition?.position || !savedCameraPosition?.target) return;
|
|
16714
17050
|
setCameraState(
|
|
16715
17051
|
{
|
|
@@ -16721,28 +17057,28 @@ function BindingBuilder({ licenseKey }) {
|
|
|
16721
17057
|
true
|
|
16722
17058
|
);
|
|
16723
17059
|
}, [savedCameraPosition, setCameraState, cameraState]);
|
|
16724
|
-
const clearSavedCameraPosition = (0,
|
|
17060
|
+
const clearSavedCameraPosition = (0, import_react42.useCallback)(() => {
|
|
16725
17061
|
if (!selectedItem) return;
|
|
16726
17062
|
updateBinding(selectedItem, (binding) => ({
|
|
16727
17063
|
...binding,
|
|
16728
17064
|
cameraState: void 0
|
|
16729
17065
|
}));
|
|
16730
17066
|
}, [selectedItem]);
|
|
16731
|
-
const handleResetView = (0,
|
|
17067
|
+
const handleResetView = (0, import_react42.useCallback)(async () => {
|
|
16732
17068
|
await resetView();
|
|
16733
17069
|
}, [resetView]);
|
|
16734
|
-
const handleFitScene = (0,
|
|
17070
|
+
const handleFitScene = (0, import_react42.useCallback)(async () => {
|
|
16735
17071
|
await fitScene();
|
|
16736
17072
|
}, [fitScene]);
|
|
16737
|
-
const openResetDialog = (0,
|
|
17073
|
+
const openResetDialog = (0, import_react42.useCallback)((target) => {
|
|
16738
17074
|
setResetTarget(target);
|
|
16739
17075
|
setResetDialogOpen(true);
|
|
16740
17076
|
}, []);
|
|
16741
|
-
const closeResetDialog = (0,
|
|
17077
|
+
const closeResetDialog = (0, import_react42.useCallback)(() => {
|
|
16742
17078
|
setResetDialogOpen(false);
|
|
16743
17079
|
setResetTarget(null);
|
|
16744
17080
|
}, []);
|
|
16745
|
-
const executeReset = (0,
|
|
17081
|
+
const executeReset = (0, import_react42.useCallback)(() => {
|
|
16746
17082
|
if (resetTarget === "bindings") {
|
|
16747
17083
|
if (!nodeKeys.length) {
|
|
16748
17084
|
closeResetDialog();
|
|
@@ -17289,7 +17625,7 @@ function BindingBuilder({ licenseKey }) {
|
|
|
17289
17625
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("strong", { children: resetTarget === "bindings" ? "Object Bindings" : "Scene Config" }),
|
|
17290
17626
|
"?"
|
|
17291
17627
|
] }),
|
|
17292
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "bb-reset-details", children: resetTarget === "bindings" ? "This will remove all your custom changes to object properties, materials, actions, camera positions, metrics, and metadata." : "This will restore all scene settings (camera, lighting, environment, background, animations, annotations, etc.) to their default values." }),
|
|
17628
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "bb-reset-details", children: resetTarget === "bindings" ? "This will remove all your custom changes to object properties, groups, tags, transforms, materials, actions, camera positions, metrics, and metadata." : "This will restore all scene settings (camera, lighting, environment, background, animations, annotations, etc.) to their default values." }),
|
|
17293
17629
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "bb-reset-warning", children: "This action cannot be undone!" })
|
|
17294
17630
|
] }),
|
|
17295
17631
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "bb-reset-footer", children: [
|
|
@@ -17301,11 +17637,11 @@ function BindingBuilder({ licenseKey }) {
|
|
|
17301
17637
|
}
|
|
17302
17638
|
|
|
17303
17639
|
// src/components/SimpleModelViewer/SimpleModelViewer.tsx
|
|
17304
|
-
var
|
|
17640
|
+
var import_react43 = require("react");
|
|
17305
17641
|
var import_drei7 = require("@react-three/drei");
|
|
17306
17642
|
var import_fiber13 = require("@react-three/fiber");
|
|
17307
17643
|
var import_fiber14 = require("@react-three/fiber");
|
|
17308
|
-
var
|
|
17644
|
+
var import_three23 = require("three");
|
|
17309
17645
|
var import_three_stdlib4 = require("three-stdlib");
|
|
17310
17646
|
|
|
17311
17647
|
// src/styles/SimpleModelViewer.css
|
|
@@ -17333,15 +17669,15 @@ function isMeshNode2(o) {
|
|
|
17333
17669
|
function applyClippingPlanes(scene, controls) {
|
|
17334
17670
|
if (!controls) return;
|
|
17335
17671
|
const camera = controls.camera;
|
|
17336
|
-
if (!(camera instanceof
|
|
17337
|
-
const size = new
|
|
17672
|
+
if (!(camera instanceof import_three23.PerspectiveCamera)) return;
|
|
17673
|
+
const size = new import_three23.Box3().setFromObject(scene).getSize(new import_three23.Vector3()).length();
|
|
17338
17674
|
if (!Number.isFinite(size) || size <= 0) return;
|
|
17339
17675
|
camera.near = size / 100;
|
|
17340
17676
|
camera.far = size * 100;
|
|
17341
17677
|
camera.updateProjectionMatrix();
|
|
17342
17678
|
controls.maxDistance = size * 10;
|
|
17343
17679
|
}
|
|
17344
|
-
var ModelErrorBoundary2 = class extends
|
|
17680
|
+
var ModelErrorBoundary2 = class extends import_react43.Component {
|
|
17345
17681
|
constructor() {
|
|
17346
17682
|
super(...arguments);
|
|
17347
17683
|
this.state = { hasError: false };
|
|
@@ -17373,12 +17709,12 @@ function InnerModel({
|
|
|
17373
17709
|
meshopt: true
|
|
17374
17710
|
});
|
|
17375
17711
|
const invalidate = (0, import_fiber13.useThree)((state) => state.invalidate);
|
|
17376
|
-
const [hoveredKey, setHoveredKey] = (0,
|
|
17377
|
-
const scene = (0,
|
|
17712
|
+
const [hoveredKey, setHoveredKey] = (0, import_react43.useState)(null);
|
|
17713
|
+
const scene = (0, import_react43.useMemo)(
|
|
17378
17714
|
() => import_three_stdlib4.SkeletonUtils.clone(rawScene),
|
|
17379
17715
|
[rawScene]
|
|
17380
17716
|
);
|
|
17381
|
-
const meshMap = (0,
|
|
17717
|
+
const meshMap = (0, import_react43.useMemo)(() => {
|
|
17382
17718
|
const map = {};
|
|
17383
17719
|
scene.traverse((obj) => {
|
|
17384
17720
|
if (!isMeshNode2(obj)) return;
|
|
@@ -17387,7 +17723,7 @@ function InnerModel({
|
|
|
17387
17723
|
});
|
|
17388
17724
|
return map;
|
|
17389
17725
|
}, [scene]);
|
|
17390
|
-
const perKeyMaterials = (0,
|
|
17726
|
+
const perKeyMaterials = (0, import_react43.useMemo)(() => {
|
|
17391
17727
|
const map = {};
|
|
17392
17728
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
17393
17729
|
const original = Array.isArray(ref.material) ? ref.material[0] : ref.material;
|
|
@@ -17403,14 +17739,14 @@ function InnerModel({
|
|
|
17403
17739
|
}
|
|
17404
17740
|
return map;
|
|
17405
17741
|
}, [meshMap]);
|
|
17406
|
-
(0,
|
|
17742
|
+
(0, import_react43.useEffect)(
|
|
17407
17743
|
() => () => {
|
|
17408
17744
|
Object.values(perKeyMaterials).forEach((m) => m.dispose());
|
|
17409
17745
|
},
|
|
17410
17746
|
[perKeyMaterials]
|
|
17411
17747
|
);
|
|
17412
|
-
const notifiedUrl = (0,
|
|
17413
|
-
(0,
|
|
17748
|
+
const notifiedUrl = (0, import_react43.useRef)(null);
|
|
17749
|
+
(0, import_react43.useEffect)(() => {
|
|
17414
17750
|
if (notifiedUrl.current === url) return;
|
|
17415
17751
|
notifiedUrl.current = url;
|
|
17416
17752
|
scene.updateWorldMatrix(true, true);
|
|
@@ -17418,7 +17754,7 @@ function InnerModel({
|
|
|
17418
17754
|
onMeshesReady(meshMap);
|
|
17419
17755
|
onModelLoaded(scene);
|
|
17420
17756
|
}, [url, scene, meshMap, onMeshesReady, onModelLoaded]);
|
|
17421
|
-
(0,
|
|
17757
|
+
(0, import_react43.useEffect)(() => {
|
|
17422
17758
|
const controller = new AbortController();
|
|
17423
17759
|
void buildSceneBVH(scene, controller.signal);
|
|
17424
17760
|
return () => {
|
|
@@ -17426,26 +17762,26 @@ function InnerModel({
|
|
|
17426
17762
|
disposeSceneBVH(scene);
|
|
17427
17763
|
};
|
|
17428
17764
|
}, [scene]);
|
|
17429
|
-
const pointerRaycastEnabled = (0,
|
|
17765
|
+
const pointerRaycastEnabled = (0, import_react43.useMemo)(
|
|
17430
17766
|
() => !exceedsBvhBudget(scene),
|
|
17431
17767
|
[scene]
|
|
17432
17768
|
);
|
|
17433
|
-
(0,
|
|
17769
|
+
(0, import_react43.useEffect)(() => {
|
|
17434
17770
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
17435
17771
|
nodeRefsOut.current[key] = ref;
|
|
17436
17772
|
}
|
|
17437
17773
|
}, [meshMap, nodeRefsOut]);
|
|
17438
|
-
(0,
|
|
17774
|
+
(0, import_react43.useEffect)(() => {
|
|
17439
17775
|
if (!highlightOnHover && hoveredKey !== null) {
|
|
17440
17776
|
setHoveredKey(null);
|
|
17441
17777
|
}
|
|
17442
17778
|
}, [highlightOnHover, hoveredKey]);
|
|
17443
|
-
(0,
|
|
17779
|
+
(0, import_react43.useEffect)(() => {
|
|
17444
17780
|
return () => {
|
|
17445
17781
|
document.body.style.cursor = "default";
|
|
17446
17782
|
};
|
|
17447
17783
|
}, []);
|
|
17448
|
-
(0,
|
|
17784
|
+
(0, import_react43.useEffect)(() => {
|
|
17449
17785
|
for (const [key, { ref }] of Object.entries(meshMap)) {
|
|
17450
17786
|
ref.visible = !hiddenKeys.has(key);
|
|
17451
17787
|
const mat = perKeyMaterials[key];
|
|
@@ -17515,11 +17851,11 @@ function CameraFitter({
|
|
|
17515
17851
|
refitOnResize,
|
|
17516
17852
|
onInitialFitComplete
|
|
17517
17853
|
}) {
|
|
17518
|
-
const fitted = (0,
|
|
17854
|
+
const fitted = (0, import_react43.useRef)(false);
|
|
17519
17855
|
const invalidate = (0, import_fiber13.useThree)((state) => state.invalidate);
|
|
17520
17856
|
const size = (0, import_fiber13.useThree)((state) => state.size);
|
|
17521
|
-
const resizeTimerRef = (0,
|
|
17522
|
-
(0,
|
|
17857
|
+
const resizeTimerRef = (0, import_react43.useRef)(null);
|
|
17858
|
+
(0, import_react43.useEffect)(() => {
|
|
17523
17859
|
fitted.current = false;
|
|
17524
17860
|
invalidate();
|
|
17525
17861
|
}, [invalidate, scene]);
|
|
@@ -17539,7 +17875,7 @@ function CameraFitter({
|
|
|
17539
17875
|
}).catch(() => {
|
|
17540
17876
|
}).finally(onInitialFitComplete);
|
|
17541
17877
|
});
|
|
17542
|
-
(0,
|
|
17878
|
+
(0, import_react43.useEffect)(() => {
|
|
17543
17879
|
if (!refitOnResize || !fitted.current || !scene || !controlsRef.current) {
|
|
17544
17880
|
return;
|
|
17545
17881
|
}
|
|
@@ -17567,7 +17903,7 @@ function CameraFitter({
|
|
|
17567
17903
|
function ExposureController({ exposure }) {
|
|
17568
17904
|
const gl = (0, import_fiber13.useThree)((state) => state.gl);
|
|
17569
17905
|
const invalidate = (0, import_fiber13.useThree)((state) => state.invalidate);
|
|
17570
|
-
(0,
|
|
17906
|
+
(0, import_react43.useEffect)(() => {
|
|
17571
17907
|
gl.toneMappingExposure = exposure;
|
|
17572
17908
|
invalidate();
|
|
17573
17909
|
}, [gl, exposure, invalidate]);
|
|
@@ -17675,7 +18011,7 @@ function EnvironmentSection({
|
|
|
17675
18011
|
title,
|
|
17676
18012
|
children
|
|
17677
18013
|
}) {
|
|
17678
|
-
const [collapsed, setCollapsed] = (0,
|
|
18014
|
+
const [collapsed, setCollapsed] = (0, import_react43.useState)(false);
|
|
17679
18015
|
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "env-section", children: [
|
|
17680
18016
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
17681
18017
|
"button",
|
|
@@ -17840,8 +18176,8 @@ function SceneObjectsPanel2({
|
|
|
17840
18176
|
onHover,
|
|
17841
18177
|
onToggleVisibility
|
|
17842
18178
|
}) {
|
|
17843
|
-
const [search, setSearch] = (0,
|
|
17844
|
-
const [rowHovered, setRowHovered] = (0,
|
|
18179
|
+
const [search, setSearch] = (0, import_react43.useState)("");
|
|
18180
|
+
const [rowHovered, setRowHovered] = (0, import_react43.useState)(null);
|
|
17845
18181
|
const entries = Object.values(meshKeys).filter(
|
|
17846
18182
|
(m) => m.label.toLowerCase().includes(search.toLowerCase())
|
|
17847
18183
|
);
|
|
@@ -17965,35 +18301,35 @@ function SimpleModelViewer({
|
|
|
17965
18301
|
onModelLoaded,
|
|
17966
18302
|
onLoadError
|
|
17967
18303
|
}) {
|
|
17968
|
-
const cameraControlsRef = (0,
|
|
17969
|
-
const nodeRefsOut = (0,
|
|
17970
|
-
const fileInputRef = (0,
|
|
17971
|
-
const uploadedModelRef = (0,
|
|
17972
|
-
const dragDepthRef = (0,
|
|
17973
|
-
const [loadedScene, setLoadedScene] = (0,
|
|
17974
|
-
const [largeModelTriangles, setLargeModelTriangles] = (0,
|
|
18304
|
+
const cameraControlsRef = (0, import_react43.useRef)(null);
|
|
18305
|
+
const nodeRefsOut = (0, import_react43.useRef)({});
|
|
18306
|
+
const fileInputRef = (0, import_react43.useRef)(null);
|
|
18307
|
+
const uploadedModelRef = (0, import_react43.useRef)(null);
|
|
18308
|
+
const dragDepthRef = (0, import_react43.useRef)(0);
|
|
18309
|
+
const [loadedScene, setLoadedScene] = (0, import_react43.useState)(null);
|
|
18310
|
+
const [largeModelTriangles, setLargeModelTriangles] = (0, import_react43.useState)(
|
|
17975
18311
|
null
|
|
17976
18312
|
);
|
|
17977
|
-
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = (0,
|
|
17978
|
-
const [meshKeys, setMeshKeys] = (0,
|
|
17979
|
-
const [hiddenKeys, setHiddenKeys] = (0,
|
|
17980
|
-
const [selectedKey, setSelectedKey] = (0,
|
|
17981
|
-
const [panelHoveredKey, setPanelHoveredKey] = (0,
|
|
17982
|
-
const [uploadedModelUrl, setUploadedModelUrl] = (0,
|
|
17983
|
-
const [uploadedModelName, setUploadedModelName] = (0,
|
|
18313
|
+
const [largeModelNoticeDismissed, setLargeModelNoticeDismissed] = (0, import_react43.useState)(false);
|
|
18314
|
+
const [meshKeys, setMeshKeys] = (0, import_react43.useState)({});
|
|
18315
|
+
const [hiddenKeys, setHiddenKeys] = (0, import_react43.useState)(/* @__PURE__ */ new Set());
|
|
18316
|
+
const [selectedKey, setSelectedKey] = (0, import_react43.useState)(null);
|
|
18317
|
+
const [panelHoveredKey, setPanelHoveredKey] = (0, import_react43.useState)(null);
|
|
18318
|
+
const [uploadedModelUrl, setUploadedModelUrl] = (0, import_react43.useState)(null);
|
|
18319
|
+
const [uploadedModelName, setUploadedModelName] = (0, import_react43.useState)(
|
|
17984
18320
|
null
|
|
17985
18321
|
);
|
|
17986
|
-
const [modelLoadFailed, setModelLoadFailed] = (0,
|
|
17987
|
-
const [isResolvingUpload, setIsResolvingUpload] = (0,
|
|
17988
|
-
const [initialFitComplete, setInitialFitComplete] = (0,
|
|
17989
|
-
const [isDragActive, setIsDragActive] = (0,
|
|
17990
|
-
const [isPanelOpen, setIsPanelOpen] = (0,
|
|
18322
|
+
const [modelLoadFailed, setModelLoadFailed] = (0, import_react43.useState)(false);
|
|
18323
|
+
const [isResolvingUpload, setIsResolvingUpload] = (0, import_react43.useState)(false);
|
|
18324
|
+
const [initialFitComplete, setInitialFitComplete] = (0, import_react43.useState)(false);
|
|
18325
|
+
const [isDragActive, setIsDragActive] = (0, import_react43.useState)(false);
|
|
18326
|
+
const [isPanelOpen, setIsPanelOpen] = (0, import_react43.useState)(
|
|
17991
18327
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
17992
18328
|
);
|
|
17993
|
-
const [isEnvPanelOpen, setIsEnvPanelOpen] = (0,
|
|
18329
|
+
const [isEnvPanelOpen, setIsEnvPanelOpen] = (0, import_react43.useState)(
|
|
17994
18330
|
() => typeof window === "undefined" ? true : window.innerWidth > 900
|
|
17995
18331
|
);
|
|
17996
|
-
const [envSettings, setEnvSettings] = (0,
|
|
18332
|
+
const [envSettings, setEnvSettings] = (0, import_react43.useState)({
|
|
17997
18333
|
backgroundEnabled,
|
|
17998
18334
|
backgroundColor,
|
|
17999
18335
|
autoRotate,
|
|
@@ -18005,7 +18341,7 @@ function SimpleModelViewer({
|
|
|
18005
18341
|
directionalColor
|
|
18006
18342
|
});
|
|
18007
18343
|
const activeModelUrl = enableModelUpload ? uploadedModelUrl : modelUrl;
|
|
18008
|
-
const resetModelState = (0,
|
|
18344
|
+
const resetModelState = (0, import_react43.useCallback)(() => {
|
|
18009
18345
|
setLoadedScene(null);
|
|
18010
18346
|
setLargeModelTriangles(null);
|
|
18011
18347
|
setLargeModelNoticeDismissed(false);
|
|
@@ -18016,11 +18352,11 @@ function SimpleModelViewer({
|
|
|
18016
18352
|
setModelLoadFailed(false);
|
|
18017
18353
|
setInitialFitComplete(false);
|
|
18018
18354
|
}, []);
|
|
18019
|
-
const revokeUploadedModelUrl = (0,
|
|
18355
|
+
const revokeUploadedModelUrl = (0, import_react43.useCallback)(() => {
|
|
18020
18356
|
uploadedModelRef.current?.cleanup();
|
|
18021
18357
|
uploadedModelRef.current = null;
|
|
18022
18358
|
}, []);
|
|
18023
|
-
const clearUploadedModel = (0,
|
|
18359
|
+
const clearUploadedModel = (0, import_react43.useCallback)(() => {
|
|
18024
18360
|
revokeUploadedModelUrl();
|
|
18025
18361
|
setUploadedModelUrl(null);
|
|
18026
18362
|
setUploadedModelName(null);
|
|
@@ -18049,7 +18385,7 @@ function SimpleModelViewer({
|
|
|
18049
18385
|
resetModelState,
|
|
18050
18386
|
revokeUploadedModelUrl
|
|
18051
18387
|
]);
|
|
18052
|
-
const handleUploadedModel = (0,
|
|
18388
|
+
const handleUploadedModel = (0, import_react43.useCallback)(
|
|
18053
18389
|
async (file) => {
|
|
18054
18390
|
setIsResolvingUpload(true);
|
|
18055
18391
|
try {
|
|
@@ -18068,7 +18404,7 @@ function SimpleModelViewer({
|
|
|
18068
18404
|
},
|
|
18069
18405
|
[onLoadError, resetModelState, revokeUploadedModelUrl]
|
|
18070
18406
|
);
|
|
18071
|
-
const handleFileSelection = (0,
|
|
18407
|
+
const handleFileSelection = (0, import_react43.useCallback)(
|
|
18072
18408
|
(files) => {
|
|
18073
18409
|
const file = files?.[0];
|
|
18074
18410
|
if (!file) return;
|
|
@@ -18076,29 +18412,29 @@ function SimpleModelViewer({
|
|
|
18076
18412
|
},
|
|
18077
18413
|
[handleUploadedModel]
|
|
18078
18414
|
);
|
|
18079
|
-
(0,
|
|
18415
|
+
(0, import_react43.useEffect)(() => {
|
|
18080
18416
|
return () => {
|
|
18081
18417
|
revokeUploadedModelUrl();
|
|
18082
18418
|
};
|
|
18083
18419
|
}, [revokeUploadedModelUrl]);
|
|
18084
|
-
const handleEnvSettingChange = (0,
|
|
18420
|
+
const handleEnvSettingChange = (0, import_react43.useCallback)(
|
|
18085
18421
|
(key, value) => {
|
|
18086
18422
|
setEnvSettings((prev) => ({ ...prev, [key]: value }));
|
|
18087
18423
|
},
|
|
18088
18424
|
[]
|
|
18089
18425
|
);
|
|
18090
|
-
(0,
|
|
18426
|
+
(0, import_react43.useEffect)(() => {
|
|
18091
18427
|
setEnvSettings((prev) => ({ ...prev, backgroundColor }));
|
|
18092
18428
|
}, [backgroundColor]);
|
|
18093
|
-
(0,
|
|
18429
|
+
(0, import_react43.useEffect)(() => {
|
|
18094
18430
|
resetModelState();
|
|
18095
18431
|
}, [activeModelUrl, resetModelState]);
|
|
18096
|
-
const handleMeshesReady = (0,
|
|
18432
|
+
const handleMeshesReady = (0, import_react43.useCallback)((meshes) => {
|
|
18097
18433
|
setMeshKeys(meshes);
|
|
18098
18434
|
setHiddenKeys(/* @__PURE__ */ new Set());
|
|
18099
18435
|
setSelectedKey(null);
|
|
18100
18436
|
}, []);
|
|
18101
|
-
const handleModelLoaded = (0,
|
|
18437
|
+
const handleModelLoaded = (0, import_react43.useCallback)(
|
|
18102
18438
|
(scene) => {
|
|
18103
18439
|
setLoadedScene(scene);
|
|
18104
18440
|
setModelLoadFailed(false);
|
|
@@ -18109,14 +18445,14 @@ function SimpleModelViewer({
|
|
|
18109
18445
|
},
|
|
18110
18446
|
[onModelLoaded]
|
|
18111
18447
|
);
|
|
18112
|
-
const handleLoadError = (0,
|
|
18448
|
+
const handleLoadError = (0, import_react43.useCallback)(
|
|
18113
18449
|
(error) => {
|
|
18114
18450
|
setModelLoadFailed(true);
|
|
18115
18451
|
onLoadError?.(error);
|
|
18116
18452
|
},
|
|
18117
18453
|
[onLoadError]
|
|
18118
18454
|
);
|
|
18119
|
-
const focusMesh = (0,
|
|
18455
|
+
const focusMesh = (0, import_react43.useCallback)(
|
|
18120
18456
|
(key) => {
|
|
18121
18457
|
setSelectedKey(key);
|
|
18122
18458
|
if (!zoomOnSelected) return;
|
|
@@ -18132,14 +18468,14 @@ function SimpleModelViewer({
|
|
|
18132
18468
|
},
|
|
18133
18469
|
[zoomOnSelected]
|
|
18134
18470
|
);
|
|
18135
|
-
const handleToggleVisibility = (0,
|
|
18471
|
+
const handleToggleVisibility = (0, import_react43.useCallback)((key) => {
|
|
18136
18472
|
setHiddenKeys((prev) => {
|
|
18137
18473
|
const next = new Set(prev);
|
|
18138
18474
|
next.has(key) ? next.delete(key) : next.add(key);
|
|
18139
18475
|
return next;
|
|
18140
18476
|
});
|
|
18141
18477
|
}, []);
|
|
18142
|
-
const handleDragEnter = (0,
|
|
18478
|
+
const handleDragEnter = (0, import_react43.useCallback)(
|
|
18143
18479
|
(event) => {
|
|
18144
18480
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
18145
18481
|
return;
|
|
@@ -18151,7 +18487,7 @@ function SimpleModelViewer({
|
|
|
18151
18487
|
},
|
|
18152
18488
|
[enableModelUpload]
|
|
18153
18489
|
);
|
|
18154
|
-
const handleDragOver = (0,
|
|
18490
|
+
const handleDragOver = (0, import_react43.useCallback)(
|
|
18155
18491
|
(event) => {
|
|
18156
18492
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
18157
18493
|
return;
|
|
@@ -18163,7 +18499,7 @@ function SimpleModelViewer({
|
|
|
18163
18499
|
},
|
|
18164
18500
|
[enableModelUpload]
|
|
18165
18501
|
);
|
|
18166
|
-
const handleDragLeave = (0,
|
|
18502
|
+
const handleDragLeave = (0, import_react43.useCallback)(
|
|
18167
18503
|
(event) => {
|
|
18168
18504
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
18169
18505
|
return;
|
|
@@ -18177,7 +18513,7 @@ function SimpleModelViewer({
|
|
|
18177
18513
|
},
|
|
18178
18514
|
[enableModelUpload]
|
|
18179
18515
|
);
|
|
18180
|
-
const handleDrop = (0,
|
|
18516
|
+
const handleDrop = (0, import_react43.useCallback)(
|
|
18181
18517
|
(event) => {
|
|
18182
18518
|
if (!enableModelUpload || !supportsDraggedFiles(event.dataTransfer)) {
|
|
18183
18519
|
return;
|
|
@@ -18270,7 +18606,7 @@ function SimpleModelViewer({
|
|
|
18270
18606
|
raycaster: { firstHitOnly: true },
|
|
18271
18607
|
style: { background: envSettings.backgroundColor },
|
|
18272
18608
|
camera: { position: [0, 0, 5], fov: 45, near: 0.1, far: 1e3 },
|
|
18273
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
18609
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_react43.Suspense, { fallback: null, children: [
|
|
18274
18610
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
18275
18611
|
"ambientLight",
|
|
18276
18612
|
{
|
|
@@ -18288,7 +18624,7 @@ function SimpleModelViewer({
|
|
|
18288
18624
|
}
|
|
18289
18625
|
),
|
|
18290
18626
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("directionalLight", { position: [-5, 5, 5], intensity: 0.5 }),
|
|
18291
|
-
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
18627
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react43.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
18292
18628
|
import_drei7.Environment,
|
|
18293
18629
|
{
|
|
18294
18630
|
preset: envSettings.envPreset,
|
|
@@ -18307,7 +18643,7 @@ function SimpleModelViewer({
|
|
|
18307
18643
|
ModelErrorBoundary2,
|
|
18308
18644
|
{
|
|
18309
18645
|
onLoadError: handleLoadError,
|
|
18310
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
18646
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react43.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
18311
18647
|
InnerModel,
|
|
18312
18648
|
{
|
|
18313
18649
|
url: activeModelUrl,
|
|
@@ -18406,18 +18742,18 @@ function SimpleModelViewer({
|
|
|
18406
18742
|
}
|
|
18407
18743
|
|
|
18408
18744
|
// src/hooks/useViewerSelection.ts
|
|
18409
|
-
var
|
|
18745
|
+
var import_react44 = require("react");
|
|
18410
18746
|
function useViewerSelection(initialSelectedObjectBinding = null) {
|
|
18411
|
-
const [selectedObjectBinding, setSelectedObjectBinding] = (0,
|
|
18747
|
+
const [selectedObjectBinding, setSelectedObjectBinding] = (0, import_react44.useState)(
|
|
18412
18748
|
initialSelectedObjectBinding
|
|
18413
18749
|
);
|
|
18414
|
-
const handleObjectSelect = (0,
|
|
18750
|
+
const handleObjectSelect = (0, import_react44.useCallback)(
|
|
18415
18751
|
(binding) => {
|
|
18416
18752
|
setSelectedObjectBinding(binding);
|
|
18417
18753
|
},
|
|
18418
18754
|
[setSelectedObjectBinding]
|
|
18419
18755
|
);
|
|
18420
|
-
const clearSelection = (0,
|
|
18756
|
+
const clearSelection = (0, import_react44.useCallback)(() => {
|
|
18421
18757
|
setSelectedObjectBinding(null);
|
|
18422
18758
|
}, []);
|
|
18423
18759
|
return {
|
|
@@ -18429,9 +18765,9 @@ function useViewerSelection(initialSelectedObjectBinding = null) {
|
|
|
18429
18765
|
}
|
|
18430
18766
|
|
|
18431
18767
|
// src/hooks/useObjectBinding.ts
|
|
18432
|
-
var
|
|
18768
|
+
var import_react45 = require("react");
|
|
18433
18769
|
function useObjectBinding(objectBindings, objectId) {
|
|
18434
|
-
return (0,
|
|
18770
|
+
return (0, import_react45.useMemo)(() => {
|
|
18435
18771
|
if (!objectId) {
|
|
18436
18772
|
return null;
|
|
18437
18773
|
}
|
|
@@ -18446,16 +18782,16 @@ function useObjectBinding(objectBindings, objectId) {
|
|
|
18446
18782
|
}
|
|
18447
18783
|
|
|
18448
18784
|
// src/hooks/useObjectBindingIds.ts
|
|
18449
|
-
var
|
|
18785
|
+
var import_react46 = require("react");
|
|
18450
18786
|
function useObjectBindingIds(objectBindings) {
|
|
18451
|
-
return (0,
|
|
18787
|
+
return (0, import_react46.useMemo)(
|
|
18452
18788
|
() => Object.values(objectBindings).map((binding) => binding.modelObjectId),
|
|
18453
18789
|
[objectBindings]
|
|
18454
18790
|
);
|
|
18455
18791
|
}
|
|
18456
18792
|
|
|
18457
18793
|
// src/hooks/useObjectBindings.ts
|
|
18458
|
-
var
|
|
18794
|
+
var import_react47 = require("react");
|
|
18459
18795
|
|
|
18460
18796
|
// src/utils/deepMerge.ts
|
|
18461
18797
|
function isPlainObject2(value) {
|
|
@@ -18473,6 +18809,27 @@ function deepMerge(base, patch) {
|
|
|
18473
18809
|
return target;
|
|
18474
18810
|
}
|
|
18475
18811
|
|
|
18812
|
+
// src/utils/selectBindings.ts
|
|
18813
|
+
function isSelector(target) {
|
|
18814
|
+
return typeof target === "object" && !Array.isArray(target);
|
|
18815
|
+
}
|
|
18816
|
+
function matchesSelector(binding, selector) {
|
|
18817
|
+
if ("group" in selector) return binding.group === selector.group;
|
|
18818
|
+
if ("tag" in selector) return binding.tags?.includes(selector.tag) ?? false;
|
|
18819
|
+
return binding.type === selector.type;
|
|
18820
|
+
}
|
|
18821
|
+
function selectBindingKeys(bindings, target) {
|
|
18822
|
+
if (isSelector(target)) {
|
|
18823
|
+
return Object.entries(bindings).filter(([, binding]) => matchesSelector(binding, target)).map(([bindingKey]) => bindingKey);
|
|
18824
|
+
}
|
|
18825
|
+
const ids = typeof target === "string" ? [target] : target;
|
|
18826
|
+
const keys = ids.map((id) => resolveBindingKey(bindings, id)).filter((key) => key !== null);
|
|
18827
|
+
return [...new Set(keys)];
|
|
18828
|
+
}
|
|
18829
|
+
function selectBindings(bindings, target) {
|
|
18830
|
+
return selectBindingKeys(bindings, target).map((key) => bindings[key]).filter((binding) => binding !== void 0);
|
|
18831
|
+
}
|
|
18832
|
+
|
|
18476
18833
|
// src/utils/patchBindings.ts
|
|
18477
18834
|
function isBindingEditArray(value) {
|
|
18478
18835
|
return Array.isArray(value) && value.length > 0 && typeof value[0] !== "string";
|
|
@@ -18481,10 +18838,7 @@ function patchBindings(bindings, objectIdOrEdits, patch) {
|
|
|
18481
18838
|
const edits = isBindingEditArray(objectIdOrEdits) ? objectIdOrEdits : [{ ids: objectIdOrEdits, patch: patch ?? {} }];
|
|
18482
18839
|
let next = bindings;
|
|
18483
18840
|
for (const edit of edits) {
|
|
18484
|
-
const
|
|
18485
|
-
for (const id of ids) {
|
|
18486
|
-
const bindingKey = resolveBindingKey(next, id);
|
|
18487
|
-
if (!bindingKey) continue;
|
|
18841
|
+
for (const bindingKey of selectBindingKeys(next, edit.ids)) {
|
|
18488
18842
|
const binding = next[bindingKey];
|
|
18489
18843
|
if (!binding) continue;
|
|
18490
18844
|
const patchedBinding = deepMerge(binding, edit.patch);
|
|
@@ -18499,14 +18853,44 @@ function material(value) {
|
|
|
18499
18853
|
}
|
|
18500
18854
|
|
|
18501
18855
|
// src/hooks/useObjectBindings.ts
|
|
18502
|
-
|
|
18856
|
+
var TEXTURE_PROPERTY_BY_CHANNEL = {
|
|
18857
|
+
baseColor: "texture",
|
|
18858
|
+
metalness: "metalnessMap",
|
|
18859
|
+
roughness: "roughnessMap",
|
|
18860
|
+
emissive: "emissiveMap",
|
|
18861
|
+
alpha: "alphaMap",
|
|
18862
|
+
normal: "normalMap",
|
|
18863
|
+
bump: "bumpMap",
|
|
18864
|
+
ao: "aoMap",
|
|
18865
|
+
displacement: "displacementMap",
|
|
18866
|
+
clearcoat: "clearcoatMap",
|
|
18867
|
+
clearcoatRoughness: "clearcoatRoughnessMap",
|
|
18868
|
+
clearcoatNormal: "clearcoatNormalMap",
|
|
18869
|
+
anisotropy: "anisotropyMap",
|
|
18870
|
+
transmission: "transmissionMap",
|
|
18871
|
+
thickness: "thicknessMap",
|
|
18872
|
+
sheenColor: "sheenColorMap",
|
|
18873
|
+
sheenRoughness: "sheenRoughnessMap",
|
|
18874
|
+
specularIntensity: "specularIntensityMap",
|
|
18875
|
+
specularColor: "specularColorMap"
|
|
18876
|
+
};
|
|
18877
|
+
function cloneMaterial(value) {
|
|
18878
|
+
if (!value) return void 0;
|
|
18879
|
+
return Object.fromEntries(
|
|
18880
|
+
Object.entries(value).map(([key, field]) => [
|
|
18881
|
+
key,
|
|
18882
|
+
field && typeof field === "object" ? { ...field } : field
|
|
18883
|
+
])
|
|
18884
|
+
);
|
|
18885
|
+
}
|
|
18886
|
+
function getHiddenObjects(objectBindings) {
|
|
18503
18887
|
return Object.fromEntries(
|
|
18504
18888
|
Object.entries(objectBindings).filter(([, binding]) => binding.visible === false).map(([bindingKey]) => [bindingKey, true])
|
|
18505
18889
|
);
|
|
18506
18890
|
}
|
|
18507
18891
|
function useObjectBindings(initialBindings) {
|
|
18508
|
-
const [objectBindings, setObjectBindings] = (0,
|
|
18509
|
-
const updateObjectBindings = (0,
|
|
18892
|
+
const [objectBindings, setObjectBindings] = (0, import_react47.useState)(initialBindings);
|
|
18893
|
+
const updateObjectBindings = (0, import_react47.useCallback)(
|
|
18510
18894
|
(objectIdOrEdits, patchValue) => {
|
|
18511
18895
|
setObjectBindings(
|
|
18512
18896
|
(current) => patchValue === void 0 ? patchBindings(current, objectIdOrEdits) : patchBindings(
|
|
@@ -18518,68 +18902,138 @@ function useObjectBindings(initialBindings) {
|
|
|
18518
18902
|
},
|
|
18519
18903
|
[]
|
|
18520
18904
|
);
|
|
18521
|
-
const setMaterial = (0,
|
|
18522
|
-
(
|
|
18905
|
+
const setMaterial = (0, import_react47.useCallback)(
|
|
18906
|
+
(target, value) => updateObjectBindings(target, material(value)),
|
|
18523
18907
|
[updateObjectBindings]
|
|
18524
18908
|
);
|
|
18525
|
-
const setBaseColor = (0,
|
|
18526
|
-
(
|
|
18909
|
+
const setBaseColor = (0, import_react47.useCallback)(
|
|
18910
|
+
(target, color) => setMaterial(target, { baseColor: color }),
|
|
18527
18911
|
[setMaterial]
|
|
18528
18912
|
);
|
|
18529
|
-
const
|
|
18530
|
-
(
|
|
18913
|
+
const setTexture = (0, import_react47.useCallback)(
|
|
18914
|
+
(target, texture, channel = "baseColor") => {
|
|
18915
|
+
const property = TEXTURE_PROPERTY_BY_CHANNEL[channel];
|
|
18916
|
+
const slot = typeof texture === "string" ? { path: texture } : texture;
|
|
18917
|
+
updateObjectBindings(target, {
|
|
18918
|
+
style: { material: { [property]: slot } }
|
|
18919
|
+
});
|
|
18920
|
+
},
|
|
18531
18921
|
[updateObjectBindings]
|
|
18532
18922
|
);
|
|
18533
|
-
const
|
|
18923
|
+
const clearTexture = (0, import_react47.useCallback)(
|
|
18924
|
+
(target, channel = "baseColor") => {
|
|
18925
|
+
const property = TEXTURE_PROPERTY_BY_CHANNEL[channel];
|
|
18926
|
+
updateObjectBindings(target, {
|
|
18927
|
+
style: { material: { [property]: void 0 } }
|
|
18928
|
+
});
|
|
18929
|
+
},
|
|
18930
|
+
[updateObjectBindings]
|
|
18931
|
+
);
|
|
18932
|
+
const copyMaterial = (0, import_react47.useCallback)(
|
|
18933
|
+
(source, target) => {
|
|
18934
|
+
setObjectBindings((current) => {
|
|
18935
|
+
const sourceKey = resolveBindingKey(current, source);
|
|
18936
|
+
if (!sourceKey) return current;
|
|
18937
|
+
const sourceMaterial = cloneMaterial(
|
|
18938
|
+
current[sourceKey]?.style?.material
|
|
18939
|
+
);
|
|
18940
|
+
const targetKeys = selectBindingKeys(current, target);
|
|
18941
|
+
if (targetKeys.length === 0) return current;
|
|
18942
|
+
let next = current;
|
|
18943
|
+
for (const targetKey of targetKeys) {
|
|
18944
|
+
const binding = current[targetKey];
|
|
18945
|
+
if (!binding) continue;
|
|
18946
|
+
if (next === current) next = { ...current };
|
|
18947
|
+
next[targetKey] = {
|
|
18948
|
+
...binding,
|
|
18949
|
+
style: {
|
|
18950
|
+
...binding.style,
|
|
18951
|
+
material: cloneMaterial(sourceMaterial)
|
|
18952
|
+
}
|
|
18953
|
+
};
|
|
18954
|
+
}
|
|
18955
|
+
return next;
|
|
18956
|
+
});
|
|
18957
|
+
},
|
|
18958
|
+
[]
|
|
18959
|
+
);
|
|
18960
|
+
const resetMaterial = (0, import_react47.useCallback)(
|
|
18961
|
+
(target) => updateObjectBindings(target, { style: { material: void 0 } }),
|
|
18962
|
+
[updateObjectBindings]
|
|
18963
|
+
);
|
|
18964
|
+
const setTransform = (0, import_react47.useCallback)(
|
|
18965
|
+
(target, transform) => updateObjectBindings(target, { transform }),
|
|
18966
|
+
[updateObjectBindings]
|
|
18967
|
+
);
|
|
18968
|
+
const resetTransform = (0, import_react47.useCallback)(
|
|
18969
|
+
(target) => updateObjectBindings(target, { transform: void 0 }),
|
|
18970
|
+
[updateObjectBindings]
|
|
18971
|
+
);
|
|
18972
|
+
const updateMetadata = (0, import_react47.useCallback)(
|
|
18973
|
+
(target, metadata) => updateObjectBindings(target, { metadata }),
|
|
18974
|
+
[updateObjectBindings]
|
|
18975
|
+
);
|
|
18976
|
+
const selectObjectBindings = (0, import_react47.useCallback)(
|
|
18977
|
+
(target) => selectBindings(objectBindings, target),
|
|
18978
|
+
[objectBindings]
|
|
18979
|
+
);
|
|
18980
|
+
const selectObjectBindingIds = (0, import_react47.useCallback)(
|
|
18981
|
+
(target) => selectBindingKeys(objectBindings, target).map(
|
|
18982
|
+
(bindingKey) => objectBindings[bindingKey]?.id ?? bindingKey
|
|
18983
|
+
),
|
|
18984
|
+
[objectBindings]
|
|
18985
|
+
);
|
|
18986
|
+
const getMaterial = (0, import_react47.useCallback)(
|
|
18534
18987
|
(objectId) => {
|
|
18535
18988
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
18536
18989
|
return bindingKey ? objectBindings[bindingKey]?.style?.material ?? null : null;
|
|
18537
18990
|
},
|
|
18538
18991
|
[objectBindings]
|
|
18539
18992
|
);
|
|
18540
|
-
const hideObject = (0,
|
|
18541
|
-
(
|
|
18993
|
+
const hideObject = (0, import_react47.useCallback)(
|
|
18994
|
+
(target) => updateObjectBindings(target, { visible: false }),
|
|
18542
18995
|
[updateObjectBindings]
|
|
18543
18996
|
);
|
|
18544
|
-
const showObject = (0,
|
|
18545
|
-
(
|
|
18997
|
+
const showObject = (0, import_react47.useCallback)(
|
|
18998
|
+
(target) => updateObjectBindings(target, { visible: true }),
|
|
18546
18999
|
[updateObjectBindings]
|
|
18547
19000
|
);
|
|
18548
|
-
const toggleObjectVisibility = (0,
|
|
18549
|
-
(
|
|
19001
|
+
const toggleObjectVisibility = (0, import_react47.useCallback)(
|
|
19002
|
+
(target) => {
|
|
18550
19003
|
setObjectBindings((current) => {
|
|
18551
|
-
const
|
|
18552
|
-
|
|
18553
|
-
|
|
18554
|
-
|
|
18555
|
-
|
|
18556
|
-
|
|
18557
|
-
|
|
18558
|
-
|
|
18559
|
-
|
|
18560
|
-
|
|
18561
|
-
|
|
19004
|
+
const edits = selectBindingKeys(current, target).reduce(
|
|
19005
|
+
(acc, bindingKey) => {
|
|
19006
|
+
const binding = current[bindingKey];
|
|
19007
|
+
if (!binding) return acc;
|
|
19008
|
+
acc.push({
|
|
19009
|
+
ids: bindingKey,
|
|
19010
|
+
patch: { visible: binding.visible === false }
|
|
19011
|
+
});
|
|
19012
|
+
return acc;
|
|
19013
|
+
},
|
|
19014
|
+
[]
|
|
19015
|
+
);
|
|
18562
19016
|
return patchBindings(current, edits);
|
|
18563
19017
|
});
|
|
18564
19018
|
},
|
|
18565
19019
|
[setObjectBindings]
|
|
18566
19020
|
);
|
|
18567
|
-
const clearHiddenObjects = (0,
|
|
19021
|
+
const clearHiddenObjects = (0, import_react47.useCallback)(() => {
|
|
18568
19022
|
setObjectBindings(
|
|
18569
19023
|
(current) => patchBindings(current, Object.keys(current), { visible: true })
|
|
18570
19024
|
);
|
|
18571
19025
|
}, [setObjectBindings]);
|
|
18572
|
-
const hiddenObjects = (0,
|
|
19026
|
+
const hiddenObjects = (0, import_react47.useMemo)(
|
|
18573
19027
|
() => getHiddenObjects(objectBindings),
|
|
18574
19028
|
[objectBindings]
|
|
18575
19029
|
);
|
|
18576
|
-
const hiddenObjectIds = (0,
|
|
19030
|
+
const hiddenObjectIds = (0, import_react47.useMemo)(
|
|
18577
19031
|
() => Object.keys(hiddenObjects).map(
|
|
18578
19032
|
(bindingKey) => objectBindings[bindingKey]?.id ?? bindingKey
|
|
18579
19033
|
),
|
|
18580
19034
|
[hiddenObjects, objectBindings]
|
|
18581
19035
|
);
|
|
18582
|
-
const isObjectHidden = (0,
|
|
19036
|
+
const isObjectHidden = (0, import_react47.useCallback)(
|
|
18583
19037
|
(objectId) => {
|
|
18584
19038
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
18585
19039
|
return bindingKey ? objectBindings[bindingKey]?.visible === false : false;
|
|
@@ -18592,8 +19046,16 @@ function useObjectBindings(initialBindings) {
|
|
|
18592
19046
|
updateObjectBindings,
|
|
18593
19047
|
setMaterial,
|
|
18594
19048
|
setBaseColor,
|
|
19049
|
+
setTexture,
|
|
19050
|
+
clearTexture,
|
|
19051
|
+
copyMaterial,
|
|
18595
19052
|
resetMaterial,
|
|
18596
19053
|
getMaterial,
|
|
19054
|
+
setTransform,
|
|
19055
|
+
resetTransform,
|
|
19056
|
+
updateMetadata,
|
|
19057
|
+
selectObjectBindings,
|
|
19058
|
+
selectObjectBindingIds,
|
|
18597
19059
|
hiddenObjects,
|
|
18598
19060
|
hiddenObjectIds,
|
|
18599
19061
|
hideObject,
|
|
@@ -18604,8 +19066,13 @@ function useObjectBindings(initialBindings) {
|
|
|
18604
19066
|
};
|
|
18605
19067
|
}
|
|
18606
19068
|
|
|
19069
|
+
// src/utils/defineObjectBindings.ts
|
|
19070
|
+
function defineObjectBindings(bindings) {
|
|
19071
|
+
return bindings;
|
|
19072
|
+
}
|
|
19073
|
+
|
|
18607
19074
|
// src/hooks/useSceneConfig.ts
|
|
18608
|
-
var
|
|
19075
|
+
var import_react48 = require("react");
|
|
18609
19076
|
|
|
18610
19077
|
// src/utils/patchSceneConfig.ts
|
|
18611
19078
|
function patchSceneConfig(sceneConfig, patch) {
|
|
@@ -18613,36 +19080,154 @@ function patchSceneConfig(sceneConfig, patch) {
|
|
|
18613
19080
|
}
|
|
18614
19081
|
|
|
18615
19082
|
// src/hooks/useSceneConfig.ts
|
|
19083
|
+
var DEFAULT_LIGHT = {
|
|
19084
|
+
type: "point",
|
|
19085
|
+
color: "#ffffff",
|
|
19086
|
+
intensity: 1,
|
|
19087
|
+
position: [0, 0, 0],
|
|
19088
|
+
distance: 0,
|
|
19089
|
+
decay: 2,
|
|
19090
|
+
attachedToCamera: false,
|
|
19091
|
+
castShadow: false,
|
|
19092
|
+
shadowBias: -5e-4,
|
|
19093
|
+
visible: true
|
|
19094
|
+
};
|
|
18616
19095
|
function useSceneConfig(initialSceneConfig) {
|
|
18617
|
-
const initialSceneConfigRef = (0,
|
|
18618
|
-
const [sceneConfig, setSceneConfig] = (0,
|
|
19096
|
+
const initialSceneConfigRef = (0, import_react48.useRef)(null);
|
|
19097
|
+
const [sceneConfig, setSceneConfig] = (0, import_react48.useState)(() => {
|
|
18619
19098
|
const initial = typeof initialSceneConfig === "function" ? initialSceneConfig() : initialSceneConfig;
|
|
18620
19099
|
initialSceneConfigRef.current = initial;
|
|
18621
19100
|
return initial;
|
|
18622
19101
|
});
|
|
18623
|
-
const updateSceneConfig = (0,
|
|
19102
|
+
const updateSceneConfig = (0, import_react48.useCallback)((update) => {
|
|
18624
19103
|
setSceneConfig((current) => {
|
|
18625
19104
|
const patch = typeof update === "function" ? update(current) : update;
|
|
18626
19105
|
return patchSceneConfig(current, patch);
|
|
18627
19106
|
});
|
|
18628
19107
|
}, []);
|
|
18629
|
-
const resetSceneConfig = (0,
|
|
19108
|
+
const resetSceneConfig = (0, import_react48.useCallback)(() => {
|
|
18630
19109
|
const initial = initialSceneConfigRef.current;
|
|
18631
19110
|
if (initial) setSceneConfig(initial);
|
|
18632
19111
|
}, []);
|
|
19112
|
+
const updateLight = (0, import_react48.useCallback)(
|
|
19113
|
+
(id, patch) => {
|
|
19114
|
+
updateSceneConfig((current) => ({
|
|
19115
|
+
lighting: {
|
|
19116
|
+
lights: current.lighting.lights.map(
|
|
19117
|
+
(light) => light.id === id ? { ...light, ...patch } : light
|
|
19118
|
+
)
|
|
19119
|
+
}
|
|
19120
|
+
}));
|
|
19121
|
+
},
|
|
19122
|
+
[updateSceneConfig]
|
|
19123
|
+
);
|
|
19124
|
+
const addLight = (0, import_react48.useCallback)(
|
|
19125
|
+
(light) => {
|
|
19126
|
+
updateSceneConfig((current) => {
|
|
19127
|
+
if (current.lighting.lights.some((item) => item.id === light.id)) {
|
|
19128
|
+
return {};
|
|
19129
|
+
}
|
|
19130
|
+
return {
|
|
19131
|
+
lighting: {
|
|
19132
|
+
lights: [
|
|
19133
|
+
...current.lighting.lights,
|
|
19134
|
+
{ ...DEFAULT_LIGHT, ...light }
|
|
19135
|
+
]
|
|
19136
|
+
}
|
|
19137
|
+
};
|
|
19138
|
+
});
|
|
19139
|
+
},
|
|
19140
|
+
[updateSceneConfig]
|
|
19141
|
+
);
|
|
19142
|
+
const removeLight = (0, import_react48.useCallback)(
|
|
19143
|
+
(id) => {
|
|
19144
|
+
updateSceneConfig((current) => ({
|
|
19145
|
+
lighting: {
|
|
19146
|
+
lights: current.lighting.lights.filter((light) => light.id !== id)
|
|
19147
|
+
}
|
|
19148
|
+
}));
|
|
19149
|
+
},
|
|
19150
|
+
[updateSceneConfig]
|
|
19151
|
+
);
|
|
19152
|
+
const setLightIntensity = (0, import_react48.useCallback)(
|
|
19153
|
+
(id, intensity) => updateLight(id, { intensity }),
|
|
19154
|
+
[updateLight]
|
|
19155
|
+
);
|
|
19156
|
+
const setLightColor = (0, import_react48.useCallback)(
|
|
19157
|
+
(id, color) => updateLight(id, { color }),
|
|
19158
|
+
[updateLight]
|
|
19159
|
+
);
|
|
19160
|
+
const showLight = (0, import_react48.useCallback)(
|
|
19161
|
+
(id) => updateLight(id, { visible: true }),
|
|
19162
|
+
[updateLight]
|
|
19163
|
+
);
|
|
19164
|
+
const hideLight = (0, import_react48.useCallback)(
|
|
19165
|
+
(id) => updateLight(id, { visible: false }),
|
|
19166
|
+
[updateLight]
|
|
19167
|
+
);
|
|
19168
|
+
const toggleLight = (0, import_react48.useCallback)(
|
|
19169
|
+
(id) => {
|
|
19170
|
+
updateSceneConfig((current) => ({
|
|
19171
|
+
lighting: {
|
|
19172
|
+
lights: current.lighting.lights.map(
|
|
19173
|
+
(light) => light.id === id ? { ...light, visible: !light.visible } : light
|
|
19174
|
+
)
|
|
19175
|
+
}
|
|
19176
|
+
}));
|
|
19177
|
+
},
|
|
19178
|
+
[updateSceneConfig]
|
|
19179
|
+
);
|
|
19180
|
+
const attachLight = (0, import_react48.useCallback)(
|
|
19181
|
+
(id, attachment) => updateLight(id, { attachment, attachedToCamera: false }),
|
|
19182
|
+
[updateLight]
|
|
19183
|
+
);
|
|
19184
|
+
const detachLight = (0, import_react48.useCallback)(
|
|
19185
|
+
(id) => updateLight(id, { attachment: void 0 }),
|
|
19186
|
+
[updateLight]
|
|
19187
|
+
);
|
|
19188
|
+
const lights = (0, import_react48.useMemo)(
|
|
19189
|
+
() => ({
|
|
19190
|
+
items: sceneConfig.lighting.lights,
|
|
19191
|
+
get: (id) => sceneConfig.lighting.lights.find((light) => light.id === id),
|
|
19192
|
+
add: addLight,
|
|
19193
|
+
update: updateLight,
|
|
19194
|
+
remove: removeLight,
|
|
19195
|
+
setIntensity: setLightIntensity,
|
|
19196
|
+
setColor: setLightColor,
|
|
19197
|
+
show: showLight,
|
|
19198
|
+
hide: hideLight,
|
|
19199
|
+
toggle: toggleLight,
|
|
19200
|
+
attach: attachLight,
|
|
19201
|
+
detach: detachLight
|
|
19202
|
+
}),
|
|
19203
|
+
[
|
|
19204
|
+
sceneConfig.lighting.lights,
|
|
19205
|
+
addLight,
|
|
19206
|
+
updateLight,
|
|
19207
|
+
removeLight,
|
|
19208
|
+
setLightIntensity,
|
|
19209
|
+
setLightColor,
|
|
19210
|
+
showLight,
|
|
19211
|
+
hideLight,
|
|
19212
|
+
toggleLight,
|
|
19213
|
+
attachLight,
|
|
19214
|
+
detachLight
|
|
19215
|
+
]
|
|
19216
|
+
);
|
|
18633
19217
|
return {
|
|
18634
19218
|
sceneConfig,
|
|
18635
19219
|
setSceneConfig,
|
|
18636
19220
|
updateSceneConfig,
|
|
18637
|
-
resetSceneConfig
|
|
19221
|
+
resetSceneConfig,
|
|
19222
|
+
lights
|
|
18638
19223
|
};
|
|
18639
19224
|
}
|
|
18640
19225
|
|
|
18641
19226
|
// src/hooks/useViewerHover.ts
|
|
18642
|
-
var
|
|
19227
|
+
var import_react49 = require("react");
|
|
18643
19228
|
function useViewerHover() {
|
|
18644
|
-
const [hoveredObjectBinding, setHoveredObjectBinding] = (0,
|
|
18645
|
-
const handleHoveredObject = (0,
|
|
19229
|
+
const [hoveredObjectBinding, setHoveredObjectBinding] = (0, import_react49.useState)(null);
|
|
19230
|
+
const handleHoveredObject = (0, import_react49.useCallback)(
|
|
18646
19231
|
(binding) => {
|
|
18647
19232
|
setHoveredObjectBinding(binding);
|
|
18648
19233
|
},
|
|
@@ -18655,23 +19240,23 @@ function useViewerHover() {
|
|
|
18655
19240
|
}
|
|
18656
19241
|
|
|
18657
19242
|
// src/hooks/useViewerModel.ts
|
|
18658
|
-
var
|
|
18659
|
-
var
|
|
19243
|
+
var import_react50 = require("react");
|
|
19244
|
+
var import_three24 = require("three");
|
|
18660
19245
|
function toTuple2(vector) {
|
|
18661
19246
|
return [vector.x, vector.y, vector.z];
|
|
18662
19247
|
}
|
|
18663
19248
|
function useViewerModel() {
|
|
18664
|
-
const [isLoading, setIsLoading] = (0,
|
|
18665
|
-
const [isReady, setIsReady] = (0,
|
|
18666
|
-
const [error, setError] = (0,
|
|
18667
|
-
const [bounds, setBounds] = (0,
|
|
18668
|
-
const [objectCount, setObjectCount] = (0,
|
|
18669
|
-
const handleModelLoaded = (0,
|
|
18670
|
-
const box = new
|
|
19249
|
+
const [isLoading, setIsLoading] = (0, import_react50.useState)(true);
|
|
19250
|
+
const [isReady, setIsReady] = (0, import_react50.useState)(false);
|
|
19251
|
+
const [error, setError] = (0, import_react50.useState)(null);
|
|
19252
|
+
const [bounds, setBounds] = (0, import_react50.useState)(null);
|
|
19253
|
+
const [objectCount, setObjectCount] = (0, import_react50.useState)(0);
|
|
19254
|
+
const handleModelLoaded = (0, import_react50.useCallback)((scene) => {
|
|
19255
|
+
const box = new import_three24.Box3().setFromObject(scene);
|
|
18671
19256
|
const min = box.min.clone();
|
|
18672
19257
|
const max = box.max.clone();
|
|
18673
|
-
const center = new
|
|
18674
|
-
const size = new
|
|
19258
|
+
const center = new import_three24.Vector3();
|
|
19259
|
+
const size = new import_three24.Vector3();
|
|
18675
19260
|
let nextObjectCount = 0;
|
|
18676
19261
|
scene.traverse((child) => {
|
|
18677
19262
|
if ("isMesh" in child && child.isMesh) {
|
|
@@ -18691,14 +19276,14 @@ function useViewerModel() {
|
|
|
18691
19276
|
setIsLoading(false);
|
|
18692
19277
|
setIsReady(true);
|
|
18693
19278
|
}, []);
|
|
18694
|
-
const handleLoadError = (0,
|
|
19279
|
+
const handleLoadError = (0, import_react50.useCallback)((nextError) => {
|
|
18695
19280
|
setError(nextError);
|
|
18696
19281
|
setBounds(null);
|
|
18697
19282
|
setObjectCount(0);
|
|
18698
19283
|
setIsLoading(false);
|
|
18699
19284
|
setIsReady(false);
|
|
18700
19285
|
}, []);
|
|
18701
|
-
const resetModelState = (0,
|
|
19286
|
+
const resetModelState = (0, import_react50.useCallback)(() => {
|
|
18702
19287
|
setIsLoading(true);
|
|
18703
19288
|
setIsReady(false);
|
|
18704
19289
|
setError(null);
|
|
@@ -18718,62 +19303,774 @@ function useViewerModel() {
|
|
|
18718
19303
|
}
|
|
18719
19304
|
|
|
18720
19305
|
// src/hooks/useViewerActions.ts
|
|
18721
|
-
var
|
|
18722
|
-
function useViewerActions(
|
|
18723
|
-
|
|
18724
|
-
|
|
19306
|
+
var import_react51 = require("react");
|
|
19307
|
+
function useViewerActions({
|
|
19308
|
+
bindings,
|
|
19309
|
+
scene,
|
|
19310
|
+
camera,
|
|
19311
|
+
animations,
|
|
19312
|
+
onAction
|
|
19313
|
+
}) {
|
|
19314
|
+
const {
|
|
19315
|
+
objectBindings,
|
|
19316
|
+
updateObjectBindings,
|
|
19317
|
+
showObject,
|
|
19318
|
+
hideObject,
|
|
19319
|
+
toggleObjectVisibility
|
|
19320
|
+
} = bindings;
|
|
19321
|
+
const updateSceneConfig = scene?.updateSceneConfig;
|
|
19322
|
+
const focusObject = camera?.focusObject;
|
|
19323
|
+
const fitScene = camera?.fitScene;
|
|
19324
|
+
const resetView = camera?.resetView;
|
|
19325
|
+
const setCameraTarget = camera?.setCameraTarget;
|
|
19326
|
+
const setCameraState = camera?.setCameraState;
|
|
19327
|
+
const playAnimation = animations?.play;
|
|
19328
|
+
const pauseAnimation = animations?.pause;
|
|
19329
|
+
const stopAnimation = animations?.stop;
|
|
19330
|
+
const setAnimationSpeed = animations?.setSpeed;
|
|
19331
|
+
const seekAnimation = animations?.seek;
|
|
19332
|
+
const getActionsForObject = (0, import_react51.useCallback)(
|
|
18725
19333
|
(objectId) => {
|
|
18726
19334
|
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
18727
|
-
|
|
18728
|
-
return [];
|
|
18729
|
-
}
|
|
18730
|
-
return objectBindings[bindingKey]?.actions ?? [];
|
|
19335
|
+
return bindingKey ? objectBindings[bindingKey]?.actions ?? [] : [];
|
|
18731
19336
|
},
|
|
18732
19337
|
[objectBindings]
|
|
18733
19338
|
);
|
|
18734
|
-
const
|
|
18735
|
-
(
|
|
18736
|
-
const
|
|
18737
|
-
|
|
18738
|
-
|
|
19339
|
+
const handleAction = (0, import_react51.useCallback)(
|
|
19340
|
+
async (event) => {
|
|
19341
|
+
for (const effect of event.action.effects ?? []) {
|
|
19342
|
+
if ("objectBindings" in effect) {
|
|
19343
|
+
updateObjectBindings(effect.target, effect.objectBindings);
|
|
19344
|
+
continue;
|
|
19345
|
+
}
|
|
19346
|
+
if ("sceneConfig" in effect) {
|
|
19347
|
+
updateSceneConfig?.(effect.sceneConfig);
|
|
19348
|
+
continue;
|
|
19349
|
+
}
|
|
19350
|
+
if ("visibility" in effect) {
|
|
19351
|
+
if (effect.visibility === "show") showObject(effect.target);
|
|
19352
|
+
else if (effect.visibility === "hide") hideObject(effect.target);
|
|
19353
|
+
else toggleObjectVisibility(effect.target);
|
|
19354
|
+
continue;
|
|
19355
|
+
}
|
|
19356
|
+
if ("camera" in effect) {
|
|
19357
|
+
const action2 = effect.camera;
|
|
19358
|
+
if (action2.type === "focus-object") {
|
|
19359
|
+
await focusObject?.(action2.objectId ?? event.objectId);
|
|
19360
|
+
} else if (action2.type === "fit-scene") {
|
|
19361
|
+
await fitScene?.();
|
|
19362
|
+
} else if (action2.type === "reset") {
|
|
19363
|
+
await resetView?.();
|
|
19364
|
+
} else if (action2.type === "set-target") {
|
|
19365
|
+
await setCameraTarget?.(action2.target, action2.transition ?? true);
|
|
19366
|
+
} else {
|
|
19367
|
+
await setCameraState?.(action2.state, action2.transition ?? true);
|
|
19368
|
+
}
|
|
19369
|
+
continue;
|
|
19370
|
+
}
|
|
19371
|
+
const action = effect.animation;
|
|
19372
|
+
if (action.type === "play") playAnimation?.(action.clip);
|
|
19373
|
+
else if (action.type === "pause") pauseAnimation?.();
|
|
19374
|
+
else if (action.type === "stop") stopAnimation?.();
|
|
19375
|
+
else if (action.type === "set-speed") {
|
|
19376
|
+
setAnimationSpeed?.(action.speed);
|
|
19377
|
+
} else {
|
|
19378
|
+
seekAnimation?.(action.time);
|
|
19379
|
+
}
|
|
18739
19380
|
}
|
|
19381
|
+
await onAction?.(event);
|
|
19382
|
+
},
|
|
19383
|
+
[
|
|
19384
|
+
fitScene,
|
|
19385
|
+
focusObject,
|
|
19386
|
+
hideObject,
|
|
19387
|
+
onAction,
|
|
19388
|
+
pauseAnimation,
|
|
19389
|
+
playAnimation,
|
|
19390
|
+
resetView,
|
|
19391
|
+
seekAnimation,
|
|
19392
|
+
setAnimationSpeed,
|
|
19393
|
+
setCameraState,
|
|
19394
|
+
setCameraTarget,
|
|
19395
|
+
showObject,
|
|
19396
|
+
stopAnimation,
|
|
19397
|
+
toggleObjectVisibility,
|
|
19398
|
+
updateObjectBindings,
|
|
19399
|
+
updateSceneConfig
|
|
19400
|
+
]
|
|
19401
|
+
);
|
|
19402
|
+
const runAction = (0, import_react51.useCallback)(
|
|
19403
|
+
async (objectId, actionId) => {
|
|
19404
|
+
const bindingKey = resolveBindingKey(objectBindings, objectId);
|
|
19405
|
+
if (!bindingKey) return null;
|
|
18740
19406
|
const binding = objectBindings[bindingKey];
|
|
18741
|
-
|
|
18742
|
-
|
|
18743
|
-
}
|
|
18744
|
-
const action = (binding.actions ?? []).find(
|
|
18745
|
-
(entry) => entry.id === actionId
|
|
18746
|
-
);
|
|
18747
|
-
if (!action) {
|
|
18748
|
-
return null;
|
|
18749
|
-
}
|
|
19407
|
+
const action = binding?.actions?.find((entry) => entry.id === actionId);
|
|
19408
|
+
if (!binding || !action) return null;
|
|
18750
19409
|
const event = {
|
|
18751
19410
|
objectId: bindingKey,
|
|
18752
19411
|
action,
|
|
18753
19412
|
binding
|
|
18754
19413
|
};
|
|
18755
|
-
|
|
18756
|
-
onObjectBindingsChange({
|
|
18757
|
-
...objectBindings,
|
|
18758
|
-
[bindingKey]: {
|
|
18759
|
-
...binding,
|
|
18760
|
-
visible: binding.visible === false
|
|
18761
|
-
}
|
|
18762
|
-
});
|
|
18763
|
-
}
|
|
18764
|
-
onAction?.(event);
|
|
19414
|
+
await handleAction(event);
|
|
18765
19415
|
return event;
|
|
18766
19416
|
},
|
|
18767
|
-
[
|
|
19417
|
+
[handleAction, objectBindings]
|
|
18768
19418
|
);
|
|
18769
19419
|
return {
|
|
18770
19420
|
getActionsForObject,
|
|
19421
|
+
handleAction,
|
|
18771
19422
|
runAction
|
|
18772
19423
|
};
|
|
18773
19424
|
}
|
|
18774
19425
|
|
|
19426
|
+
// src/hooks/useViewerConnection.ts
|
|
19427
|
+
var import_react52 = require("react");
|
|
19428
|
+
function getReadyHandler(consumer) {
|
|
19429
|
+
if (typeof consumer === "function") {
|
|
19430
|
+
return consumer;
|
|
19431
|
+
}
|
|
19432
|
+
return consumer?.handleViewerReady ?? null;
|
|
19433
|
+
}
|
|
19434
|
+
function useViewerConnection(...consumers) {
|
|
19435
|
+
const viewerRef = (0, import_react52.useRef)(null);
|
|
19436
|
+
const consumersRef = (0, import_react52.useRef)(consumers);
|
|
19437
|
+
consumersRef.current = consumers;
|
|
19438
|
+
const handleViewerReady = (0, import_react52.useCallback)((viewer) => {
|
|
19439
|
+
viewerRef.current = viewer;
|
|
19440
|
+
const invoked = /* @__PURE__ */ new Set();
|
|
19441
|
+
for (const consumer of consumersRef.current) {
|
|
19442
|
+
const handler = getReadyHandler(consumer);
|
|
19443
|
+
if (handler && !invoked.has(handler)) {
|
|
19444
|
+
invoked.add(handler);
|
|
19445
|
+
handler(viewer);
|
|
19446
|
+
}
|
|
19447
|
+
}
|
|
19448
|
+
}, []);
|
|
19449
|
+
const getViewer = (0, import_react52.useCallback)(() => viewerRef.current, []);
|
|
19450
|
+
const captureImage2 = (0, import_react52.useCallback)((options) => {
|
|
19451
|
+
const viewer = viewerRef.current;
|
|
19452
|
+
if (!viewer) {
|
|
19453
|
+
return Promise.reject(new Error("ModelViewer is not ready yet."));
|
|
19454
|
+
}
|
|
19455
|
+
return viewer.captureImage(options);
|
|
19456
|
+
}, []);
|
|
19457
|
+
const invalidate = (0, import_react52.useCallback)(() => {
|
|
19458
|
+
const viewer = viewerRef.current;
|
|
19459
|
+
if (!viewer) {
|
|
19460
|
+
return false;
|
|
19461
|
+
}
|
|
19462
|
+
viewer.invalidate();
|
|
19463
|
+
return true;
|
|
19464
|
+
}, []);
|
|
19465
|
+
return {
|
|
19466
|
+
handleViewerReady,
|
|
19467
|
+
getViewer,
|
|
19468
|
+
captureImage: captureImage2,
|
|
19469
|
+
invalidate
|
|
19470
|
+
};
|
|
19471
|
+
}
|
|
19472
|
+
|
|
19473
|
+
// src/hooks/useViewerEffects.ts
|
|
19474
|
+
var import_react53 = require("react");
|
|
19475
|
+
var import_three25 = require("three");
|
|
19476
|
+
var NUMERIC_MATERIAL_KEYS = [
|
|
19477
|
+
"emissiveIntensity",
|
|
19478
|
+
"opacity",
|
|
19479
|
+
"metalness",
|
|
19480
|
+
"roughness",
|
|
19481
|
+
"transmission",
|
|
19482
|
+
"clearcoat",
|
|
19483
|
+
"clearcoatRoughness",
|
|
19484
|
+
"ior",
|
|
19485
|
+
"thickness"
|
|
19486
|
+
];
|
|
19487
|
+
var nextEffectId = 1;
|
|
19488
|
+
function stringList(target) {
|
|
19489
|
+
return typeof target === "string" ? [target] : target;
|
|
19490
|
+
}
|
|
19491
|
+
function clamp2(value, min = 0, max = 1) {
|
|
19492
|
+
return Math.min(max, Math.max(min, value));
|
|
19493
|
+
}
|
|
19494
|
+
function randomBetween(min, max) {
|
|
19495
|
+
return min + Math.random() * (max - min);
|
|
19496
|
+
}
|
|
19497
|
+
function interpolate([from, to], level) {
|
|
19498
|
+
return from + (to - from) * level;
|
|
19499
|
+
}
|
|
19500
|
+
function interpolateVector([from, to], level) {
|
|
19501
|
+
return [
|
|
19502
|
+
from[0] + (to[0] - from[0]) * level,
|
|
19503
|
+
from[1] + (to[1] - from[1]) * level,
|
|
19504
|
+
from[2] + (to[2] - from[2]) * level
|
|
19505
|
+
];
|
|
19506
|
+
}
|
|
19507
|
+
function ease(value, easing) {
|
|
19508
|
+
const t = clamp2(value);
|
|
19509
|
+
switch (easing) {
|
|
19510
|
+
case "ease-in":
|
|
19511
|
+
return t * t;
|
|
19512
|
+
case "ease-out":
|
|
19513
|
+
return 1 - (1 - t) * (1 - t);
|
|
19514
|
+
case "ease-in-out":
|
|
19515
|
+
return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
|
|
19516
|
+
default:
|
|
19517
|
+
return t;
|
|
19518
|
+
}
|
|
19519
|
+
}
|
|
19520
|
+
function materialList(node) {
|
|
19521
|
+
const value = node.material;
|
|
19522
|
+
if (!value) return [];
|
|
19523
|
+
return (Array.isArray(value) ? value : [value]).filter(
|
|
19524
|
+
(entry) => entry instanceof import_three25.Material
|
|
19525
|
+
);
|
|
19526
|
+
}
|
|
19527
|
+
function readMaterial(material2) {
|
|
19528
|
+
const snapshot = {
|
|
19529
|
+
opacity: material2.opacity,
|
|
19530
|
+
transparent: material2.transparent
|
|
19531
|
+
};
|
|
19532
|
+
if (material2.color) snapshot.baseColor = `#${material2.color.getHexString()}`;
|
|
19533
|
+
if (material2.emissive) snapshot.emissive = `#${material2.emissive.getHexString()}`;
|
|
19534
|
+
for (const key of NUMERIC_MATERIAL_KEYS) {
|
|
19535
|
+
const value = material2[key];
|
|
19536
|
+
if (typeof value === "number") snapshot[key] = value;
|
|
19537
|
+
}
|
|
19538
|
+
return snapshot;
|
|
19539
|
+
}
|
|
19540
|
+
function applyMaterial(material2, patch) {
|
|
19541
|
+
if (patch.baseColor !== void 0 && material2.color) {
|
|
19542
|
+
material2.color.set(patch.baseColor);
|
|
19543
|
+
}
|
|
19544
|
+
if (patch.emissive !== void 0 && material2.emissive) {
|
|
19545
|
+
material2.emissive.set(patch.emissive);
|
|
19546
|
+
}
|
|
19547
|
+
for (const key of NUMERIC_MATERIAL_KEYS) {
|
|
19548
|
+
const value = patch[key];
|
|
19549
|
+
if (value !== void 0 && typeof material2[key] === "number") {
|
|
19550
|
+
material2[key] = value;
|
|
19551
|
+
}
|
|
19552
|
+
}
|
|
19553
|
+
if (patch.transparent !== void 0) material2.transparent = patch.transparent;
|
|
19554
|
+
material2.needsUpdate = true;
|
|
19555
|
+
}
|
|
19556
|
+
function readTransform(node) {
|
|
19557
|
+
return {
|
|
19558
|
+
position: [node.position.x, node.position.y, node.position.z],
|
|
19559
|
+
rotation: [node.rotation.x, node.rotation.y, node.rotation.z],
|
|
19560
|
+
scale: [node.scale.x, node.scale.y, node.scale.z],
|
|
19561
|
+
visible: node.visible
|
|
19562
|
+
};
|
|
19563
|
+
}
|
|
19564
|
+
function applyTransform(node, patch) {
|
|
19565
|
+
if (patch.position) node.position.set(...patch.position);
|
|
19566
|
+
if (patch.rotation) node.rotation.set(...patch.rotation);
|
|
19567
|
+
if (patch.scale !== void 0) {
|
|
19568
|
+
const scale = typeof patch.scale === "number" ? [patch.scale, patch.scale, patch.scale] : patch.scale;
|
|
19569
|
+
node.scale.set(...scale);
|
|
19570
|
+
}
|
|
19571
|
+
if (patch.visible !== void 0) node.visible = patch.visible;
|
|
19572
|
+
node.updateMatrix();
|
|
19573
|
+
node.updateMatrixWorld(true);
|
|
19574
|
+
}
|
|
19575
|
+
function useViewerEffects(options = {}) {
|
|
19576
|
+
const optionsRef = (0, import_react53.useRef)(options);
|
|
19577
|
+
optionsRef.current = options;
|
|
19578
|
+
const viewerRef = (0, import_react53.useRef)(null);
|
|
19579
|
+
const sceneRef = (0, import_react53.useRef)(null);
|
|
19580
|
+
const frameRef = (0, import_react53.useRef)(null);
|
|
19581
|
+
const effectsRef = (0, import_react53.useRef)(/* @__PURE__ */ new Map());
|
|
19582
|
+
const lightsRef = (0, import_react53.useRef)(/* @__PURE__ */ new Map());
|
|
19583
|
+
const materialSnapshotsRef = (0, import_react53.useRef)(
|
|
19584
|
+
/* @__PURE__ */ new Map()
|
|
19585
|
+
);
|
|
19586
|
+
const transformSnapshotsRef = (0, import_react53.useRef)(/* @__PURE__ */ new Map());
|
|
19587
|
+
const resolveObject = (0, import_react53.useCallback)((target) => {
|
|
19588
|
+
const viewer = viewerRef.current;
|
|
19589
|
+
if (!viewer) return null;
|
|
19590
|
+
const bindingKey = resolveBindingKey(viewer.objectBindings, target);
|
|
19591
|
+
return viewer.nodeRefs[target] ?? (bindingKey ? viewer.nodeRefs[bindingKey] : null) ?? viewer.scene?.getObjectByName(target) ?? null;
|
|
19592
|
+
}, []);
|
|
19593
|
+
const resolveTargets = (0, import_react53.useCallback)(
|
|
19594
|
+
(target) => {
|
|
19595
|
+
const viewer = viewerRef.current;
|
|
19596
|
+
if (!viewer) {
|
|
19597
|
+
return typeof target === "string" ? [target] : Array.isArray(target) ? target : [];
|
|
19598
|
+
}
|
|
19599
|
+
if (typeof target === "object" && !Array.isArray(target)) {
|
|
19600
|
+
return selectBindingKeys(viewer.objectBindings, target);
|
|
19601
|
+
}
|
|
19602
|
+
return stringList(target).map(
|
|
19603
|
+
(id) => resolveBindingKey(viewer.objectBindings, id) ?? id
|
|
19604
|
+
);
|
|
19605
|
+
},
|
|
19606
|
+
[]
|
|
19607
|
+
);
|
|
19608
|
+
const invalidate = (0, import_react53.useCallback)(() => {
|
|
19609
|
+
viewerRef.current?.invalidate();
|
|
19610
|
+
}, []);
|
|
19611
|
+
const setMaterialInternal = (0, import_react53.useCallback)(
|
|
19612
|
+
(target, patch) => {
|
|
19613
|
+
let changed = false;
|
|
19614
|
+
for (const id of resolveTargets(target)) {
|
|
19615
|
+
const node = resolveObject(id);
|
|
19616
|
+
if (!node) continue;
|
|
19617
|
+
for (const entry of materialList(node)) {
|
|
19618
|
+
if (!materialSnapshotsRef.current.has(entry)) {
|
|
19619
|
+
materialSnapshotsRef.current.set(entry, readMaterial(entry));
|
|
19620
|
+
}
|
|
19621
|
+
applyMaterial(entry, patch);
|
|
19622
|
+
changed = true;
|
|
19623
|
+
}
|
|
19624
|
+
}
|
|
19625
|
+
return changed;
|
|
19626
|
+
},
|
|
19627
|
+
[resolveObject, resolveTargets]
|
|
19628
|
+
);
|
|
19629
|
+
const setMaterial = (0, import_react53.useCallback)(
|
|
19630
|
+
(target, patch) => {
|
|
19631
|
+
const changed = setMaterialInternal(target, patch);
|
|
19632
|
+
if (changed) invalidate();
|
|
19633
|
+
return changed;
|
|
19634
|
+
},
|
|
19635
|
+
[invalidate, setMaterialInternal]
|
|
19636
|
+
);
|
|
19637
|
+
const setTransformInternal = (0, import_react53.useCallback)(
|
|
19638
|
+
(target, patch) => {
|
|
19639
|
+
let changed = false;
|
|
19640
|
+
for (const id of resolveTargets(target)) {
|
|
19641
|
+
const node = resolveObject(id);
|
|
19642
|
+
if (!node) continue;
|
|
19643
|
+
if (!transformSnapshotsRef.current.has(node)) {
|
|
19644
|
+
transformSnapshotsRef.current.set(node, readTransform(node));
|
|
19645
|
+
}
|
|
19646
|
+
applyTransform(node, patch);
|
|
19647
|
+
changed = true;
|
|
19648
|
+
}
|
|
19649
|
+
return changed;
|
|
19650
|
+
},
|
|
19651
|
+
[resolveObject, resolveTargets]
|
|
19652
|
+
);
|
|
19653
|
+
const setTransform = (0, import_react53.useCallback)(
|
|
19654
|
+
(target, patch) => {
|
|
19655
|
+
const changed = setTransformInternal(target, patch);
|
|
19656
|
+
if (changed) invalidate();
|
|
19657
|
+
return changed;
|
|
19658
|
+
},
|
|
19659
|
+
[invalidate, setTransformInternal]
|
|
19660
|
+
);
|
|
19661
|
+
const getWorldPosition = (0, import_react53.useCallback)(
|
|
19662
|
+
(target) => {
|
|
19663
|
+
const node = resolveObject(target);
|
|
19664
|
+
if (!node) return null;
|
|
19665
|
+
node.updateWorldMatrix(true, false);
|
|
19666
|
+
const position = node.getWorldPosition(new import_three25.Vector3());
|
|
19667
|
+
return [position.x, position.y, position.z];
|
|
19668
|
+
},
|
|
19669
|
+
[resolveObject]
|
|
19670
|
+
);
|
|
19671
|
+
const removePointLight = (0, import_react53.useCallback)(
|
|
19672
|
+
(id) => {
|
|
19673
|
+
const runtime = lightsRef.current.get(id);
|
|
19674
|
+
if (!runtime) return false;
|
|
19675
|
+
runtime.light.removeFromParent();
|
|
19676
|
+
runtime.light.dispose();
|
|
19677
|
+
lightsRef.current.delete(id);
|
|
19678
|
+
invalidate();
|
|
19679
|
+
return true;
|
|
19680
|
+
},
|
|
19681
|
+
[invalidate]
|
|
19682
|
+
);
|
|
19683
|
+
const addPointLight = (0, import_react53.useCallback)(
|
|
19684
|
+
(lightOptions) => {
|
|
19685
|
+
const target = resolveObject(lightOptions.target);
|
|
19686
|
+
if (!target) return false;
|
|
19687
|
+
const existing = lightsRef.current.get(lightOptions.id);
|
|
19688
|
+
const light = existing?.light ?? new import_three25.PointLight();
|
|
19689
|
+
if (existing && existing.target !== lightOptions.target) {
|
|
19690
|
+
light.removeFromParent();
|
|
19691
|
+
}
|
|
19692
|
+
light.name = `react-immersive-runtime-light:${lightOptions.id}`;
|
|
19693
|
+
light.color.set(lightOptions.color ?? "#ffffff");
|
|
19694
|
+
light.intensity = lightOptions.intensity ?? 1;
|
|
19695
|
+
light.distance = lightOptions.distance ?? 0;
|
|
19696
|
+
light.decay = lightOptions.decay ?? 2;
|
|
19697
|
+
light.castShadow = lightOptions.castShadow ?? false;
|
|
19698
|
+
light.position.set(...lightOptions.offset ?? [0, 0, 0]);
|
|
19699
|
+
if (light.parent !== target) target.add(light);
|
|
19700
|
+
lightsRef.current.set(lightOptions.id, {
|
|
19701
|
+
light,
|
|
19702
|
+
target: lightOptions.target
|
|
19703
|
+
});
|
|
19704
|
+
invalidate();
|
|
19705
|
+
return true;
|
|
19706
|
+
},
|
|
19707
|
+
[invalidate, resolveObject]
|
|
19708
|
+
);
|
|
19709
|
+
const updatePointLight = (0, import_react53.useCallback)(
|
|
19710
|
+
(id, patch) => {
|
|
19711
|
+
const runtime = lightsRef.current.get(id);
|
|
19712
|
+
if (!runtime) return false;
|
|
19713
|
+
if (patch.target && patch.target !== runtime.target) {
|
|
19714
|
+
const target = resolveObject(patch.target);
|
|
19715
|
+
if (!target) return false;
|
|
19716
|
+
runtime.light.removeFromParent();
|
|
19717
|
+
target.add(runtime.light);
|
|
19718
|
+
runtime.target = patch.target;
|
|
19719
|
+
}
|
|
19720
|
+
if (patch.color !== void 0) runtime.light.color.set(patch.color);
|
|
19721
|
+
if (patch.intensity !== void 0) {
|
|
19722
|
+
runtime.light.intensity = patch.intensity;
|
|
19723
|
+
}
|
|
19724
|
+
if (patch.distance !== void 0) runtime.light.distance = patch.distance;
|
|
19725
|
+
if (patch.decay !== void 0) runtime.light.decay = patch.decay;
|
|
19726
|
+
if (patch.offset !== void 0) {
|
|
19727
|
+
runtime.light.position.set(...patch.offset);
|
|
19728
|
+
}
|
|
19729
|
+
if (patch.castShadow !== void 0) {
|
|
19730
|
+
runtime.light.castShadow = patch.castShadow;
|
|
19731
|
+
}
|
|
19732
|
+
invalidate();
|
|
19733
|
+
return true;
|
|
19734
|
+
},
|
|
19735
|
+
[invalidate, resolveObject]
|
|
19736
|
+
);
|
|
19737
|
+
const applyChannels = (0, import_react53.useCallback)(
|
|
19738
|
+
(channels, levels) => {
|
|
19739
|
+
const levelFor = (index) => levels[index % levels.length] ?? 0;
|
|
19740
|
+
for (const channel of channels.materials ?? []) {
|
|
19741
|
+
resolveTargets(channel.targets).forEach((target, index) => {
|
|
19742
|
+
const level = levelFor(index);
|
|
19743
|
+
const patch = {};
|
|
19744
|
+
for (const key of NUMERIC_MATERIAL_KEYS) {
|
|
19745
|
+
const range = channel.values[key];
|
|
19746
|
+
if (range) patch[key] = interpolate(range, level);
|
|
19747
|
+
}
|
|
19748
|
+
setMaterialInternal(target, patch);
|
|
19749
|
+
});
|
|
19750
|
+
}
|
|
19751
|
+
for (const channel of channels.transforms ?? []) {
|
|
19752
|
+
resolveTargets(channel.targets).forEach((target, index) => {
|
|
19753
|
+
const level = levelFor(index);
|
|
19754
|
+
const patch = {};
|
|
19755
|
+
if (channel.values.position) {
|
|
19756
|
+
patch.position = interpolateVector(channel.values.position, level);
|
|
19757
|
+
}
|
|
19758
|
+
if (channel.values.rotation) {
|
|
19759
|
+
patch.rotation = interpolateVector(channel.values.rotation, level);
|
|
19760
|
+
}
|
|
19761
|
+
if (channel.values.scale) {
|
|
19762
|
+
patch.scale = interpolateVector(channel.values.scale, level);
|
|
19763
|
+
}
|
|
19764
|
+
setTransformInternal(target, patch);
|
|
19765
|
+
});
|
|
19766
|
+
}
|
|
19767
|
+
for (const channel of channels.lights ?? []) {
|
|
19768
|
+
stringList(channel.lights).forEach((id, index) => {
|
|
19769
|
+
const runtime = lightsRef.current.get(id);
|
|
19770
|
+
if (runtime) {
|
|
19771
|
+
runtime.light.intensity = interpolate(
|
|
19772
|
+
channel.intensity,
|
|
19773
|
+
levelFor(index)
|
|
19774
|
+
);
|
|
19775
|
+
}
|
|
19776
|
+
});
|
|
19777
|
+
}
|
|
19778
|
+
},
|
|
19779
|
+
[resolveTargets, setMaterialInternal, setTransformInternal]
|
|
19780
|
+
);
|
|
19781
|
+
const scheduleFrameRef = (0, import_react53.useRef)(() => void 0);
|
|
19782
|
+
const runFrameRef = (0, import_react53.useRef)(() => void 0);
|
|
19783
|
+
runFrameRef.current = (now) => {
|
|
19784
|
+
frameRef.current = null;
|
|
19785
|
+
let rendered = false;
|
|
19786
|
+
for (const [id, effect] of effectsRef.current) {
|
|
19787
|
+
if (effect.startedAt === null) effect.startedAt = now;
|
|
19788
|
+
const previous = effect.lastFrameAt ?? now;
|
|
19789
|
+
effect.lastFrameAt = now;
|
|
19790
|
+
const override = effect.override && effect.override.until > now ? effect.override.level : null;
|
|
19791
|
+
if (effect.override && override === null) effect.override = null;
|
|
19792
|
+
const keepRunning = effect.update(
|
|
19793
|
+
now,
|
|
19794
|
+
now - effect.startedAt,
|
|
19795
|
+
Math.min(100, now - previous),
|
|
19796
|
+
override
|
|
19797
|
+
);
|
|
19798
|
+
rendered = true;
|
|
19799
|
+
if (!keepRunning) {
|
|
19800
|
+
effectsRef.current.delete(id);
|
|
19801
|
+
effect.onComplete?.();
|
|
19802
|
+
}
|
|
19803
|
+
}
|
|
19804
|
+
if (rendered) invalidate();
|
|
19805
|
+
if (effectsRef.current.size > 0) scheduleFrameRef.current();
|
|
19806
|
+
};
|
|
19807
|
+
scheduleFrameRef.current = () => {
|
|
19808
|
+
if (frameRef.current !== null || typeof requestAnimationFrame === "undefined") {
|
|
19809
|
+
return;
|
|
19810
|
+
}
|
|
19811
|
+
frameRef.current = requestAnimationFrame((now) => runFrameRef.current(now));
|
|
19812
|
+
};
|
|
19813
|
+
const registerEffect = (0, import_react53.useCallback)((effect) => {
|
|
19814
|
+
effectsRef.current.set(effect.id, effect);
|
|
19815
|
+
scheduleFrameRef.current();
|
|
19816
|
+
return effect.id;
|
|
19817
|
+
}, []);
|
|
19818
|
+
const transition = (0, import_react53.useCallback)(
|
|
19819
|
+
(channels, transitionOptions = {}) => {
|
|
19820
|
+
const id = transitionOptions.id ?? `transition-${nextEffectId++}`;
|
|
19821
|
+
const duration = Math.max(1, transitionOptions.duration ?? 400);
|
|
19822
|
+
const easing = transitionOptions.easing ?? "ease-in-out";
|
|
19823
|
+
return registerEffect({
|
|
19824
|
+
id,
|
|
19825
|
+
startedAt: null,
|
|
19826
|
+
lastFrameAt: null,
|
|
19827
|
+
override: null,
|
|
19828
|
+
onComplete: transitionOptions.onComplete,
|
|
19829
|
+
update: (_now, elapsed, _delta, override) => {
|
|
19830
|
+
const progress = override ?? ease(elapsed / duration, easing);
|
|
19831
|
+
applyChannels(channels, [progress]);
|
|
19832
|
+
return elapsed < duration;
|
|
19833
|
+
}
|
|
19834
|
+
});
|
|
19835
|
+
},
|
|
19836
|
+
[applyChannels, registerEffect]
|
|
19837
|
+
);
|
|
19838
|
+
const pulse = (0, import_react53.useCallback)(
|
|
19839
|
+
(channels, pulseOptions = {}) => {
|
|
19840
|
+
const id = pulseOptions.id ?? `pulse-${nextEffectId++}`;
|
|
19841
|
+
const duration = Math.max(1, pulseOptions.duration ?? 1e3);
|
|
19842
|
+
const minLevel = pulseOptions.minLevel ?? 0;
|
|
19843
|
+
const maxLevel = pulseOptions.maxLevel ?? 1;
|
|
19844
|
+
const repeat = pulseOptions.repeat ?? Infinity;
|
|
19845
|
+
const synchronized = pulseOptions.synchronized ?? true;
|
|
19846
|
+
const unitCount = Math.max(
|
|
19847
|
+
1,
|
|
19848
|
+
...[...channels.materials ?? [], ...channels.transforms ?? []].map(
|
|
19849
|
+
(channel) => resolveTargets(channel.targets).length
|
|
19850
|
+
),
|
|
19851
|
+
...(channels.lights ?? []).map(
|
|
19852
|
+
(channel) => stringList(channel.lights).length
|
|
19853
|
+
)
|
|
19854
|
+
);
|
|
19855
|
+
return registerEffect({
|
|
19856
|
+
id,
|
|
19857
|
+
startedAt: null,
|
|
19858
|
+
lastFrameAt: null,
|
|
19859
|
+
override: null,
|
|
19860
|
+
onComplete: pulseOptions.onComplete,
|
|
19861
|
+
update: (_now, elapsed, _delta, override) => {
|
|
19862
|
+
const cycles = elapsed / duration;
|
|
19863
|
+
const levels = Array.from(
|
|
19864
|
+
{ length: synchronized ? 1 : unitCount },
|
|
19865
|
+
(_, index) => {
|
|
19866
|
+
if (override !== null) return override;
|
|
19867
|
+
const phase = cycles + (synchronized ? 0 : index / unitCount);
|
|
19868
|
+
const wave = 0.5 - Math.cos(phase % 1 * Math.PI * 2) / 2;
|
|
19869
|
+
return minLevel + (maxLevel - minLevel) * wave;
|
|
19870
|
+
}
|
|
19871
|
+
);
|
|
19872
|
+
applyChannels(channels, levels);
|
|
19873
|
+
return repeat === Infinity || cycles < repeat;
|
|
19874
|
+
}
|
|
19875
|
+
});
|
|
19876
|
+
},
|
|
19877
|
+
[applyChannels, registerEffect, resolveTargets]
|
|
19878
|
+
);
|
|
19879
|
+
const timeline = (0, import_react53.useCallback)(
|
|
19880
|
+
(steps, timelineOptions = {}) => {
|
|
19881
|
+
const id = timelineOptions.id ?? `timeline-${nextEffectId++}`;
|
|
19882
|
+
const repeat = timelineOptions.repeat ?? 1;
|
|
19883
|
+
const normalizedSteps = steps.map((step) => ({
|
|
19884
|
+
...step,
|
|
19885
|
+
duration: Math.max(1, step.duration),
|
|
19886
|
+
hold: Math.max(0, step.hold ?? 0)
|
|
19887
|
+
}));
|
|
19888
|
+
const cycleDuration = Math.max(
|
|
19889
|
+
1,
|
|
19890
|
+
normalizedSteps.reduce(
|
|
19891
|
+
(total, step) => total + step.duration + step.hold,
|
|
19892
|
+
0
|
|
19893
|
+
)
|
|
19894
|
+
);
|
|
19895
|
+
return registerEffect({
|
|
19896
|
+
id,
|
|
19897
|
+
startedAt: null,
|
|
19898
|
+
lastFrameAt: null,
|
|
19899
|
+
override: null,
|
|
19900
|
+
onComplete: timelineOptions.onComplete,
|
|
19901
|
+
update: (_now, elapsed, _delta, override) => {
|
|
19902
|
+
if (normalizedSteps.length === 0 || repeat <= 0) return false;
|
|
19903
|
+
const cycles = elapsed / cycleDuration;
|
|
19904
|
+
if (repeat !== Infinity && cycles >= repeat) {
|
|
19905
|
+
for (const step of normalizedSteps) {
|
|
19906
|
+
applyChannels(step.channels, [1]);
|
|
19907
|
+
}
|
|
19908
|
+
return false;
|
|
19909
|
+
}
|
|
19910
|
+
const cycleTime = elapsed % cycleDuration;
|
|
19911
|
+
let cursor = 0;
|
|
19912
|
+
for (const step of normalizedSteps) {
|
|
19913
|
+
const transitionEnd = cursor + step.duration;
|
|
19914
|
+
const stepEnd = transitionEnd + step.hold;
|
|
19915
|
+
if (cycleTime >= transitionEnd) {
|
|
19916
|
+
applyChannels(step.channels, [override ?? 1]);
|
|
19917
|
+
cursor = stepEnd;
|
|
19918
|
+
continue;
|
|
19919
|
+
}
|
|
19920
|
+
if (cycleTime >= cursor) {
|
|
19921
|
+
const progress = override ?? ease(
|
|
19922
|
+
(cycleTime - cursor) / step.duration,
|
|
19923
|
+
step.easing ?? "ease-in-out"
|
|
19924
|
+
);
|
|
19925
|
+
applyChannels(step.channels, [progress]);
|
|
19926
|
+
break;
|
|
19927
|
+
}
|
|
19928
|
+
}
|
|
19929
|
+
return true;
|
|
19930
|
+
}
|
|
19931
|
+
});
|
|
19932
|
+
},
|
|
19933
|
+
[applyChannels, registerEffect]
|
|
19934
|
+
);
|
|
19935
|
+
const flicker = (0, import_react53.useCallback)(
|
|
19936
|
+
(channels, flickerOptions = {}) => {
|
|
19937
|
+
const id = flickerOptions.id ?? `flicker-${nextEffectId++}`;
|
|
19938
|
+
const synchronized = flickerOptions.synchronized ?? true;
|
|
19939
|
+
const minLevel = flickerOptions.minLevel ?? 0.15;
|
|
19940
|
+
const maxLevel = flickerOptions.maxLevel ?? 1;
|
|
19941
|
+
const interval = flickerOptions.interval ?? [45, 160];
|
|
19942
|
+
const rawIntervalRange = typeof interval === "number" ? [interval, interval] : interval;
|
|
19943
|
+
const intervalRange = [
|
|
19944
|
+
Math.max(1, Math.min(...rawIntervalRange)),
|
|
19945
|
+
Math.max(1, Math.max(...rawIntervalRange))
|
|
19946
|
+
];
|
|
19947
|
+
const response = clamp2(flickerOptions.response ?? 0.7, 0.01, 1);
|
|
19948
|
+
const blackoutChance = clamp2(flickerOptions.blackoutChance ?? 0.08);
|
|
19949
|
+
const flashChance = clamp2(flickerOptions.flashChance ?? 0.12);
|
|
19950
|
+
const flashLevel = flickerOptions.flashLevel ?? 1.15;
|
|
19951
|
+
const unitCount = Math.max(
|
|
19952
|
+
1,
|
|
19953
|
+
...[...channels.materials ?? [], ...channels.transforms ?? []].map(
|
|
19954
|
+
(channel) => resolveTargets(channel.targets).length
|
|
19955
|
+
),
|
|
19956
|
+
...(channels.lights ?? []).map(
|
|
19957
|
+
(channel) => stringList(channel.lights).length
|
|
19958
|
+
)
|
|
19959
|
+
);
|
|
19960
|
+
const count = synchronized ? 1 : unitCount;
|
|
19961
|
+
const levels = Array.from({ length: count }, () => minLevel);
|
|
19962
|
+
const targets = Array.from({ length: count }, () => minLevel);
|
|
19963
|
+
const nextChanges = Array.from({ length: count }, () => 0);
|
|
19964
|
+
return registerEffect({
|
|
19965
|
+
id,
|
|
19966
|
+
startedAt: null,
|
|
19967
|
+
lastFrameAt: null,
|
|
19968
|
+
override: null,
|
|
19969
|
+
onComplete: flickerOptions.onComplete,
|
|
19970
|
+
update: (now, elapsed, delta, override) => {
|
|
19971
|
+
for (let index = 0; index < count; index += 1) {
|
|
19972
|
+
if (override !== null) {
|
|
19973
|
+
targets[index] = override;
|
|
19974
|
+
} else if (now >= nextChanges[index]) {
|
|
19975
|
+
const roll = Math.random();
|
|
19976
|
+
targets[index] = roll < blackoutChance ? randomBetween(0, Math.min(0.04, minLevel)) : roll < blackoutChance + flashChance ? flashLevel : randomBetween(minLevel, maxLevel);
|
|
19977
|
+
nextChanges[index] = now + randomBetween(...intervalRange);
|
|
19978
|
+
}
|
|
19979
|
+
const frameResponse = 1 - Math.pow(1 - response, Math.max(1, delta) / 16.67);
|
|
19980
|
+
levels[index] += (targets[index] - levels[index]) * frameResponse;
|
|
19981
|
+
}
|
|
19982
|
+
applyChannels(channels, levels);
|
|
19983
|
+
return flickerOptions.duration === void 0 || elapsed < flickerOptions.duration;
|
|
19984
|
+
}
|
|
19985
|
+
});
|
|
19986
|
+
},
|
|
19987
|
+
[applyChannels, registerEffect, resolveTargets]
|
|
19988
|
+
);
|
|
19989
|
+
const overrideEffect = (0, import_react53.useCallback)(
|
|
19990
|
+
(id, level, duration) => {
|
|
19991
|
+
const effect = effectsRef.current.get(id);
|
|
19992
|
+
if (!effect) return false;
|
|
19993
|
+
const now = typeof performance === "undefined" ? Date.now() : performance.now();
|
|
19994
|
+
effect.override = { level, until: now + Math.max(0, duration) };
|
|
19995
|
+
scheduleFrameRef.current();
|
|
19996
|
+
return true;
|
|
19997
|
+
},
|
|
19998
|
+
[]
|
|
19999
|
+
);
|
|
20000
|
+
const stopEffect = (0, import_react53.useCallback)((id) => {
|
|
20001
|
+
return effectsRef.current.delete(id);
|
|
20002
|
+
}, []);
|
|
20003
|
+
const stopAllEffects = (0, import_react53.useCallback)(() => {
|
|
20004
|
+
effectsRef.current.clear();
|
|
20005
|
+
if (frameRef.current !== null && typeof cancelAnimationFrame !== "undefined") {
|
|
20006
|
+
cancelAnimationFrame(frameRef.current);
|
|
20007
|
+
frameRef.current = null;
|
|
20008
|
+
}
|
|
20009
|
+
}, []);
|
|
20010
|
+
const restoreRuntimeValues = (0, import_react53.useCallback)(() => {
|
|
20011
|
+
for (const [entry, snapshot] of materialSnapshotsRef.current) {
|
|
20012
|
+
applyMaterial(entry, snapshot);
|
|
20013
|
+
}
|
|
20014
|
+
materialSnapshotsRef.current.clear();
|
|
20015
|
+
for (const [node, snapshot] of transformSnapshotsRef.current) {
|
|
20016
|
+
node.position.set(...snapshot.position);
|
|
20017
|
+
node.rotation.set(...snapshot.rotation);
|
|
20018
|
+
node.scale.set(...snapshot.scale);
|
|
20019
|
+
node.visible = snapshot.visible;
|
|
20020
|
+
node.updateMatrix();
|
|
20021
|
+
node.updateMatrixWorld(true);
|
|
20022
|
+
}
|
|
20023
|
+
transformSnapshotsRef.current.clear();
|
|
20024
|
+
for (const runtime of lightsRef.current.values()) {
|
|
20025
|
+
runtime.light.removeFromParent();
|
|
20026
|
+
runtime.light.dispose();
|
|
20027
|
+
}
|
|
20028
|
+
lightsRef.current.clear();
|
|
20029
|
+
invalidate();
|
|
20030
|
+
}, [invalidate]);
|
|
20031
|
+
const resetRuntime = (0, import_react53.useCallback)(() => {
|
|
20032
|
+
stopAllEffects();
|
|
20033
|
+
restoreRuntimeValues();
|
|
20034
|
+
}, [restoreRuntimeValues, stopAllEffects]);
|
|
20035
|
+
const handleViewerReady = (0, import_react53.useCallback)(
|
|
20036
|
+
(viewer) => {
|
|
20037
|
+
if (sceneRef.current && sceneRef.current !== viewer.scene) {
|
|
20038
|
+
restoreRuntimeValues();
|
|
20039
|
+
}
|
|
20040
|
+
viewerRef.current = viewer;
|
|
20041
|
+
sceneRef.current = viewer.scene;
|
|
20042
|
+
optionsRef.current.onViewerReady?.(viewer);
|
|
20043
|
+
},
|
|
20044
|
+
[restoreRuntimeValues]
|
|
20045
|
+
);
|
|
20046
|
+
(0, import_react53.useEffect)(() => {
|
|
20047
|
+
return () => {
|
|
20048
|
+
resetRuntime();
|
|
20049
|
+
viewerRef.current = null;
|
|
20050
|
+
sceneRef.current = null;
|
|
20051
|
+
};
|
|
20052
|
+
}, [resetRuntime]);
|
|
20053
|
+
return {
|
|
20054
|
+
handleViewerReady,
|
|
20055
|
+
setMaterial,
|
|
20056
|
+
setTransform,
|
|
20057
|
+
getWorldPosition,
|
|
20058
|
+
addPointLight,
|
|
20059
|
+
updatePointLight,
|
|
20060
|
+
removePointLight,
|
|
20061
|
+
transition,
|
|
20062
|
+
timeline,
|
|
20063
|
+
pulse,
|
|
20064
|
+
flicker,
|
|
20065
|
+
overrideEffect,
|
|
20066
|
+
stopEffect,
|
|
20067
|
+
stopAllEffects,
|
|
20068
|
+
resetRuntime
|
|
20069
|
+
};
|
|
20070
|
+
}
|
|
20071
|
+
|
|
18775
20072
|
// src/hooks/useShareableViewerState.ts
|
|
18776
|
-
var
|
|
20073
|
+
var import_react54 = require("react");
|
|
18777
20074
|
function serializeState(cameraState, objectBindings, selectedObjectBinding) {
|
|
18778
20075
|
const hidden = Object.values(objectBindings).filter((binding) => binding.visible === false).map((binding) => binding.id);
|
|
18779
20076
|
return {
|
|
@@ -18793,8 +20090,8 @@ function useShareableViewerState({
|
|
|
18793
20090
|
debounceMs = 400,
|
|
18794
20091
|
autoSync = true
|
|
18795
20092
|
}) {
|
|
18796
|
-
const lastWrittenRawRef = (0,
|
|
18797
|
-
const buildHash = (0,
|
|
20093
|
+
const lastWrittenRawRef = (0, import_react54.useRef)(null);
|
|
20094
|
+
const buildHash = (0, import_react54.useCallback)(() => {
|
|
18798
20095
|
const state = serializeState(
|
|
18799
20096
|
cameraState,
|
|
18800
20097
|
objectBindings,
|
|
@@ -18802,12 +20099,12 @@ function useShareableViewerState({
|
|
|
18802
20099
|
);
|
|
18803
20100
|
return `${paramKey}=${encodeURIComponent(JSON.stringify(state))}`;
|
|
18804
20101
|
}, [cameraState, objectBindings, selectedObjectBinding, paramKey]);
|
|
18805
|
-
const getShareUrl = (0,
|
|
20102
|
+
const getShareUrl = (0, import_react54.useCallback)(() => {
|
|
18806
20103
|
const url = new URL(window.location.href);
|
|
18807
20104
|
url.hash = buildHash();
|
|
18808
20105
|
return url.toString();
|
|
18809
20106
|
}, [buildHash]);
|
|
18810
|
-
const syncUrl = (0,
|
|
20107
|
+
const syncUrl = (0, import_react54.useCallback)(() => {
|
|
18811
20108
|
const url = new URL(window.location.href);
|
|
18812
20109
|
url.hash = buildHash();
|
|
18813
20110
|
window.history.replaceState(null, "", url.toString());
|
|
@@ -18815,7 +20112,7 @@ function useShareableViewerState({
|
|
|
18815
20112
|
paramKey
|
|
18816
20113
|
);
|
|
18817
20114
|
}, [buildHash, paramKey]);
|
|
18818
|
-
const latestStateRef = (0,
|
|
20115
|
+
const latestStateRef = (0, import_react54.useRef)({
|
|
18819
20116
|
cameraState,
|
|
18820
20117
|
objectBindings,
|
|
18821
20118
|
selectedObjectBinding
|
|
@@ -18825,9 +20122,9 @@ function useShareableViewerState({
|
|
|
18825
20122
|
objectBindings,
|
|
18826
20123
|
selectedObjectBinding
|
|
18827
20124
|
};
|
|
18828
|
-
const [restoreAttempted, setRestoreAttempted] = (0,
|
|
18829
|
-
const lastSyncedHashRef = (0,
|
|
18830
|
-
(0,
|
|
20125
|
+
const [restoreAttempted, setRestoreAttempted] = (0, import_react54.useState)(false);
|
|
20126
|
+
const lastSyncedHashRef = (0, import_react54.useRef)(null);
|
|
20127
|
+
(0, import_react54.useEffect)(() => {
|
|
18831
20128
|
if (!autoSync || !restoreAttempted) return;
|
|
18832
20129
|
const tick = () => {
|
|
18833
20130
|
const current = latestStateRef.current;
|
|
@@ -18849,9 +20146,9 @@ function useShareableViewerState({
|
|
|
18849
20146
|
const id = window.setInterval(tick, debounceMs);
|
|
18850
20147
|
return () => window.clearInterval(id);
|
|
18851
20148
|
}, [autoSync, restoreAttempted, debounceMs, paramKey]);
|
|
18852
|
-
const lastRestoredRawRef = (0,
|
|
18853
|
-
const restoringRawRef = (0,
|
|
18854
|
-
const restoreFromUrl = (0,
|
|
20149
|
+
const lastRestoredRawRef = (0, import_react54.useRef)(null);
|
|
20150
|
+
const restoringRawRef = (0, import_react54.useRef)(null);
|
|
20151
|
+
const restoreFromUrl = (0, import_react54.useCallback)(async () => {
|
|
18855
20152
|
setRestoreAttempted(true);
|
|
18856
20153
|
const rawHash = window.location.hash.startsWith("#") ? window.location.hash.slice(1) : window.location.hash;
|
|
18857
20154
|
const raw = new URLSearchParams(rawHash).get(paramKey);
|
|
@@ -18904,7 +20201,7 @@ function useShareableViewerState({
|
|
|
18904
20201
|
setCameraState,
|
|
18905
20202
|
setSelectedObjectBinding
|
|
18906
20203
|
]);
|
|
18907
|
-
(0,
|
|
20204
|
+
(0, import_react54.useEffect)(() => {
|
|
18908
20205
|
const handleHashChange = () => {
|
|
18909
20206
|
void restoreFromUrl();
|
|
18910
20207
|
};
|
|
@@ -18918,13 +20215,17 @@ function useShareableViewerState({
|
|
|
18918
20215
|
// Annotate the CommonJS export names for ESM import in node:
|
|
18919
20216
|
0 && (module.exports = {
|
|
18920
20217
|
BindingBuilder,
|
|
20218
|
+
DEFAULT_SCENE_CONFIG,
|
|
18921
20219
|
MATERIAL_BLENDING_MODES,
|
|
18922
20220
|
MATERIAL_SIDES,
|
|
18923
20221
|
ModelViewer,
|
|
18924
20222
|
SimpleModelViewer,
|
|
20223
|
+
defineObjectBindings,
|
|
18925
20224
|
material,
|
|
18926
20225
|
patchBindings,
|
|
18927
20226
|
patchSceneConfig,
|
|
20227
|
+
selectBindingKeys,
|
|
20228
|
+
selectBindings,
|
|
18928
20229
|
useObjectBinding,
|
|
18929
20230
|
useObjectBindingIds,
|
|
18930
20231
|
useObjectBindings,
|
|
@@ -18933,6 +20234,8 @@ function useShareableViewerState({
|
|
|
18933
20234
|
useViewerActions,
|
|
18934
20235
|
useViewerAnimations,
|
|
18935
20236
|
useViewerCamera,
|
|
20237
|
+
useViewerConnection,
|
|
20238
|
+
useViewerEffects,
|
|
18936
20239
|
useViewerHover,
|
|
18937
20240
|
useViewerModel,
|
|
18938
20241
|
useViewerSelection
|