Browse Source

VTK header is now parsed and used to extract all the compressed data

Andrii Iudin 8 years ago
parent
commit
f5d9a003a6
1 changed files with 138 additions and 103 deletions
  1. 138 103
      examples/js/loaders/VTKLoader.js

+ 138 - 103
examples/js/loaders/VTKLoader.js

@@ -1,6 +1,12 @@
 /**
  * @author mrdoob / http://mrdoob.com/
  * @author Alex Pletzer
+ * 
+ * Updated on 22.03.2017
+ * VTK header is now parsed and used to extract all the compressed data
+ * @author Andrii Iudin https://github.com/andreyyudin
+ * @author Paul Kibet Korir https://github.com/polarise
+ * @author Sriram Somasundharam https://github.com/raamssundar
  */
 
 THREE.VTKLoader = function( manager ) {
@@ -526,6 +532,28 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 		}
 
+		function Float32Concat(first, second)
+		{
+		    var firstLength = first.length,
+		        result = new Float32Array(firstLength + second.length);
+
+		    result.set(first);
+		    result.set(second, firstLength);
+
+		    return result;
+		}
+
+		function Int32Concat(first, second)
+		{
+		    var firstLength = first.length,
+		        result = new Int32Array(firstLength + second.length);
+
+		    result.set(first);
+		    result.set(second, firstLength);
+
+		    return result;
+		}
+
 		function parseXML( stringFile ) {
 
 			// Changes XML to JSON, based on https://davidwalsh.name/convert-xml-json
@@ -666,132 +694,144 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 			}
 
 			function parseDataArray( ele, compressed ) {
+				var numBytes = 0;
+				if ( json.attributes.header_type == 'UInt64' )
+					numBytes = 8;
+				else if( json.attributes.header_type == 'UInt32' )
+					numBytes = 4;
 
 				// Check the format
+				if ( ele.attributes.format == 'binary' && compressed ) {
+						var rawData, content, byteData, blocks, cSizeStart, headerSize, padding, dataOffsets, currentOffset;
 
-				if ( ele.attributes.format == 'binary' ) {
-
-					if ( compressed ) {
-
-						// Split the blob_header and compressed Data
-						if ( ele[ '#text' ].indexOf( '==' ) != - 1 ) {
-
-							var data = ele[ '#text' ].split( '==' );
-
-							// console.log( data );
+						if ( ele.attributes.type == 'Float32' ) {
+							var txt = new Float32Array( );
+						} else if ( ele.attributes.type === 'Int64' ) {
+							var txt = new Int32Array( );
+						}
 
-							if ( data.length == 2 ) {
+						// VTP data with the header has the following structure:
+						// [#blocks][#u-size][#p-size][#c-size-1][#c-size-2]...[#c-size-#blocks][DATA]
+						//
+						// Each token is an integer value whose type is specified by "header_type" at the top of the file (UInt32 if no type specified). The token meanings are:
+						// [#blocks] = Number of blocks
+						// [#u-size] = Block size before compression
+						// [#p-size] = Size of last partial block (zero if it not needed)
+						// [#c-size-i] = Size in bytes of block i after compression
+						//
+						// The [DATA] portion stores contiguously every block appended together. The offset from the beginning of the data section to the beginning of a block is 
+						// computed by summing the compressed block sizes from preceding blocks according to the header. 
+
+						rawData = ele[ '#text' ];
+
+						byteData = Base64toByteArray( rawData );
+
+						blocks = byteData[0]
+						for ( var i = 1; i<numBytes-1; i++ ) {
+							blocks = blocks | ( byteData[i] << (i*numBytes) );
+						}
 
-								var blob = data.shift();
-								var content = data.shift();
+						headerSize = (blocks + 3) * numBytes;
+						padding = ( (headerSize % 3) > 0 ) ? 3 - (headerSize % 3) : 0;
+						headerSize = headerSize + padding;
+						
+						dataOffsets = [];
+						currentOffset = headerSize;
+						dataOffsets.push( currentOffset );
+
+						// Get the blocks sizes after the compression.
+						// There are three blocks before c-size-i, so we skip 3*numBytes
+						cSizeStart = 3*numBytes;
+						for ( var i = 0; i<blocks; i++ ) {
+							var currentBlockSize = byteData[i*numBytes + cSizeStart];
+							for ( var j = 1; j<numBytes-1; j++ ) {
+								currentBlockSize = currentBlockSize | ( byteData[i*numBytes + cSizeStart + j] << (j*numBytes) );
+							}
+							
+							currentOffset = currentOffset + currentBlockSize;
+							dataOffsets.push( currentOffset );
+						}
 
-								if ( content === '' ) {
+						for ( var i=0; i<dataOffsets.length-1; i++ ) {
+							var inflate = new Zlib.Inflate( byteData.slice( dataOffsets[i], dataOffsets[i+1] ), { resize: true, verify: true } );
+							content = inflate.decompress();
+							content = content.buffer;
+
+							if ( ele.attributes.type == 'Float32' ) {
+								content = new Float32Array( content );
+								txt = Float32Concat(txt, content);
+							} else if ( ele.attributes.type === 'Int64' ) {
+								content = new Int32Array( content );
+								txt = Int32Concat(txt, content);
+							}
+						}
 
-									content = blob + '==';
+						delete ele[ '#text' ];
 
+						// Get the content and optimize it
+						if ( ele.attributes.type == 'Float32' ) {
+							if ( ele.attributes.format == 'binary' ) {
+								if ( ! compressed ) {
+									txt = txt.filter( function( el, idx, arr ) {
+										if ( idx !== 0 ) return true;
+									} );
 								}
-
-							} else if ( data.length > 2 ) {
-
-								var blob = data.shift();
-								var content = data.shift();
-								content = content + '==';
-
-							} else if ( data.length < 2 ) {
-
-								var content = data.shift();
-								content = content + '==';
-
 							}
 
-							// Convert to bytearray
-							var arr = Base64toByteArray( content );
-
-							// decompress
-							var inflate = new Zlib.Inflate( arr, { resize: true, verify: true } );
-							var content = inflate.decompress();
-
-						} else {
-
-							var content = Base64toByteArray( ele[ '#text' ] );
+						} else if ( ele.attributes.type === 'Int64' ) {
+							if ( ele.attributes.format == 'binary' ) {
+								if ( ! compressed ) {
+									txt = txt.filter( function ( el, idx, arr ) {
+										if ( idx !== 0 ) return true;
+									} );
+								}
 
+								txt = txt.filter( function ( el, idx, arr ) {
+									if ( idx % 2 !== 1 ) return true;
+								} );
+							}
 						}
 
-					} else {
-
-						var content = Base64toByteArray( ele[ '#text' ] );
-
-					}
-
-					var content = content.buffer;
-
 				} else {
+					if ( ele.attributes.format == 'binary' && ! compressed ) {
 
-					if ( ele[ '#text' ] ) {
-
-						var content = ele[ '#text' ].replace( /\n/g, ' ' ).split( ' ' ).filter( function ( el, idx, arr ) {
-
-							if ( el !== '' ) return el;
+						var content = Base64toByteArray( ele[ '#text' ] );
 
-						} );
+						//  VTP data for the uncompressed case has the following structure:
+						// [#bytes][DATA]
+						// where "[#bytes]" is an integer value specifying the number of bytes in the block of data following it.
+						content = content.slice(numBytes).buffer;
 
 					} else {
 
-						var content = new Int32Array( 0 ).buffer;
-
-					}
-
-				}
-
-				delete ele[ '#text' ];
-
-				// Get the content and optimize it
-
-				if ( ele.attributes.type == 'Float32' ) {
-
-					var txt = new Float32Array( content );
-
-					if ( ele.attributes.format == 'binary' ) {
-
-						if ( ! compressed ) {
-
-							txt = txt.filter( function( el, idx, arr ) {
-
-								if ( idx !== 0 ) return true;
-
+						if ( ele[ '#text' ] ) {
+							var content = ele[ '#text' ].replace( /\n/g, ' ' ).split( ' ' ).filter( function ( el, idx, arr ) {
+								if ( el !== '' ) return el;
 							} );
-
+						} else {
+							var content = new Int32Array( 0 ).buffer;
 						}
 
 					}
 
-				} else if ( ele.attributes.type === 'Int64' ) {
+					delete ele[ '#text' ];
 
-					var txt = new Int32Array( content );
+					// Get the content and optimize it
+					if ( ele.attributes.type == 'Float32' ) {
 
-					if ( ele.attributes.format == 'binary' ) {
+						var txt = new Float32Array( content );
 
-						if ( ! compressed ) {
+					} else if ( ele.attributes.type === 'Int64' ) {
+						var txt = new Int32Array( content );
 
+						if ( ele.attributes.format == 'binary' ) {
 							txt = txt.filter( function ( el, idx, arr ) {
-
-								if ( idx !== 0 ) return true;
-
+								if ( idx % 2 !== 1 ) return true;
 							} );
-
 						}
-
-						txt = txt.filter( function ( el, idx, arr ) {
-
-							if ( idx % 2 !== 1 ) return true;
-
-						} );
-
 					}
 
-				}
-
-				// console.log( txt );
+				} // endif ( ele.attributes.format == 'binary' && compressed )
 
 				return txt;
 
@@ -881,8 +921,11 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 						while ( dataArrayIndex < numberOfDataArrays ) {
 
 							// Parse the DataArray
-							arr[ dataArrayIndex ].text = parseDataArray( arr[ dataArrayIndex ], compressed );
-							dataArrayIndex ++;
+							if ( ('#text' in arr[ dataArrayIndex ]) && (arr[ dataArrayIndex ][ '#text' ].length > 0) )
+							{
+								arr[ dataArrayIndex ].text = parseDataArray( arr[ dataArrayIndex ], compressed );
+							}
+								dataArrayIndex ++;
 
 						}
 
@@ -910,8 +953,6 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 								}
 
-								// console.log('Normals', normals);
-
 								break;
 
 							// if it is points
@@ -927,8 +968,6 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 								}
 
-								// console.log('Points', points);
-
 								break;
 
 							// if it is strips
@@ -984,8 +1023,6 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 								}
 
-								//console.log('Strips', indices);
-
 								break;
 
 							// if it is polys
@@ -1034,7 +1071,7 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 									}
 
 								}
-								//console.log('Polys', indices);
+
 								break;
 
 							default:
@@ -1058,8 +1095,6 @@ Object.assign( THREE.VTKLoader.prototype, THREE.EventDispatcher.prototype, {
 
 				}
 
-				// console.log( json );
-
 				return geometry;
 
 			} else {