@lingbi/studio 0.8.2 → 0.9.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/Application.d.ts +3 -0
- package/ImportProgressCompositor.d.ts +13 -0
- package/StudioToolbarState.d.ts +2 -0
- package/StudioWorkspace.d.ts +5 -0
- package/archive/ArchiveImportWorkerClient.d.ts +3 -1
- package/archive/ArchiveImportWorkerMessage.d.ts +7 -1
- package/archive/ArchiveImportWorkerMessageParser.d.ts +2 -0
- package/archive/ArchiveImportWorkerRuntime.d.ts +3 -0
- package/assets/{ArchiveImportWorkerEntry-Dh4RZo22.js → ArchiveImportWorkerEntry-wkxjE56k.js} +36 -0
- package/assets/{StabilityWorkerEntry-CXCNaUyw.js → StabilityWorkerEntry-L0_8iZmQ.js} +13327 -13205
- package/index.js +675 -107
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -29202,7 +29202,8 @@ const MINIMUM_FRUSTUM_HEIGHT = 1e-4;
|
|
|
29202
29202
|
const MINIMUM_NEAR = 0.01;
|
|
29203
29203
|
const MINIMUM_ZOOM = 0.01;
|
|
29204
29204
|
const MAXIMUM_ZOOM = 100;
|
|
29205
|
-
const CLIP_MARGIN_RATIO = 0.
|
|
29205
|
+
const CLIP_MARGIN_RATIO = 0.5;
|
|
29206
|
+
const VIEW_MARGIN_RATIO = 1.3;
|
|
29206
29207
|
const WHEEL_ZOOM_SPEED = 15e-4;
|
|
29207
29208
|
var CameraNavigationMode = /* @__PURE__ */ ((CameraNavigationMode2) => {
|
|
29208
29209
|
CameraNavigationMode2[CameraNavigationMode2["Rotate"] = 0] = "Rotate";
|
|
@@ -29716,7 +29717,7 @@ class CameraController {
|
|
|
29716
29717
|
);
|
|
29717
29718
|
const frustumHeight = this.getFullFrustumHeight(safeFrustumHeight);
|
|
29718
29719
|
const depthRange = maximumForward - minimumForward;
|
|
29719
|
-
const clipMargin =
|
|
29720
|
+
const clipMargin = this.createClipMargin(depthRange, frustumHeight);
|
|
29720
29721
|
const distance = Math.max(
|
|
29721
29722
|
this.initialCameraDistance,
|
|
29722
29723
|
-minimumForward + clipMargin * 2,
|
|
@@ -29921,7 +29922,10 @@ class CameraController {
|
|
|
29921
29922
|
maximumDepth = Math.max(maximumDepth, depth);
|
|
29922
29923
|
}
|
|
29923
29924
|
const depthRange = maximumDepth - minimumDepth;
|
|
29924
|
-
const clipMargin =
|
|
29925
|
+
const clipMargin = this.createClipMargin(
|
|
29926
|
+
depthRange,
|
|
29927
|
+
(this.camera.top - this.camera.bottom) / this.camera.zoom
|
|
29928
|
+
);
|
|
29925
29929
|
const minimumVisibleDepth = MINIMUM_NEAR + clipMargin;
|
|
29926
29930
|
if (minimumDepth >= minimumVisibleDepth) {
|
|
29927
29931
|
return;
|
|
@@ -29944,7 +29948,10 @@ class CameraController {
|
|
|
29944
29948
|
maximumDepth = Math.max(maximumDepth, depth);
|
|
29945
29949
|
}
|
|
29946
29950
|
const depthRange = maximumDepth - minimumDepth;
|
|
29947
|
-
const clipMargin =
|
|
29951
|
+
const clipMargin = this.createClipMargin(
|
|
29952
|
+
depthRange,
|
|
29953
|
+
(this.camera.top - this.camera.bottom) / this.camera.zoom
|
|
29954
|
+
);
|
|
29948
29955
|
this.camera.near = Math.max(MINIMUM_NEAR, minimumDepth - clipMargin);
|
|
29949
29956
|
this.camera.far = Math.max(
|
|
29950
29957
|
this.camera.near + MINIMUM_CLIP_RANGE,
|
|
@@ -29984,6 +29991,19 @@ class CameraController {
|
|
|
29984
29991
|
getSafeAspect() {
|
|
29985
29992
|
return this.viewportWidth / this.getSafeViewportHeight();
|
|
29986
29993
|
}
|
|
29994
|
+
/**
|
|
29995
|
+
* Orthographic depth is linear, so generous margins cost nothing. The
|
|
29996
|
+
* model ratio covers scene depth, and the viewport ratio keeps the ground
|
|
29997
|
+
* grid inside the clip window as the camera zooms out.
|
|
29998
|
+
*/
|
|
29999
|
+
createClipMargin(depthRange, frustumHeight) {
|
|
30000
|
+
const aspect2 = this.getSafeAspect();
|
|
30001
|
+
const halfDiagonal = frustumHeight / 2 * Math.sqrt(1 + aspect2 * aspect2);
|
|
30002
|
+
return Math.max(
|
|
30003
|
+
MINIMUM_CLIP_MARGIN,
|
|
30004
|
+
Math.max(depthRange * CLIP_MARGIN_RATIO, halfDiagonal * VIEW_MARGIN_RATIO)
|
|
30005
|
+
);
|
|
30006
|
+
}
|
|
29987
30007
|
getSafeViewportHeight() {
|
|
29988
30008
|
const maximumInset = Math.max(0, this.viewportHeight - 1);
|
|
29989
30009
|
const effectiveInset = Math.min(this.viewportBottomInset, maximumInset);
|
|
@@ -30545,6 +30565,148 @@ class CanvasController {
|
|
|
30545
30565
|
}
|
|
30546
30566
|
}
|
|
30547
30567
|
}
|
|
30568
|
+
const CENTER_LINE_COLOR = 10132122;
|
|
30569
|
+
const CENTER_LINE_EXTRA_WIDTH = "2.0";
|
|
30570
|
+
const FADE_END_RATIO = 1.4;
|
|
30571
|
+
const FADE_START_RATIO = 0.8;
|
|
30572
|
+
const GRID_DIVISIONS = 24;
|
|
30573
|
+
const GRID_LINE_COLOR = 9474192;
|
|
30574
|
+
const GRID_LINE_OPACITY = 1;
|
|
30575
|
+
const GRID_LINE_WIDTH_PIXELS = 2;
|
|
30576
|
+
const GRID_SEPARATION_RATIO = 2e-3;
|
|
30577
|
+
const MINIMUM_ALPHA = "0.01";
|
|
30578
|
+
const QUAD_COVERAGE = 2.1;
|
|
30579
|
+
const VIEW_RATIO = 0.6;
|
|
30580
|
+
const VERTEX_SHADER = (
|
|
30581
|
+
/* glsl */
|
|
30582
|
+
`
|
|
30583
|
+
varying vec2 vUv;
|
|
30584
|
+
|
|
30585
|
+
void main() {
|
|
30586
|
+
vUv = uv;
|
|
30587
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
30588
|
+
}
|
|
30589
|
+
`
|
|
30590
|
+
);
|
|
30591
|
+
const FRAGMENT_SHADER = (
|
|
30592
|
+
/* glsl */
|
|
30593
|
+
`
|
|
30594
|
+
uniform vec3 uCenterColor;
|
|
30595
|
+
uniform float uDivisionCount;
|
|
30596
|
+
uniform float uFadeEnd;
|
|
30597
|
+
uniform float uFadeStart;
|
|
30598
|
+
uniform vec3 uGridColor;
|
|
30599
|
+
uniform float uGridSize;
|
|
30600
|
+
uniform float uLineOpacity;
|
|
30601
|
+
uniform float uLineWidth;
|
|
30602
|
+
varying vec2 vUv;
|
|
30603
|
+
|
|
30604
|
+
void main() {
|
|
30605
|
+
vec2 coord = (vUv - 0.5) * uGridSize;
|
|
30606
|
+
float cellSize = uGridSize / uDivisionCount;
|
|
30607
|
+
vec2 cellCoord = coord / cellSize;
|
|
30608
|
+
vec2 gridDistance = abs(fract(cellCoord - 0.5) - 0.5) / (fwidth(cellCoord) * uLineWidth);
|
|
30609
|
+
float gridLine = 1.0 - min(min(gridDistance.x, gridDistance.y), 1.0);
|
|
30610
|
+
vec2 centerDistance = abs(coord) / (fwidth(coord) * uLineWidth * ${CENTER_LINE_EXTRA_WIDTH});
|
|
30611
|
+
float centerLine = 1.0 - min(min(centerDistance.x, centerDistance.y), 1.0);
|
|
30612
|
+
float lineStrength = max(gridLine, centerLine);
|
|
30613
|
+
vec3 lineColor = mix(uGridColor, uCenterColor, centerLine);
|
|
30614
|
+
float radial = length(coord);
|
|
30615
|
+
float alpha = lineStrength * (1.0 - smoothstep(uFadeStart, uFadeEnd, radial)) * uLineOpacity;
|
|
30616
|
+
|
|
30617
|
+
if (alpha < ${MINIMUM_ALPHA}) {
|
|
30618
|
+
discard;
|
|
30619
|
+
}
|
|
30620
|
+
|
|
30621
|
+
gl_FragColor = vec4(lineColor, alpha);
|
|
30622
|
+
}
|
|
30623
|
+
`
|
|
30624
|
+
);
|
|
30625
|
+
class GroundGrid {
|
|
30626
|
+
material;
|
|
30627
|
+
mesh;
|
|
30628
|
+
anchorCenterX = 0;
|
|
30629
|
+
anchorCenterY = 0;
|
|
30630
|
+
anchorMinZ = 0;
|
|
30631
|
+
anchorSpan = 1;
|
|
30632
|
+
gridUniforms = {
|
|
30633
|
+
uCenterColor: { value: new Color(CENTER_LINE_COLOR) },
|
|
30634
|
+
uDivisionCount: { value: GRID_DIVISIONS },
|
|
30635
|
+
uFadeEnd: { value: FADE_END_RATIO },
|
|
30636
|
+
uFadeStart: { value: FADE_START_RATIO },
|
|
30637
|
+
uGridColor: { value: new Color(GRID_LINE_COLOR) },
|
|
30638
|
+
uGridSize: { value: 1 },
|
|
30639
|
+
uLineOpacity: { value: GRID_LINE_OPACITY },
|
|
30640
|
+
uLineWidth: { value: 1 / GRID_LINE_WIDTH_PIXELS }
|
|
30641
|
+
};
|
|
30642
|
+
constructor() {
|
|
30643
|
+
this.material = new ShaderMaterial({
|
|
30644
|
+
depthWrite: false,
|
|
30645
|
+
fragmentShader: FRAGMENT_SHADER,
|
|
30646
|
+
side: DoubleSide,
|
|
30647
|
+
transparent: true,
|
|
30648
|
+
uniforms: this.gridUniforms,
|
|
30649
|
+
vertexShader: VERTEX_SHADER
|
|
30650
|
+
});
|
|
30651
|
+
this.mesh = new Mesh(new PlaneGeometry(1, 1), this.material);
|
|
30652
|
+
this.mesh.visible = false;
|
|
30653
|
+
}
|
|
30654
|
+
getHelper() {
|
|
30655
|
+
return this.mesh;
|
|
30656
|
+
}
|
|
30657
|
+
attach(scene) {
|
|
30658
|
+
scene.add(this.mesh);
|
|
30659
|
+
}
|
|
30660
|
+
updateFromBounds(bounds) {
|
|
30661
|
+
this.anchorCenterX = (bounds.min.x + bounds.max.x) / 2;
|
|
30662
|
+
this.anchorCenterY = (bounds.min.y + bounds.max.y) / 2;
|
|
30663
|
+
this.anchorMinZ = bounds.min.z;
|
|
30664
|
+
this.anchorSpan = Math.max(bounds.max.x - bounds.min.x, bounds.max.y - bounds.min.y);
|
|
30665
|
+
this.applyLayout(this.anchorSpan * FADE_END_RATIO);
|
|
30666
|
+
this.mesh.visible = true;
|
|
30667
|
+
}
|
|
30668
|
+
/**
|
|
30669
|
+
* Grows the grid when the viewport zooms out beyond the model-anchored
|
|
30670
|
+
* radius, so the ground keeps covering most of the visible area instead
|
|
30671
|
+
* of shrinking into a patch around the model.
|
|
30672
|
+
*/
|
|
30673
|
+
updateForViewport(halfDiagonal) {
|
|
30674
|
+
if (!this.mesh.visible) {
|
|
30675
|
+
return;
|
|
30676
|
+
}
|
|
30677
|
+
const viewportRadius = halfDiagonal * VIEW_RATIO;
|
|
30678
|
+
const fadeRadius = Math.max(this.anchorSpan * FADE_END_RATIO, viewportRadius);
|
|
30679
|
+
this.applyLayout(fadeRadius);
|
|
30680
|
+
}
|
|
30681
|
+
applyLayout(fadeRadius) {
|
|
30682
|
+
const size = fadeRadius * QUAD_COVERAGE;
|
|
30683
|
+
this.gridUniforms.uGridSize.value = size;
|
|
30684
|
+
this.gridUniforms.uFadeStart.value = fadeRadius * (FADE_START_RATIO / FADE_END_RATIO);
|
|
30685
|
+
this.gridUniforms.uFadeEnd.value = fadeRadius;
|
|
30686
|
+
this.mesh.scale.set(size, size, 1);
|
|
30687
|
+
this.mesh.position.set(
|
|
30688
|
+
this.anchorCenterX,
|
|
30689
|
+
this.anchorCenterY,
|
|
30690
|
+
this.anchorMinZ - this.anchorSpan * GRID_SEPARATION_RATIO
|
|
30691
|
+
);
|
|
30692
|
+
}
|
|
30693
|
+
/**
|
|
30694
|
+
* Keeps the drawn line width at the intended CSS pixel size on high-DPI
|
|
30695
|
+
* outputs: fwidth-based lines live in framebuffer pixels, so the inverse
|
|
30696
|
+
* width must scale with the device pixel ratio.
|
|
30697
|
+
*/
|
|
30698
|
+
setPixelRatio(pixelRatio) {
|
|
30699
|
+
this.gridUniforms.uLineWidth.value = 1 / (GRID_LINE_WIDTH_PIXELS * pixelRatio);
|
|
30700
|
+
}
|
|
30701
|
+
hide() {
|
|
30702
|
+
this.mesh.visible = false;
|
|
30703
|
+
}
|
|
30704
|
+
dispose() {
|
|
30705
|
+
this.mesh.removeFromParent();
|
|
30706
|
+
this.mesh.geometry.dispose();
|
|
30707
|
+
this.material.dispose();
|
|
30708
|
+
}
|
|
30709
|
+
}
|
|
30548
30710
|
class ConsoleImportPerformanceLogSink {
|
|
30549
30711
|
categoryByStageName = {
|
|
30550
30712
|
"boolean-source-snapshot": "scene",
|
|
@@ -30840,6 +31002,51 @@ class ImportPerformanceTracer {
|
|
|
30840
31002
|
return trace;
|
|
30841
31003
|
}
|
|
30842
31004
|
}
|
|
31005
|
+
const COMMITTED_PERCENT = 100;
|
|
31006
|
+
const ROLE_SPAN_TOTAL = 0.96;
|
|
31007
|
+
class ImportProgressTracker {
|
|
31008
|
+
listener;
|
|
31009
|
+
percent = 0;
|
|
31010
|
+
rolePositions = /* @__PURE__ */ new Map();
|
|
31011
|
+
roleSpans;
|
|
31012
|
+
constructor(roles, listener) {
|
|
31013
|
+
this.listener = listener;
|
|
31014
|
+
const roleSpans = /* @__PURE__ */ new Map();
|
|
31015
|
+
const spanPerRole = ROLE_SPAN_TOTAL / roles.length;
|
|
31016
|
+
for (const [roleIndex, role] of roles.entries()) {
|
|
31017
|
+
roleSpans.set(role, { span: spanPerRole, start: spanPerRole * roleIndex });
|
|
31018
|
+
}
|
|
31019
|
+
this.roleSpans = roleSpans;
|
|
31020
|
+
}
|
|
31021
|
+
completeRole(role) {
|
|
31022
|
+
this.setRolePosition(role, 1);
|
|
31023
|
+
}
|
|
31024
|
+
completeScene() {
|
|
31025
|
+
this.setPercent(COMMITTED_PERCENT);
|
|
31026
|
+
}
|
|
31027
|
+
getPercent() {
|
|
31028
|
+
return this.percent;
|
|
31029
|
+
}
|
|
31030
|
+
setRolePosition(role, position) {
|
|
31031
|
+
const roleSpan = this.roleSpans.get(role);
|
|
31032
|
+
if (roleSpan === void 0) {
|
|
31033
|
+
return;
|
|
31034
|
+
}
|
|
31035
|
+
const nextPosition = Math.max(this.rolePositions.get(role) ?? 0, Math.min(1, position));
|
|
31036
|
+
this.rolePositions.set(role, nextPosition);
|
|
31037
|
+
this.setPercent(
|
|
31038
|
+
Math.floor((roleSpan.start + roleSpan.span * nextPosition) * COMMITTED_PERCENT)
|
|
31039
|
+
);
|
|
31040
|
+
}
|
|
31041
|
+
setPercent(nextPercent) {
|
|
31042
|
+
const percent = Math.max(this.percent, Math.min(COMMITTED_PERCENT, nextPercent));
|
|
31043
|
+
if (percent === this.percent) {
|
|
31044
|
+
return;
|
|
31045
|
+
}
|
|
31046
|
+
this.percent = percent;
|
|
31047
|
+
this.listener?.(this);
|
|
31048
|
+
}
|
|
31049
|
+
}
|
|
30843
31050
|
const LOCAL_RESOURCE_BASE_URL = "https://renderer-local.invalid/";
|
|
30844
31051
|
class ObjResourceList {
|
|
30845
31052
|
mtlUrls;
|
|
@@ -32022,12 +32229,15 @@ class TextureLoadMonitor {
|
|
|
32022
32229
|
encounteredError = false;
|
|
32023
32230
|
finishedScheduling = false;
|
|
32024
32231
|
manager;
|
|
32232
|
+
onTextureSettled;
|
|
32025
32233
|
originalTextureLoader;
|
|
32026
32234
|
pendingRequests = 0;
|
|
32235
|
+
scheduledTextureCount = 0;
|
|
32027
32236
|
settled = false;
|
|
32028
32237
|
textures = /* @__PURE__ */ new Set();
|
|
32029
|
-
constructor(manager, creator) {
|
|
32238
|
+
constructor(manager, creator, onTextureSettled) {
|
|
32030
32239
|
this.manager = manager;
|
|
32240
|
+
this.onTextureSettled = onTextureSettled;
|
|
32031
32241
|
this.completionPromise = new Promise(
|
|
32032
32242
|
this.captureCompletionResolver.bind(this)
|
|
32033
32243
|
);
|
|
@@ -32038,6 +32248,9 @@ class TextureLoadMonitor {
|
|
|
32038
32248
|
this.finishedScheduling = true;
|
|
32039
32249
|
this.resolveIfSettled();
|
|
32040
32250
|
}
|
|
32251
|
+
getScheduledTextureCount() {
|
|
32252
|
+
return this.scheduledTextureCount;
|
|
32253
|
+
}
|
|
32041
32254
|
getTextures() {
|
|
32042
32255
|
return [...this.textures];
|
|
32043
32256
|
}
|
|
@@ -32065,14 +32278,17 @@ class TextureLoadMonitor {
|
|
|
32065
32278
|
handleTextureError() {
|
|
32066
32279
|
this.encounteredError = true;
|
|
32067
32280
|
this.pendingRequests -= 1;
|
|
32281
|
+
this.onTextureSettled?.();
|
|
32068
32282
|
this.resolveIfSettled();
|
|
32069
32283
|
}
|
|
32070
32284
|
handleTextureLoad() {
|
|
32071
32285
|
this.pendingRequests -= 1;
|
|
32286
|
+
this.onTextureSettled?.();
|
|
32072
32287
|
this.resolveIfSettled();
|
|
32073
32288
|
}
|
|
32074
32289
|
loadTexture(url, mapping) {
|
|
32075
32290
|
this.pendingRequests += 1;
|
|
32291
|
+
this.scheduledTextureCount += 1;
|
|
32076
32292
|
try {
|
|
32077
32293
|
const texture = this.originalTextureLoader(
|
|
32078
32294
|
url,
|
|
@@ -32108,15 +32324,16 @@ class ObjLoader {
|
|
|
32108
32324
|
constructor(parser = new OBJLoader()) {
|
|
32109
32325
|
this.parser = parser;
|
|
32110
32326
|
}
|
|
32111
|
-
async loadResources(resources, signal, performanceTrace, role) {
|
|
32327
|
+
async loadResources(resources, signal, performanceTrace, role, progress) {
|
|
32112
32328
|
return this.loadInternal(
|
|
32113
32329
|
resources,
|
|
32114
32330
|
signal,
|
|
32115
32331
|
performanceTrace ?? ImportPerformanceTrace.createDisabled(),
|
|
32116
|
-
role ?? "stone"
|
|
32332
|
+
role ?? "stone",
|
|
32333
|
+
progress
|
|
32117
32334
|
);
|
|
32118
32335
|
}
|
|
32119
|
-
async loadInternal(resources, signal, performanceTrace, role) {
|
|
32336
|
+
async loadInternal(resources, signal, performanceTrace, role, progress) {
|
|
32120
32337
|
if (this.hasEmptyRequiredUrl(resources)) {
|
|
32121
32338
|
return {
|
|
32122
32339
|
code: ObjImportFailureCode$1.EmptySource,
|
|
@@ -32130,6 +32347,7 @@ class ObjLoader {
|
|
|
32130
32347
|
const objFetchStage = performanceTrace.startStage("obj-fetch", role);
|
|
32131
32348
|
const objSource = await this.loadText(resources.objUrl, signal);
|
|
32132
32349
|
performanceTrace.endStage(objFetchStage, "success");
|
|
32350
|
+
progress?.markModelFetched();
|
|
32133
32351
|
this.assertNotCancelled(signal);
|
|
32134
32352
|
const materialScanStage = performanceTrace.startStage("obj-material-scan", role);
|
|
32135
32353
|
const materialReferences = this.getMaterialLibraryReferences(objSource);
|
|
@@ -32149,9 +32367,11 @@ class ObjLoader {
|
|
|
32149
32367
|
materialContext = await this.loadMaterialContext(
|
|
32150
32368
|
materialUrls,
|
|
32151
32369
|
resources,
|
|
32152
|
-
signal
|
|
32370
|
+
signal,
|
|
32371
|
+
progress
|
|
32153
32372
|
);
|
|
32154
32373
|
performanceTrace.endStage(materialLoadStage, "success");
|
|
32374
|
+
progress?.markMaterialsResolved(this.getScheduledTextureCount(materialContext));
|
|
32155
32375
|
} catch {
|
|
32156
32376
|
performanceTrace.endStage(
|
|
32157
32377
|
materialLoadStage,
|
|
@@ -32175,6 +32395,7 @@ class ObjLoader {
|
|
|
32175
32395
|
resources.upAxis
|
|
32176
32396
|
);
|
|
32177
32397
|
performanceTrace.endStage(modelCreateStage, "success");
|
|
32398
|
+
progress?.markModelParsed();
|
|
32178
32399
|
if (!this.hasRenderablePrimitive(group) || !model.hasFiniteBounds()) {
|
|
32179
32400
|
model.dispose();
|
|
32180
32401
|
return {
|
|
@@ -32335,6 +32556,13 @@ class ObjLoader {
|
|
|
32335
32556
|
}
|
|
32336
32557
|
return [...textures];
|
|
32337
32558
|
}
|
|
32559
|
+
getScheduledTextureCount(context) {
|
|
32560
|
+
let scheduledTextureCount = 0;
|
|
32561
|
+
for (const monitor of context.monitors) {
|
|
32562
|
+
scheduledTextureCount += monitor.getScheduledTextureCount();
|
|
32563
|
+
}
|
|
32564
|
+
return scheduledTextureCount;
|
|
32565
|
+
}
|
|
32338
32566
|
getObjectTree(root) {
|
|
32339
32567
|
const objectTree = [root];
|
|
32340
32568
|
for (const object of objectTree) {
|
|
@@ -32451,11 +32679,11 @@ class ObjLoader {
|
|
|
32451
32679
|
isPoints(object) {
|
|
32452
32680
|
return object instanceof Points;
|
|
32453
32681
|
}
|
|
32454
|
-
async loadMaterialContext(mtlUrls, resources, signal) {
|
|
32682
|
+
async loadMaterialContext(mtlUrls, resources, signal, progress) {
|
|
32455
32683
|
const libraries = [];
|
|
32456
32684
|
try {
|
|
32457
32685
|
for (const mtlUrl of mtlUrls) {
|
|
32458
|
-
libraries.push(await this.loadMaterialLibrary(mtlUrl, resources, signal));
|
|
32686
|
+
libraries.push(await this.loadMaterialLibrary(mtlUrl, resources, signal, progress));
|
|
32459
32687
|
}
|
|
32460
32688
|
return this.createMaterialContext(libraries);
|
|
32461
32689
|
} catch (error2) {
|
|
@@ -32463,7 +32691,7 @@ class ObjLoader {
|
|
|
32463
32691
|
throw error2;
|
|
32464
32692
|
}
|
|
32465
32693
|
}
|
|
32466
|
-
async loadMaterialLibrary(mtlUrl, resources, signal) {
|
|
32694
|
+
async loadMaterialLibrary(mtlUrl, resources, signal, progress) {
|
|
32467
32695
|
const materialSource = await this.loadText(mtlUrl, signal);
|
|
32468
32696
|
this.assertNotCancelled(signal);
|
|
32469
32697
|
const materialBaseUrl = this.getResourceDirectoryUrl(resources, mtlUrl);
|
|
@@ -32474,7 +32702,11 @@ class ObjLoader {
|
|
|
32474
32702
|
side: DoubleSide
|
|
32475
32703
|
});
|
|
32476
32704
|
const creator = materialLoader.parse(materialSource, materialBaseUrl);
|
|
32477
|
-
const monitor = new TextureLoadMonitor(
|
|
32705
|
+
const monitor = new TextureLoadMonitor(
|
|
32706
|
+
manager,
|
|
32707
|
+
creator,
|
|
32708
|
+
progress === void 0 ? void 0 : progress.markTextureSettled.bind(progress)
|
|
32709
|
+
);
|
|
32478
32710
|
try {
|
|
32479
32711
|
const pbrMaterialReferences = this.parsePbrMaterialReferences(materialSource);
|
|
32480
32712
|
this.assertMaterialTextures(resources, materialSource, materialBaseUrl, creator);
|
|
@@ -33450,7 +33682,7 @@ class PlyLoader {
|
|
|
33450
33682
|
this.parser = parser;
|
|
33451
33683
|
this.textureLoader = textureLoader;
|
|
33452
33684
|
}
|
|
33453
|
-
async load(resources, signal) {
|
|
33685
|
+
async load(resources, signal, progress) {
|
|
33454
33686
|
if (signal.aborted) {
|
|
33455
33687
|
return {
|
|
33456
33688
|
code: ObjImportFailureCode$1.Cancelled,
|
|
@@ -33461,6 +33693,7 @@ class PlyLoader {
|
|
|
33461
33693
|
if (!(source instanceof ArrayBuffer)) {
|
|
33462
33694
|
return source;
|
|
33463
33695
|
}
|
|
33696
|
+
progress?.markModelFetched();
|
|
33464
33697
|
let geometry;
|
|
33465
33698
|
let metadata;
|
|
33466
33699
|
try {
|
|
@@ -33486,7 +33719,14 @@ class PlyLoader {
|
|
|
33486
33719
|
success: false
|
|
33487
33720
|
};
|
|
33488
33721
|
}
|
|
33489
|
-
|
|
33722
|
+
progress?.markModelParsed();
|
|
33723
|
+
const modelResult = metadata.hasFaces ? await this.createMeshModel(
|
|
33724
|
+
geometry,
|
|
33725
|
+
metadata.textureReference,
|
|
33726
|
+
resources,
|
|
33727
|
+
signal,
|
|
33728
|
+
progress
|
|
33729
|
+
) : this.createPointsModel(geometry, resources);
|
|
33490
33730
|
if ("success" in modelResult) {
|
|
33491
33731
|
return modelResult;
|
|
33492
33732
|
}
|
|
@@ -33514,13 +33754,14 @@ class PlyLoader {
|
|
|
33514
33754
|
vertexColors: geometry.hasAttribute("color")
|
|
33515
33755
|
});
|
|
33516
33756
|
}
|
|
33517
|
-
async createMeshModel(geometry, textureReference, resources, signal) {
|
|
33757
|
+
async createMeshModel(geometry, textureReference, resources, signal, progress) {
|
|
33518
33758
|
if (!geometry.hasAttribute("normal")) {
|
|
33519
33759
|
geometry.computeVertexNormals();
|
|
33520
33760
|
}
|
|
33521
33761
|
const warnings = [];
|
|
33522
33762
|
let texture;
|
|
33523
33763
|
if (textureReference !== void 0 && geometry.hasAttribute("uv")) {
|
|
33764
|
+
progress?.markMaterialsResolved(1);
|
|
33524
33765
|
const textureResult = await this.textureLoader.load(
|
|
33525
33766
|
resources.resolveTextureUrl(textureReference),
|
|
33526
33767
|
signal
|
|
@@ -33532,6 +33773,7 @@ class PlyLoader {
|
|
|
33532
33773
|
success: false
|
|
33533
33774
|
};
|
|
33534
33775
|
}
|
|
33776
|
+
progress?.markTextureSettled();
|
|
33535
33777
|
if (textureResult.outcome === "succeeded") {
|
|
33536
33778
|
texture = textureResult.texture;
|
|
33537
33779
|
} else {
|
|
@@ -33617,26 +33859,73 @@ class PlyLoader {
|
|
|
33617
33859
|
return value;
|
|
33618
33860
|
}
|
|
33619
33861
|
}
|
|
33862
|
+
const OBJ_MODEL_LOAD_PROGRESS_PROFILE = {
|
|
33863
|
+
fetchedPosition: 0.2,
|
|
33864
|
+
materialsPosition: 0.3,
|
|
33865
|
+
parsedPosition: 0.95,
|
|
33866
|
+
textureCeiling: 0.99
|
|
33867
|
+
};
|
|
33868
|
+
const PLY_MODEL_LOAD_PROGRESS_PROFILE = {
|
|
33869
|
+
fetchedPosition: 0.4,
|
|
33870
|
+
materialsPosition: 0.85,
|
|
33871
|
+
parsedPosition: 0.85,
|
|
33872
|
+
textureCeiling: 0.99
|
|
33873
|
+
};
|
|
33874
|
+
class RoleImportProgressReporter {
|
|
33875
|
+
profile;
|
|
33876
|
+
role;
|
|
33877
|
+
textureSettled = 0;
|
|
33878
|
+
textureTotal;
|
|
33879
|
+
tracker;
|
|
33880
|
+
constructor(tracker, role, profile) {
|
|
33881
|
+
this.profile = profile;
|
|
33882
|
+
this.role = role;
|
|
33883
|
+
this.tracker = tracker;
|
|
33884
|
+
}
|
|
33885
|
+
markMaterialsResolved(textureTotal) {
|
|
33886
|
+
this.textureTotal = textureTotal;
|
|
33887
|
+
this.tracker.setRolePosition(this.role, this.profile.materialsPosition);
|
|
33888
|
+
this.reportTexturePosition();
|
|
33889
|
+
}
|
|
33890
|
+
markModelFetched() {
|
|
33891
|
+
this.tracker.setRolePosition(this.role, this.profile.fetchedPosition);
|
|
33892
|
+
}
|
|
33893
|
+
markModelParsed() {
|
|
33894
|
+
this.tracker.setRolePosition(this.role, this.profile.parsedPosition);
|
|
33895
|
+
}
|
|
33896
|
+
markTextureSettled() {
|
|
33897
|
+
this.textureSettled += 1;
|
|
33898
|
+
this.reportTexturePosition();
|
|
33899
|
+
}
|
|
33900
|
+
reportTexturePosition() {
|
|
33901
|
+
const textureTotal = this.textureTotal;
|
|
33902
|
+
if (textureTotal === void 0 || textureTotal <= 0 || this.textureSettled === 0) {
|
|
33903
|
+
return;
|
|
33904
|
+
}
|
|
33905
|
+
const textureSpan = this.profile.textureCeiling - this.profile.parsedPosition;
|
|
33906
|
+
const settledShare = Math.min(this.textureSettled, textureTotal) / textureTotal;
|
|
33907
|
+
this.tracker.setRolePosition(
|
|
33908
|
+
this.role,
|
|
33909
|
+
this.profile.parsedPosition + textureSpan * settledShare
|
|
33910
|
+
);
|
|
33911
|
+
}
|
|
33912
|
+
}
|
|
33620
33913
|
class SimulationSnapshotFactory {
|
|
33621
|
-
|
|
33914
|
+
constructor(scheduler) {
|
|
33915
|
+
this.scheduler = scheduler;
|
|
33916
|
+
}
|
|
33917
|
+
scheduler;
|
|
33918
|
+
// Allocation granularity, not a per-task limit: tasks copy until their time budget expires.
|
|
33919
|
+
static CHUNK_BYTES = 4 * 1024 * 1024;
|
|
33920
|
+
static ATTRIBUTE_CHUNK_BYTES = 256 * 1024;
|
|
33921
|
+
async create(stone, base) {
|
|
33622
33922
|
const started = performance.now();
|
|
33623
33923
|
const stoneRoot = this.root(stone);
|
|
33624
33924
|
const baseRoot = this.root(base);
|
|
33625
33925
|
stoneRoot.updateMatrixWorld(true);
|
|
33626
33926
|
if (baseRoot !== stoneRoot) baseRoot.updateMatrixWorld(true);
|
|
33627
|
-
const stoneInput = this.collect(stone);
|
|
33628
|
-
const baseInput = this.collect(base);
|
|
33629
|
-
const bounds = new Box3();
|
|
33630
|
-
const point = new Vector3();
|
|
33631
|
-
for (let index = 0; index < stoneInput.positions.length; index += 3) {
|
|
33632
|
-
bounds.expandByPoint(point.fromArray(stoneInput.positions, index));
|
|
33633
|
-
}
|
|
33634
|
-
const origin = bounds.getCenter(new Vector3()).toArray();
|
|
33635
|
-
for (const input of [stoneInput, baseInput]) {
|
|
33636
|
-
for (let index = 0; index < input.positions.length; index++) {
|
|
33637
|
-
input.positions[index] = Number(input.positions[index]) - Number(origin[index % 3]);
|
|
33638
|
-
}
|
|
33639
|
-
}
|
|
33927
|
+
const stoneInput = await this.collect(stone);
|
|
33928
|
+
const baseInput = await this.collect(base);
|
|
33640
33929
|
return {
|
|
33641
33930
|
stone: stoneInput,
|
|
33642
33931
|
base: baseInput,
|
|
@@ -33648,54 +33937,126 @@ class SimulationSnapshotFactory {
|
|
|
33648
33937
|
while (root.parent !== null) root = root.parent;
|
|
33649
33938
|
return root;
|
|
33650
33939
|
}
|
|
33651
|
-
collect(root) {
|
|
33652
|
-
const
|
|
33653
|
-
const
|
|
33654
|
-
const
|
|
33655
|
-
|
|
33656
|
-
if (
|
|
33940
|
+
async collect(root) {
|
|
33941
|
+
const result = [];
|
|
33942
|
+
const objects = [root];
|
|
33943
|
+
for (const object of objects) {
|
|
33944
|
+
const pause = this.scheduler.checkpoint();
|
|
33945
|
+
if (pause !== void 0) await pause;
|
|
33946
|
+
objects.push(...object.children);
|
|
33947
|
+
if (!(object instanceof Mesh)) continue;
|
|
33657
33948
|
const mesh = object;
|
|
33658
|
-
if (!mesh.geometry.hasAttribute("position")) {
|
|
33659
|
-
throw new Error("The model has no triangular position attribute.");
|
|
33660
|
-
}
|
|
33661
33949
|
const position = mesh.geometry.getAttribute("position");
|
|
33662
|
-
if (position.itemSize < 3) {
|
|
33950
|
+
if (!mesh.geometry.hasAttribute("position") || position.itemSize < 3) {
|
|
33663
33951
|
throw new Error("The model has no triangular position attribute.");
|
|
33664
33952
|
}
|
|
33665
|
-
const
|
|
33666
|
-
if (!Number.isFinite
|
|
33667
|
-
throw new Error("The model transform is
|
|
33668
|
-
}
|
|
33669
|
-
const offset = positions.length / 3;
|
|
33670
|
-
for (let index = 0; index < position.count; index++) {
|
|
33671
|
-
point.set(position.getX(index), position.getY(index), position.getZ(index)).applyMatrix4(mesh.matrixWorld);
|
|
33672
|
-
if (!point.toArray().every(Number.isFinite)) {
|
|
33673
|
-
throw new Error("The model contains nonfinite positions.");
|
|
33674
|
-
}
|
|
33675
|
-
positions.push(point.x, point.y, point.z);
|
|
33676
|
-
}
|
|
33677
|
-
const sourceIndex = mesh.geometry.index;
|
|
33678
|
-
const count = sourceIndex?.count ?? position.count;
|
|
33679
|
-
if (count === 0 || count % 3 !== 0) {
|
|
33680
|
-
throw new Error("The model has incomplete triangles.");
|
|
33681
|
-
}
|
|
33682
|
-
for (let index = 0; index < count; index += 3) {
|
|
33683
|
-
const a = sourceIndex?.getX(index) ?? index;
|
|
33684
|
-
const b = sourceIndex?.getX(index + 1) ?? index + 1;
|
|
33685
|
-
const c = sourceIndex?.getX(index + 2) ?? index + 2;
|
|
33686
|
-
if ([a, b, c].some(
|
|
33687
|
-
(value) => !Number.isSafeInteger(value) || value < 0 || value >= position.count
|
|
33688
|
-
)) {
|
|
33689
|
-
throw new Error("The model contains an invalid triangle index.");
|
|
33690
|
-
}
|
|
33691
|
-
indices.push(
|
|
33692
|
-
offset + a,
|
|
33693
|
-
offset + (determinant < 0 ? c : b),
|
|
33694
|
-
offset + (determinant < 0 ? b : c)
|
|
33695
|
-
);
|
|
33953
|
+
const matrixWorld = mesh.matrixWorld.toArray();
|
|
33954
|
+
if (!matrixWorld.every(Number.isFinite)) {
|
|
33955
|
+
throw new Error("The model transform is nonfinite.");
|
|
33696
33956
|
}
|
|
33957
|
+
const positions = await this.copyPositions(position);
|
|
33958
|
+
const index = mesh.geometry.index;
|
|
33959
|
+
const indices = index === null ? null : await this.copyIndices(index);
|
|
33960
|
+
result.push({ positions, indices, vertexCount: position.count, matrixWorld });
|
|
33961
|
+
}
|
|
33962
|
+
return result;
|
|
33963
|
+
}
|
|
33964
|
+
async copyPositions(attribute) {
|
|
33965
|
+
const result = [];
|
|
33966
|
+
const source = attribute.array;
|
|
33967
|
+
const packed = !("isInterleavedBufferAttribute" in attribute) && !attribute.normalized && attribute.itemSize === 3 && (source instanceof Float32Array || source instanceof Float64Array);
|
|
33968
|
+
const vertexChunk = Math.floor(
|
|
33969
|
+
(packed ? SimulationSnapshotFactory.CHUNK_BYTES : SimulationSnapshotFactory.ATTRIBUTE_CHUNK_BYTES) / 24
|
|
33970
|
+
);
|
|
33971
|
+
for (let start = 0; start < attribute.count; start += vertexChunk) {
|
|
33972
|
+
const pause = this.scheduler.checkpoint();
|
|
33973
|
+
if (pause !== void 0) await pause;
|
|
33974
|
+
const end = Math.min(attribute.count, start + vertexChunk);
|
|
33975
|
+
if (packed) {
|
|
33976
|
+
result.push(source.slice(start * 3, end * 3));
|
|
33977
|
+
} else {
|
|
33978
|
+
const values = new Float64Array((end - start) * 3);
|
|
33979
|
+
for (let index = start; index < end; index++) {
|
|
33980
|
+
const offset = (index - start) * 3;
|
|
33981
|
+
values[offset] = attribute.getX(index);
|
|
33982
|
+
values[offset + 1] = attribute.getY(index);
|
|
33983
|
+
values[offset + 2] = attribute.getZ(index);
|
|
33984
|
+
}
|
|
33985
|
+
result.push(values);
|
|
33986
|
+
}
|
|
33987
|
+
}
|
|
33988
|
+
return result;
|
|
33989
|
+
}
|
|
33990
|
+
async copyIndices(attribute) {
|
|
33991
|
+
const result = [];
|
|
33992
|
+
const source = attribute.array;
|
|
33993
|
+
const packed = attribute.itemSize === 1 && !attribute.normalized && (source instanceof Uint16Array || source instanceof Uint32Array);
|
|
33994
|
+
const indexChunk = (packed ? SimulationSnapshotFactory.CHUNK_BYTES : SimulationSnapshotFactory.ATTRIBUTE_CHUNK_BYTES) / 8;
|
|
33995
|
+
for (let start = 0; start < attribute.count; start += indexChunk) {
|
|
33996
|
+
const pause = this.scheduler.checkpoint();
|
|
33997
|
+
if (pause !== void 0) await pause;
|
|
33998
|
+
const end = Math.min(attribute.count, start + indexChunk);
|
|
33999
|
+
if (packed) result.push(source.slice(start, end));
|
|
34000
|
+
else {
|
|
34001
|
+
const values = new Float64Array(end - start);
|
|
34002
|
+
for (let index = start; index < end; index++) {
|
|
34003
|
+
values[index - start] = attribute.getX(index);
|
|
34004
|
+
}
|
|
34005
|
+
result.push(values);
|
|
34006
|
+
}
|
|
34007
|
+
}
|
|
34008
|
+
return result;
|
|
34009
|
+
}
|
|
34010
|
+
}
|
|
34011
|
+
class SimulationTaskScheduler {
|
|
34012
|
+
constructor(signal, started) {
|
|
34013
|
+
this.signal = signal;
|
|
34014
|
+
this.started = started;
|
|
34015
|
+
}
|
|
34016
|
+
signal;
|
|
34017
|
+
started;
|
|
34018
|
+
sliceStarted = performance.now();
|
|
34019
|
+
async waitForPaint() {
|
|
34020
|
+
await this.wait(true);
|
|
34021
|
+
this.checkActive();
|
|
34022
|
+
this.sliceStarted = performance.now();
|
|
34023
|
+
}
|
|
34024
|
+
checkpoint() {
|
|
34025
|
+
this.checkActive();
|
|
34026
|
+
if (performance.now() - this.sliceStarted < 4) return void 0;
|
|
34027
|
+
return this.yieldTask();
|
|
34028
|
+
}
|
|
34029
|
+
async yieldTask() {
|
|
34030
|
+
await this.wait(false);
|
|
34031
|
+
this.checkActive();
|
|
34032
|
+
this.sliceStarted = performance.now();
|
|
34033
|
+
}
|
|
34034
|
+
checkActive() {
|
|
34035
|
+
this.signal.throwIfAborted();
|
|
34036
|
+
if (performance.now() - this.started >= 3e4) {
|
|
34037
|
+
throw new Error("Simulation timed out during snapshot capture.");
|
|
34038
|
+
}
|
|
34039
|
+
}
|
|
34040
|
+
wait(paint) {
|
|
34041
|
+
this.checkActive();
|
|
34042
|
+
return new Promise((resolve) => {
|
|
34043
|
+
let frame;
|
|
34044
|
+
let task;
|
|
34045
|
+
const finish = () => {
|
|
34046
|
+
if (frame !== void 0) cancelAnimationFrame(frame);
|
|
34047
|
+
clearTimeout(task);
|
|
34048
|
+
this.signal.removeEventListener("abort", finish);
|
|
34049
|
+
resolve();
|
|
34050
|
+
};
|
|
34051
|
+
this.signal.addEventListener("abort", finish, { once: true });
|
|
34052
|
+
if (paint) {
|
|
34053
|
+
frame = requestAnimationFrame(() => {
|
|
34054
|
+
clearTimeout(task);
|
|
34055
|
+
task = setTimeout(finish, 0);
|
|
34056
|
+
});
|
|
34057
|
+
}
|
|
34058
|
+
task = setTimeout(finish, paint ? 100 : 0);
|
|
33697
34059
|
});
|
|
33698
|
-
return { positions: Float64Array.from(positions), indices: Uint32Array.from(indices) };
|
|
33699
34060
|
}
|
|
33700
34061
|
}
|
|
33701
34062
|
class StabilityMessages {
|
|
@@ -33703,7 +34064,7 @@ class StabilityMessages {
|
|
|
33703
34064
|
if (!this.record(value) || !this.token(value["token"]) || !this.record(value["input"]))
|
|
33704
34065
|
return void 0;
|
|
33705
34066
|
const input = value["input"];
|
|
33706
|
-
if (!this.
|
|
34067
|
+
if (!this.role(input["stone"]) || !this.role(input["base"]) || !this.nonnegative(input["captureMilliseconds"]))
|
|
33707
34068
|
return void 0;
|
|
33708
34069
|
return value;
|
|
33709
34070
|
}
|
|
@@ -33736,8 +34097,20 @@ class StabilityMessages {
|
|
|
33736
34097
|
}
|
|
33737
34098
|
return value["outcome"] !== "supported" || Number(value["margin"]) > 1e-6 && Number(value["residual"]) <= 1e-7 && Number(value["contactCount"]) > 0;
|
|
33738
34099
|
}
|
|
34100
|
+
role(value) {
|
|
34101
|
+
return this.array(value) && value.every((mesh) => this.mesh(mesh));
|
|
34102
|
+
}
|
|
34103
|
+
array(value) {
|
|
34104
|
+
return Array.isArray(value);
|
|
34105
|
+
}
|
|
33739
34106
|
mesh(value) {
|
|
33740
|
-
return this.record(value) && value["positions"]
|
|
34107
|
+
return this.record(value) && Array.isArray(value["positions"]) && value["positions"].every(
|
|
34108
|
+
(chunk) => chunk instanceof Float32Array || chunk instanceof Float64Array
|
|
34109
|
+
) && (value["indices"] === null || Array.isArray(value["indices"]) && value["indices"].every(
|
|
34110
|
+
(chunk) => chunk instanceof Uint16Array || chunk instanceof Uint32Array || chunk instanceof Float64Array
|
|
34111
|
+
)) && this.nonnegative(value["vertexCount"]) && Number.isSafeInteger(value["vertexCount"]) && Array.isArray(value["matrixWorld"]) && value["matrixWorld"].length === 16 && value["matrixWorld"].every(
|
|
34112
|
+
(entry) => typeof entry === "number" && Number.isFinite(entry)
|
|
34113
|
+
);
|
|
33741
34114
|
}
|
|
33742
34115
|
record(value) {
|
|
33743
34116
|
return typeof value === "object" && value !== null;
|
|
@@ -33753,7 +34126,7 @@ class StabilityWorkerFactory {
|
|
|
33753
34126
|
create() {
|
|
33754
34127
|
return new Worker(new URL(
|
|
33755
34128
|
/* @vite-ignore */
|
|
33756
|
-
"" + new URL("assets/StabilityWorkerEntry-
|
|
34129
|
+
"" + new URL("assets/StabilityWorkerEntry-L0_8iZmQ.js", import.meta.url).href,
|
|
33757
34130
|
import.meta.url
|
|
33758
34131
|
), {
|
|
33759
34132
|
type: "module"
|
|
@@ -33802,12 +34175,16 @@ class StabilityWorkerClient {
|
|
|
33802
34175
|
this.fail("Simulation Worker message could not be read.");
|
|
33803
34176
|
});
|
|
33804
34177
|
}
|
|
33805
|
-
|
|
33806
|
-
|
|
33807
|
-
|
|
33808
|
-
|
|
33809
|
-
|
|
33810
|
-
|
|
34178
|
+
const transfer = [];
|
|
34179
|
+
for (const role of [input.stone, input.base]) {
|
|
34180
|
+
for (const mesh of role) {
|
|
34181
|
+
for (const chunk of mesh.positions) transfer.push(chunk.buffer);
|
|
34182
|
+
if (mesh.indices !== null) {
|
|
34183
|
+
for (const chunk of mesh.indices) transfer.push(chunk.buffer);
|
|
34184
|
+
}
|
|
34185
|
+
}
|
|
34186
|
+
}
|
|
34187
|
+
worker.postMessage({ token, input }, transfer);
|
|
33811
34188
|
} catch {
|
|
33812
34189
|
this.fail("Simulation Worker could not start.");
|
|
33813
34190
|
}
|
|
@@ -35826,6 +36203,7 @@ class Renderer {
|
|
|
35826
36203
|
contextLost = false;
|
|
35827
36204
|
booleanOperationInProgress = false;
|
|
35828
36205
|
simulationInProgress = false;
|
|
36206
|
+
simulationCapture;
|
|
35829
36207
|
simulationToken = 0;
|
|
35830
36208
|
simulationWorker = new StabilityWorkerClient();
|
|
35831
36209
|
booleanOperationWorkerClient;
|
|
@@ -35837,6 +36215,7 @@ class Renderer {
|
|
|
35837
36215
|
fillLight;
|
|
35838
36216
|
frameHandle;
|
|
35839
36217
|
gizmoController;
|
|
36218
|
+
groundGrid;
|
|
35840
36219
|
hemisphereLight;
|
|
35841
36220
|
pendingFirstFramePerformance;
|
|
35842
36221
|
importPerformanceTracer = new ImportPerformanceTracer();
|
|
@@ -35874,6 +36253,7 @@ class Renderer {
|
|
|
35874
36253
|
let contextRestoreListenerAttached = false;
|
|
35875
36254
|
let controller;
|
|
35876
36255
|
let gizmoController;
|
|
36256
|
+
let groundGrid;
|
|
35877
36257
|
let resizeObserver;
|
|
35878
36258
|
let viewCube;
|
|
35879
36259
|
this.canvas = canvas;
|
|
@@ -35906,6 +36286,9 @@ class Renderer {
|
|
|
35906
36286
|
);
|
|
35907
36287
|
this.gizmoController = gizmoController;
|
|
35908
36288
|
this.scene.add(gizmoController.getHelper());
|
|
36289
|
+
groundGrid = new GroundGrid();
|
|
36290
|
+
this.groundGrid = groundGrid;
|
|
36291
|
+
groundGrid.attach(this.scene);
|
|
35909
36292
|
canvasController = new CanvasController(
|
|
35910
36293
|
canvas,
|
|
35911
36294
|
viewCube,
|
|
@@ -35959,9 +36342,13 @@ class Renderer {
|
|
|
35959
36342
|
viewCube?.dispose();
|
|
35960
36343
|
} finally {
|
|
35961
36344
|
try {
|
|
35962
|
-
|
|
36345
|
+
groundGrid?.dispose();
|
|
35963
36346
|
} finally {
|
|
35964
|
-
|
|
36347
|
+
try {
|
|
36348
|
+
controller?.dispose();
|
|
36349
|
+
} finally {
|
|
36350
|
+
renderer.dispose();
|
|
36351
|
+
}
|
|
35965
36352
|
}
|
|
35966
36353
|
}
|
|
35967
36354
|
}
|
|
@@ -36008,25 +36395,29 @@ class Renderer {
|
|
|
36008
36395
|
this.viewCube.dispose();
|
|
36009
36396
|
} finally {
|
|
36010
36397
|
try {
|
|
36011
|
-
this.
|
|
36398
|
+
this.groundGrid.dispose();
|
|
36012
36399
|
} finally {
|
|
36013
36400
|
try {
|
|
36014
|
-
this.
|
|
36401
|
+
this.releaseActiveModels();
|
|
36015
36402
|
} finally {
|
|
36016
36403
|
try {
|
|
36017
|
-
this.
|
|
36018
|
-
"webglcontextrestored",
|
|
36019
|
-
this.contextRestoreListener
|
|
36020
|
-
);
|
|
36404
|
+
this.resizeObserver.disconnect();
|
|
36021
36405
|
} finally {
|
|
36022
36406
|
try {
|
|
36023
36407
|
this.canvas.removeEventListener(
|
|
36024
|
-
"
|
|
36025
|
-
this.
|
|
36408
|
+
"webglcontextrestored",
|
|
36409
|
+
this.contextRestoreListener
|
|
36026
36410
|
);
|
|
36027
36411
|
} finally {
|
|
36028
|
-
|
|
36029
|
-
|
|
36412
|
+
try {
|
|
36413
|
+
this.canvas.removeEventListener(
|
|
36414
|
+
"webglcontextlost",
|
|
36415
|
+
this.contextLossListener
|
|
36416
|
+
);
|
|
36417
|
+
} finally {
|
|
36418
|
+
this.renderer.forceContextLoss();
|
|
36419
|
+
this.renderer.dispose();
|
|
36420
|
+
}
|
|
36030
36421
|
}
|
|
36031
36422
|
}
|
|
36032
36423
|
}
|
|
@@ -36064,7 +36455,10 @@ class Renderer {
|
|
|
36064
36455
|
stone: new ModelResourceList(source.stone.fileUrls, source.stone.upAxis)
|
|
36065
36456
|
};
|
|
36066
36457
|
return this.completeSceneImportFromResources(
|
|
36067
|
-
this.startImportSession(
|
|
36458
|
+
this.startImportSession(
|
|
36459
|
+
performanceTrace,
|
|
36460
|
+
this.createImportProgressTracker(resources)
|
|
36461
|
+
),
|
|
36068
36462
|
resources
|
|
36069
36463
|
);
|
|
36070
36464
|
} catch {
|
|
@@ -36106,7 +36500,10 @@ class Renderer {
|
|
|
36106
36500
|
stone: new ModelResourceList(source.stone.files, source.stone.upAxis)
|
|
36107
36501
|
};
|
|
36108
36502
|
return this.completeSceneImportFromResources(
|
|
36109
|
-
this.startImportSession(
|
|
36503
|
+
this.startImportSession(
|
|
36504
|
+
performanceTrace,
|
|
36505
|
+
this.createImportProgressTracker(resources)
|
|
36506
|
+
),
|
|
36110
36507
|
resources
|
|
36111
36508
|
);
|
|
36112
36509
|
} catch {
|
|
@@ -36153,6 +36550,7 @@ class Renderer {
|
|
|
36153
36550
|
available: this.activeStoneModel !== void 0,
|
|
36154
36551
|
contextLost: this.contextLost,
|
|
36155
36552
|
importInProgress: this.activeImport !== void 0,
|
|
36553
|
+
...this.activeImport === void 0 ? {} : { importProgress: this.activeImport.progressTracker.getPercent() },
|
|
36156
36554
|
interactionInProgress,
|
|
36157
36555
|
revision: this.sceneRevision,
|
|
36158
36556
|
resourceRevision: this.sceneResourceRevision
|
|
@@ -36359,6 +36757,8 @@ class Renderer {
|
|
|
36359
36757
|
const started = performance.now();
|
|
36360
36758
|
const token = ++this.simulationToken;
|
|
36361
36759
|
const revision = this.sceneRevision;
|
|
36760
|
+
const capture = new AbortController();
|
|
36761
|
+
this.simulationCapture = capture;
|
|
36362
36762
|
this.simulationInProgress = true;
|
|
36363
36763
|
try {
|
|
36364
36764
|
this.gizmoController.end();
|
|
@@ -36387,7 +36787,13 @@ class Renderer {
|
|
|
36387
36787
|
console.info("[stone-stability]", response2);
|
|
36388
36788
|
return response2;
|
|
36389
36789
|
}
|
|
36390
|
-
const
|
|
36790
|
+
const scheduler = new SimulationTaskScheduler(capture.signal, started);
|
|
36791
|
+
await scheduler.waitForPaint();
|
|
36792
|
+
const input = await new SimulationSnapshotFactory(scheduler).create(
|
|
36793
|
+
stone.object,
|
|
36794
|
+
base.object
|
|
36795
|
+
);
|
|
36796
|
+
capture.signal.throwIfAborted();
|
|
36391
36797
|
const response = await this.simulationWorker.run(input, started);
|
|
36392
36798
|
if (token !== this.simulationToken || revision !== this.sceneRevision || !this.isDualModelSceneReady()) {
|
|
36393
36799
|
return { status: "cancelled" };
|
|
@@ -36395,11 +36801,13 @@ class Renderer {
|
|
|
36395
36801
|
console.info("[stone-stability]", response);
|
|
36396
36802
|
return response;
|
|
36397
36803
|
} catch (error2) {
|
|
36804
|
+
if (capture.signal.aborted) return { status: "cancelled" };
|
|
36398
36805
|
const reason = error2 instanceof Error ? error2.message : "Simulation failed.";
|
|
36399
36806
|
console.info("[stone-stability]", { status: "failed", reason });
|
|
36400
36807
|
return { status: "failed", reason };
|
|
36401
36808
|
} finally {
|
|
36402
36809
|
if (token === this.simulationToken) {
|
|
36810
|
+
this.simulationCapture = void 0;
|
|
36403
36811
|
this.simulationInProgress = false;
|
|
36404
36812
|
this.notifyWorkspaceState();
|
|
36405
36813
|
}
|
|
@@ -36407,6 +36815,8 @@ class Renderer {
|
|
|
36407
36815
|
}
|
|
36408
36816
|
cancelSimulation() {
|
|
36409
36817
|
const wasActive = this.simulationInProgress;
|
|
36818
|
+
this.simulationCapture?.abort();
|
|
36819
|
+
this.simulationCapture = void 0;
|
|
36410
36820
|
this.simulationToken++;
|
|
36411
36821
|
this.simulationInProgress = false;
|
|
36412
36822
|
this.simulationWorker.cancel();
|
|
@@ -36603,12 +37013,22 @@ class Renderer {
|
|
|
36603
37013
|
let result;
|
|
36604
37014
|
try {
|
|
36605
37015
|
try {
|
|
37016
|
+
const progress = new RoleImportProgressReporter(
|
|
37017
|
+
session.progressTracker,
|
|
37018
|
+
role,
|
|
37019
|
+
source.resources instanceof ObjResourceList ? OBJ_MODEL_LOAD_PROGRESS_PROFILE : PLY_MODEL_LOAD_PROGRESS_PROFILE
|
|
37020
|
+
);
|
|
36606
37021
|
result = source.resources instanceof ObjResourceList ? await this.modelLoader.loadResources(
|
|
36607
37022
|
source.resources,
|
|
36608
37023
|
session.controller.signal,
|
|
36609
37024
|
session.performanceTrace,
|
|
36610
|
-
role
|
|
36611
|
-
|
|
37025
|
+
role,
|
|
37026
|
+
progress
|
|
37027
|
+
) : await this.plyLoader.load(
|
|
37028
|
+
source.resources,
|
|
37029
|
+
session.controller.signal,
|
|
37030
|
+
progress
|
|
37031
|
+
);
|
|
36612
37032
|
} catch {
|
|
36613
37033
|
return this.completeUnexpectedImportFailure(session);
|
|
36614
37034
|
}
|
|
@@ -36653,6 +37073,7 @@ class Renderer {
|
|
|
36653
37073
|
};
|
|
36654
37074
|
}
|
|
36655
37075
|
loadStatus = "success";
|
|
37076
|
+
session.progressTracker.completeRole(role);
|
|
36656
37077
|
return {
|
|
36657
37078
|
model: result.model,
|
|
36658
37079
|
warnings: result.warnings ?? []
|
|
@@ -36740,6 +37161,7 @@ class Renderer {
|
|
|
36740
37161
|
this.booleanEditSessionActive = false;
|
|
36741
37162
|
this.activeBooleanOperationSessionToken = void 0;
|
|
36742
37163
|
this.abortEditingState();
|
|
37164
|
+
this.refreshGroundGrid([stone, candidate]);
|
|
36743
37165
|
} catch {
|
|
36744
37166
|
this.activeBaseModel = previousBase;
|
|
36745
37167
|
(candidate.transformRoot ?? candidate.object).removeFromParent();
|
|
@@ -37056,6 +37478,7 @@ class Renderer {
|
|
|
37056
37478
|
...this.collectPresentModels(this.activeStoneModel, this.activeBaseModel)
|
|
37057
37479
|
];
|
|
37058
37480
|
this.gizmoController.setActiveScene({ base: void 0, stone: void 0 });
|
|
37481
|
+
this.groundGrid.hide();
|
|
37059
37482
|
this.transformedSceneBoundsDirty = false;
|
|
37060
37483
|
this.activeStoneModel = void 0;
|
|
37061
37484
|
this.activeBaseModel = void 0;
|
|
@@ -37104,6 +37527,18 @@ class Renderer {
|
|
|
37104
37527
|
this.gizmoController.setModelVisibility("stone", this.modelVisibility.stoneVisible);
|
|
37105
37528
|
this.gizmoController.setModelVisibility("base", this.modelVisibility.baseVisible);
|
|
37106
37529
|
}
|
|
37530
|
+
/**
|
|
37531
|
+
* Retargets the ground grid from the present models. The grid anchors to
|
|
37532
|
+
* models that are merely hidden as well, so toggling base visibility never
|
|
37533
|
+
* moves the ground plane; only a committed paired scene shows it.
|
|
37534
|
+
*/
|
|
37535
|
+
refreshGroundGrid(models) {
|
|
37536
|
+
if (models.length < 2) {
|
|
37537
|
+
this.groundGrid.hide();
|
|
37538
|
+
return;
|
|
37539
|
+
}
|
|
37540
|
+
this.groundGrid.updateFromBounds(this.mergeBounds(models));
|
|
37541
|
+
}
|
|
37107
37542
|
applyModelVisibility() {
|
|
37108
37543
|
const stoneRoot = this.activeStoneModel?.transformRoot ?? this.activeStoneModel?.object;
|
|
37109
37544
|
const baseRoot = this.activeBaseModel?.transformRoot ?? this.activeBaseModel?.object;
|
|
@@ -37152,6 +37587,7 @@ class Renderer {
|
|
|
37152
37587
|
}
|
|
37153
37588
|
renderScene(includeViewCube = true) {
|
|
37154
37589
|
this.syncTransformedSceneBounds();
|
|
37590
|
+
this.syncGroundGridForCamera();
|
|
37155
37591
|
this.gizmoController.updateForRender();
|
|
37156
37592
|
if (includeViewCube) {
|
|
37157
37593
|
this.viewCube.update(this.camera);
|
|
@@ -37161,6 +37597,17 @@ class Renderer {
|
|
|
37161
37597
|
this.renderViewCube();
|
|
37162
37598
|
}
|
|
37163
37599
|
}
|
|
37600
|
+
/**
|
|
37601
|
+
* Keeps the ground grid covering most of the visible area as the camera
|
|
37602
|
+
* zooms. Runs per rendered frame; updating only touches uniforms and the
|
|
37603
|
+
* mesh transform, so it stays allocation-free.
|
|
37604
|
+
*/
|
|
37605
|
+
syncGroundGridForCamera() {
|
|
37606
|
+
const frustumHeight = (this.camera.top - this.camera.bottom) / this.camera.zoom;
|
|
37607
|
+
const frustumWidth = (this.camera.right - this.camera.left) / (this.camera.top - this.camera.bottom) * frustumHeight;
|
|
37608
|
+
const halfDiagonal = Math.sqrt(frustumHeight * frustumHeight + frustumWidth * frustumWidth) / 2;
|
|
37609
|
+
this.groundGrid.updateForViewport(halfDiagonal);
|
|
37610
|
+
}
|
|
37164
37611
|
renderViewCube() {
|
|
37165
37612
|
const viewport = this.viewCube.getViewport();
|
|
37166
37613
|
const previousAutoClear = this.renderer.autoClear;
|
|
@@ -37199,6 +37646,9 @@ class Renderer {
|
|
|
37199
37646
|
}
|
|
37200
37647
|
}
|
|
37201
37648
|
this.controller.syncTransformedBounds(this.mergeBounds(models));
|
|
37649
|
+
this.refreshGroundGrid(
|
|
37650
|
+
this.collectPresentModels(this.activeStoneModel, this.activeBaseModel)
|
|
37651
|
+
);
|
|
37202
37652
|
}
|
|
37203
37653
|
/**
|
|
37204
37654
|
* Atomically replaces the committed active scene. New model references are
|
|
@@ -37273,6 +37723,7 @@ class Renderer {
|
|
|
37273
37723
|
};
|
|
37274
37724
|
this.gizmoController.setActiveScene(activeScene);
|
|
37275
37725
|
this.applyGizmoVisibility();
|
|
37726
|
+
this.refreshGroundGrid(candidateModels);
|
|
37276
37727
|
this.sceneRevision += 1;
|
|
37277
37728
|
this.sceneResourceRevision = this.sceneRevision;
|
|
37278
37729
|
this.pendingFirstFramePerformance = {
|
|
@@ -37282,6 +37733,7 @@ class Renderer {
|
|
|
37282
37733
|
};
|
|
37283
37734
|
session.performanceTrace.markStage("import-promise-resolved", "scene");
|
|
37284
37735
|
session.performanceTrace.endStage(sceneCommitStage, "success");
|
|
37736
|
+
session.progressTracker.completeScene();
|
|
37285
37737
|
this.finishImportSession(session);
|
|
37286
37738
|
this.releasePair(previousStone, previousBase);
|
|
37287
37739
|
this.requestRender();
|
|
@@ -37405,6 +37857,13 @@ class Renderer {
|
|
|
37405
37857
|
this.notifyWorkspaceState();
|
|
37406
37858
|
}
|
|
37407
37859
|
}
|
|
37860
|
+
handleImportProgressChanged(_source) {
|
|
37861
|
+
this.notifyWorkspaceState();
|
|
37862
|
+
}
|
|
37863
|
+
createImportProgressTracker(resources) {
|
|
37864
|
+
const roles = resources.base === void 0 ? ["stone"] : ["stone", "base"];
|
|
37865
|
+
return new ImportProgressTracker(roles, this.handleImportProgressChanged.bind(this));
|
|
37866
|
+
}
|
|
37408
37867
|
notifyWorkspaceState() {
|
|
37409
37868
|
this.workspaceStateListener?.(this.getWorkspaceState());
|
|
37410
37869
|
}
|
|
@@ -37453,6 +37912,7 @@ class Renderer {
|
|
|
37453
37912
|
this.renderer.setSize(width, height, false);
|
|
37454
37913
|
this.controller.resize(width, height);
|
|
37455
37914
|
this.viewCube.resize(width, height, pixelRatio);
|
|
37915
|
+
this.groundGrid.setPixelRatio(pixelRatio);
|
|
37456
37916
|
this.requestRender();
|
|
37457
37917
|
}
|
|
37458
37918
|
invalidateActiveImport(code) {
|
|
@@ -37498,7 +37958,7 @@ class Renderer {
|
|
|
37498
37958
|
this.notifyWorkspaceState();
|
|
37499
37959
|
}
|
|
37500
37960
|
}
|
|
37501
|
-
startImportSession(performanceTrace) {
|
|
37961
|
+
startImportSession(performanceTrace, progressTracker) {
|
|
37502
37962
|
this.cancelSimulation();
|
|
37503
37963
|
this.cancelBooleanOperation();
|
|
37504
37964
|
this.cancelPendingFirstFramePerformance("cancelled");
|
|
@@ -37511,7 +37971,8 @@ class Renderer {
|
|
|
37511
37971
|
const session = {
|
|
37512
37972
|
controller: new AbortController(),
|
|
37513
37973
|
invalidationCode: void 0,
|
|
37514
|
-
performanceTrace
|
|
37974
|
+
performanceTrace,
|
|
37975
|
+
progressTracker
|
|
37515
37976
|
};
|
|
37516
37977
|
performanceTrace.markStage("import-session-start", "scene");
|
|
37517
37978
|
this.activeImport = session;
|
|
@@ -37752,6 +38213,8 @@ class ArchiveImportWorkerMessageParser {
|
|
|
37752
38213
|
return this.parseEntryStart(record, taskId);
|
|
37753
38214
|
case "error":
|
|
37754
38215
|
return typeof record["message"] === "string" ? { message: record["message"], taskId, type: "error" } : void 0;
|
|
38216
|
+
case "progress":
|
|
38217
|
+
return this.parseProgress(record, taskId);
|
|
37755
38218
|
default:
|
|
37756
38219
|
return void 0;
|
|
37757
38220
|
}
|
|
@@ -37769,6 +38232,17 @@ class ArchiveImportWorkerMessageParser {
|
|
|
37769
38232
|
isPositiveSafeInteger(value) {
|
|
37770
38233
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
37771
38234
|
}
|
|
38235
|
+
isNonNegativeSafeInteger(value) {
|
|
38236
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
38237
|
+
}
|
|
38238
|
+
parseProgress(record, taskId) {
|
|
38239
|
+
const bytesLoaded = record["bytesLoaded"];
|
|
38240
|
+
const bytesTotal = record["bytesTotal"];
|
|
38241
|
+
if (!this.isNonNegativeSafeInteger(bytesLoaded) || !this.isNonNegativeSafeInteger(bytesTotal)) {
|
|
38242
|
+
return void 0;
|
|
38243
|
+
}
|
|
38244
|
+
return { bytesLoaded, bytesTotal, taskId, type: "progress" };
|
|
38245
|
+
}
|
|
37772
38246
|
isRecord(value) {
|
|
37773
38247
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37774
38248
|
}
|
|
@@ -37828,7 +38302,7 @@ class BrowserArchiveImportWorkerEndpoint {
|
|
|
37828
38302
|
constructor() {
|
|
37829
38303
|
this.worker = new Worker(new URL(
|
|
37830
38304
|
/* @vite-ignore */
|
|
37831
|
-
"" + new URL("assets/ArchiveImportWorkerEntry-
|
|
38305
|
+
"" + new URL("assets/ArchiveImportWorkerEntry-wkxjE56k.js", import.meta.url).href,
|
|
37832
38306
|
import.meta.url
|
|
37833
38307
|
), {
|
|
37834
38308
|
type: "module"
|
|
@@ -37868,9 +38342,11 @@ class ArchiveImportWorkerClient {
|
|
|
37868
38342
|
jobValidator = new ArchiveImportJobValidator();
|
|
37869
38343
|
lastStartedTaskId = 0;
|
|
37870
38344
|
messageParser = new ArchiveImportWorkerMessageParser();
|
|
38345
|
+
onProgress;
|
|
37871
38346
|
workerFactory;
|
|
37872
|
-
constructor(workerFactory = new BrowserArchiveImportWorkerFactory()) {
|
|
38347
|
+
constructor(workerFactory = new BrowserArchiveImportWorkerFactory(), onProgress) {
|
|
37873
38348
|
this.workerFactory = workerFactory;
|
|
38349
|
+
this.onProgress = onProgress;
|
|
37874
38350
|
}
|
|
37875
38351
|
cancel(taskId) {
|
|
37876
38352
|
const task = this.activeTask;
|
|
@@ -37945,6 +38421,10 @@ class ArchiveImportWorkerClient {
|
|
|
37945
38421
|
outbound.message
|
|
37946
38422
|
)
|
|
37947
38423
|
);
|
|
38424
|
+
return;
|
|
38425
|
+
case "progress":
|
|
38426
|
+
this.handleProgress(outbound);
|
|
38427
|
+
return;
|
|
37948
38428
|
}
|
|
37949
38429
|
} catch (error2) {
|
|
37950
38430
|
this.rejectTask(task, this.toError(error2));
|
|
@@ -38014,6 +38494,10 @@ class ArchiveImportWorkerClient {
|
|
|
38014
38494
|
type: "acknowledge-chunk"
|
|
38015
38495
|
});
|
|
38016
38496
|
}
|
|
38497
|
+
handleProgress(message) {
|
|
38498
|
+
const percent = message.bytesTotal <= 0 ? 0 : Math.min(100, Math.floor(message.bytesLoaded / message.bytesTotal * 100));
|
|
38499
|
+
this.onProgress?.(percent);
|
|
38500
|
+
}
|
|
38017
38501
|
rejectTask(task, error2) {
|
|
38018
38502
|
if (this.activeTask !== task) {
|
|
38019
38503
|
return;
|
|
@@ -38195,6 +38679,26 @@ var ImportPerformanceMode = /* @__PURE__ */ ((ImportPerformanceMode2) => {
|
|
|
38195
38679
|
ImportPerformanceMode2["Verbose"] = "verbose";
|
|
38196
38680
|
return ImportPerformanceMode2;
|
|
38197
38681
|
})(ImportPerformanceMode || {});
|
|
38682
|
+
const ARCHIVE_SEGMENT_PERCENT = 20;
|
|
38683
|
+
class ImportProgressCompositor {
|
|
38684
|
+
compose(input) {
|
|
38685
|
+
const archiveProgress = this.clampPercent(input.archiveProgress);
|
|
38686
|
+
const rendererProgress = this.clampPercent(input.rendererProgress);
|
|
38687
|
+
if (archiveProgress === void 0) {
|
|
38688
|
+
return rendererProgress;
|
|
38689
|
+
}
|
|
38690
|
+
if (rendererProgress === void 0) {
|
|
38691
|
+
return Math.floor(archiveProgress / 100 * ARCHIVE_SEGMENT_PERCENT);
|
|
38692
|
+
}
|
|
38693
|
+
return ARCHIVE_SEGMENT_PERCENT + Math.floor(rendererProgress / 100 * (100 - ARCHIVE_SEGMENT_PERCENT));
|
|
38694
|
+
}
|
|
38695
|
+
clampPercent(percent) {
|
|
38696
|
+
if (percent === void 0) {
|
|
38697
|
+
return void 0;
|
|
38698
|
+
}
|
|
38699
|
+
return Math.max(0, Math.min(100, percent));
|
|
38700
|
+
}
|
|
38701
|
+
}
|
|
38198
38702
|
class DownloadSaveSink {
|
|
38199
38703
|
aborted = false;
|
|
38200
38704
|
blobChunks = [];
|
|
@@ -39914,7 +40418,7 @@ var StoneBasePoseMode = /* @__PURE__ */ ((StoneBasePoseMode2) => {
|
|
|
39914
40418
|
StoneBasePoseMode2["PreservePose"] = "preserve-pose";
|
|
39915
40419
|
return StoneBasePoseMode2;
|
|
39916
40420
|
})(StoneBasePoseMode || {});
|
|
39917
|
-
const studioWorkspaceStyles = ".lingbi-studio-workspace {\n position: relative;\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport > canvas {\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n background: #3d3f41;\n}\n\n.lingbi-studio-boolean-operation-overlay {\n position: absolute;\n z-index: 3;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n background: rgba(17, 17, 17, 0.32);\n color: #ffffff;\n cursor: wait;\n}\n\n.lingbi-studio-boolean-operation-overlay[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation {\n position: absolute;\n z-index: 4;\n top: 50%;\n left: 50%;\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n width: min(420px, calc(100% - 32px));\n padding: 24px;\n border-radius: 8px;\n background: #ffffff;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.22);\n transform: translate(-50%, -50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-edit-confirmation[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation-text {\n grid-column: 1 / -1;\n margin: 0 0 8px;\n color: #171717;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n text-align: center;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel,\n.lingbi-studio-boolean-edit-confirmation-continue {\n height: 40px;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font: inherit;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel {\n background: #ffffff;\n color: #171717;\n}\n\n.lingbi-studio-boolean-edit-confirmation-continue {\n border-color: #f27c38;\n background: #f27c38;\n color: #ffffff;\n}\n\n.lingbi-studio-boolean-operation-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid rgba(255, 105, 0, 0.28);\n border-top-color: #ff6900;\n border-radius: 50%;\n animation: lingbi-studio-boolean-operation-spin 0.8s linear infinite;\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-operation-status {\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-bottom-toolbar {\n position: absolute;\n z-index: 1;\n bottom: 36px;\n left: 50%;\n display: grid;\n align-items: center;\n grid-template-columns: minmax(0, 1fr);\n width: min(120px, calc(100% - 32px));\n height: 64px;\n padding: 8px 12px;\n transform: translateX(-50%);\n box-sizing: border-box;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n width: min(480px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-inventory-control {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n width: min(240px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-inventory-control {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n width: min(360px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(6, minmax(0, 1fr));\n width: min(720px, calc(100% - 32px));\n}\n\n.lingbi-studio-model-visibility {\n position: absolute;\n z-index: 1;\n bottom: 108px;\n left: 50%;\n display: grid;\n gap: 8px;\n width: 144px;\n padding: 12px;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n transform: translateX(-240px);\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility[hidden] {\n display: none;\n}\n\n.lingbi-studio-model-visibility-title {\n color: #171717;\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n color: #171717;\n font-size: 14px;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n appearance: none;\n width: 20px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n line-height: 0;\n outline: none;\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility-icon {\n display: block;\n width: 18px;\n height: 18px;\n flex: 0 0 auto;\n pointer-events: none;\n}\n\n.lingbi-studio-model-visibility-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-model-visibility-button:disabled {\n color: #8c8c8c;\n}\n\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-boolean-button,\n.lingbi-studio-simulation-button,\n.lingbi-studio-download-button,\n.lingbi-studio-inventory-button,\n.lingbi-studio-edit-button,\n.lingbi-studio-model-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n min-width: 0;\n height: 48px;\n padding: 0 8px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n font-size: 18px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 1;\n box-sizing: border-box;\n white-space: nowrap;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n.lingbi-studio-operation-button-wrapper {\n display: block;\n width: 100%;\n min-width: 0;\n height: 48px;\n box-sizing: border-box;\n}\n\n.lingbi-studio-operation-button-wrapper > button:disabled {\n pointer-events: none;\n}\n\n.lingbi-studio-boolean-button[hidden],\n.lingbi-studio-simulation-button[hidden],\n.lingbi-studio-download-button[hidden],\n.lingbi-studio-inventory-button[hidden],\n.lingbi-studio-edit-button[hidden],\n.lingbi-studio-model-button[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-button:not(:disabled),\n.lingbi-studio-simulation-button:not(:disabled),\n.lingbi-studio-download-button:not(:disabled),\n.lingbi-studio-inventory-button:not(:disabled),\n.lingbi-studio-edit-button:not(:disabled),\n.lingbi-studio-model-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-button:disabled,\n.lingbi-studio-simulation-button:disabled,\n.lingbi-studio-download-button:disabled,\n.lingbi-studio-inventory-button:disabled,\n.lingbi-studio-edit-button:disabled,\n.lingbi-studio-model-button:disabled,\n.lingbi-studio-model-visibility-button:disabled {\n background: transparent;\n color: #8c8c8c;\n opacity: 1;\n}\n\n.lingbi-studio-edit-button.is-active,\n.lingbi-studio-edit-button.is-active:disabled,\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-download-status {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 6px 10px;\n border: 1px solid #f4b5b0;\n border-radius: 4px;\n background: #fff1f0;\n color: #b42318;\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-download-success {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 8px 16px;\n border: 1px solid #86d39a;\n border-radius: 6px;\n background: #f0fff4;\n color: #176b32;\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-success[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status {\n position: absolute;\n z-index: 2;\n bottom: 112px;\n left: 50%;\n display: flex;\n align-items: center;\n gap: 12px;\n max-width: calc(100% - 24px);\n padding: 10px 14px;\n border: 0;\n border-radius: 8px;\n background: #e9e9e9;\n color: #171717;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-edit-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status-text {\n display: inline-block;\n min-width: 0;\n font-size: 20px;\n line-height: 28px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-exit-button {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n height: 40px;\n padding: 0 18px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: #f27c38;\n color: #ffffff;\n cursor: pointer;\n font: inherit;\n font-size: 18px;\n font-weight: 600;\n line-height: 24px;\n letter-spacing: 0;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .lingbi-studio-boolean-button:not(:disabled):hover,\n .lingbi-studio-simulation-button:not(:disabled):hover,\n .lingbi-studio-download-button:not(:disabled):hover,\n .lingbi-studio-inventory-button:not(:disabled):hover,\n .lingbi-studio-edit-button:not(:disabled):hover,\n .lingbi-studio-model-button:not(:disabled):hover {\n background: #e8e8e8;\n }\n\n .lingbi-studio-simulation-button:not(:disabled):hover,\n .lingbi-studio-download-button:not(:disabled):hover,\n .lingbi-studio-inventory-button:not(:disabled):hover,\n .lingbi-studio-edit-button:not(:disabled):hover,\n .lingbi-studio-model-button:not(:disabled):hover {\n color: #f06400;\n }\n\n .lingbi-studio-model-visibility-button:not(:disabled):hover {\n background: transparent;\n }\n\n .lingbi-studio-exit-button:hover {\n background: #ff8a1f;\n }\n\n .lingbi-studio-boolean-edit-confirmation-cancel:hover {\n background: #f5f5f5;\n }\n\n .lingbi-studio-boolean-edit-confirmation-continue:hover {\n background: #ff8a1f;\n }\n}\n\n.lingbi-studio-boolean-button:not(:disabled):active,\n.lingbi-studio-download-button:not(:disabled):active,\n.lingbi-studio-inventory-button:not(:disabled):active,\n.lingbi-studio-edit-button:not(:disabled):active,\n.lingbi-studio-model-button:not(:disabled):active,\n.lingbi-studio-model-visibility-button:not(:disabled):active {\n background: transparent;\n}\n\n.lingbi-studio-exit-button:active {\n background: #e66d00;\n}\n\n@keyframes lingbi-studio-boolean-operation-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button,\n .lingbi-studio-exit-button {\n transition: none;\n }\n\n .lingbi-studio-boolean-operation-spinner {\n animation: none;\n }\n}\n\n@media (max-width: 420px) {\n .lingbi-studio-bottom-toolbar.has-edit-control {\n width: calc(100% - 16px);\n padding: 8px 4px;\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-model-visibility {\n left: 8px;\n transform: none;\n }\n\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button {\n padding: 0 2px;\n font-size: 16px;\n }\n\n .lingbi-studio-edit-status-text {\n font-size: 16px;\n line-height: 24px;\n }\n\n .lingbi-studio-exit-button {\n height: 36px;\n padding: 0 14px;\n font-size: 16px;\n }\n}\n\n.lingbi-studio-viewport > canvas:focus-visible,\n.lingbi-studio-boolean-button:focus-visible,\n.lingbi-studio-simulation-button:focus-visible,\n.lingbi-studio-download-button:focus-visible,\n.lingbi-studio-inventory-button:focus-visible,\n.lingbi-studio-edit-button:focus-visible,\n.lingbi-studio-model-button:focus-visible,\n.lingbi-studio-model-visibility-button:focus-visible,\n.lingbi-studio-exit-button:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-cancel:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-continue:focus-visible {\n outline: 2px solid #7cc0ff;\n outline-offset: 2px;\n}\n";
|
|
40421
|
+
const studioWorkspaceStyles = ".lingbi-studio-workspace {\n position: relative;\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport {\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n\n.lingbi-studio-viewport > canvas {\n display: block;\n width: 100%;\n height: 100%;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n background: #3d3f41;\n}\n\n.lingbi-studio-boolean-operation-overlay {\n position: absolute;\n z-index: 3;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n background: rgba(17, 17, 17, 0.32);\n color: #ffffff;\n cursor: wait;\n}\n\n.lingbi-studio-boolean-operation-overlay[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation {\n position: absolute;\n z-index: 4;\n top: 50%;\n left: 50%;\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n width: min(420px, calc(100% - 32px));\n padding: 24px;\n border-radius: 8px;\n background: #ffffff;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.22);\n transform: translate(-50%, -50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-edit-confirmation[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-edit-confirmation-text {\n grid-column: 1 / -1;\n margin: 0 0 8px;\n color: #171717;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n text-align: center;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel,\n.lingbi-studio-boolean-edit-confirmation-continue {\n height: 40px;\n border: 1px solid #d9d9d9;\n border-radius: 4px;\n font: inherit;\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-edit-confirmation-cancel {\n background: #ffffff;\n color: #171717;\n}\n\n.lingbi-studio-boolean-edit-confirmation-continue {\n border-color: #f27c38;\n background: #f27c38;\n color: #ffffff;\n}\n\n.lingbi-studio-boolean-operation-spinner {\n width: 32px;\n height: 32px;\n border: 3px solid rgba(255, 105, 0, 0.28);\n border-top-color: #ff6900;\n border-radius: 50%;\n animation: lingbi-studio-boolean-operation-spin 0.8s linear infinite;\n box-sizing: border-box;\n}\n\n.lingbi-studio-boolean-operation-status {\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-import-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.lingbi-studio-import-row[hidden] {\n display: none;\n}\n\n.lingbi-studio-import-progress {\n display: block;\n width: 160px;\n height: 4px;\n overflow: hidden;\n border-radius: 999px;\n background: rgba(255, 105, 0, 0.28);\n box-sizing: border-box;\n}\n\n.lingbi-studio-import-progress[hidden] {\n display: none;\n}\n\n.lingbi-studio-import-progress-fill {\n width: 0;\n height: 100%;\n border-radius: 999px;\n background: #ff6900;\n}\n\n.lingbi-studio-import-percent {\n min-width: 36px;\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n text-align: left;\n}\n\n.lingbi-studio-bottom-toolbar {\n position: absolute;\n z-index: 1;\n bottom: 36px;\n left: 50%;\n display: grid;\n align-items: center;\n grid-template-columns: minmax(0, 1fr);\n width: min(120px, calc(100% - 32px));\n height: 64px;\n padding: 8px 12px;\n transform: translateX(-50%);\n box-sizing: border-box;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n width: min(480px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-inventory-control {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n width: min(240px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-inventory-control {\n grid-template-columns: repeat(3, minmax(0, 1fr));\n width: min(360px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n grid-template-columns: repeat(5, minmax(0, 1fr));\n width: min(600px, calc(100% - 32px));\n}\n\n.lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n grid-template-columns: repeat(6, minmax(0, 1fr));\n width: min(720px, calc(100% - 32px));\n}\n\n.lingbi-studio-model-visibility {\n position: absolute;\n z-index: 1;\n bottom: 108px;\n left: 50%;\n display: grid;\n gap: 8px;\n width: 144px;\n padding: 12px;\n border-radius: 8px;\n background: #f4f4f4;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n transform: translateX(-240px);\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility[hidden] {\n display: none;\n}\n\n.lingbi-studio-model-visibility-title {\n color: #171717;\n font-size: 14px;\n font-weight: 600;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto;\n gap: 8px;\n align-items: center;\n color: #171717;\n font-size: 14px;\n line-height: 20px;\n}\n\n.lingbi-studio-model-visibility-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n appearance: none;\n width: 20px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n box-shadow: none;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n line-height: 0;\n outline: none;\n box-sizing: border-box;\n}\n\n.lingbi-studio-model-visibility-icon {\n display: block;\n width: 18px;\n height: 18px;\n flex: 0 0 auto;\n pointer-events: none;\n}\n\n.lingbi-studio-model-visibility-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-model-visibility-button:disabled {\n color: #8c8c8c;\n}\n\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-boolean-button,\n.lingbi-studio-simulation-button,\n.lingbi-studio-download-button,\n.lingbi-studio-inventory-button,\n.lingbi-studio-edit-button,\n.lingbi-studio-model-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n min-width: 0;\n height: 48px;\n padding: 0 8px;\n border: 0;\n border-radius: 4px;\n background: transparent;\n color: #171717;\n cursor: not-allowed;\n font: inherit;\n font-size: 18px;\n font-weight: 500;\n line-height: 24px;\n letter-spacing: 0;\n opacity: 1;\n box-sizing: border-box;\n white-space: nowrap;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n.lingbi-studio-operation-button-wrapper {\n display: block;\n width: 100%;\n min-width: 0;\n height: 48px;\n box-sizing: border-box;\n}\n\n.lingbi-studio-operation-button-wrapper > button:disabled {\n pointer-events: none;\n}\n\n.lingbi-studio-boolean-button[hidden],\n.lingbi-studio-simulation-button[hidden],\n.lingbi-studio-download-button[hidden],\n.lingbi-studio-inventory-button[hidden],\n.lingbi-studio-edit-button[hidden],\n.lingbi-studio-model-button[hidden] {\n display: none;\n}\n\n.lingbi-studio-boolean-button:not(:disabled),\n.lingbi-studio-simulation-button:not(:disabled),\n.lingbi-studio-download-button:not(:disabled),\n.lingbi-studio-inventory-button:not(:disabled),\n.lingbi-studio-edit-button:not(:disabled),\n.lingbi-studio-model-button:not(:disabled) {\n cursor: pointer;\n}\n\n.lingbi-studio-boolean-button:disabled,\n.lingbi-studio-simulation-button:disabled,\n.lingbi-studio-download-button:disabled,\n.lingbi-studio-inventory-button:disabled,\n.lingbi-studio-edit-button:disabled,\n.lingbi-studio-model-button:disabled,\n.lingbi-studio-model-visibility-button:disabled {\n background: transparent;\n color: #8c8c8c;\n opacity: 1;\n}\n\n.lingbi-studio-edit-button.is-active,\n.lingbi-studio-edit-button.is-active:disabled,\n.lingbi-studio-model-button.is-active,\n.lingbi-studio-model-button.is-active:disabled {\n color: #ff6900;\n font-weight: 600;\n}\n\n.lingbi-studio-download-status {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 6px 10px;\n border: 1px solid #f4b5b0;\n border-radius: 4px;\n background: #fff1f0;\n color: #b42318;\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-download-success {\n position: absolute;\n z-index: 2;\n top: 24px;\n left: 50%;\n max-width: calc(100% - 24px);\n padding: 8px 16px;\n border: 1px solid #86d39a;\n border-radius: 6px;\n background: #f0fff4;\n color: #176b32;\n font-size: 14px;\n font-weight: 500;\n line-height: 20px;\n text-align: center;\n transform: translateX(-50%);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n box-sizing: border-box;\n}\n\n.lingbi-studio-download-success[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status {\n position: absolute;\n z-index: 2;\n bottom: 112px;\n left: 50%;\n display: flex;\n align-items: center;\n gap: 12px;\n max-width: calc(100% - 24px);\n padding: 10px 14px;\n border: 0;\n border-radius: 8px;\n background: #e9e9e9;\n color: #171717;\n transform: translateX(-50%);\n box-sizing: border-box;\n}\n\n.lingbi-studio-edit-status[hidden] {\n display: none;\n}\n\n.lingbi-studio-edit-status-text {\n display: inline-block;\n min-width: 0;\n font-size: 20px;\n line-height: 28px;\n letter-spacing: 0;\n}\n\n.lingbi-studio-exit-button {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n height: 40px;\n padding: 0 18px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: #f27c38;\n color: #ffffff;\n cursor: pointer;\n font: inherit;\n font-size: 18px;\n font-weight: 600;\n line-height: 24px;\n letter-spacing: 0;\n transition:\n background-color 140ms ease,\n color 140ms ease;\n}\n\n@media (hover: hover) and (pointer: fine) {\n .lingbi-studio-boolean-button:not(:disabled):hover,\n .lingbi-studio-simulation-button:not(:disabled):hover,\n .lingbi-studio-download-button:not(:disabled):hover,\n .lingbi-studio-inventory-button:not(:disabled):hover,\n .lingbi-studio-edit-button:not(:disabled):hover,\n .lingbi-studio-model-button:not(:disabled):hover {\n background: #e8e8e8;\n }\n\n .lingbi-studio-simulation-button:not(:disabled):hover,\n .lingbi-studio-download-button:not(:disabled):hover,\n .lingbi-studio-inventory-button:not(:disabled):hover,\n .lingbi-studio-edit-button:not(:disabled):hover,\n .lingbi-studio-model-button:not(:disabled):hover {\n color: #f06400;\n }\n\n .lingbi-studio-model-visibility-button:not(:disabled):hover {\n background: transparent;\n }\n\n .lingbi-studio-exit-button:hover {\n background: #ff8a1f;\n }\n\n .lingbi-studio-boolean-edit-confirmation-cancel:hover {\n background: #f5f5f5;\n }\n\n .lingbi-studio-boolean-edit-confirmation-continue:hover {\n background: #ff8a1f;\n }\n}\n\n.lingbi-studio-boolean-button:not(:disabled):active,\n.lingbi-studio-download-button:not(:disabled):active,\n.lingbi-studio-inventory-button:not(:disabled):active,\n.lingbi-studio-edit-button:not(:disabled):active,\n.lingbi-studio-model-button:not(:disabled):active,\n.lingbi-studio-model-visibility-button:not(:disabled):active {\n background: transparent;\n}\n\n.lingbi-studio-exit-button:active {\n background: #e66d00;\n}\n\n@keyframes lingbi-studio-boolean-operation-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button,\n .lingbi-studio-exit-button {\n transition: none;\n }\n\n .lingbi-studio-boolean-operation-spinner {\n animation: none;\n }\n}\n\n@media (max-width: 420px) {\n .lingbi-studio-bottom-toolbar.has-edit-control {\n width: calc(100% - 16px);\n padding: 8px 4px;\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-bottom-toolbar.has-model-control.has-edit-control.has-inventory-control {\n width: calc(100% - 16px);\n }\n\n .lingbi-studio-model-visibility {\n left: 8px;\n transform: none;\n }\n\n .lingbi-studio-boolean-button,\n .lingbi-studio-simulation-button,\n .lingbi-studio-download-button,\n .lingbi-studio-inventory-button,\n .lingbi-studio-edit-button,\n .lingbi-studio-model-button {\n padding: 0 2px;\n font-size: 16px;\n }\n\n .lingbi-studio-edit-status-text {\n font-size: 16px;\n line-height: 24px;\n }\n\n .lingbi-studio-exit-button {\n height: 36px;\n padding: 0 14px;\n font-size: 16px;\n }\n}\n\n.lingbi-studio-viewport > canvas:focus-visible,\n.lingbi-studio-boolean-button:focus-visible,\n.lingbi-studio-simulation-button:focus-visible,\n.lingbi-studio-download-button:focus-visible,\n.lingbi-studio-inventory-button:focus-visible,\n.lingbi-studio-edit-button:focus-visible,\n.lingbi-studio-model-button:focus-visible,\n.lingbi-studio-model-visibility-button:focus-visible,\n.lingbi-studio-exit-button:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-cancel:focus-visible,\n.lingbi-studio-boolean-edit-confirmation-continue:focus-visible {\n outline: 2px solid #7cc0ff;\n outline-offset: 2px;\n}\n";
|
|
39918
40422
|
class StudioWorkspace {
|
|
39919
40423
|
canvas;
|
|
39920
40424
|
static SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
@@ -39925,7 +40429,12 @@ class StudioWorkspace {
|
|
|
39925
40429
|
booleanButtonElement;
|
|
39926
40430
|
simulationButtonElement;
|
|
39927
40431
|
booleanOperationOverlayElement;
|
|
40432
|
+
booleanOperationSpinnerElement;
|
|
39928
40433
|
booleanOperationStatusElement;
|
|
40434
|
+
importPercentElement;
|
|
40435
|
+
importProgressElement;
|
|
40436
|
+
importProgressFillElement;
|
|
40437
|
+
importProgressRowElement;
|
|
39929
40438
|
booleanEditConfirmationElement;
|
|
39930
40439
|
bottomToolbarElement;
|
|
39931
40440
|
downloadButtonElement;
|
|
@@ -39957,6 +40466,10 @@ class StudioWorkspace {
|
|
|
39957
40466
|
const booleanOperationOverlayElement = ownerDocument.createElement("div");
|
|
39958
40467
|
const booleanOperationSpinnerElement = ownerDocument.createElement("div");
|
|
39959
40468
|
const booleanOperationStatusElement = ownerDocument.createElement("div");
|
|
40469
|
+
const importPercentElement = ownerDocument.createElement("div");
|
|
40470
|
+
const importProgressElement = ownerDocument.createElement("div");
|
|
40471
|
+
const importProgressFillElement = ownerDocument.createElement("div");
|
|
40472
|
+
const importProgressRowElement = ownerDocument.createElement("div");
|
|
39960
40473
|
const booleanEditConfirmationElement = ownerDocument.createElement("div");
|
|
39961
40474
|
const booleanEditConfirmationTextElement = ownerDocument.createElement("p");
|
|
39962
40475
|
const booleanEditConfirmationCancelButtonElement = ownerDocument.createElement("button");
|
|
@@ -40006,10 +40519,24 @@ class StudioWorkspace {
|
|
|
40006
40519
|
booleanOperationOverlayElement.hidden = true;
|
|
40007
40520
|
booleanOperationSpinnerElement.className = "lingbi-studio-boolean-operation-spinner";
|
|
40008
40521
|
booleanOperationSpinnerElement.setAttribute("aria-hidden", "true");
|
|
40522
|
+
importProgressRowElement.className = "lingbi-studio-import-row";
|
|
40523
|
+
importProgressRowElement.hidden = true;
|
|
40524
|
+
importPercentElement.className = "lingbi-studio-import-percent";
|
|
40525
|
+
importPercentElement.textContent = "0%";
|
|
40526
|
+
importProgressElement.className = "lingbi-studio-import-progress";
|
|
40527
|
+
importProgressElement.setAttribute("aria-label", "模型加载进度");
|
|
40528
|
+
importProgressElement.setAttribute("aria-valuemax", "100");
|
|
40529
|
+
importProgressElement.setAttribute("aria-valuemin", "0");
|
|
40530
|
+
importProgressElement.setAttribute("aria-valuenow", "0");
|
|
40531
|
+
importProgressElement.setAttribute("role", "progressbar");
|
|
40532
|
+
importProgressFillElement.className = "lingbi-studio-import-progress-fill";
|
|
40533
|
+
importProgressElement.append(importProgressFillElement);
|
|
40534
|
+
importProgressRowElement.append(importProgressElement, importPercentElement);
|
|
40009
40535
|
booleanOperationStatusElement.className = "lingbi-studio-boolean-operation-status";
|
|
40010
40536
|
booleanOperationStatusElement.textContent = "布尔计算中";
|
|
40011
40537
|
booleanOperationOverlayElement.append(
|
|
40012
40538
|
booleanOperationSpinnerElement,
|
|
40539
|
+
importProgressRowElement,
|
|
40013
40540
|
booleanOperationStatusElement
|
|
40014
40541
|
);
|
|
40015
40542
|
booleanEditConfirmationElement.className = "lingbi-studio-boolean-edit-confirmation";
|
|
@@ -40137,7 +40664,12 @@ class StudioWorkspace {
|
|
|
40137
40664
|
this.booleanButtonElement = booleanButtonElement;
|
|
40138
40665
|
this.simulationButtonElement = simulationButtonElement;
|
|
40139
40666
|
this.booleanOperationOverlayElement = booleanOperationOverlayElement;
|
|
40667
|
+
this.booleanOperationSpinnerElement = booleanOperationSpinnerElement;
|
|
40140
40668
|
this.booleanOperationStatusElement = booleanOperationStatusElement;
|
|
40669
|
+
this.importPercentElement = importPercentElement;
|
|
40670
|
+
this.importProgressElement = importProgressElement;
|
|
40671
|
+
this.importProgressFillElement = importProgressFillElement;
|
|
40672
|
+
this.importProgressRowElement = importProgressRowElement;
|
|
40141
40673
|
this.booleanEditConfirmationElement = booleanEditConfirmationElement;
|
|
40142
40674
|
this.bottomToolbarElement = bottomToolbarElement;
|
|
40143
40675
|
this.downloadButtonElement = downloadButtonElement;
|
|
@@ -40260,11 +40792,14 @@ class StudioWorkspace {
|
|
|
40260
40792
|
}
|
|
40261
40793
|
/** Applies Application's combined renderer and download state. */
|
|
40262
40794
|
setToolbarState(state) {
|
|
40795
|
+
const importInProgress = state.importInProgress ?? false;
|
|
40796
|
+
const importProgress = Math.max(0, Math.min(100, Math.floor(state.importProgress ?? 0)));
|
|
40263
40797
|
const booleanOperationInProgress = state.booleanInProgress ?? false;
|
|
40264
40798
|
const downloadInProgress = state.downloadInProgress ?? false;
|
|
40265
40799
|
const inventoryInProgress = state.inventoryInProgress ?? false;
|
|
40266
40800
|
const simulationInProgress = state.simulationInProgress ?? false;
|
|
40267
|
-
const
|
|
40801
|
+
const otherOperationInProgress = booleanOperationInProgress || downloadInProgress || inventoryInProgress || simulationInProgress;
|
|
40802
|
+
const operationInProgress = importInProgress || otherOperationInProgress;
|
|
40268
40803
|
const confirmationVisible = state.booleanEditConfirmationVisible ?? false;
|
|
40269
40804
|
const modelVisibility = state.modelVisibility;
|
|
40270
40805
|
this.currentEditActive = state.editActive;
|
|
@@ -40288,7 +40823,15 @@ class StudioWorkspace {
|
|
|
40288
40823
|
this.booleanButtonElement.setAttribute("aria-busy", String(booleanOperationInProgress));
|
|
40289
40824
|
this.booleanOperationOverlayElement.hidden = !operationInProgress;
|
|
40290
40825
|
this.booleanOperationOverlayElement.setAttribute("aria-busy", String(operationInProgress));
|
|
40291
|
-
this.
|
|
40826
|
+
this.booleanOperationSpinnerElement.hidden = importInProgress;
|
|
40827
|
+
this.importProgressRowElement.hidden = !importInProgress;
|
|
40828
|
+
if (importInProgress) {
|
|
40829
|
+
const percentText = `${String(importProgress)}%`;
|
|
40830
|
+
this.importProgressElement.setAttribute("aria-valuenow", String(importProgress));
|
|
40831
|
+
this.importProgressFillElement.style.width = percentText;
|
|
40832
|
+
this.importPercentElement.textContent = percentText;
|
|
40833
|
+
}
|
|
40834
|
+
this.booleanOperationStatusElement.textContent = importInProgress ? "模型加载中" : simulationInProgress ? "仿真计算中" : booleanOperationInProgress ? "布尔计算中" : inventoryInProgress ? "入库处理中" : "下载处理中";
|
|
40292
40835
|
this.canvas.toggleAttribute("inert", operationInProgress || confirmationVisible);
|
|
40293
40836
|
this.bottomToolbarElement.toggleAttribute(
|
|
40294
40837
|
"inert",
|
|
@@ -40476,6 +41019,7 @@ class StudioWorkspace {
|
|
|
40476
41019
|
class Application {
|
|
40477
41020
|
archiveImportController;
|
|
40478
41021
|
archiveImporting = false;
|
|
41022
|
+
archiveImportProgress;
|
|
40479
41023
|
booleanEditConfirmationVisible = false;
|
|
40480
41024
|
disposed = false;
|
|
40481
41025
|
downloadController;
|
|
@@ -40488,6 +41032,7 @@ class Application {
|
|
|
40488
41032
|
editedArchiveResourceContext;
|
|
40489
41033
|
importGeneration = 0;
|
|
40490
41034
|
importPerformanceMode = ImportPerformanceMode.Off;
|
|
41035
|
+
importProgressCompositor = new ImportProgressCompositor();
|
|
40491
41036
|
inventoryConfirmationController;
|
|
40492
41037
|
inventoryConfirming = false;
|
|
40493
41038
|
inventoryError;
|
|
@@ -40545,7 +41090,10 @@ class Application {
|
|
|
40545
41090
|
this.handleInventorySuccess.bind(this),
|
|
40546
41091
|
this.handleInventoryStateChanged.bind(this)
|
|
40547
41092
|
);
|
|
40548
|
-
this.archiveImportController = new ArchiveImportWorkerClient(
|
|
41093
|
+
this.archiveImportController = new ArchiveImportWorkerClient(
|
|
41094
|
+
void 0,
|
|
41095
|
+
this.handleArchiveImportProgress.bind(this)
|
|
41096
|
+
);
|
|
40549
41097
|
this.handleRendererWorkspaceStateChanged(renderer.getWorkspaceState());
|
|
40550
41098
|
} catch (error2) {
|
|
40551
41099
|
this.workspace = void 0;
|
|
@@ -40631,12 +41179,14 @@ class Application {
|
|
|
40631
41179
|
}
|
|
40632
41180
|
this.nextArchiveTaskId += 1;
|
|
40633
41181
|
this.archiveImporting = true;
|
|
41182
|
+
this.archiveImportProgress = 0;
|
|
40634
41183
|
this.downloadError = void 0;
|
|
40635
41184
|
this.downloadSuccess = void 0;
|
|
40636
41185
|
this.publishWorkspaceState();
|
|
40637
41186
|
const controller = this.archiveImportController;
|
|
40638
41187
|
if (controller === void 0) {
|
|
40639
41188
|
this.archiveImporting = false;
|
|
41189
|
+
this.archiveImportProgress = void 0;
|
|
40640
41190
|
this.publishWorkspaceState();
|
|
40641
41191
|
return Promise.resolve({
|
|
40642
41192
|
code: ObjImportFailureCode.Unavailable,
|
|
@@ -40694,6 +41244,7 @@ class Application {
|
|
|
40694
41244
|
cancelArchiveImport() {
|
|
40695
41245
|
this.archiveImportController?.cancel();
|
|
40696
41246
|
this.archiveImporting = false;
|
|
41247
|
+
this.archiveImportProgress = void 0;
|
|
40697
41248
|
this.invalidateImportGeneration();
|
|
40698
41249
|
}
|
|
40699
41250
|
async completeModelResourceSceneImport(source, candidate, generation) {
|
|
@@ -40764,6 +41315,7 @@ class Application {
|
|
|
40764
41315
|
} finally {
|
|
40765
41316
|
if (this.isCurrentImportGeneration(generation)) {
|
|
40766
41317
|
this.archiveImporting = false;
|
|
41318
|
+
this.archiveImportProgress = void 0;
|
|
40767
41319
|
this.publishWorkspaceState();
|
|
40768
41320
|
}
|
|
40769
41321
|
}
|
|
@@ -40771,6 +41323,7 @@ class Application {
|
|
|
40771
41323
|
completeEditedArchiveImportFailure(generation, error2) {
|
|
40772
41324
|
if (this.isCurrentImportGeneration(generation)) {
|
|
40773
41325
|
this.archiveImporting = false;
|
|
41326
|
+
this.archiveImportProgress = void 0;
|
|
40774
41327
|
this.publishWorkspaceState();
|
|
40775
41328
|
}
|
|
40776
41329
|
return Promise.resolve(this.mapEditedArchiveError(error2));
|
|
@@ -41141,6 +41694,13 @@ class Application {
|
|
|
41141
41694
|
}
|
|
41142
41695
|
this.clearBooleanEditConfirmation();
|
|
41143
41696
|
}
|
|
41697
|
+
handleArchiveImportProgress(percent) {
|
|
41698
|
+
if (this.disposed || !this.archiveImporting) {
|
|
41699
|
+
return;
|
|
41700
|
+
}
|
|
41701
|
+
this.archiveImportProgress = percent;
|
|
41702
|
+
this.publishWorkspaceState();
|
|
41703
|
+
}
|
|
41144
41704
|
handleRendererWorkspaceStateChanged(state) {
|
|
41145
41705
|
if (this.disposed) {
|
|
41146
41706
|
return;
|
|
@@ -41258,6 +41818,10 @@ class Application {
|
|
|
41258
41818
|
if (workspace === void 0 || state === void 0) {
|
|
41259
41819
|
return;
|
|
41260
41820
|
}
|
|
41821
|
+
const importProgress = this.importProgressCompositor.compose({
|
|
41822
|
+
archiveProgress: this.archiveImportProgress,
|
|
41823
|
+
rendererProgress: state.scene.importProgress
|
|
41824
|
+
});
|
|
41261
41825
|
const toolbarState = {
|
|
41262
41826
|
simulationEnabled: state.simulation?.enabled === true && !this.downloading && !this.inventorying && !this.inventoryConfirming && !this.archiveImporting && !this.booleanEditConfirmationVisible,
|
|
41263
41827
|
simulationInProgress: state.simulation?.inProgress ?? false,
|
|
@@ -41275,6 +41839,8 @@ class Application {
|
|
|
41275
41839
|
editEnabled: state.edit.enabled && !this.downloading && !this.inventorying && !this.archiveImporting && !this.booleanEditConfirmationVisible,
|
|
41276
41840
|
editVisible: state.edit.visible,
|
|
41277
41841
|
...this.editStatusMessage === void 0 ? {} : { editStatusMessage: this.editStatusMessage },
|
|
41842
|
+
importInProgress: state.scene.importInProgress || this.archiveImporting,
|
|
41843
|
+
...importProgress === void 0 ? {} : { importProgress },
|
|
41278
41844
|
inventoryEnabled: this.canInventory(state),
|
|
41279
41845
|
...this.inventoryError === void 0 ? {} : { inventoryError: this.inventoryError },
|
|
41280
41846
|
inventoryInProgress: this.inventorying,
|
|
@@ -41320,9 +41886,11 @@ class Application {
|
|
|
41320
41886
|
state.scene.available,
|
|
41321
41887
|
state.scene.contextLost,
|
|
41322
41888
|
state.scene.importInProgress,
|
|
41889
|
+
state.scene.importProgress ?? null,
|
|
41323
41890
|
state.scene.revision,
|
|
41324
41891
|
state.scene.resourceRevision ?? null,
|
|
41325
41892
|
this.archiveImporting,
|
|
41893
|
+
this.archiveImportProgress ?? null,
|
|
41326
41894
|
this.downloading,
|
|
41327
41895
|
this.inventoryConfirming,
|
|
41328
41896
|
this.inventorying,
|
|
@@ -41335,7 +41903,7 @@ class Application {
|
|
|
41335
41903
|
]);
|
|
41336
41904
|
}
|
|
41337
41905
|
isSameToolbarState(state, previousState) {
|
|
41338
|
-
return previousState !== void 0 && state.simulationEnabled === previousState.simulationEnabled && state.simulationInProgress === previousState.simulationInProgress && state.simulationOutcome === previousState.simulationOutcome && state.booleanEnabled === previousState.booleanEnabled && state.booleanInProgress === previousState.booleanInProgress && state.booleanReediting === previousState.booleanReediting && state.booleanResultInvalidated === previousState.booleanResultInvalidated && state.booleanEditConfirmationVisible === previousState.booleanEditConfirmationVisible && state.downloadEnabled === previousState.downloadEnabled && state.downloadError === previousState.downloadError && state.downloadInProgress === previousState.downloadInProgress && state.downloadSuccess === previousState.downloadSuccess && state.editActive === previousState.editActive && state.editEnabled === previousState.editEnabled && state.editVisible === previousState.editVisible && state.editStatusMessage === previousState.editStatusMessage && state.inventoryEnabled === previousState.inventoryEnabled && state.inventoryError === previousState.inventoryError && state.inventoryInProgress === previousState.inventoryInProgress && state.inventoryVisible === previousState.inventoryVisible && this.isSameModelVisibility(state.modelVisibility, previousState.modelVisibility);
|
|
41906
|
+
return previousState !== void 0 && state.simulationEnabled === previousState.simulationEnabled && state.simulationInProgress === previousState.simulationInProgress && state.simulationOutcome === previousState.simulationOutcome && state.booleanEnabled === previousState.booleanEnabled && state.booleanInProgress === previousState.booleanInProgress && state.booleanReediting === previousState.booleanReediting && state.booleanResultInvalidated === previousState.booleanResultInvalidated && state.booleanEditConfirmationVisible === previousState.booleanEditConfirmationVisible && state.downloadEnabled === previousState.downloadEnabled && state.downloadError === previousState.downloadError && state.downloadInProgress === previousState.downloadInProgress && state.downloadSuccess === previousState.downloadSuccess && state.editActive === previousState.editActive && state.editEnabled === previousState.editEnabled && state.editVisible === previousState.editVisible && state.editStatusMessage === previousState.editStatusMessage && state.importInProgress === previousState.importInProgress && state.importProgress === previousState.importProgress && state.inventoryEnabled === previousState.inventoryEnabled && state.inventoryError === previousState.inventoryError && state.inventoryInProgress === previousState.inventoryInProgress && state.inventoryVisible === previousState.inventoryVisible && this.isSameModelVisibility(state.modelVisibility, previousState.modelVisibility);
|
|
41339
41907
|
}
|
|
41340
41908
|
isSameModelVisibility(state, previousState) {
|
|
41341
41909
|
return state?.baseVisible === previousState?.baseVisible && state?.enabled === previousState?.enabled && state?.stoneVisible === previousState?.stoneVisible;
|