zincjs 1.9.0 → 1.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zincjs",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "ZincJS (Web-based-Zinc-Visualisation)",
5
5
  "main": "build/zinc.js",
6
6
  "directories": {
@@ -1,4 +1,137 @@
1
1
  const JSONLoader = require('./JSONLoader').JSONLoader;
2
+ const THREE = require('three');
3
+ const FileLoader = THREE.FileLoader;
4
+
5
+ const mergeGeometries = (geometries) => {
6
+ const merge = (geometry1, geometry2) => {
7
+ geometry1.merge(geometry2);
8
+ }
9
+
10
+ if (geometries && geometries.length > 0) {
11
+ while (geometries.length > 1) {
12
+ const geometry2 = geometries.splice(1,1);
13
+ merge(geometries[0], geometry2[0]);
14
+ }
15
+ return geometries[0];
16
+ }
17
+ return undefined;
18
+ }
19
+
20
+ const IndexedSourcesHandler = function(urlIn, crossOrigin, onDownloadedCallback) {
21
+ const allData = [];
22
+ const loader = new FileLoader();
23
+ const jsonLoader = new JSONLoader();
24
+ loader.crossOrigin = crossOrigin;
25
+ const url = urlIn;
26
+ const onDownloaded = onDownloadedCallback;
27
+ let data = undefined;
28
+ let downloading = false;
29
+ let finished = false;
30
+ let error = undefined;
31
+ const items = [];
32
+
33
+ const processItemDownloaded = (item) => {
34
+ const modelData = data[item.index];
35
+ if (modelData) {
36
+ let obj = jsonLoader.parse( modelData );
37
+ item.onLoad(obj.geometry, obj.materials);
38
+ } else {
39
+ processItemError(item, {responseURL: url});
40
+ }
41
+ }
42
+
43
+ const processItemError = (item) => {
44
+ if (item.onError) {
45
+ if (!error) {
46
+ error = {responseURL: url};
47
+ }
48
+ item.onError(error);
49
+ }
50
+ }
51
+
52
+ this.downloadCompleted = (args) => {
53
+ try {
54
+ data = JSON.parse(args[0]);
55
+ downloading = false;
56
+ finished = true;
57
+ if (Array.isArray(data)) {
58
+ items.forEach(item => processItemDownloaded(item));
59
+ } else {
60
+ items.forEach(item => processItemError(item));
61
+ }
62
+ } catch {
63
+ items.forEach(item => processItemError(item));
64
+ }
65
+ }
66
+
67
+ const errorHandling = () => {
68
+ return xhr => {
69
+ error = xhr;
70
+ finished = true;
71
+ downloading = false;
72
+ items.forEach((item) => {
73
+ processItemError(item);
74
+ });
75
+ }
76
+ }
77
+
78
+ const progressHandling = () => {
79
+ return xhr => {
80
+ items.forEach((item) => {
81
+ if (item.onProgress) {
82
+ item.onProgress(xhr);
83
+ }
84
+ });
85
+ }
86
+ }
87
+
88
+ this.load = (index, onLoad, onProgress, onError) => {
89
+ const item = {
90
+ index,
91
+ onLoad,
92
+ onProgress,
93
+ onError,
94
+ };
95
+ if (finished) {
96
+ if (data) {
97
+ processItemDownloaded(item);
98
+ } else {
99
+ processItemError(error);
100
+ }
101
+ } else if (downloading) {
102
+ //quene it up
103
+ items.push(item);
104
+ } else {
105
+ items.push(item);
106
+ downloading = true;
107
+ loader.load(url, onDownloaded, progressHandling, errorHandling);
108
+ }
109
+ }
110
+ }
111
+
112
+ const MultiSourcesHandler = function(numberIn, onLoadCallback) {
113
+ const allData = [];
114
+ const number = numberIn;
115
+ const onLoad = onLoadCallback;
116
+ let totalDownloaded = 0;
117
+
118
+ this.itemDownloaded = (order, args) => {
119
+ allData[order]= args;
120
+ totalDownloaded++;
121
+ if (totalDownloaded == number) {
122
+ const materials = allData[0][1];
123
+ const geometries = allData.map((data) => data[0]);
124
+ //All geometries will be merged into the first one
125
+ const geometry = mergeGeometries(geometries);
126
+ for (let i = 1; i < number; i++) {
127
+ allData[order][0].dispose();
128
+ allData[order][1].forEach((material) => material.dispose());
129
+ }
130
+ onLoad(geometry, materials);
131
+ }
132
+ }
133
+
134
+ }
2
135
 
3
136
  exports.PrimitivesLoader = function () {
4
137
  let concurrentDownloads = 0;
@@ -6,41 +139,97 @@ exports.PrimitivesLoader = function () {
6
139
  this.crossOrigin = "Anonymous";
7
140
  const loader = new JSONLoader();
8
141
  const waitingList = [];
142
+ //URL to loader pair
143
+ const indexedLoaders = {};
9
144
 
10
- this.load = (url, onLoad, onProgress, onError) => {
11
- if (MAX_DOWNLOAD > concurrentDownloads) {
145
+ //Load the first file then the rest will be handled separately
146
+ const loadFromMultipleSources = (urls, onLoad, onProgress, onError, options) => {
147
+ const number = urls.length;
148
+ const msHandler = new MultiSourcesHandler(number, onLoad);
149
+ //The order here will give us hint on the sequence on merging the primitives
150
+ let order = 0;
151
+ urls.forEach((url) => {
152
+ const newOptions = options ? {...options} : {};
153
+ newOptions.msHandler = msHandler;
154
+ newOptions.order = order;
155
+ order++;
156
+ loadFromSingleSource(url, onLoad, onProgress, onError, newOptions);
157
+ });
158
+ }
159
+
160
+ const handleIndexedSource = (url, onLoad, onProgress, onError, options) => {
161
+ const newOptions = options ? {...options} : {};
162
+ let indexedLoader = indexedLoaders[url];
163
+ if (!indexedLoader) {
164
+ const onLoadCallback = new onFinally(undefined, this, newOptions);
12
165
  ++concurrentDownloads;
13
- loader.crossOrigin = this.crossOrigin;
14
- const onLoadCallback = new onFinally(onLoad, this);
15
- const onErrorCallback = new onFinally(onError, this);
16
- loader.load(url, onLoadCallback, onProgress, onErrorCallback);
166
+ indexedLoader = new IndexedSourcesHandler(url, this.crossOrigin, onLoadCallback);
167
+ indexedLoaders[url] = indexedLoader;
168
+ }
169
+ newOptions.isHandler = indexedLoader;
170
+ indexedLoader.load(options.index, onLoad, onProgress, onError);
171
+ }
172
+
173
+ const loadFromSingleSource = (url, onLoad, onProgress, onError, options) => {
174
+ if (MAX_DOWNLOAD > concurrentDownloads) {
175
+ if (options && (options.index !== undefined) ) {
176
+ handleIndexedSource(url, onLoad, onProgress, onError, options);
177
+ } else {
178
+ //Standard loading
179
+ ++concurrentDownloads;
180
+ const onLoadCallback = new onFinally(onLoad, this, options);
181
+ const onErrorCallback = new onFinally(onError, this, options);
182
+ loader.crossOrigin = this.crossOrigin;
183
+ loader.load(url, onLoadCallback, onProgress, onErrorCallback);
184
+ }
17
185
  } else {
18
186
  waitingList.push({
19
187
  url,
20
188
  onLoad,
21
189
  onProgress,
22
- onError
190
+ onError,
191
+ options,
23
192
  });
24
193
  }
25
194
  }
26
195
 
196
+ this.load = (url, onLoad, onProgress, onError, options) => {
197
+ if (Array.isArray(url)) {
198
+ loadFromMultipleSources(url, onLoad, onProgress, onError, options);
199
+ } else {
200
+ loadFromSingleSource(url, onLoad, onProgress, onError, options);
201
+ }
202
+ }
203
+
27
204
  this.loadFromWaitingList = () => {
28
205
  const item = waitingList.shift();
29
- if (item)
30
- this.load(item.url, item.onLoad, item.onProgress, item.onError);
206
+ if (item) {
207
+ this.load(item.url, item.onLoad, item.onProgress, item.onError, item.options);
208
+ } else {
209
+ for (let key in indexedLoaders) {
210
+ if (indexedLoaders.hasOwnProperty(key)) {
211
+ delete indexedLoaders[key];
212
+ }
213
+ }
214
+ }
31
215
  }
32
216
 
33
- const onFinally = function(callback, loader) {
217
+ const onFinally = function(callback, loader, options) {
34
218
  return (...args) => {
35
219
  --concurrentDownloads;
36
- if (callback) {
37
- callback(...args);
220
+ if (options?.msHandler) {
221
+ options.msHandler.itemDownloaded(options.order, args);
222
+ } else if (options?.isHandler) {
223
+ options.isHandler.downloadCompleted(args);
224
+ } else {
225
+ if (callback) {
226
+ callback(...args);
227
+ }
38
228
  }
39
229
  loader.loadFromWaitingList();
40
230
  }
41
231
  }
42
232
 
43
-
44
233
  this.parse = data => {
45
234
  return loader.parse(data);
46
235
  }
@@ -71,7 +71,7 @@ const LOD = function (parent) {
71
71
  }
72
72
  }
73
73
 
74
- this.addLevelFromURL = (loader, level, url, preload) => {
74
+ this.addLevelFromURL = (loader, level, url, index, preload) => {
75
75
  this._loader = loader;
76
76
  const distance = this.calculateDistance(level);
77
77
  const levelObject = {
@@ -80,6 +80,7 @@ const LOD = function (parent) {
80
80
  loaded: false,
81
81
  loading: false,
82
82
  url: url,
83
+ index: index,
83
84
  };
84
85
  let l;
85
86
  for (l = 0; l < this.levels.length; l++) {
@@ -100,7 +101,7 @@ const LOD = function (parent) {
100
101
  !level.loading) {
101
102
  level.loading = true;
102
103
  this._loader.load(level.url, this.lodLoader(level.distance),
103
- undefined, undefined);
104
+ undefined, undefined, {index: level.index});
104
105
  }
105
106
  return (level.morph === undefined);
106
107
  }
@@ -1,5 +1,6 @@
1
1
  const THREE = require('three');
2
2
  const createBufferGeometry = require('../utilities').createBufferGeometry;
3
+ const resolveURL = require('../utilities').resolveURL;
3
4
 
4
5
  let uniqueiId = 0;
5
6
 
@@ -664,12 +665,11 @@ ZincObject.prototype.render = function(delta, playAnimation,
664
665
  this.updateMarker(playAnimation, options);
665
666
  }
666
667
 
667
-
668
668
  /**
669
669
  * Add lod from an url into the lod object.
670
670
  */
671
- ZincObject.prototype.addLOD = function(loader, level, url, preload) {
672
- this._lod.addLevelFromURL(loader, level, url, preload);
671
+ ZincObject.prototype.addLOD = function(loader, level, url, index, preload) {
672
+ this._lod.addLevelFromURL(loader, level, url, index, preload);
673
673
  }
674
674
 
675
675
  /**
@@ -679,7 +679,7 @@ ZincObject.prototype.addVertices = function(coords) {
679
679
  let mesh = this.getMorph();
680
680
  let geometry = undefined;
681
681
  if (!mesh) {
682
- geometry = createBufferGeometry((coords.length + 500), coords);
682
+ geometry = createBufferGeometry(500, coords);
683
683
  this.drawRange = coords.length;
684
684
  } else {
685
685
  if (this.drawRange > -1) {
@@ -713,6 +713,11 @@ ZincObject.prototype.setPosition = function(x, y, z) {
713
713
  }
714
714
  }
715
715
 
716
+ ZincObject.prototype.loadAdditionalSources = function(primitivesLoader, sources) {
717
+ primitivesLoader.load(resolveURL(filename), meshloader(region, colour, opacity, localTimeEnabled, localMorphColour, undefined, undefined,
718
+ undefined, undefined, finishCallback), this.onProgress(filename), this.onError(finishCallback));
719
+ }
720
+
716
721
  /**
717
722
  * Set the objects scale.
718
723
  *
@@ -59,6 +59,7 @@ exports.SceneLoader = function (sceneIn) {
59
59
  return xhr => {
60
60
  this.toBeDownloaded = this.toBeDownloaded - 1;
61
61
  errorDownload = true;
62
+ console.error(`There is an issue with one of the external resource: ${xhr?.responseURL}.`);
62
63
  if (finishCallback) {
63
64
  finishCallback();
64
65
  }
@@ -182,7 +183,8 @@ exports.SceneLoader = function (sceneIn) {
182
183
  }
183
184
 
184
185
  //Internal loader for a regular zinc geometry.
185
- const linesloader = (region, localTimeEnabled, localMorphColour, groupName, anatomicalId, renderOrder, lod, finishCallback) => {
186
+ const linesloader = (region, localTimeEnabled, localMorphColour, groupName,
187
+ anatomicalId, renderOrder, lod, finishCallback) => {
186
188
  return (geometry, materials) => {
187
189
  const newLines = new (require('./primitives/lines').Lines)();
188
190
  let material = undefined;
@@ -198,7 +200,6 @@ exports.SceneLoader = function (sceneIn) {
198
200
  let options = {};
199
201
  options.localTimeEnabled = localTimeEnabled;
200
202
  options.localMorphColour = localMorphColour;
201
-
202
203
  if (newLines) {
203
204
  newLines.createLineSegment(geometry, material, options);
204
205
  newLines.setName(groupName);
@@ -206,9 +207,11 @@ exports.SceneLoader = function (sceneIn) {
206
207
  newLines.setRenderOrder(renderOrder);
207
208
  region.addZincObject(newLines);
208
209
  newLines.setDuration(scene.getDuration());
210
+ console.log(lod)
209
211
  if (lod && lod.levels) {
210
212
  for (const [key, value] of Object.entries(lod.levels)) {
211
- newLines.addLOD(primitivesLoader, key, value.URL, lod.preload);
213
+
214
+ newLines.addLOD(primitivesLoader, key, value.URL, value.Index, lod.preload);
212
215
  }
213
216
  }
214
217
  }
@@ -240,12 +243,13 @@ exports.SceneLoader = function (sceneIn) {
240
243
  if (morphColour != undefined)
241
244
  localMorphColour = morphColour ? true : false;
242
245
  if (isInline) {
243
- var object = primitivesLoader.parse( url );
246
+ let object = primitivesLoader.parse( url );
244
247
  (linesloader(region, localTimeEnabled, localMorphColour, groupName, anatomicalId,
245
248
  renderOrder, options.lod, finishCallback))( object.geometry, object.materials );
246
249
  } else {
247
250
  primitivesLoader.load(url, linesloader(region, localTimeEnabled, localMorphColour, groupName,
248
- anatomicalId, renderOrder, options.lod, finishCallback), this.onProgress(url), this.onError(finishCallback));
251
+ anatomicalId, renderOrder, options.lod, finishCallback), this.onProgress(url), this.onError(finishCallback),
252
+ options.loaderOptions);
249
253
  }
250
254
  }
251
255
 
@@ -389,7 +393,8 @@ exports.SceneLoader = function (sceneIn) {
389
393
  loader = new OBJLoader();
390
394
  loader.crossOrigin = "Anonymous";
391
395
  loader.load(url, objloader(region, colour, opacity, localTimeEnabled,
392
- localMorphColour, groupName, anatomicalId, finishCallback), this.onProgress(url), this.onError);
396
+ localMorphColour, groupName, anatomicalId, finishCallback), this.onProgress(url), this.onError,
397
+ options.loaderOptions);
393
398
  return;
394
399
  }
395
400
  }
@@ -400,7 +405,8 @@ exports.SceneLoader = function (sceneIn) {
400
405
  } else {
401
406
  loader.crossOrigin = "Anonymous";
402
407
  primitivesLoader.load(url, meshloader(region, colour, opacity, localTimeEnabled,
403
- localMorphColour, groupName, anatomicalId, renderOrder, options, finishCallback), this.onProgress(url), this.onError(finishCallback));
408
+ localMorphColour, groupName, anatomicalId, renderOrder, options, finishCallback),
409
+ this.onProgress(url), this.onError(finishCallback), options.loaderOptions);
404
410
  }
405
411
  };
406
412
 
@@ -454,7 +460,7 @@ exports.SceneLoader = function (sceneIn) {
454
460
  } else {
455
461
  primitivesLoader.load(url, pointsetloader(region, localTimeEnabled, localMorphColour,
456
462
  groupName, anatomicalId, renderOrder, finishCallback),
457
- this.onProgress(url), this.onError(finishCallback));
463
+ this.onProgress(url), this.onError(finishCallback), options.loaderOptions);
458
464
  }
459
465
  }
460
466
 
@@ -613,7 +619,7 @@ exports.SceneLoader = function (sceneIn) {
613
619
  zincGeometry.setRenderOrder(renderOrder);
614
620
  if (options.lod && options.lod.levels) {
615
621
  for (const [key, value] of Object.entries(options.lod.levels)) {
616
- zincGeometry.addLOD(primitivesLoader, key, value.URL, options.lod.preload);
622
+ zincGeometry.addLOD(primitivesLoader, key, value.URL, value.Index, options.lod.preload);
617
623
  }
618
624
  }
619
625
  --this.toBeDownloaded;
@@ -660,9 +666,10 @@ exports.SceneLoader = function (sceneIn) {
660
666
  let newURL = undefined;
661
667
  let isInline = false;
662
668
  if (item.URL) {
669
+ //Convert it into an array
663
670
  newURL = item.URL;
664
671
  if (referenceURL)
665
- newURL = createNewURL(item.URL, referenceURL);
672
+ newURL = createNewURL(newURL, referenceURL);
666
673
  } else if (item.Inline) {
667
674
  newURL = item.Inline.URL;
668
675
  isInline = true;
@@ -674,6 +681,7 @@ exports.SceneLoader = function (sceneIn) {
674
681
  for (const [key, value] of Object.entries(item.LOD.Levels)) {
675
682
  lod.levels[key] = {};
676
683
  lod.levels[key]["URL"] = createNewURL(value.URL, referenceURL);
684
+ lod.levels[key]["Index"] = value.Index;
677
685
  }
678
686
  }
679
687
  let groupName = item.GroupName;
@@ -682,6 +690,9 @@ exports.SceneLoader = function (sceneIn) {
682
690
  }
683
691
 
684
692
  let options = {
693
+ loaderOptions: {
694
+ index: item.Index,
695
+ },
685
696
  isInline: isInline,
686
697
  fileFormat: item.FileFormat,
687
698
  anatomicalId: item.AnatomicalId,
@@ -17,6 +17,7 @@ import {
17
17
  const _m1 = new Matrix4();
18
18
  const _obj = new Object3D();
19
19
  const _offset = new Vector3();
20
+ const _temp = new Vector3();
20
21
 
21
22
  function Geometry() {
22
23
 
@@ -648,6 +649,71 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
648
649
 
649
650
  },
650
651
 
652
+ mergeMorph: function ( geometry, matrix ) {
653
+
654
+ const m = this.morphTargets.length,
655
+ morphTargets1 = this.morphTargets,
656
+ morphTargets2 = geometry.morphTargets,
657
+ n = this.morphColors.length,
658
+ morphColors1 = this.morphColors,
659
+ morphColors2 = geometry.morphColors;
660
+
661
+ if ( m > 0 && m == morphTargets2.length) {
662
+
663
+ for ( let i = 0, l = morphTargets1.length; i < l; i ++ ) {
664
+
665
+ const morphTarget1 = morphTargets1[ i ];
666
+ const morphTarget2 = morphTargets2[ i ];
667
+
668
+ for ( let k = 0, kl = morphTarget2.vertices.length; k < kl; k ++ ) {
669
+
670
+ const vertex = morphTarget2.vertices[ k ];
671
+
672
+ const vertexCopy = vertex.clone();
673
+
674
+ if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix );
675
+
676
+ morphTarget1.vertices.push( vertexCopy );
677
+
678
+ }
679
+
680
+ if ( morphTarget1.normals && morphTarget2.normals ) {
681
+
682
+ for ( let k = 0; k < morphTarget2.normals.length; k = k + 3) {
683
+
684
+ _temp.set(morphTarget2.normals2[k], morphTarget2.normals2[k + 1], morphTarget2.normals2[k + 2]);
685
+
686
+ if ( matrix !== undefined ) _temp.applyMatrix4( matrix );
687
+
688
+ morphTarget1.normals.push(_temp.x, _temp.y, _temp.z);
689
+
690
+ }
691
+
692
+ }
693
+
694
+ }
695
+
696
+ }
697
+
698
+ if ( n > 0 && n == morphColors2.length) {
699
+
700
+ for ( let i = 0, l = morphColors1.length; i < l; i ++ ) {
701
+
702
+ const morphColor1 = morphColors1[ i ];
703
+ const morphColor2 = morphColors2[ i ];
704
+
705
+ for ( let k = 0, kl = morphColor2.colors; k < kl; k ++ ) {
706
+
707
+ morphColor1.colors.push( morphColor2.colors[ k ].clone() );
708
+
709
+ }
710
+
711
+ }
712
+
713
+ }
714
+
715
+ },
716
+
651
717
  merge: function ( geometry, matrix, materialIndexOffset = 0 ) {
652
718
 
653
719
  if ( ! ( geometry && geometry.isGeometry ) ) {
@@ -661,6 +727,7 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
661
727
  const vertexOffset = this.vertices.length,
662
728
  vertices1 = this.vertices,
663
729
  vertices2 = geometry.vertices,
730
+ normals2 = geometry.normals,
664
731
  faces1 = this.faces,
665
732
  faces2 = geometry.faces,
666
733
  colors1 = this.colors,
@@ -686,6 +753,16 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
686
753
 
687
754
  }
688
755
 
756
+ for ( let i = 0; i < normals2.length; i = i + 3) {
757
+
758
+ _temp.set(normals2[i], normals2[i + 1], normals2[i + 2]);
759
+
760
+ if ( matrix !== undefined ) _temp.applyMatrix4( matrix );
761
+
762
+ this.normals.push(_temp.x, _temp.y, _temp.z);
763
+
764
+ }
765
+
689
766
  // colors
690
767
 
691
768
  for ( let i = 0, il = colors2.length; i < il; i ++ ) {
@@ -765,6 +842,8 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
765
842
 
766
843
  }
767
844
 
845
+ this.mergeMorph( geometry, matrix );
846
+
768
847
  },
769
848
 
770
849
  mergeMesh: function ( mesh ) {
package/src/utilities.js CHANGED
@@ -19,16 +19,31 @@ function resolveURL(url) {
19
19
  }
20
20
 
21
21
  function createNewURL(target, reference) {
22
- let newURL = (new URL(target, reference)).href;
23
- //Make sure the target url does not contain parameters
24
- if (target && target.split("?").length < 2) {
25
- const paramsStrings = reference.split("?");
26
- //There are parameters, add them to the target
27
- if (paramsStrings.length === 2) {
28
- newURL = newURL + "?" + paramsStrings[1];
22
+ const getNewURL = (target, reference) => {
23
+ try {
24
+ let newURL = (new URL(target, reference)).href;
25
+ //Make sure the target url does not contain parameters
26
+ if (target && target.split("?").length < 2) {
27
+ const paramsStrings = reference.split("?");
28
+ //There are parameters, add them to the target
29
+ if (paramsStrings.length === 2) {
30
+ newURL = newURL + "?" + paramsStrings[1];
31
+ }
32
+ }
33
+ return newURL;
34
+ } catch {
35
+ console.error(`There is an issue creting the url link with: ${target}.` );
29
36
  }
30
37
  }
31
- return newURL;
38
+ if (!Array.isArray(target)) {
39
+ return getNewURL(target, reference);
40
+ } else {
41
+ const urls = [];
42
+ target.forEach((url) => {
43
+ urls.push(getNewURL(url, reference));
44
+ });
45
+ return urls;
46
+ }
32
47
  }
33
48
 
34
49
  /*