@archvisioninc/canvas 3.3.6 → 3.3.8
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/.claude/settings.local.json +8 -0
- package/README_DEV.md +4 -1
- package/package.json +1 -1
- package/src/package/helpers/canvasUpdateHelpers.js +145 -0
- package/src/package/helpers/initHelpers.js +10 -0
- package/src/package/helpers/utilityHelpers.js +17 -0
- package/dist/Canvas.js +0 -67
- package/dist/actions/index.js +0 -1
- package/dist/actions/shortcutActions.js +0 -313
- package/dist/constants/constants.js +0 -80
- package/dist/constants/index.js +0 -1
- package/dist/enums/aspectRatios.js +0 -17
- package/dist/enums/dimensions.js +0 -20
- package/dist/enums/downscaling.js +0 -16
- package/dist/enums/exclusions.js +0 -4
- package/dist/enums/formats.js +0 -1
- package/dist/enums/index.js +0 -8
- package/dist/enums/orthoOptions.js +0 -28
- package/dist/enums/scaleUnits.js +0 -25
- package/dist/enums/shortcuts.js +0 -89
- package/dist/helpers/cameraHelpers.js +0 -86
- package/dist/helpers/canvasAddHelpers.js +0 -161
- package/dist/helpers/canvasCommunicationHelpers.js +0 -52
- package/dist/helpers/canvasRemoveHelpers.js +0 -230
- package/dist/helpers/canvasUpdateHelpers.js +0 -1247
- package/dist/helpers/gizmoHelpers.js +0 -156
- package/dist/helpers/guiHelpers.js +0 -46
- package/dist/helpers/index.js +0 -16
- package/dist/helpers/initHelpers.js +0 -507
- package/dist/helpers/lightHelpers.js +0 -17
- package/dist/helpers/loadHelpers.js +0 -269
- package/dist/helpers/materialHelpers.js +0 -34
- package/dist/helpers/meshHelpers.js +0 -169
- package/dist/helpers/rayHelpers.js +0 -11
- package/dist/helpers/shortcutHelpers.js +0 -35
- package/dist/helpers/utilityHelpers.js +0 -697
- package/dist/helpers/viewportHelpers.js +0 -364
- package/dist/styles.js +0 -25
package/README_DEV.md
CHANGED
|
@@ -30,7 +30,10 @@ You will need to be invited to the Archvision, Inc. team on [NPM](https://npmjs.
|
|
|
30
30
|
|
|
31
31
|
Since the canvas package is under Archvision, Inc. It will be identified as [@archvisioninc/canvas](https://npmjs.com/package/@archvisioninc/canvas).
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
> npm has updated their packaging publishing process that may require the developer to add 2FA to their account. Once this is added to the developer's account, they will need to run
|
|
34
|
+
>> npm login
|
|
35
|
+
>
|
|
36
|
+
> to reauthenticate with the 2FA enabled. This 2FA process will also be required when publishing a new version of canvas to npm.
|
|
34
37
|
|
|
35
38
|
## Project structure and organization
|
|
36
39
|
This project's main development environment is found in `/src`. Each file and folder, except the `/src/package` folder,
|
package/package.json
CHANGED
|
@@ -317,6 +317,85 @@ const dataUrlToBlob = dataURI => {
|
|
|
317
317
|
return blob;
|
|
318
318
|
};
|
|
319
319
|
|
|
320
|
+
const getActiveAnimationGroup = animationGroups => {
|
|
321
|
+
const selectedAnimationIndex = scene.metadata?.selectedAnimation;
|
|
322
|
+
|
|
323
|
+
if (selectedAnimationIndex === null || selectedAnimationIndex === undefined) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return animationGroups.find((_group, index) => {
|
|
328
|
+
return `${index}` === `${selectedAnimationIndex}`;
|
|
329
|
+
}) || null;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
let animationSyncObserver = null;
|
|
333
|
+
let lastAnimationSerializeAt = 0;
|
|
334
|
+
const ANIMATION_SYNC_MS = 50;
|
|
335
|
+
|
|
336
|
+
const pushAnimationStateToReact = () => {
|
|
337
|
+
const activeAnimationGroup = getActiveAnimationGroup(scene.animationGroups || []);
|
|
338
|
+
|
|
339
|
+
syncSelectedAnimationMeta(activeAnimationGroup);
|
|
340
|
+
|
|
341
|
+
const now = performance.now();
|
|
342
|
+
if (
|
|
343
|
+
props.setSerializedData
|
|
344
|
+
&& now - lastAnimationSerializeAt >= ANIMATION_SYNC_MS
|
|
345
|
+
) {
|
|
346
|
+
lastAnimationSerializeAt = now;
|
|
347
|
+
props.setSerializedData(serializeScene());
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const ensureAnimationStateSync = () => {
|
|
352
|
+
if (animationSyncObserver || !scene?.onBeforeRenderObservable) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
animationSyncObserver = scene.onBeforeRenderObservable.add(() => {
|
|
357
|
+
const activeAnimationGroup = getActiveAnimationGroup(scene.animationGroups || []);
|
|
358
|
+
|
|
359
|
+
if (!activeAnimationGroup) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
pushAnimationStateToReact();
|
|
364
|
+
});
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const syncSelectedAnimationMeta = animationGroup => {
|
|
368
|
+
if (!scene.metadata) return;
|
|
369
|
+
|
|
370
|
+
if (!animationGroup) {
|
|
371
|
+
scene.metadata.selectedAnimationTime = 0;
|
|
372
|
+
scene.metadata.selectedAnimationProgress = 0;
|
|
373
|
+
scene.metadata.selectedAnimationDuration = 0;
|
|
374
|
+
scene.metadata.selectedAnimationIsPlaying = false;
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const runtimeAnimation = animationGroup.animatables?.[0]?.getAnimations?.()?.[0];
|
|
379
|
+
const currentFrame = runtimeAnimation?.currentFrame ?? animationGroup.from ?? 0;
|
|
380
|
+
const from = animationGroup.from ?? 0;
|
|
381
|
+
const to = animationGroup.to ?? 0;
|
|
382
|
+
const totalFrames = Math.max(to - from, 1);
|
|
383
|
+
|
|
384
|
+
const progress = BABYLON.Scalar.Clamp(
|
|
385
|
+
(currentFrame - from) / totalFrames,
|
|
386
|
+
0,
|
|
387
|
+
1
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
const duration = animationGroup.getLength?.() ?? 0;
|
|
391
|
+
|
|
392
|
+
scene.metadata.selectedAnimationProgress = progress;
|
|
393
|
+
scene.metadata.selectedAnimationTime = progress * duration;
|
|
394
|
+
scene.metadata.selectedAnimationDuration = duration;
|
|
395
|
+
scene.metadata.selectedAnimationIsPlaying = Boolean(animationGroup.isPlaying);
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
|
|
320
399
|
export const updateMaterial = inboundData => {
|
|
321
400
|
const { payload } = inboundData;
|
|
322
401
|
const {
|
|
@@ -666,6 +745,13 @@ export const updateMesh = inboundData => {
|
|
|
666
745
|
globalTransform,
|
|
667
746
|
variantName,
|
|
668
747
|
resetVariant,
|
|
748
|
+
animationId,
|
|
749
|
+
resetAnimation,
|
|
750
|
+
loopAnimation = true,
|
|
751
|
+
playAnimation,
|
|
752
|
+
pauseAnimation,
|
|
753
|
+
seekAnimation,
|
|
754
|
+
animationProgress,
|
|
669
755
|
sourceUnit,
|
|
670
756
|
displayUnit,
|
|
671
757
|
} = payload;
|
|
@@ -773,6 +859,65 @@ export const updateMesh = inboundData => {
|
|
|
773
859
|
newMetaDataEntry('selectedMaterialVariant', null);
|
|
774
860
|
}
|
|
775
861
|
|
|
862
|
+
const animationGroups = scene.animationGroups || [];
|
|
863
|
+
const hasAnimationId = animationId !== undefined && animationId !== null && `${animationId}` !== '';
|
|
864
|
+
|
|
865
|
+
const stopAllAnimationGroups = () => {
|
|
866
|
+
animationGroups.forEach(group => {
|
|
867
|
+
group.stop();
|
|
868
|
+
group.reset();
|
|
869
|
+
});
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
// Reset current animation
|
|
873
|
+
if (resetAnimation) {
|
|
874
|
+
stopAllAnimationGroups();
|
|
875
|
+
newMetaDataEntry('selectedAnimation', null);
|
|
876
|
+
syncSelectedAnimationMeta(null);
|
|
877
|
+
props.setSerializedData?.(serializeScene());
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Change active animation
|
|
881
|
+
if (hasAnimationId) {
|
|
882
|
+
const animationGroup = animationGroups.find((group, index) => `${index}` === `${animationId}`);
|
|
883
|
+
|
|
884
|
+
stopAllAnimationGroups();
|
|
885
|
+
|
|
886
|
+
if (animationGroup) {
|
|
887
|
+
animationGroup.start(Boolean(loopAnimation));
|
|
888
|
+
newMetaDataEntry('selectedAnimation', `${animationId}`);
|
|
889
|
+
syncSelectedAnimationMeta(animationGroup);
|
|
890
|
+
ensureAnimationStateSync();
|
|
891
|
+
props.setSerializedData?.(serializeScene());
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
const activeAnimationGroup = getActiveAnimationGroup(animationGroups);
|
|
896
|
+
|
|
897
|
+
// Play current animation
|
|
898
|
+
if (playAnimation && activeAnimationGroup) {
|
|
899
|
+
activeAnimationGroup.play(Boolean(loopAnimation));
|
|
900
|
+
syncSelectedAnimationMeta(activeAnimationGroup);
|
|
901
|
+
ensureAnimationStateSync();
|
|
902
|
+
props.setSerializedData?.(serializeScene());
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// Pause current animation
|
|
906
|
+
if (pauseAnimation && activeAnimationGroup) {
|
|
907
|
+
activeAnimationGroup.pause();
|
|
908
|
+
syncSelectedAnimationMeta(activeAnimationGroup);
|
|
909
|
+
props.setSerializedData?.(serializeScene());
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Seek current animation to a progress position (0–1)
|
|
913
|
+
if (seekAnimation && activeAnimationGroup && animationProgress !== undefined) {
|
|
914
|
+
const { from, to } = activeAnimationGroup;
|
|
915
|
+
const frame = from + animationProgress * (to - from);
|
|
916
|
+
activeAnimationGroup.goToFrame(frame);
|
|
917
|
+
syncSelectedAnimationMeta(activeAnimationGroup);
|
|
918
|
+
props.setSerializedData?.(serializeScene());
|
|
919
|
+
}
|
|
920
|
+
|
|
776
921
|
// Bounding Box Toggle
|
|
777
922
|
if (boundingBoxEnabled !== undefined) {
|
|
778
923
|
toggleBoundingBoxWidget();
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
serializeScene,
|
|
40
40
|
buildMeshPositionsArray,
|
|
41
41
|
buildMaterialsArray,
|
|
42
|
+
buildAnimationsArray,
|
|
42
43
|
updatePublish,
|
|
43
44
|
buildSelectedMaterialArray,
|
|
44
45
|
buildMaterialVariantsArray,
|
|
@@ -388,6 +389,8 @@ const initMetadataKeyDefaults = () => {
|
|
|
388
389
|
newMetaDataEntry('fileName', null);
|
|
389
390
|
newMetaDataEntry('uvScaleLock', false);
|
|
390
391
|
newMetaDataEntry('lights', []);
|
|
392
|
+
newMetaDataEntry('animations', []);
|
|
393
|
+
newMetaDataEntry('selectedAnimation', null);
|
|
391
394
|
};
|
|
392
395
|
|
|
393
396
|
const checkForRootNode = () => {
|
|
@@ -506,6 +509,13 @@ export const initSceneFromFile = (sceneFromFile, fileName) => {
|
|
|
506
509
|
newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
|
|
507
510
|
newMetaDataEntry('fileName', fileName);
|
|
508
511
|
newMetaDataEntry('materialVariants', buildMaterialVariantsArray());
|
|
512
|
+
const animations = buildAnimationsArray();
|
|
513
|
+
const activeAnimationIndex = scene.animationGroups?.findIndex(group => group.isPlaying) ?? -1;
|
|
514
|
+
newMetaDataEntry('animations', animations);
|
|
515
|
+
newMetaDataEntry(
|
|
516
|
+
'selectedAnimation',
|
|
517
|
+
activeAnimationIndex >= 0 ? `${activeAnimationIndex}` : null,
|
|
518
|
+
);
|
|
509
519
|
newMetaDataEntry('uvScaleLock', scene.metadata.uvScaleLock || false);
|
|
510
520
|
newMetaDataEntry('lights', buildLightsArray());
|
|
511
521
|
|
|
@@ -336,6 +336,23 @@ export const buildMaterialVariantsArray = () => {
|
|
|
336
336
|
return variants;
|
|
337
337
|
};
|
|
338
338
|
|
|
339
|
+
|
|
340
|
+
export const buildAnimationsArray = () => {
|
|
341
|
+
const animationGroups = scene?.animationGroups || [];
|
|
342
|
+
|
|
343
|
+
return animationGroups.map((group, index) => {
|
|
344
|
+
const fallbackLabel = `Animation ${index + 1}`;
|
|
345
|
+
const label = group.name || group.id || fallbackLabel;
|
|
346
|
+
|
|
347
|
+
return {
|
|
348
|
+
id: `${index}`,
|
|
349
|
+
index,
|
|
350
|
+
label,
|
|
351
|
+
name: group.name || '',
|
|
352
|
+
};
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
|
|
339
356
|
export const buildSelectedMaterialArray = () => {
|
|
340
357
|
if (props.materialMode) {
|
|
341
358
|
const mainMaterial = scene.metadata?.materials?.find?.(item => item.materialId === 'material');
|
package/dist/Canvas.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
|
-
import React, { useRef, useEffect, useCallback } from 'react';
|
|
3
|
-
import { initScene, cleanup, createScene, engine, scene, warningChecks, updateItem, getGLTFData, takePreviewScreenshots } from './helpers';
|
|
4
|
-
import { CanvasContainer, RenderCanvas } from './styles';
|
|
5
|
-
export let reactProps;
|
|
6
|
-
const Canvas = props => {
|
|
7
|
-
const {
|
|
8
|
-
file,
|
|
9
|
-
guidURL,
|
|
10
|
-
reinitialize,
|
|
11
|
-
updateData,
|
|
12
|
-
exportModel,
|
|
13
|
-
toDownload,
|
|
14
|
-
exportType
|
|
15
|
-
} = props;
|
|
16
|
-
const canvasRef = useRef();
|
|
17
|
-
const checkForWarnings = useCallback(() => {
|
|
18
|
-
if (scene) {
|
|
19
|
-
const hasPlugin = scene?.loadingPluginName;
|
|
20
|
-
if (hasPlugin) warningChecks();
|
|
21
|
-
}
|
|
22
|
-
}, []);
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
return () => {
|
|
25
|
-
cleanup();
|
|
26
|
-
};
|
|
27
|
-
}, []);
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
initScene({
|
|
30
|
-
canvasRef
|
|
31
|
-
});
|
|
32
|
-
}, [canvasRef, guidURL]);
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
reactProps = props;
|
|
35
|
-
}, [props]);
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
const createNewScene = true;
|
|
38
|
-
if (file || guidURL && engine) createScene();
|
|
39
|
-
if (!file && !guidURL && scene) createScene(createNewScene);
|
|
40
|
-
}, [file, guidURL]);
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
checkForWarnings();
|
|
43
|
-
}, [checkForWarnings]);
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
if (scene && !_.isEmpty(updateData)) {
|
|
46
|
-
updateItem(updateData);
|
|
47
|
-
}
|
|
48
|
-
}, [updateData]);
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
if (canvasRef && reinitialize) {
|
|
51
|
-
cleanup();
|
|
52
|
-
initScene({
|
|
53
|
-
canvasRef
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}, [canvasRef, reinitialize]);
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
if (exportModel && exportType) {
|
|
59
|
-
getGLTFData(exportType, toDownload);
|
|
60
|
-
takePreviewScreenshots();
|
|
61
|
-
}
|
|
62
|
-
}, [exportModel, exportType, toDownload]);
|
|
63
|
-
return /*#__PURE__*/React.createElement(CanvasContainer, props, /*#__PURE__*/React.createElement(RenderCanvas, {
|
|
64
|
-
ref: canvasRef
|
|
65
|
-
}));
|
|
66
|
-
};
|
|
67
|
-
export default Canvas;
|
package/dist/actions/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './shortcutActions';
|
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
import * as BABYLON from 'babylonjs';
|
|
2
|
-
import * as BABYLONGUI from 'babylonjs-gui';
|
|
3
|
-
import { getUserMeshes, gizmoManager, newFraming, scene, selectedMeshes, theme, $selectedTheme, newColor, toggleCameraMode, getParamOfSelectedMeshes, getUserMaterials, guiTexture, getBoundingMeshData, singleMeshTNode, multiMeshTNode, createBoundingMesh, restoreParents, createSafeFrame, newMetaDataEntry, resetSelectedMeshes, buildMaterialsArray, buildSelectedMaterialArray, addAutoRotation, removeAutoRotation, autoRotation, buildMeshPositionsArray } from '../helpers';
|
|
4
|
-
import { GIZMOS, GUI, MESH_PARAMS } from '../constants';
|
|
5
|
-
import { exclusionMaterials, orthoOptions, dimensions, scaleUnits } from '../enums';
|
|
6
|
-
import _ from 'lodash';
|
|
7
|
-
export let cameraView;
|
|
8
|
-
export const resetManagerGizmos = type => {
|
|
9
|
-
Object.values(GIZMOS).forEach(value => {
|
|
10
|
-
if (value !== type) {
|
|
11
|
-
const propertyName = _.camelCase(`${value}Enabled`);
|
|
12
|
-
gizmoManager[propertyName] = false;
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
dimensions.forEach(dimension => {
|
|
16
|
-
guiTexture.getControlByName(dimension.rectangle)?.dispose?.();
|
|
17
|
-
guiTexture.getControlByName(dimension.label)?.dispose?.();
|
|
18
|
-
guiTexture.getControlByName(dimension.container)?.dispose?.();
|
|
19
|
-
});
|
|
20
|
-
switch (selectedMeshes.length) {
|
|
21
|
-
case 1:
|
|
22
|
-
gizmoManager.attachToNode(singleMeshTNode);
|
|
23
|
-
break;
|
|
24
|
-
default:
|
|
25
|
-
gizmoManager.attachToNode(multiMeshTNode);
|
|
26
|
-
break;
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
export const toggleTurntable = override => {
|
|
30
|
-
const camera = scene.activeCamera;
|
|
31
|
-
const rotationEnabled = camera.useAutoRotationBehavior;
|
|
32
|
-
if (!autoRotation) addAutoRotation();
|
|
33
|
-
if (override !== undefined) {
|
|
34
|
-
camera.useAutoRotationBehavior = override;
|
|
35
|
-
if (override) {
|
|
36
|
-
autoRotation.attach(camera);
|
|
37
|
-
autoRotation.idleRotationSpeed = -0.1;
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
autoRotation.detach(camera);
|
|
41
|
-
removeAutoRotation();
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
if (rotationEnabled) {
|
|
45
|
-
autoRotation.detach(camera);
|
|
46
|
-
camera.useAutoRotationBehavior = false;
|
|
47
|
-
removeAutoRotation();
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
autoRotation.attach(camera);
|
|
51
|
-
camera.useAutoRotationBehavior = true;
|
|
52
|
-
autoRotation.idleRotationSpeed = -0.1;
|
|
53
|
-
};
|
|
54
|
-
export const focusCamera = () => {
|
|
55
|
-
const camera = scene.activeCamera;
|
|
56
|
-
const userMeshes = getUserMeshes();
|
|
57
|
-
const framingBehavior = newFraming();
|
|
58
|
-
const babylonOrthographic = BABYLON.Camera.ORTHOGRAPHIC_CAMERA;
|
|
59
|
-
const isOrtho = camera.mode === babylonOrthographic;
|
|
60
|
-
const boundingMesh = scene.getMeshById('boundingMesh');
|
|
61
|
-
const meshCenter = getParamOfSelectedMeshes(MESH_PARAMS.center);
|
|
62
|
-
const framingMeshes = boundingMesh ? [boundingMesh] : userMeshes;
|
|
63
|
-
framingBehavior.framingTime = 0;
|
|
64
|
-
framingBehavior.defaultElevation = 0;
|
|
65
|
-
framingBehavior.attach?.(camera);
|
|
66
|
-
!_.isEmpty(userMeshes) ? framingBehavior.zoomOnMeshesHierarchy(framingMeshes) : camera.setTarget(BABYLON.Vector3.Zero());
|
|
67
|
-
camera.beta = Math.PI / 2;
|
|
68
|
-
if (isOrtho) {
|
|
69
|
-
camera.target = meshCenter;
|
|
70
|
-
camera.alpha = orthoOptions[cameraView].alpha;
|
|
71
|
-
camera.beta = orthoOptions[cameraView].beta;
|
|
72
|
-
}
|
|
73
|
-
framingBehavior.detach?.(camera);
|
|
74
|
-
};
|
|
75
|
-
export const toggleSafeFrame = (e, aspectRatio, override) => {
|
|
76
|
-
let outerFrame = guiTexture.getControlByName(GUI.outerSafeFrame);
|
|
77
|
-
if (aspectRatio) {
|
|
78
|
-
outerFrame?.dispose();
|
|
79
|
-
createSafeFrame(aspectRatio);
|
|
80
|
-
outerFrame = guiTexture.getControlByName(GUI.outerSafeFrame);
|
|
81
|
-
outerFrame.isVisible = override;
|
|
82
|
-
newMetaDataEntry('safeFrameEnabled', outerFrame.isVisible);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
if (override === undefined && outerFrame) {
|
|
86
|
-
outerFrame.isVisible = !outerFrame.isVisible;
|
|
87
|
-
newMetaDataEntry('safeFrameEnabled', outerFrame.isVisible);
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
export const toggleInspector = () => {
|
|
92
|
-
const inspector = scene.debugLayer;
|
|
93
|
-
inspector.isVisible() ? inspector.hide() : inspector.show();
|
|
94
|
-
};
|
|
95
|
-
export const toggleMoveWidget = () => {
|
|
96
|
-
if (!_.isEmpty(selectedMeshes)) {
|
|
97
|
-
resetManagerGizmos(GIZMOS.PositionGizmo);
|
|
98
|
-
gizmoManager.positionGizmoEnabled = !gizmoManager.positionGizmoEnabled;
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
export const toggleRotateWidget = () => {
|
|
102
|
-
if (!_.isEmpty(selectedMeshes)) {
|
|
103
|
-
resetManagerGizmos(GIZMOS.RotationGizmo);
|
|
104
|
-
gizmoManager.rotationGizmoEnabled = !gizmoManager.rotationGizmoEnabled;
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
export const toggleScaleWidget = () => {
|
|
108
|
-
if (!_.isEmpty(selectedMeshes)) {
|
|
109
|
-
resetManagerGizmos(GIZMOS.ScaleGizmo);
|
|
110
|
-
gizmoManager.scaleGizmoEnabled = !gizmoManager.scaleGizmoEnabled;
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
export const toggleBoundingBoxWidget = () => {
|
|
114
|
-
const boundingMesh = scene.getMeshById('boundingMesh');
|
|
115
|
-
const boxColor = newColor(theme.colors[$selectedTheme].accent);
|
|
116
|
-
const isEnabled = gizmoManager.boundingBoxGizmoEnabled;
|
|
117
|
-
const boundingInfo = getBoundingMeshData([boundingMesh]);
|
|
118
|
-
dimensions.forEach(dimension => {
|
|
119
|
-
guiTexture.getControlByName(dimension.rectangle)?.dispose?.();
|
|
120
|
-
guiTexture.getControlByName(dimension.label)?.dispose?.();
|
|
121
|
-
guiTexture.getControlByName(dimension.container)?.dispose?.();
|
|
122
|
-
});
|
|
123
|
-
if (!gizmoManager.boundingBoxGizmoEnabled) {
|
|
124
|
-
resetManagerGizmos(GIZMOS.BoundingBoxGizmo);
|
|
125
|
-
gizmoManager.attachToMesh(boundingMesh);
|
|
126
|
-
}
|
|
127
|
-
gizmoManager.boundingBoxGizmoEnabled = !isEnabled;
|
|
128
|
-
gizmoManager.boundingBoxDragBehavior.rotateDraggedObject = false;
|
|
129
|
-
gizmoManager.boundingBoxDragBehavior.disableMovement = true;
|
|
130
|
-
gizmoManager.gizmos.boundingBoxGizmo.setEnabledScaling(false);
|
|
131
|
-
gizmoManager.gizmos.boundingBoxGizmo.setEnabledRotationAxis('');
|
|
132
|
-
gizmoManager.gizmos.boundingBoxGizmo.setColor(boxColor);
|
|
133
|
-
newMetaDataEntry('boundingBoxEnabled', !isEnabled);
|
|
134
|
-
|
|
135
|
-
// ================ Bounding Box Labels ================
|
|
136
|
-
const buildUnitLabel = dimension => {
|
|
137
|
-
const {
|
|
138
|
-
name,
|
|
139
|
-
color,
|
|
140
|
-
label,
|
|
141
|
-
rectangle,
|
|
142
|
-
container
|
|
143
|
-
} = dimension;
|
|
144
|
-
const {
|
|
145
|
-
center,
|
|
146
|
-
minimum,
|
|
147
|
-
maximumWorld,
|
|
148
|
-
minimumWorld
|
|
149
|
-
} = boundingInfo;
|
|
150
|
-
const displayUnit = scene.metadata?.selectedDisplayUnit || scene.metadata?.selectedSourceUnit;
|
|
151
|
-
const labelHeight = 36;
|
|
152
|
-
let x, y, z, value;
|
|
153
|
-
const buildValue = axis => {
|
|
154
|
-
const workingValue = Math.abs(minimumWorld[axis] - maximumWorld[axis]);
|
|
155
|
-
const meterScaleFactor = scaleUnits.find(unit => unit.abbreviation === 'm').scaleFactor;
|
|
156
|
-
const displayScaleFactor = scaleUnits.find(unit => unit.abbreviation === displayUnit || unit.name === displayUnit)?.scaleFactor || meterScaleFactor;
|
|
157
|
-
return (workingValue / displayScaleFactor).toFixed(6);
|
|
158
|
-
};
|
|
159
|
-
switch (name) {
|
|
160
|
-
case 'length':
|
|
161
|
-
x = center.x + (maximumWorld.x - minimumWorld.x) / 2;
|
|
162
|
-
y = center.y - Math.abs(minimum.y);
|
|
163
|
-
z = center.z;
|
|
164
|
-
value = buildValue('z');
|
|
165
|
-
break;
|
|
166
|
-
case 'width':
|
|
167
|
-
x = center.x;
|
|
168
|
-
y = center.y - Math.abs(minimum.y);
|
|
169
|
-
z = center.z + (maximumWorld.z - minimumWorld.z) / 2;
|
|
170
|
-
value = buildValue('x');
|
|
171
|
-
break;
|
|
172
|
-
default:
|
|
173
|
-
x = center.x + (maximumWorld.x - minimumWorld.x) / 2;
|
|
174
|
-
y = center.y;
|
|
175
|
-
z = center.z + (maximumWorld.z - minimumWorld.z) / 2;
|
|
176
|
-
value = buildValue('y');
|
|
177
|
-
}
|
|
178
|
-
const lengthPosition = new BABYLON.Vector3(x, y, z);
|
|
179
|
-
const labelRectangle = new BABYLONGUI.Rectangle(rectangle);
|
|
180
|
-
labelRectangle.width = displayUnit ? 0.14 : 0.12;
|
|
181
|
-
labelRectangle.heightInPixels = labelHeight;
|
|
182
|
-
labelRectangle.cornerRadius = 3;
|
|
183
|
-
labelRectangle.color = color;
|
|
184
|
-
labelRectangle.thickness = 1;
|
|
185
|
-
labelRectangle.background = color + 20;
|
|
186
|
-
guiTexture.addControl(labelRectangle);
|
|
187
|
-
const lengthLabel = new BABYLONGUI.TextBlock(label);
|
|
188
|
-
lengthLabel.text = `${_.startCase(name)}: ${value}${displayUnit || ''}`;
|
|
189
|
-
labelRectangle.addControl(lengthLabel);
|
|
190
|
-
const labelLinkBox = BABYLON.MeshBuilder.CreateBox(container, scene);
|
|
191
|
-
labelLinkBox.position = lengthPosition;
|
|
192
|
-
labelLinkBox.isPickable = false;
|
|
193
|
-
labelLinkBox.visibility = 0;
|
|
194
|
-
labelLinkBox.setParent(boundingMesh);
|
|
195
|
-
labelRectangle.linkWithMesh(labelLinkBox);
|
|
196
|
-
labelRectangle.linkOffsetX = 30;
|
|
197
|
-
labelRectangle.linkOffsetY = 30;
|
|
198
|
-
};
|
|
199
|
-
if (gizmoManager.boundingBoxGizmoEnabled) {
|
|
200
|
-
dimensions.forEach(dimension => buildUnitLabel(dimension));
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
export const hideSelected = () => {
|
|
204
|
-
selectedMeshes.forEach(mesh => {
|
|
205
|
-
mesh.isVisible = false;
|
|
206
|
-
});
|
|
207
|
-
resetManagerGizmos();
|
|
208
|
-
};
|
|
209
|
-
export const unhideAll = () => {
|
|
210
|
-
scene.meshes.forEach(mesh => {
|
|
211
|
-
if (!mesh.isVisible) {
|
|
212
|
-
mesh.isVisible = true;
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
};
|
|
216
|
-
export const deleteSelected = () => {
|
|
217
|
-
selectedMeshes.forEach(mesh => {
|
|
218
|
-
const material = mesh.material;
|
|
219
|
-
if (material?.getBindedMeshes().length === 1) {
|
|
220
|
-
const subMaterials = material.subMaterials;
|
|
221
|
-
const defaultMaterial = name => exclusionMaterials.includes(name);
|
|
222
|
-
if (subMaterials) {
|
|
223
|
-
subMaterials?.forEach(subMaterial => !defaultMaterial(subMaterial.name) && subMaterial.dispose());
|
|
224
|
-
}
|
|
225
|
-
if (!defaultMaterial(material.name)) material.dispose();
|
|
226
|
-
material.getActiveTextures().forEach(texture => texture.dispose());
|
|
227
|
-
}
|
|
228
|
-
resetManagerGizmos();
|
|
229
|
-
mesh.dispose();
|
|
230
|
-
});
|
|
231
|
-
resetSelectedMeshes();
|
|
232
|
-
};
|
|
233
|
-
export const toggleOrthographicViews = (e, orthoCamera) => {
|
|
234
|
-
const isDifferent = cameraView !== orthoCamera;
|
|
235
|
-
const babylonOrthographic = BABYLON.Camera.ORTHOGRAPHIC_CAMERA;
|
|
236
|
-
const babylonPerspective = BABYLON.Camera.PERSPECTIVE_CAMERA;
|
|
237
|
-
if (scene?.debugLayer.isVisible()) return;
|
|
238
|
-
if (isDifferent) {
|
|
239
|
-
cameraView = orthoCamera;
|
|
240
|
-
toggleCameraMode(orthoCamera, babylonOrthographic);
|
|
241
|
-
newMetaDataEntry('selectedOrthoView', orthoCamera);
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
toggleCameraMode(orthoCamera, babylonPerspective);
|
|
245
|
-
newMetaDataEntry('selectedOrthoView', '');
|
|
246
|
-
cameraView = null;
|
|
247
|
-
};
|
|
248
|
-
export const toggleCulling = () => {
|
|
249
|
-
const materials = getUserMaterials();
|
|
250
|
-
materials.forEach(material => {
|
|
251
|
-
const isCullingBackfaces = material.backFaceCulling;
|
|
252
|
-
if (isCullingBackfaces) return material.backFaceCulling = false;
|
|
253
|
-
material.backFaceCulling = !material.backFaceCulling;
|
|
254
|
-
});
|
|
255
|
-
newMetaDataEntry('materials', buildMaterialsArray());
|
|
256
|
-
newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
|
|
257
|
-
};
|
|
258
|
-
export const modelToOrigin = sourceUnitScaleOperation => {
|
|
259
|
-
const boundingBoxEnabled = gizmoManager.boundingBoxGizmoEnabled;
|
|
260
|
-
const meshes = selectedMeshes.length > 0 ? selectedMeshes : getUserMeshes();
|
|
261
|
-
const parentList = [];
|
|
262
|
-
const handleBoundingBox = () => {
|
|
263
|
-
const newBox = createBoundingMesh();
|
|
264
|
-
if (boundingBoxEnabled && !sourceUnitScaleOperation) toggleBoundingBoxWidget();
|
|
265
|
-
if (gizmoManager.boundingBoxGizmoEnabled) gizmoManager.attachToMesh(newBox);
|
|
266
|
-
resetManagerGizmos(GIZMOS.BoundingBoxGizmo);
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
// Single selections or only single mesh in scene.
|
|
270
|
-
if (meshes.length === 1) {
|
|
271
|
-
const mesh = meshes[0];
|
|
272
|
-
const boundingBox = mesh.getBoundingInfo().boundingBox;
|
|
273
|
-
const meshCenter = boundingBox.centerWorld;
|
|
274
|
-
const parent = mesh.parent;
|
|
275
|
-
parentList.push({
|
|
276
|
-
mesh,
|
|
277
|
-
parent
|
|
278
|
-
});
|
|
279
|
-
mesh.setParent(singleMeshTNode);
|
|
280
|
-
singleMeshTNode.position.subtractInPlace(meshCenter);
|
|
281
|
-
singleMeshTNode.position.y += boundingBox.extendSizeWorld.y;
|
|
282
|
-
selectedMeshes.length === 1 ? mesh.setParent(singleMeshTNode) : restoreParents(parentList);
|
|
283
|
-
handleBoundingBox();
|
|
284
|
-
newMetaDataEntry('meshChangeTracking', buildMeshPositionsArray({
|
|
285
|
-
preserveScale: true,
|
|
286
|
-
preserveRotation: true
|
|
287
|
-
}));
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Multiple selections or multiple meshes in scene.
|
|
292
|
-
const multiBoundingData = getBoundingMeshData(meshes);
|
|
293
|
-
const boundingCenter = multiBoundingData.center;
|
|
294
|
-
const compensationY = multiBoundingData.maximum.y - boundingCenter.y;
|
|
295
|
-
meshes.forEach(mesh => {
|
|
296
|
-
const parent = mesh.parent;
|
|
297
|
-
parentList.push({
|
|
298
|
-
mesh,
|
|
299
|
-
parent
|
|
300
|
-
});
|
|
301
|
-
mesh.setParent(multiMeshTNode);
|
|
302
|
-
});
|
|
303
|
-
if (!sourceUnitScaleOperation) {
|
|
304
|
-
multiMeshTNode.position.subtractInPlace(boundingCenter);
|
|
305
|
-
multiMeshTNode.position.y += compensationY;
|
|
306
|
-
}
|
|
307
|
-
restoreParents(parentList);
|
|
308
|
-
handleBoundingBox();
|
|
309
|
-
newMetaDataEntry('meshChangeTracking', buildMeshPositionsArray({
|
|
310
|
-
preserveScale: true,
|
|
311
|
-
preserveRotation: true
|
|
312
|
-
}));
|
|
313
|
-
};
|