zincjs 2.4.1-beta.3 → 2.4.1-beta.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zincjs",
3
- "version": "2.4.1-beta.3",
3
+ "version": "2.4.1-beta.4",
4
4
  "description": "ZincJS (Web-based-Zinc-Visualisation)",
5
5
  "type": "module",
6
6
  "main": "build/zinc.js",
@@ -123,13 +123,16 @@ const GLTFToZincJSLoader = function () {
123
123
  * @param {String} url - URL to the GLTF file
124
124
  * @param {Function} finishCallback - Callback function which will be called
125
125
  * once the glyphset is succssfully load in.
126
+ * @param {Function} isCancelled - Optional function returning true if this
127
+ * load has been cancelled, the loaded content will be discarded.
126
128
  */
127
- this.load = (scene, region, url, finishCallback, allCompletedCallback, options) => {
129
+ this.load = (scene, region, url, finishCallback, allCompletedCallback, options, isCancelled) => {
128
130
  const path = url.substring(0, url.lastIndexOf("/") + 1);
129
131
  const filename = url.substring(url.lastIndexOf("/") + 1, url.length);
130
132
  const loader = new GLTFLoader().setPath(path);
131
133
 
132
134
  loader.load( filename, function ( gltf ) {
135
+ if (isCancelled && isCancelled()) return;
133
136
  console.log(gltf)
134
137
  _this.parseGLTFObjects(gltf.scene, region, 0, finishCallback);
135
138
  _this.setCamera(scene);
@@ -302,7 +302,7 @@ Object.assign( JSONLoader.prototype, {
302
302
 
303
303
  }
304
304
 
305
- const attribute = new Float32BufferAttribute( array, 3 );
305
+ const attribute = new BufferAttribute( array, 3 );
306
306
  attribute.name = json.morphTargets[ i ].name;
307
307
  morphPositions.push( attribute );
308
308
 
@@ -321,7 +321,7 @@ Object.assign( JSONLoader.prototype, {
321
321
  const srcNormals = json.morphNormals[ i ].normals;
322
322
  const array = Float32Array.from( srcNormals );
323
323
 
324
- const attribute = new Float32BufferAttribute( array, 3 );
324
+ const attribute = new BufferAttribute( array, 3 );
325
325
  attribute.name = morphPositions[ i ].name;
326
326
  morphNormals[ i ] = attribute;
327
327
 
@@ -124,7 +124,7 @@ const IndexedSourcesHandler = function(urlIn, crossOrigin, onDownloadedCallback)
124
124
  if (data) {
125
125
  processItemDownloaded(item);
126
126
  } else {
127
- processItemError(error);
127
+ processItemError(item);
128
128
  }
129
129
  } else if (downloading) {
130
130
  //quene it up
@@ -22,6 +22,8 @@ const Glyphset = function () {
22
22
  let axis2s = undefined;
23
23
  let axis3s = undefined;
24
24
  let positions = undefined;
25
+ //Glyph geometry loaded after dispose is discarded
26
+ let disposed = false;
25
27
  let scales = undefined;
26
28
  let colors = undefined;
27
29
  let labels = undefined;
@@ -149,6 +151,7 @@ const Glyphset = function () {
149
151
  * geometry is loaded.
150
152
  */
151
153
  this.load = (glyphsetData, glyphURL, finishCallback, isInline, displayLabels) => {
154
+ disposed = false;
152
155
  axis1s = glyphsetData.axis1;
153
156
  axis2s = glyphsetData.axis2;
154
157
  axis3s = glyphsetData.axis3;
@@ -856,6 +859,10 @@ const Glyphset = function () {
856
859
 
857
860
  var meshloader = (finishCallback, displayLabels) => {
858
861
  return (geometry, materials) => {
862
+ if (disposed) {
863
+ geometry.dispose();
864
+ return;
865
+ }
859
866
  this.geometry.copy(geometry);
860
867
  this.geometry.computeBoundingSphere();
861
868
  this.geometry.computeBoundingBox();
@@ -1063,6 +1070,7 @@ const Glyphset = function () {
1063
1070
  * Clear this glyphset and its list of glyphs which will release them from the memory.
1064
1071
  */
1065
1072
  this.dispose = () => {
1073
+ disposed = true;
1066
1074
  for (let i = glyphList.length - 1; i >= 0; i--) {
1067
1075
  glyphList[i].dispose();
1068
1076
  }
@@ -26,6 +26,8 @@ const LOD = function (parent) {
26
26
  this._loader = undefined;
27
27
  //The owning Zinc Object
28
28
  this._parent = parent;
29
+ //Levels loaded after dispose are discarded
30
+ this._disposed = false;
29
31
 
30
32
  /*
31
33
  * Add a level of LOD at the specified distance
@@ -182,6 +184,7 @@ const LOD = function (parent) {
182
184
  }
183
185
 
184
186
  this.dispose = () => {
187
+ this._disposed = true;
185
188
  this.levels.forEach((level) => {
186
189
  if (level.morph && level.morph.geometry) {
187
190
  level.morph.geometry.dispose();
@@ -212,6 +215,10 @@ const LOD = function (parent) {
212
215
  */
213
216
  this.lodLoader = function (distance) {
214
217
  return (geometryIn) => {
218
+ if (this._disposed) {
219
+ geometryIn.dispose();
220
+ return;
221
+ }
215
222
  const material = this._material;
216
223
  const options = {
217
224
  localTimeEnabled: this._parent.timeEnabled,
@@ -70,4 +70,16 @@ const TexturePrimitive = function (textureIn) {
70
70
 
71
71
  TexturePrimitive.prototype = Object.create(ZincObject.prototype);
72
72
  TexturePrimitive.prototype.constructor = TexturePrimitive;
73
+
74
+ /**
75
+ * Free the textures and the other allocated memory.
76
+ */
77
+ TexturePrimitive.prototype.dispose = function() {
78
+ const textures = new Set(this.textureList);
79
+ if (this.texture) textures.add(this.texture);
80
+ textures.forEach(texture => texture.dispose());
81
+ this.texture = undefined;
82
+ this.textureList = [];
83
+ ZincObject.prototype.dispose.call(this);
84
+ }
73
85
  export { TexturePrimitive };
@@ -277,6 +277,11 @@ const TextureSlides = function (textureIn) {
277
277
  if (slide.material)
278
278
  slide.material.dispose();
279
279
  });
280
+ if (maskTexture) {
281
+ maskTexture.dispose();
282
+ maskTexture = undefined;
283
+ maskEnabled = false;
284
+ }
280
285
  TexturePrimitive.prototype.dispose.call(this);
281
286
  this.boundingBoxUpdateRequired = true;
282
287
  }
@@ -565,6 +565,10 @@ ZincObject.prototype.getBoundingBox = function() {
565
565
  ZincObject.prototype.dispose = function() {
566
566
  //multilayyers
567
567
  this._lod.dispose();
568
+ if (this.marker) {
569
+ this.marker.dispose();
570
+ this.marker = undefined;
571
+ }
568
572
  this.animationGroup = undefined;
569
573
  this.mixer = undefined;
570
574
  this.morph = undefined;
package/src/region.js CHANGED
@@ -445,13 +445,18 @@ let Region = function (parentIn, sceneIn) {
445
445
  */
446
446
  this.clear = transverse => {
447
447
  if (transverse) {
448
- children.forEach(childRegion => childRegion.clear(transverse));
448
+ children.forEach(childRegion => {
449
+ childRegion.clear(transverse);
450
+ group.remove(childRegion.getGroup());
451
+ });
449
452
  }
450
453
  zincObjects.forEach(zincObject => {
451
454
  group.remove(zincObject.getGroup());
452
455
  zincObject.dispose();
453
456
  });
454
- children = [];
457
+ if (transverse) {
458
+ children = [];
459
+ }
455
460
  zincObjects = [];
456
461
  }
457
462
 
package/src/scene.js CHANGED
@@ -1025,7 +1025,7 @@ const Scene = function (containerIn, rendererIn) {
1025
1025
  rootRegion.clear(true);
1026
1026
  this.clearZincObjectAddedCallbacks();
1027
1027
  this.clearZincObjectRemovedCallbacks();
1028
- sceneLoader.toBeDwonloaded = 0;
1028
+ sceneLoader.cancelPendingLoads();
1029
1029
  if (zincCameraControls) {
1030
1030
  zincCameraControls.calculateMaxAllowedDistance(this);
1031
1031
  }
@@ -27,7 +27,30 @@ const SceneLoader = function (sceneIn) {
27
27
  this.progressMap = {};
28
28
  let viewLoaded = false;
29
29
  let errorDownload = false;
30
+ //Incremented when pending loads are cancelled, loads requested
31
+ //before the increment are discarded once they complete.
32
+ let generation = 0;
30
33
  const primitivesLoader = new PrimitivesLoader();
34
+
35
+ /**
36
+ * Cancel all pending downloads, objects from the cancelled downloads
37
+ * will be discarded instead of being added into the scene once they
38
+ * arrive.
39
+ */
40
+ this.cancelPendingLoads = () => {
41
+ generation++;
42
+ this.toBeDownloaded = 0;
43
+ this.progressMap = {};
44
+ viewLoaded = false;
45
+ errorDownload = false;
46
+ }
47
+
48
+ //Return a function which returns true if the loads requested before
49
+ //this call have been cancelled.
50
+ const createStaleCheck = () => {
51
+ const requestGeneration = generation;
52
+ return () => requestGeneration !== generation;
53
+ }
31
54
  /**
32
55
  * This function returns a three component array, which contains
33
56
  * [totalsize, totalLoaded and errorDownload] of all the downloads happening
@@ -62,7 +85,9 @@ const SceneLoader = function (sceneIn) {
62
85
  }
63
86
 
64
87
  this.onError = finishCallback => {
88
+ const isStale = createStaleCheck();
65
89
  return xhr => {
90
+ if (isStale()) return;
66
91
  this.toBeDownloaded = this.toBeDownloaded - 1;
67
92
  errorDownload = true;
68
93
  console.error(`There is an issue with external resource ${xhr?.responseURL ? ": " + xhr?.responseURL: ""}.`);
@@ -116,6 +141,7 @@ const SceneLoader = function (sceneIn) {
116
141
  this.loadViewURL = (url, finishCallback) => {
117
142
  this.toBeDownloaded += 1;
118
143
  const requestURL = url;
144
+ const isStale = createStaleCheck();
119
145
  fetch(requestURL)
120
146
  .then((response) => {
121
147
  if (!response.ok) {
@@ -124,6 +150,7 @@ const SceneLoader = function (sceneIn) {
124
150
  return response.json();
125
151
  })
126
152
  .then((viewData) => {
153
+ if (isStale()) return;
127
154
  scene.setupMultipleViews("default", { "default" : viewData });
128
155
  scene.resetView();
129
156
  viewLoaded = true;
@@ -176,6 +203,7 @@ const SceneLoader = function (sceneIn) {
176
203
  */
177
204
  this.loadFromViewURL = (targetRegion, jsonFilePrefix, finishCallback) => {
178
205
  const requestURL = jsonFilePrefix + "_view.json";
206
+ const isStale = createStaleCheck();
179
207
  fetch(requestURL)
180
208
  .then((response) => {
181
209
  if (!response.ok) {
@@ -184,6 +212,7 @@ const SceneLoader = function (sceneIn) {
184
212
  return response.json();
185
213
  })
186
214
  .then((viewData) => {
215
+ if (isStale()) return;
187
216
  scene.loadView(viewData);
188
217
  const urls = [];
189
218
  const filename_prefix = jsonFilePrefix + "_";
@@ -209,7 +238,12 @@ const SceneLoader = function (sceneIn) {
209
238
  //Internal loader for a regular zinc geometry.
210
239
  const linesloader = (region, localTimeEnabled, localMorphColour, groupName,
211
240
  anatomicalId, renderOrder, lod, tubeLines, finishCallback) => {
241
+ const isStale = createStaleCheck();
212
242
  return (geometry, materials) => {
243
+ if (isStale()) {
244
+ geometry.dispose();
245
+ return;
246
+ }
213
247
  const newLines = tubeLines ? new TubeLines() : new Lines();
214
248
  let material = undefined;
215
249
  if (materials && materials[0]) {
@@ -276,7 +310,9 @@ const SceneLoader = function (sceneIn) {
276
310
  }
277
311
 
278
312
  const glyphsetloader = (region, glyphurl, groupName, finishCallback, options) => {
313
+ const isStale = createStaleCheck();
279
314
  return (data) => {
315
+ if (isStale()) return;
280
316
  let glyphsetData = data;
281
317
  if (typeof glyphsetData === 'string' || glyphsetData instanceof String) {
282
318
  glyphsetData = JSON.parse(data);
@@ -292,6 +328,7 @@ const SceneLoader = function (sceneIn) {
292
328
  newGlyphset.setDuration(scene.getDuration());
293
329
  newGlyphset.groupName = groupName;
294
330
  let myCallback = () => {
331
+ if (isStale()) return;
295
332
  --this.toBeDownloaded;
296
333
  if (finishCallback != undefined && (typeof finishCallback == 'function'))
297
334
  finishCallback(newGlyphset);
@@ -358,7 +395,12 @@ const SceneLoader = function (sceneIn) {
358
395
 
359
396
  //Internal loader for zinc pointset.
360
397
  const pointsetloader = (region, localTimeEnabled, localMorphColour, groupName, anatomicalId, renderOrder, finishCallback) => {
398
+ const isStale = createStaleCheck();
361
399
  return (geometry, materials) => {
400
+ if (isStale()) {
401
+ geometry.dispose();
402
+ return;
403
+ }
362
404
  const newPointset = new Pointset();
363
405
  let material = new THREE.PointsMaterial(
364
406
  {
@@ -554,7 +596,12 @@ const SceneLoader = function (sceneIn) {
554
596
  }
555
597
  if (newTexture) {
556
598
  newTexture.groupName = groupName;
599
+ const isStale = createStaleCheck();
557
600
  let myCallback = () => {
601
+ if (isStale()) {
602
+ newTexture.dispose();
603
+ return;
604
+ }
558
605
  //Add zincObject after it has sort out all the required download
559
606
  region.addZincObject(newTexture);
560
607
  --this.toBeDownloaded;
@@ -582,6 +629,7 @@ const SceneLoader = function (sceneIn) {
582
629
  loadTexture(region, undefined, url, groupName, finishCallback, options);
583
630
  } else {
584
631
  const requestURL = url;
632
+ const isStale = createStaleCheck();
585
633
  fetch(requestURL)
586
634
  .then((response) => {
587
635
  if (!response.ok) {
@@ -591,6 +639,7 @@ const SceneLoader = function (sceneIn) {
591
639
  return response.json().then((textureData) => ({ textureData, referenceURL }));
592
640
  })
593
641
  .then(({textureData, referenceURL}) => {
642
+ if (isStale()) return;
594
643
  loadTexture(region, referenceURL, textureData, groupName, finishCallback, options);
595
644
  })
596
645
  .catch((error) => {
@@ -662,7 +711,12 @@ const SceneLoader = function (sceneIn) {
662
711
  options,
663
712
  finishCallback
664
713
  ) => {
714
+ const isStale = createStaleCheck();
665
715
  return (geometry, materials) => {
716
+ if (isStale()) {
717
+ geometry.dispose();
718
+ return;
719
+ }
666
720
  let material = undefined;
667
721
  if (materials && materials[0]) {
668
722
  material = materials[0];
@@ -831,7 +885,8 @@ const SceneLoader = function (sceneIn) {
831
885
  */
832
886
  this.loadGLTF = (region, url, finishCallback, allCompletedCallback, options) => {
833
887
  const loader = new GLTFToZincJSLoader();
834
- loader.load(scene, region, url, finishCallback, allCompletedCallback, options);
888
+ loader.load(scene, region, url, finishCallback, allCompletedCallback, options,
889
+ createStaleCheck());
835
890
  }
836
891
 
837
892
  let loadRegions = (currentRegion, referenceURL, regions, callback) => {
@@ -993,6 +1048,7 @@ const SceneLoader = function (sceneIn) {
993
1048
  */
994
1049
  this.loadMetadataURL = (targetRegion, url, finishCallback, allCompletedCallback, options) => {
995
1050
  const requestURL = url;
1051
+ const isStale = createStaleCheck();
996
1052
  fetch(requestURL)
997
1053
  .then((response) => {
998
1054
  if (!response.ok) {
@@ -1002,6 +1058,7 @@ const SceneLoader = function (sceneIn) {
1002
1058
  return response.json().then((metadata) => ({ metadata, referenceURL }));
1003
1059
  })
1004
1060
  .then(({metadata, referenceURL}) => {
1061
+ if (isStale()) return;
1005
1062
  scene.resetMetadata();
1006
1063
  scene.resetDuration();
1007
1064
  viewLoaded = false;
@@ -70,6 +70,16 @@ Texture.prototype.loadFromImages = async function (srcArrays) {
70
70
  return
71
71
  }
72
72
 
73
+ /**
74
+ * Free the memory allocated for this texture.
75
+ */
76
+ Texture.prototype.dispose = function () {
77
+ if (this.impl) {
78
+ this.impl.dispose();
79
+ this.impl = undefined;
80
+ }
81
+ }
82
+
73
83
  /**
74
84
  * Return true if the texture is ready for consumption.
75
85
  *
@@ -121,4 +121,13 @@ const TextureArray = function () {
121
121
  }
122
122
 
123
123
  TextureArray.prototype = Object.create(Texture.prototype);
124
+
125
+ /**
126
+ * Free the memory allocated for this texture array.
127
+ */
128
+ TextureArray.prototype.dispose = function () {
129
+ Texture.prototype.dispose.call(this);
130
+ this.imageData = undefined;
131
+ }
132
+
124
133
  export { TextureArray };