@archvisioninc/canvas 3.3.7 → 3.3.9

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.
@@ -0,0 +1,8 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(wc -l /c/Users/ArchVision/Documents/git/archvision-canvas/src/package/helpers/*.js)",
5
+ "Bash(npx eslint:*)"
6
+ ]
7
+ }
8
+ }
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,
@@ -257,6 +257,60 @@ const dataUrlToBlob = dataURI => {
257
257
  });
258
258
  return blob;
259
259
  };
260
+ const getActiveAnimationGroup = animationGroups => {
261
+ const selectedAnimationIndex = scene.metadata?.selectedAnimation;
262
+ if (selectedAnimationIndex === null || selectedAnimationIndex === undefined) {
263
+ return null;
264
+ }
265
+ return animationGroups.find((_group, index) => {
266
+ return `${index}` === `${selectedAnimationIndex}`;
267
+ }) || null;
268
+ };
269
+ let animationSyncObserver = null;
270
+ let lastAnimationSerializeAt = 0;
271
+ const ANIMATION_SYNC_MS = 50;
272
+ const pushAnimationStateToReact = () => {
273
+ const activeAnimationGroup = getActiveAnimationGroup(scene.animationGroups || []);
274
+ syncSelectedAnimationMeta(activeAnimationGroup);
275
+ const now = performance.now();
276
+ if (props.setSerializedData && now - lastAnimationSerializeAt >= ANIMATION_SYNC_MS) {
277
+ lastAnimationSerializeAt = now;
278
+ props.setSerializedData(serializeScene());
279
+ }
280
+ };
281
+ const ensureAnimationStateSync = () => {
282
+ if (animationSyncObserver || !scene?.onBeforeRenderObservable) {
283
+ return;
284
+ }
285
+ animationSyncObserver = scene.onBeforeRenderObservable.add(() => {
286
+ const activeAnimationGroup = getActiveAnimationGroup(scene.animationGroups || []);
287
+ if (!activeAnimationGroup) {
288
+ return;
289
+ }
290
+ pushAnimationStateToReact();
291
+ });
292
+ };
293
+ const syncSelectedAnimationMeta = animationGroup => {
294
+ if (!scene.metadata) return;
295
+ if (!animationGroup) {
296
+ scene.metadata.selectedAnimationTime = 0;
297
+ scene.metadata.selectedAnimationProgress = 0;
298
+ scene.metadata.selectedAnimationDuration = 0;
299
+ scene.metadata.selectedAnimationIsPlaying = false;
300
+ return;
301
+ }
302
+ const runtimeAnimation = animationGroup.animatables?.[0]?.getAnimations?.()?.[0];
303
+ const currentFrame = runtimeAnimation?.currentFrame ?? animationGroup.from ?? 0;
304
+ const from = animationGroup.from ?? 0;
305
+ const to = animationGroup.to ?? 0;
306
+ const totalFrames = Math.max(to - from, 1);
307
+ const progress = BABYLON.Scalar.Clamp((currentFrame - from) / totalFrames, 0, 1);
308
+ const duration = animationGroup.getLength?.() ?? 0;
309
+ scene.metadata.selectedAnimationProgress = progress;
310
+ scene.metadata.selectedAnimationTime = progress * duration;
311
+ scene.metadata.selectedAnimationDuration = duration;
312
+ scene.metadata.selectedAnimationIsPlaying = Boolean(animationGroup.isPlaying);
313
+ };
260
314
  export const updateMaterial = inboundData => {
261
315
  const {
262
316
  payload
@@ -599,6 +653,10 @@ export const updateMesh = inboundData => {
599
653
  animationId,
600
654
  resetAnimation,
601
655
  loopAnimation = true,
656
+ playAnimation,
657
+ pauseAnimation,
658
+ seekAnimation,
659
+ animationProgress,
602
660
  sourceUnit,
603
661
  displayUnit
604
662
  } = payload;
@@ -741,6 +799,8 @@ export const updateMesh = inboundData => {
741
799
  if (resetAnimation) {
742
800
  stopAllAnimationGroups();
743
801
  newMetaDataEntry('selectedAnimation', null);
802
+ syncSelectedAnimationMeta(null);
803
+ props.setSerializedData?.(serializeScene());
744
804
  }
745
805
 
746
806
  // Change active animation
@@ -750,8 +810,39 @@ export const updateMesh = inboundData => {
750
810
  if (animationGroup) {
751
811
  animationGroup.start(Boolean(loopAnimation));
752
812
  newMetaDataEntry('selectedAnimation', `${animationId}`);
813
+ syncSelectedAnimationMeta(animationGroup);
814
+ ensureAnimationStateSync();
815
+ props.setSerializedData?.(serializeScene());
753
816
  }
754
817
  }
818
+ const activeAnimationGroup = getActiveAnimationGroup(animationGroups);
819
+
820
+ // Play current animation
821
+ if (playAnimation && activeAnimationGroup) {
822
+ activeAnimationGroup.play(Boolean(loopAnimation));
823
+ syncSelectedAnimationMeta(activeAnimationGroup);
824
+ ensureAnimationStateSync();
825
+ props.setSerializedData?.(serializeScene());
826
+ }
827
+
828
+ // Pause current animation
829
+ if (pauseAnimation && activeAnimationGroup) {
830
+ activeAnimationGroup.pause();
831
+ syncSelectedAnimationMeta(activeAnimationGroup);
832
+ props.setSerializedData?.(serializeScene());
833
+ }
834
+
835
+ // Seek current animation to a progress position (0–1)
836
+ if (seekAnimation && activeAnimationGroup && animationProgress !== undefined) {
837
+ const {
838
+ from,
839
+ to
840
+ } = activeAnimationGroup;
841
+ const frame = from + animationProgress * (to - from);
842
+ activeAnimationGroup.goToFrame(frame);
843
+ syncSelectedAnimationMeta(activeAnimationGroup);
844
+ props.setSerializedData?.(serializeScene());
845
+ }
755
846
 
756
847
  // Bounding Box Toggle
757
848
  if (boundingBoxEnabled !== undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archvisioninc/canvas",
3
- "version": "3.3.7",
3
+ "version": "3.3.9",
4
4
  "private": false,
5
5
  "main": "dist/Canvas.js",
6
6
  "module": "dist/Canvas.js",
@@ -28,6 +28,7 @@
28
28
  ]
29
29
  },
30
30
  "dependencies": {
31
+ "@archvisioninc/canvas": "^3.3.8",
31
32
  "d3-array": "^3.2.2",
32
33
  "lodash": "^4.17.21",
33
34
  "styled-components": "^5.3.6"
@@ -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 {
@@ -669,6 +748,10 @@ export const updateMesh = inboundData => {
669
748
  animationId,
670
749
  resetAnimation,
671
750
  loopAnimation = true,
751
+ playAnimation,
752
+ pauseAnimation,
753
+ seekAnimation,
754
+ animationProgress,
672
755
  sourceUnit,
673
756
  displayUnit,
674
757
  } = payload;
@@ -790,6 +873,8 @@ export const updateMesh = inboundData => {
790
873
  if (resetAnimation) {
791
874
  stopAllAnimationGroups();
792
875
  newMetaDataEntry('selectedAnimation', null);
876
+ syncSelectedAnimationMeta(null);
877
+ props.setSerializedData?.(serializeScene());
793
878
  }
794
879
 
795
880
  // Change active animation
@@ -801,9 +886,38 @@ export const updateMesh = inboundData => {
801
886
  if (animationGroup) {
802
887
  animationGroup.start(Boolean(loopAnimation));
803
888
  newMetaDataEntry('selectedAnimation', `${animationId}`);
889
+ syncSelectedAnimationMeta(animationGroup);
890
+ ensureAnimationStateSync();
891
+ props.setSerializedData?.(serializeScene());
804
892
  }
805
893
  }
806
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
+
807
921
  // Bounding Box Toggle
808
922
  if (boundingBoxEnabled !== undefined) {
809
923
  toggleBoundingBoxWidget();