@archvisioninc/canvas 3.3.8 → 3.3.10

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.
Files changed (35) hide show
  1. package/dist/Canvas.js +67 -0
  2. package/dist/actions/index.js +1 -0
  3. package/dist/actions/shortcutActions.js +313 -0
  4. package/dist/constants/constants.js +80 -0
  5. package/dist/constants/index.js +1 -0
  6. package/dist/enums/aspectRatios.js +17 -0
  7. package/dist/enums/dimensions.js +20 -0
  8. package/dist/enums/downscaling.js +16 -0
  9. package/dist/enums/exclusions.js +4 -0
  10. package/dist/enums/formats.js +1 -0
  11. package/dist/enums/index.js +8 -0
  12. package/dist/enums/orthoOptions.js +28 -0
  13. package/dist/enums/scaleUnits.js +25 -0
  14. package/dist/enums/shortcuts.js +89 -0
  15. package/dist/helpers/cameraHelpers.js +86 -0
  16. package/dist/helpers/canvasAddHelpers.js +161 -0
  17. package/dist/helpers/canvasCommunicationHelpers.js +52 -0
  18. package/dist/helpers/canvasRemoveHelpers.js +230 -0
  19. package/dist/helpers/canvasUpdateHelpers.js +1368 -0
  20. package/dist/helpers/gizmoHelpers.js +156 -0
  21. package/dist/helpers/guiHelpers.js +46 -0
  22. package/dist/helpers/index.js +16 -0
  23. package/dist/helpers/initHelpers.js +514 -0
  24. package/dist/helpers/lightHelpers.js +17 -0
  25. package/dist/helpers/loadHelpers.js +269 -0
  26. package/dist/helpers/materialHelpers.js +34 -0
  27. package/dist/helpers/meshHelpers.js +169 -0
  28. package/dist/helpers/rayHelpers.js +11 -0
  29. package/dist/helpers/shortcutHelpers.js +35 -0
  30. package/dist/helpers/utilityHelpers.js +710 -0
  31. package/dist/helpers/viewportHelpers.js +364 -0
  32. package/dist/styles.js +25 -0
  33. package/package.json +1 -1
  34. package/src/package/helpers/canvasUpdateHelpers.js +4 -0
  35. package/src/package/helpers/initHelpers.js +2 -0
@@ -0,0 +1,86 @@
1
+ import * as BABYLON from 'babylonjs';
2
+ import { newVector } from './utilityHelpers';
3
+ import { scene, engine, canvas, selectedMeshes, getParamOfSelectedMeshes, getUserMeshes, defaultCameraPosition, getBoundingMeshData } from '../helpers';
4
+ import { CAMERAS, MESH_PARAMS } from '../constants';
5
+ import { focusCamera, toggleTurntable } from '../actions';
6
+ import { orthoOptions, ratios } from '../enums';
7
+ import _ from 'lodash';
8
+ export const newCamera = (name, location, type, meta = {}) => {
9
+ const defaultName = `camera_${getSceneCameras().length + 1}`;
10
+ if (type === CAMERAS.ArcRotateCamera) {
11
+ return new BABYLON.ArcRotateCamera(name || defaultName, meta.alpha || 0, meta.beta || 0, meta.radius || 0, newVector(location.x, location.y, location.z), scene);
12
+ }
13
+ if (CAMERAS[type] && typeof BABYLON[type] === 'function') {
14
+ return new BABYLON[type](name || defaultName, newVector(location.x, location.y, location.z), scene);
15
+ }
16
+ };
17
+ export const prepareCamera = () => {
18
+ const camera = scene.activeCamera;
19
+ const canvasDimensions = engine?.getRenderingCanvasClientRect?.();
20
+ const width = canvasDimensions?.width || ratios.square.value;
21
+ const height = canvasDimensions?.height || ratios.square.value;
22
+ const aspect = height / width;
23
+ const userMeshes = getUserMeshes();
24
+ camera.orthoLeft = -camera.radius;
25
+ camera.orthoTop = camera.radius * aspect;
26
+ camera.orthoRight = camera.radius;
27
+ camera.orthoBottom = -camera.radius * aspect;
28
+ camera.lowerRadiusLimit = 1;
29
+ camera.upperRadiusLimit = 5000;
30
+ camera.minZ = 0.1;
31
+ camera.setPosition?.(defaultCameraPosition());
32
+ camera.setTarget(BABYLON.Vector3.Zero());
33
+ camera.attachControl(canvas, true);
34
+ _.isEmpty(userMeshes) ? toggleTurntable(true) : focusCamera();
35
+ };
36
+ export const prepareMaterialCamera = () => {
37
+ const camera = scene.activeCamera;
38
+ const sphere = scene.getMeshById('node0');
39
+ camera.position = defaultCameraPosition();
40
+ if (sphere) {
41
+ const primarySurfaces = [sphere];
42
+ const meshCenter = getBoundingMeshData(primarySurfaces).center;
43
+ camera.setTarget(meshCenter);
44
+ camera.alpha = 1.57;
45
+ camera.beta = 1.65;
46
+ camera.lowerRadiusLimit = 0.3239;
47
+ camera.upperRadiusLimit = 5000;
48
+ camera.minZ = 0.1;
49
+ camera.maxZ = 10000;
50
+ camera.radius = 4;
51
+ }
52
+ };
53
+ export const toggleCameraMode = (orthoCamera, requestedMode) => {
54
+ const camera = scene.activeCamera;
55
+ const babylonOrthographic = BABYLON.Camera.ORTHOGRAPHIC_CAMERA;
56
+ const ratio = canvas.height / canvas.width;
57
+ if (!camera) return;
58
+ camera.mode = requestedMode;
59
+ if (camera.mode === babylonOrthographic) {
60
+ if (!_.isEmpty(selectedMeshes)) {
61
+ camera.target = getParamOfSelectedMeshes(MESH_PARAMS.center);
62
+ }
63
+ camera.alpha = orthoOptions[orthoCamera].alpha;
64
+ camera.beta = orthoOptions[orthoCamera].beta;
65
+
66
+ // Zooming
67
+ camera.lowerRadiusLimit = 5;
68
+ camera.upperRadiusLimit = 5000;
69
+ camera.minZ = 0.1;
70
+ camera.maxZ = 10000;
71
+ camera.wheelDeltaPercentage = 0.01;
72
+ let oldRadius = camera.radius;
73
+ scene.onBeforeRenderObservable.add(() => {
74
+ if (oldRadius !== camera.radius) {
75
+ const radiusChangeRatio = camera.radius / oldRadius;
76
+ camera.orthoLeft *= radiusChangeRatio;
77
+ camera.orthoRight *= radiusChangeRatio;
78
+ oldRadius = camera.radius;
79
+ camera.orthoTop = camera.orthoRight * ratio;
80
+ camera.orthoBottom = camera.orthoLeft * ratio;
81
+ }
82
+ });
83
+ }
84
+ focusCamera?.();
85
+ };
86
+ export const getSceneCameras = () => scene.cameras;
@@ -0,0 +1,161 @@
1
+ import { scene } from './initHelpers';
2
+ import { newMetaDataEntry, newTexture } from './utilityHelpers';
3
+ import { selectedMeshes } from './viewportHelpers';
4
+ import { singleMeshTNode, toDegrees } from '../helpers';
5
+ import { MESSAGE_TYPES } from '../constants';
6
+ import { reactProps as props } from '../Canvas';
7
+ import _ from 'lodash';
8
+ export const addToMaterial = inboundData => {
9
+ const {
10
+ payload
11
+ } = inboundData;
12
+ const {
13
+ id,
14
+ albedoTexture,
15
+ metallicTexture,
16
+ microSurfaceTexture,
17
+ emissiveTexture,
18
+ bumpTexture
19
+ } = payload;
20
+ const material = scene.getMaterialByName(id, true);
21
+ if (material) {
22
+ // Basic attribute textures.
23
+ if (_.isString(albedoTexture)) material.albedoTexture = newTexture(albedoTexture);
24
+ if (_.isString(metallicTexture)) material.metallicTexture = newTexture(metallicTexture);
25
+ if (_.isString(microSurfaceTexture)) material.microSurfaceTexture = newTexture(microSurfaceTexture);
26
+ if (_.isString(emissiveTexture)) material.emissiveTexture = newTexture(emissiveTexture);
27
+ if (_.isString(bumpTexture)) material.bumpTexture = newTexture(bumpTexture);
28
+ }
29
+ };
30
+ export const addToMesh = inboundData => {
31
+ const {
32
+ payload
33
+ } = inboundData;
34
+ const {
35
+ id
36
+ } = payload;
37
+ if (_.isEmpty(selectedMeshes) || selectedMeshes.length > 1) {
38
+ return;
39
+ }
40
+ const mesh = selectedMeshes[0];
41
+ if (mesh) {
42
+ // Custom user mesh positions.
43
+ const duplicate = scene.metadata?.userMeshPositions?.find(meshPos => meshPos.meshId === mesh.id && meshPos.positionId === id);
44
+ if (_.isString(id) && !duplicate) {
45
+ const {
46
+ position,
47
+ rotation,
48
+ scaling
49
+ } = singleMeshTNode;
50
+ const [tx, ty, tz] = position.asArray();
51
+ const [rx, ry, rz] = rotation.asArray().map(val => toDegrees(val * -1));
52
+ const [sx, sy, sz] = scaling.asArray();
53
+ const positionObj = {
54
+ meshId: mesh.id,
55
+ positionId: id,
56
+ tx,
57
+ ty,
58
+ tz,
59
+ rx,
60
+ ry,
61
+ rz,
62
+ sx,
63
+ sy,
64
+ sz
65
+ };
66
+ const updatedPositions = [...(scene.metadata?.userMeshPositions || {}), positionObj];
67
+ const selectedUserMeshPositions = scene.metadata.selectedUserMeshPositions;
68
+ const filteredPositions = selectedUserMeshPositions.filter(pos => pos.meshId !== mesh.id);
69
+ filteredPositions.push(positionObj);
70
+ newMetaDataEntry('userMeshPositions', updatedPositions.filter(item => item));
71
+ newMetaDataEntry('selectedUserMeshPositions', filteredPositions);
72
+ }
73
+ }
74
+ };
75
+ export const addToCamera = inboundData => {
76
+ const {
77
+ payload
78
+ } = inboundData;
79
+ const {
80
+ id
81
+ } = payload;
82
+ const camera = scene.activeCamera;
83
+ if (camera) {
84
+ // Custom user camera views.
85
+ const duplicate = scene.metadata?.userCameraViews.find(cameraPos => cameraPos.id === id);
86
+ if (_.isString(id) && !duplicate) {
87
+ const {
88
+ position,
89
+ rotation,
90
+ target
91
+ } = scene.activeCamera;
92
+ const [tx, ty, tz] = [position.x, position.y, position.z];
93
+ const [rx, ry, rz] = [rotation.x, rotation.y, rotation.z];
94
+ const [tarX, tarY, tarZ] = [target.x, target.y, target.z];
95
+ const updatedPositions = [...(scene.metadata?.userCameraViews || []), {
96
+ id,
97
+ tx,
98
+ ty,
99
+ tz,
100
+ rx,
101
+ ry,
102
+ rz,
103
+ tarX,
104
+ tarY,
105
+ tarZ
106
+ }];
107
+ newMetaDataEntry('userCameraViews', updatedPositions.filter(item => item));
108
+ newMetaDataEntry('selectedUserCameraView', {
109
+ id,
110
+ tx,
111
+ ty,
112
+ tz,
113
+ rx,
114
+ ry,
115
+ rz,
116
+ tarX,
117
+ tarY,
118
+ tarZ
119
+ });
120
+ }
121
+ }
122
+ };
123
+ export const addToEnvironment = inboundData => {
124
+ const {
125
+ payload
126
+ } = inboundData;
127
+ const {
128
+ id,
129
+ url
130
+ } = payload;
131
+ const skyBox = scene.getMaterialById('skyBox', true);
132
+ if (skyBox) {
133
+ // Add custom user environment.
134
+ const duplicate = scene.metadata?.userEnvironments.find(env => env.id === id);
135
+ const isNotDefault = url !== scene.metadata.defaultEnvironment;
136
+ const isValid = isNotDefault && _.isString(id) && _.isString(url) && !duplicate;
137
+ if (isValid) {
138
+ const message = 'Adding custom environment...';
139
+ const clearExisting = true;
140
+ const isUserFile = true;
141
+ const config = {
142
+ type: MESSAGE_TYPES.loading,
143
+ showModal: true,
144
+ closeButton: false
145
+ };
146
+ props.addNotification?.([{
147
+ message,
148
+ config
149
+ }], clearExisting);
150
+ newTexture(url, isUserFile);
151
+ const updatedEnvironments = [...(scene.metadata?.userEnvironments || {}), {
152
+ id,
153
+ url
154
+ }];
155
+ newMetaDataEntry('userEnvironments', updatedEnvironments.filter(item => item));
156
+ }
157
+ }
158
+ };
159
+ export const addToViewport = () => {
160
+ // Add functions here...
161
+ };
@@ -0,0 +1,52 @@
1
+ import { UPDATABLE_ITEMS } from '../constants';
2
+ import { updateMaterial, addToMaterial, removeFromMaterial, updateMesh, addToMesh, removeFromMesh, updateCamera, addToCamera, removeFromCamera, updateEnvironment, addToEnvironment, removeFromEnvironment, updateViewport, addToViewport, removeFromViewport, serializeScene, updatePublish, newMetaDataEntry, buildMeshIdArray, updateLighting } from '../helpers';
3
+ import { reactProps as props } from '../Canvas';
4
+ export const updateItem = inboundData => {
5
+ const {
6
+ type,
7
+ method
8
+ } = inboundData;
9
+ const types = Object.keys(UPDATABLE_ITEMS);
10
+ const isValidType = types.includes(type);
11
+ const hidden = true;
12
+ if (isValidType) {
13
+ switch (type) {
14
+ case UPDATABLE_ITEMS.material:
15
+ if (method === 'update') updateMaterial(inboundData);
16
+ if (method === 'add') addToMaterial(inboundData);
17
+ if (method === 'remove') removeFromMaterial(inboundData);
18
+ break;
19
+ case UPDATABLE_ITEMS.mesh:
20
+ if (method === 'update') updateMesh(inboundData);
21
+ if (method === 'add') addToMesh(inboundData);
22
+ if (method === 'remove') removeFromMesh(inboundData);
23
+ break;
24
+ case UPDATABLE_ITEMS.camera:
25
+ if (method === 'update') updateCamera(inboundData);
26
+ if (method === 'add') addToCamera(inboundData);
27
+ if (method === 'remove') removeFromCamera(inboundData);
28
+ break;
29
+ case UPDATABLE_ITEMS.environment:
30
+ if (method === 'update') updateEnvironment(inboundData);
31
+ if (method === 'add') addToEnvironment(inboundData);
32
+ if (method === 'remove') removeFromEnvironment(inboundData);
33
+ break;
34
+ case UPDATABLE_ITEMS.viewport:
35
+ if (method === 'update') updateViewport(inboundData);
36
+ if (method === 'add') addToViewport(inboundData);
37
+ if (method === 'remove') removeFromViewport(inboundData);
38
+ break;
39
+ case UPDATABLE_ITEMS.publish:
40
+ if (method === 'update') updatePublish(inboundData);
41
+ break;
42
+ case UPDATABLE_ITEMS.lighting:
43
+ if (method === 'update') updateLighting(inboundData);
44
+ break;
45
+ default:
46
+ break;
47
+ }
48
+ newMetaDataEntry('hiddenMeshes', buildMeshIdArray(hidden));
49
+ props.setSerializedData?.(serializeScene());
50
+ props.clearUpdateData?.();
51
+ }
52
+ };
@@ -0,0 +1,230 @@
1
+ import { scene, newMetaDataEntry, selectedMeshes, updateCamera, updateEnvironment, buildMaterialsArray, buildSelectedMaterialArray, serializeScene } from '../helpers';
2
+ import { reactProps as props } from '../Canvas';
3
+ import { deleteSelected } from '../actions';
4
+ import { TRANSPARENCY_MODES } from '../constants';
5
+ import * as BABYLON from 'babylonjs';
6
+ import _ from 'lodash';
7
+ const removeChannelFromTexture = (image, channel) => {
8
+ const imageHeight = image?.naturalHeight ?? 0;
9
+ const imageWidth = image?.naturalWidth ?? 0;
10
+ let imageData;
11
+ const maxHeight = Math.max(...[imageHeight, 1024]);
12
+ const maxWidth = Math.max(...[imageWidth, 1024]);
13
+ const canvas = document.createElement('canvas');
14
+ const ctx = canvas.getContext('2d');
15
+ canvas.width = maxWidth;
16
+ canvas.height = maxHeight;
17
+ if (image) {
18
+ ctx.drawImage(image, 0, 0, maxWidth, maxHeight);
19
+ imageData = ctx.getImageData(0, 0, maxWidth, maxHeight).data;
20
+ }
21
+ const combinedImageData = ctx.createImageData(maxWidth, maxHeight);
22
+ const combinedData = combinedImageData.data;
23
+ for (let i = 0; i < combinedData.length; i += 4) {
24
+ combinedData[i] = channel === 'R' ? 0 : imageData[i];
25
+ combinedData[i + 1] = channel === 'G' ? 0 : imageData[i + 1];
26
+ combinedData[i + 2] = channel === 'B' ? 0 : imageData[i + 2];
27
+ combinedData[i + 3] = channel === 'A' ? 255 : imageData[i + 3];
28
+ }
29
+ ctx.putImageData(combinedImageData, 0, 0);
30
+ return canvas.toDataURL('image/png');
31
+ };
32
+ const textureToImage = texture => {
33
+ return new Promise(resolve => {
34
+ if (!texture.readPixels()) {
35
+ resolve(null);
36
+ return;
37
+ }
38
+ texture.readPixels().then(pixels => {
39
+ const canvas = document.createElement('canvas');
40
+ const ctx = canvas.getContext('2d');
41
+ const {
42
+ width,
43
+ height
44
+ } = texture.getSize();
45
+ canvas.width = width;
46
+ canvas.height = height;
47
+ const img = new Image();
48
+ const imageData = ctx.createImageData(width, height);
49
+ imageData.data.set(new Uint8ClampedArray(pixels));
50
+ ctx.putImageData(imageData, 0, 0);
51
+ img.onload = () => {
52
+ resolve(img);
53
+ };
54
+ img.onerror = () => {
55
+ resolve(null);
56
+ };
57
+ img.src = canvas.toDataURL();
58
+ }).catch(() => {
59
+ resolve(null);
60
+ });
61
+ });
62
+ };
63
+ export const removeFromMaterial = inboundData => {
64
+ const {
65
+ payload
66
+ } = inboundData;
67
+ const {
68
+ id,
69
+ removeAlbedoTexture,
70
+ removeMetallicTexture,
71
+ removeMicroSurfaceTexture,
72
+ removeEmissiveTexture,
73
+ removeBumpTexture,
74
+ removeOpacityTexture,
75
+ removeRoughnessTexture,
76
+ removeAmbientTexture
77
+ } = payload;
78
+ const material = scene.getMaterialByName(id, true);
79
+ if (material) {
80
+ // Basic attribute textures.
81
+ if (removeAlbedoTexture) {
82
+ const isStencil = material.transparencyType === TRANSPARENCY_MODES.stencil;
83
+ if (isStencil) {
84
+ material.albedoTexture.hasAlpha = false;
85
+ material.albedoTexture.useAlphaFromAlbedoTexture = false;
86
+ material.transparencyMode = BABYLON.PBRMaterial.PBRMATERIAL_ALPHABLEND;
87
+ material.transparencyType = TRANSPARENCY_MODES.simple;
88
+ }
89
+ material.albedoTexture?.dispose();
90
+ material.albedoTexture = null;
91
+ }
92
+ if (removeMetallicTexture) {
93
+ if (material.metallicTexture) {
94
+ textureToImage(material.metallicTexture).then(data => {
95
+ const updatedTexture = removeChannelFromTexture(data, 'B');
96
+ material.metallicTexture.updateURL(updatedTexture);
97
+ newMetaDataEntry('materials', buildMaterialsArray());
98
+ newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
99
+ props.clearNotifications?.();
100
+ props.setSerializedData?.(serializeScene());
101
+ }).catch(() => {
102
+ console.log('Failed to remove metallic texture');
103
+ });
104
+ }
105
+ }
106
+ if (removeMicroSurfaceTexture) {
107
+ material.microSurfaceTexture?.dispose();
108
+ material.microSurfaceTexture = null;
109
+ }
110
+ if (removeEmissiveTexture) {
111
+ material.emissiveTexture?.dispose();
112
+ material.emissiveTexture = null;
113
+ }
114
+ if (removeBumpTexture) {
115
+ material.bumpTexture?.dispose();
116
+ material.bumpTexture = null;
117
+ }
118
+ if (removeOpacityTexture) {
119
+ material.opacityTexture?.dispose();
120
+ material.opacityTexture = null;
121
+ }
122
+ if (removeAmbientTexture) {
123
+ material.ambientTexture?.dispose();
124
+ material.ambientTexture = null;
125
+ }
126
+ if (removeRoughnessTexture) {
127
+ if (material.metallicTexture) {
128
+ textureToImage(material.metallicTexture).then(data => {
129
+ const updatedTexture = removeChannelFromTexture(data, 'G');
130
+ material.metallicTexture.updateURL(updatedTexture);
131
+ newMetaDataEntry('materials', buildMaterialsArray());
132
+ newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
133
+ props.clearNotifications?.();
134
+ props.setSerializedData?.(serializeScene());
135
+ }).catch(() => {
136
+ console.log('Failed to remove roughness texture');
137
+ });
138
+ }
139
+ }
140
+ newMetaDataEntry('materials', buildMaterialsArray());
141
+ newMetaDataEntry('selectedMaterials', buildSelectedMaterialArray());
142
+ }
143
+ };
144
+ export const removeFromMesh = inboundData => {
145
+ const {
146
+ payload
147
+ } = inboundData;
148
+ const {
149
+ id
150
+ } = payload;
151
+ if (_.isString(id)) {
152
+ if (_.isEmpty(selectedMeshes) || selectedMeshes.length > 1) {
153
+ return;
154
+ }
155
+ const selectedMesh = selectedMeshes[0];
156
+
157
+ // Remove custom user mesh positions.
158
+ const userMeshPositions = scene.metadata?.userMeshPositions?.filter(mesh => mesh.meshId !== selectedMesh.id || mesh.positionId !== id) || [];
159
+ const filteredSelectedPositions = scene.metadata.selectedUserMeshPositions.filter(pos => pos.meshId !== selectedMesh.id || pos.positionId !== id);
160
+ const defaultPosition = userMeshPositions.find(pos => pos.meshId === selectedMesh.id && pos.positionId === 'Default');
161
+ filteredSelectedPositions.push(defaultPosition);
162
+ newMetaDataEntry('userMeshPositions', userMeshPositions);
163
+ newMetaDataEntry('selectedUserMeshPositions', filteredSelectedPositions);
164
+ }
165
+ };
166
+ export const removeFromCamera = inboundData => {
167
+ const {
168
+ payload
169
+ } = inboundData;
170
+ const {
171
+ id
172
+ } = payload;
173
+ if (_.isString(id)) {
174
+ // Custom user camera views.
175
+ const filteredPositions = scene.metadata?.userCameraViews.filter(cameraPos => cameraPos.id !== id) || [];
176
+ const inboundData = {
177
+ payload: {
178
+ resetCamera: true
179
+ }
180
+ };
181
+ newMetaDataEntry('userCameraViews', filteredPositions);
182
+ updateCamera(inboundData);
183
+ }
184
+ };
185
+ export const removeFromEnvironment = inboundData => {
186
+ const {
187
+ payload
188
+ } = inboundData;
189
+ const {
190
+ id
191
+ } = payload;
192
+ const userEnv = scene.metadata?.userEnvironments.find(env => env.id === id);
193
+
194
+ // remove custom user environment.
195
+ if (_.isString(id) && _.isString(userEnv?.url)) {
196
+ const filteredEnvs = scene.metadata?.userEnvironments.filter(env => env.id != id) || [];
197
+ const inboundData = {
198
+ payload: {
199
+ url: scene.metadata.defaultEnvironment,
200
+ transforms: {
201
+ rotation: {
202
+ ry: scene.metadata.environmentRotation
203
+ }
204
+ }
205
+ }
206
+ };
207
+ newMetaDataEntry('userEnvironments', filteredEnvs);
208
+ updateEnvironment(inboundData);
209
+ }
210
+ };
211
+ export const removeFromViewport = inboundData => {
212
+ const {
213
+ payload
214
+ } = inboundData;
215
+ const {
216
+ deleteSelectedMesh
217
+ } = payload;
218
+ const hasSelection = selectedMeshes.length > 0;
219
+
220
+ // Delete selected mesh.
221
+ if (deleteSelectedMesh !== undefined && hasSelection) {
222
+ const multiSelection = selectedMeshes.length > 1;
223
+ const confirmConfig = {
224
+ message: `Are you sure you want to delete ${multiSelection ? 'these meshes' : 'this mesh'}?`,
225
+ onConfirm: () => deleteSelected(),
226
+ onClose: () => props.setConfirmMessage?.(null)
227
+ };
228
+ props.setConfirmMessage?.(confirmConfig);
229
+ }
230
+ };