VTKLoader.js 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. ( function () {
  2. class VTKLoader extends THREE.Loader {
  3. constructor( manager ) {
  4. super( manager );
  5. }
  6. load( url, onLoad, onProgress, onError ) {
  7. const scope = this;
  8. const loader = new THREE.FileLoader( scope.manager );
  9. loader.setPath( scope.path );
  10. loader.setResponseType( 'arraybuffer' );
  11. loader.setRequestHeader( scope.requestHeader );
  12. loader.setWithCredentials( scope.withCredentials );
  13. loader.load( url, function ( text ) {
  14. try {
  15. onLoad( scope.parse( text ) );
  16. } catch ( e ) {
  17. if ( onError ) {
  18. onError( e );
  19. } else {
  20. console.error( e );
  21. }
  22. scope.manager.itemError( url );
  23. }
  24. }, onProgress, onError );
  25. }
  26. parse( data ) {
  27. function parseASCII( data ) {
  28. // connectivity of the triangles
  29. const indices = [];
  30. // triangles vertices
  31. const positions = [];
  32. // red, green, blue colors in the range 0 to 1
  33. const colors = [];
  34. // normal vector, one per vertex
  35. const normals = [];
  36. let result;
  37. // pattern for detecting the end of a number sequence
  38. const patWord = /^[^\d.\s-]+/;
  39. // pattern for reading vertices, 3 floats or integers
  40. const pat3Floats = /(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)/g;
  41. // pattern for connectivity, an integer followed by any number of ints
  42. // the first integer is the number of polygon nodes
  43. const patConnectivity = /^(\d+)\s+([\s\d]*)/;
  44. // indicates start of vertex data section
  45. const patPOINTS = /^POINTS /;
  46. // indicates start of polygon connectivity section
  47. const patPOLYGONS = /^POLYGONS /;
  48. // indicates start of triangle strips section
  49. const patTRIANGLE_STRIPS = /^TRIANGLE_STRIPS /;
  50. // POINT_DATA number_of_values
  51. const patPOINT_DATA = /^POINT_DATA[ ]+(\d+)/;
  52. // CELL_DATA number_of_polys
  53. const patCELL_DATA = /^CELL_DATA[ ]+(\d+)/;
  54. // Start of color section
  55. const patCOLOR_SCALARS = /^COLOR_SCALARS[ ]+(\w+)[ ]+3/;
  56. // NORMALS Normals float
  57. const patNORMALS = /^NORMALS[ ]+(\w+)[ ]+(\w+)/;
  58. let inPointsSection = false;
  59. let inPolygonsSection = false;
  60. let inTriangleStripSection = false;
  61. let inPointDataSection = false;
  62. let inCellDataSection = false;
  63. let inColorSection = false;
  64. let inNormalsSection = false;
  65. const lines = data.split( '\n' );
  66. for ( const i in lines ) {
  67. const line = lines[ i ].trim();
  68. if ( line.indexOf( 'DATASET' ) === 0 ) {
  69. const dataset = line.split( ' ' )[ 1 ];
  70. if ( dataset !== 'POLYDATA' ) throw new Error( 'Unsupported DATASET type: ' + dataset );
  71. } else if ( inPointsSection ) {
  72. // get the vertices
  73. while ( ( result = pat3Floats.exec( line ) ) !== null ) {
  74. if ( patWord.exec( line ) !== null ) break;
  75. const x = parseFloat( result[ 1 ] );
  76. const y = parseFloat( result[ 2 ] );
  77. const z = parseFloat( result[ 3 ] );
  78. positions.push( x, y, z );
  79. }
  80. } else if ( inPolygonsSection ) {
  81. if ( ( result = patConnectivity.exec( line ) ) !== null ) {
  82. // numVertices i0 i1 i2 ...
  83. const numVertices = parseInt( result[ 1 ] );
  84. const inds = result[ 2 ].split( /\s+/ );
  85. if ( numVertices >= 3 ) {
  86. const i0 = parseInt( inds[ 0 ] );
  87. let k = 1;
  88. // split the polygon in numVertices - 2 triangles
  89. for ( let j = 0; j < numVertices - 2; ++ j ) {
  90. const i1 = parseInt( inds[ k ] );
  91. const i2 = parseInt( inds[ k + 1 ] );
  92. indices.push( i0, i1, i2 );
  93. k ++;
  94. }
  95. }
  96. }
  97. } else if ( inTriangleStripSection ) {
  98. if ( ( result = patConnectivity.exec( line ) ) !== null ) {
  99. // numVertices i0 i1 i2 ...
  100. const numVertices = parseInt( result[ 1 ] );
  101. const inds = result[ 2 ].split( /\s+/ );
  102. if ( numVertices >= 3 ) {
  103. // split the polygon in numVertices - 2 triangles
  104. for ( let j = 0; j < numVertices - 2; j ++ ) {
  105. if ( j % 2 === 1 ) {
  106. const i0 = parseInt( inds[ j ] );
  107. const i1 = parseInt( inds[ j + 2 ] );
  108. const i2 = parseInt( inds[ j + 1 ] );
  109. indices.push( i0, i1, i2 );
  110. } else {
  111. const i0 = parseInt( inds[ j ] );
  112. const i1 = parseInt( inds[ j + 1 ] );
  113. const i2 = parseInt( inds[ j + 2 ] );
  114. indices.push( i0, i1, i2 );
  115. }
  116. }
  117. }
  118. }
  119. } else if ( inPointDataSection || inCellDataSection ) {
  120. if ( inColorSection ) {
  121. // Get the colors
  122. while ( ( result = pat3Floats.exec( line ) ) !== null ) {
  123. if ( patWord.exec( line ) !== null ) break;
  124. const r = parseFloat( result[ 1 ] );
  125. const g = parseFloat( result[ 2 ] );
  126. const b = parseFloat( result[ 3 ] );
  127. colors.push( r, g, b );
  128. }
  129. } else if ( inNormalsSection ) {
  130. // Get the normal vectors
  131. while ( ( result = pat3Floats.exec( line ) ) !== null ) {
  132. if ( patWord.exec( line ) !== null ) break;
  133. const nx = parseFloat( result[ 1 ] );
  134. const ny = parseFloat( result[ 2 ] );
  135. const nz = parseFloat( result[ 3 ] );
  136. normals.push( nx, ny, nz );
  137. }
  138. }
  139. }
  140. if ( patPOLYGONS.exec( line ) !== null ) {
  141. inPolygonsSection = true;
  142. inPointsSection = false;
  143. inTriangleStripSection = false;
  144. } else if ( patPOINTS.exec( line ) !== null ) {
  145. inPolygonsSection = false;
  146. inPointsSection = true;
  147. inTriangleStripSection = false;
  148. } else if ( patTRIANGLE_STRIPS.exec( line ) !== null ) {
  149. inPolygonsSection = false;
  150. inPointsSection = false;
  151. inTriangleStripSection = true;
  152. } else if ( patPOINT_DATA.exec( line ) !== null ) {
  153. inPointDataSection = true;
  154. inPointsSection = false;
  155. inPolygonsSection = false;
  156. inTriangleStripSection = false;
  157. } else if ( patCELL_DATA.exec( line ) !== null ) {
  158. inCellDataSection = true;
  159. inPointsSection = false;
  160. inPolygonsSection = false;
  161. inTriangleStripSection = false;
  162. } else if ( patCOLOR_SCALARS.exec( line ) !== null ) {
  163. inColorSection = true;
  164. inNormalsSection = false;
  165. inPointsSection = false;
  166. inPolygonsSection = false;
  167. inTriangleStripSection = false;
  168. } else if ( patNORMALS.exec( line ) !== null ) {
  169. inNormalsSection = true;
  170. inColorSection = false;
  171. inPointsSection = false;
  172. inPolygonsSection = false;
  173. inTriangleStripSection = false;
  174. }
  175. }
  176. let geometry = new THREE.BufferGeometry();
  177. geometry.setIndex( indices );
  178. geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
  179. if ( normals.length === positions.length ) {
  180. geometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
  181. }
  182. if ( colors.length !== indices.length ) {
  183. // stagger
  184. if ( colors.length === positions.length ) {
  185. geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
  186. }
  187. } else {
  188. // cell
  189. geometry = geometry.toNonIndexed();
  190. const numTriangles = geometry.attributes.position.count / 3;
  191. if ( colors.length === numTriangles * 3 ) {
  192. const newColors = [];
  193. for ( let i = 0; i < numTriangles; i ++ ) {
  194. const r = colors[ 3 * i + 0 ];
  195. const g = colors[ 3 * i + 1 ];
  196. const b = colors[ 3 * i + 2 ];
  197. newColors.push( r, g, b );
  198. newColors.push( r, g, b );
  199. newColors.push( r, g, b );
  200. }
  201. geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( newColors, 3 ) );
  202. }
  203. }
  204. return geometry;
  205. }
  206. function parseBinary( data ) {
  207. const buffer = new Uint8Array( data );
  208. const dataView = new DataView( data );
  209. // Points and normals, by default, are empty
  210. let points = [];
  211. let normals = [];
  212. let indices = [];
  213. let index = 0;
  214. function findString( buffer, start ) {
  215. let index = start;
  216. let c = buffer[ index ];
  217. const s = [];
  218. while ( c !== 10 ) {
  219. s.push( String.fromCharCode( c ) );
  220. index ++;
  221. c = buffer[ index ];
  222. }
  223. return {
  224. start: start,
  225. end: index,
  226. next: index + 1,
  227. parsedString: s.join( '' )
  228. };
  229. }
  230. let state, line;
  231. while ( true ) {
  232. // Get a string
  233. state = findString( buffer, index );
  234. line = state.parsedString;
  235. if ( line.indexOf( 'DATASET' ) === 0 ) {
  236. const dataset = line.split( ' ' )[ 1 ];
  237. if ( dataset !== 'POLYDATA' ) throw new Error( 'Unsupported DATASET type: ' + dataset );
  238. } else if ( line.indexOf( 'POINTS' ) === 0 ) {
  239. // Add the points
  240. const numberOfPoints = parseInt( line.split( ' ' )[ 1 ], 10 );
  241. // Each point is 3 4-byte floats
  242. const count = numberOfPoints * 4 * 3;
  243. points = new Float32Array( numberOfPoints * 3 );
  244. let pointIndex = state.next;
  245. for ( let i = 0; i < numberOfPoints; i ++ ) {
  246. points[ 3 * i ] = dataView.getFloat32( pointIndex, false );
  247. points[ 3 * i + 1 ] = dataView.getFloat32( pointIndex + 4, false );
  248. points[ 3 * i + 2 ] = dataView.getFloat32( pointIndex + 8, false );
  249. pointIndex = pointIndex + 12;
  250. }
  251. // increment our next pointer
  252. state.next = state.next + count + 1;
  253. } else if ( line.indexOf( 'TRIANGLE_STRIPS' ) === 0 ) {
  254. const numberOfStrips = parseInt( line.split( ' ' )[ 1 ], 10 );
  255. const size = parseInt( line.split( ' ' )[ 2 ], 10 );
  256. // 4 byte integers
  257. const count = size * 4;
  258. indices = new Uint32Array( 3 * size - 9 * numberOfStrips );
  259. let indicesIndex = 0;
  260. let pointIndex = state.next;
  261. for ( let i = 0; i < numberOfStrips; i ++ ) {
  262. // For each strip, read the first value, then record that many more points
  263. const indexCount = dataView.getInt32( pointIndex, false );
  264. const strip = [];
  265. pointIndex += 4;
  266. for ( let s = 0; s < indexCount; s ++ ) {
  267. strip.push( dataView.getInt32( pointIndex, false ) );
  268. pointIndex += 4;
  269. }
  270. // retrieves the n-2 triangles from the triangle strip
  271. for ( let j = 0; j < indexCount - 2; j ++ ) {
  272. if ( j % 2 ) {
  273. indices[ indicesIndex ++ ] = strip[ j ];
  274. indices[ indicesIndex ++ ] = strip[ j + 2 ];
  275. indices[ indicesIndex ++ ] = strip[ j + 1 ];
  276. } else {
  277. indices[ indicesIndex ++ ] = strip[ j ];
  278. indices[ indicesIndex ++ ] = strip[ j + 1 ];
  279. indices[ indicesIndex ++ ] = strip[ j + 2 ];
  280. }
  281. }
  282. }
  283. // increment our next pointer
  284. state.next = state.next + count + 1;
  285. } else if ( line.indexOf( 'POLYGONS' ) === 0 ) {
  286. const numberOfStrips = parseInt( line.split( ' ' )[ 1 ], 10 );
  287. const size = parseInt( line.split( ' ' )[ 2 ], 10 );
  288. // 4 byte integers
  289. const count = size * 4;
  290. indices = new Uint32Array( 3 * size - 9 * numberOfStrips );
  291. let indicesIndex = 0;
  292. let pointIndex = state.next;
  293. for ( let i = 0; i < numberOfStrips; i ++ ) {
  294. // For each strip, read the first value, then record that many more points
  295. const indexCount = dataView.getInt32( pointIndex, false );
  296. const strip = [];
  297. pointIndex += 4;
  298. for ( let s = 0; s < indexCount; s ++ ) {
  299. strip.push( dataView.getInt32( pointIndex, false ) );
  300. pointIndex += 4;
  301. }
  302. // divide the polygon in n-2 triangle
  303. for ( let j = 1; j < indexCount - 1; j ++ ) {
  304. indices[ indicesIndex ++ ] = strip[ 0 ];
  305. indices[ indicesIndex ++ ] = strip[ j ];
  306. indices[ indicesIndex ++ ] = strip[ j + 1 ];
  307. }
  308. }
  309. // increment our next pointer
  310. state.next = state.next + count + 1;
  311. } else if ( line.indexOf( 'POINT_DATA' ) === 0 ) {
  312. const numberOfPoints = parseInt( line.split( ' ' )[ 1 ], 10 );
  313. // Grab the next line
  314. state = findString( buffer, state.next );
  315. // Now grab the binary data
  316. const count = numberOfPoints * 4 * 3;
  317. normals = new Float32Array( numberOfPoints * 3 );
  318. let pointIndex = state.next;
  319. for ( let i = 0; i < numberOfPoints; i ++ ) {
  320. normals[ 3 * i ] = dataView.getFloat32( pointIndex, false );
  321. normals[ 3 * i + 1 ] = dataView.getFloat32( pointIndex + 4, false );
  322. normals[ 3 * i + 2 ] = dataView.getFloat32( pointIndex + 8, false );
  323. pointIndex += 12;
  324. }
  325. // Increment past our data
  326. state.next = state.next + count;
  327. }
  328. // Increment index
  329. index = state.next;
  330. if ( index >= buffer.byteLength ) {
  331. break;
  332. }
  333. }
  334. const geometry = new THREE.BufferGeometry();
  335. geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
  336. geometry.setAttribute( 'position', new THREE.BufferAttribute( points, 3 ) );
  337. if ( normals.length === points.length ) {
  338. geometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
  339. }
  340. return geometry;
  341. }
  342. function Float32Concat( first, second ) {
  343. const firstLength = first.length,
  344. result = new Float32Array( firstLength + second.length );
  345. result.set( first );
  346. result.set( second, firstLength );
  347. return result;
  348. }
  349. function Int32Concat( first, second ) {
  350. const firstLength = first.length,
  351. result = new Int32Array( firstLength + second.length );
  352. result.set( first );
  353. result.set( second, firstLength );
  354. return result;
  355. }
  356. function parseXML( stringFile ) {
  357. // Changes XML to JSON, based on https://davidwalsh.name/convert-xml-json
  358. function xmlToJson( xml ) {
  359. // Create the return object
  360. let obj = {};
  361. if ( xml.nodeType === 1 ) {
  362. // element
  363. // do attributes
  364. if ( xml.attributes ) {
  365. if ( xml.attributes.length > 0 ) {
  366. obj[ 'attributes' ] = {};
  367. for ( let j = 0; j < xml.attributes.length; j ++ ) {
  368. const attribute = xml.attributes.item( j );
  369. obj[ 'attributes' ][ attribute.nodeName ] = attribute.nodeValue.trim();
  370. }
  371. }
  372. }
  373. } else if ( xml.nodeType === 3 ) {
  374. // text
  375. obj = xml.nodeValue.trim();
  376. }
  377. // do children
  378. if ( xml.hasChildNodes() ) {
  379. for ( let i = 0; i < xml.childNodes.length; i ++ ) {
  380. const item = xml.childNodes.item( i );
  381. const nodeName = item.nodeName;
  382. if ( typeof obj[ nodeName ] === 'undefined' ) {
  383. const tmp = xmlToJson( item );
  384. if ( tmp !== '' ) obj[ nodeName ] = tmp;
  385. } else {
  386. if ( typeof obj[ nodeName ].push === 'undefined' ) {
  387. const old = obj[ nodeName ];
  388. obj[ nodeName ] = [ old ];
  389. }
  390. const tmp = xmlToJson( item );
  391. if ( tmp !== '' ) obj[ nodeName ].push( tmp );
  392. }
  393. }
  394. }
  395. return obj;
  396. }
  397. // Taken from Base64-js
  398. function Base64toByteArray( b64 ) {
  399. const Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array;
  400. const revLookup = [];
  401. const code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  402. for ( let i = 0, l = code.length; i < l; ++ i ) {
  403. revLookup[ code.charCodeAt( i ) ] = i;
  404. }
  405. revLookup[ '-'.charCodeAt( 0 ) ] = 62;
  406. revLookup[ '_'.charCodeAt( 0 ) ] = 63;
  407. const len = b64.length;
  408. if ( len % 4 > 0 ) {
  409. throw new Error( 'Invalid string. Length must be a multiple of 4' );
  410. }
  411. const placeHolders = b64[ len - 2 ] === '=' ? 2 : b64[ len - 1 ] === '=' ? 1 : 0;
  412. const arr = new Arr( len * 3 / 4 - placeHolders );
  413. const l = placeHolders > 0 ? len - 4 : len;
  414. let L = 0;
  415. let i, j;
  416. for ( i = 0, j = 0; i < l; i += 4, j += 3 ) {
  417. const tmp = revLookup[ b64.charCodeAt( i ) ] << 18 | revLookup[ b64.charCodeAt( i + 1 ) ] << 12 | revLookup[ b64.charCodeAt( i + 2 ) ] << 6 | revLookup[ b64.charCodeAt( i + 3 ) ];
  418. arr[ L ++ ] = ( tmp & 0xFF0000 ) >> 16;
  419. arr[ L ++ ] = ( tmp & 0xFF00 ) >> 8;
  420. arr[ L ++ ] = tmp & 0xFF;
  421. }
  422. if ( placeHolders === 2 ) {
  423. const tmp = revLookup[ b64.charCodeAt( i ) ] << 2 | revLookup[ b64.charCodeAt( i + 1 ) ] >> 4;
  424. arr[ L ++ ] = tmp & 0xFF;
  425. } else if ( placeHolders === 1 ) {
  426. const tmp = revLookup[ b64.charCodeAt( i ) ] << 10 | revLookup[ b64.charCodeAt( i + 1 ) ] << 4 | revLookup[ b64.charCodeAt( i + 2 ) ] >> 2;
  427. arr[ L ++ ] = tmp >> 8 & 0xFF;
  428. arr[ L ++ ] = tmp & 0xFF;
  429. }
  430. return arr;
  431. }
  432. function parseDataArray( ele, compressed ) {
  433. let numBytes = 0;
  434. if ( json.attributes.header_type === 'UInt64' ) {
  435. numBytes = 8;
  436. } else if ( json.attributes.header_type === 'UInt32' ) {
  437. numBytes = 4;
  438. }
  439. let txt, content;
  440. // Check the format
  441. if ( ele.attributes.format === 'binary' && compressed ) {
  442. if ( ele.attributes.type === 'Float32' ) {
  443. txt = new Float32Array();
  444. } else if ( ele.attributes.type === 'Int32' || ele.attributes.type === 'Int64' ) {
  445. txt = new Int32Array();
  446. }
  447. // VTP data with the header has the following structure:
  448. // [#blocks][#u-size][#p-size][#c-size-1][#c-size-2]...[#c-size-#blocks][DATA]
  449. //
  450. // 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:
  451. // [#blocks] = Number of blocks
  452. // [#u-size] = Block size before compression
  453. // [#p-size] = Size of last partial block (zero if it not needed)
  454. // [#c-size-i] = Size in bytes of block i after compression
  455. //
  456. // 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
  457. // computed by summing the compressed block sizes from preceding blocks according to the header.
  458. const textNode = ele[ '#text' ];
  459. const rawData = Array.isArray( textNode ) ? textNode[ 0 ] : textNode;
  460. const byteData = Base64toByteArray( rawData );
  461. // Each data point consists of 8 bits regardless of the header type
  462. const dataPointSize = 8;
  463. let blocks = byteData[ 0 ];
  464. for ( let i = 1; i < numBytes - 1; i ++ ) {
  465. blocks = blocks | byteData[ i ] << i * dataPointSize;
  466. }
  467. let headerSize = ( blocks + 3 ) * numBytes;
  468. const padding = headerSize % 3 > 0 ? 3 - headerSize % 3 : 0;
  469. headerSize = headerSize + padding;
  470. const dataOffsets = [];
  471. let currentOffset = headerSize;
  472. dataOffsets.push( currentOffset );
  473. // Get the blocks sizes after the compression.
  474. // There are three blocks before c-size-i, so we skip 3*numBytes
  475. const cSizeStart = 3 * numBytes;
  476. for ( let i = 0; i < blocks; i ++ ) {
  477. let currentBlockSize = byteData[ i * numBytes + cSizeStart ];
  478. for ( let j = 1; j < numBytes - 1; j ++ ) {
  479. currentBlockSize = currentBlockSize | byteData[ i * numBytes + cSizeStart + j ] << j * dataPointSize;
  480. }
  481. currentOffset = currentOffset + currentBlockSize;
  482. dataOffsets.push( currentOffset );
  483. }
  484. for ( let i = 0; i < dataOffsets.length - 1; i ++ ) {
  485. const data = fflate.unzlibSync( byteData.slice( dataOffsets[ i ], dataOffsets[ i + 1 ] ) ); // eslint-disable-line no-undef
  486. content = data.buffer;
  487. if ( ele.attributes.type === 'Float32' ) {
  488. content = new Float32Array( content );
  489. txt = Float32Concat( txt, content );
  490. } else if ( ele.attributes.type === 'Int32' || ele.attributes.type === 'Int64' ) {
  491. content = new Int32Array( content );
  492. txt = Int32Concat( txt, content );
  493. }
  494. }
  495. delete ele[ '#text' ];
  496. if ( ele.attributes.type === 'Int64' ) {
  497. if ( ele.attributes.format === 'binary' ) {
  498. txt = txt.filter( function ( el, idx ) {
  499. if ( idx % 2 !== 1 ) return true;
  500. } );
  501. }
  502. }
  503. } else {
  504. if ( ele.attributes.format === 'binary' && ! compressed ) {
  505. content = Base64toByteArray( ele[ '#text' ] );
  506. // VTP data for the uncompressed case has the following structure:
  507. // [#bytes][DATA]
  508. // where "[#bytes]" is an integer value specifying the number of bytes in the block of data following it.
  509. content = content.slice( numBytes ).buffer;
  510. } else {
  511. if ( ele[ '#text' ] ) {
  512. content = ele[ '#text' ].split( /\s+/ ).filter( function ( el ) {
  513. if ( el !== '' ) return el;
  514. } );
  515. } else {
  516. content = new Int32Array( 0 ).buffer;
  517. }
  518. }
  519. delete ele[ '#text' ];
  520. // Get the content and optimize it
  521. if ( ele.attributes.type === 'Float32' ) {
  522. txt = new Float32Array( content );
  523. } else if ( ele.attributes.type === 'Int32' ) {
  524. txt = new Int32Array( content );
  525. } else if ( ele.attributes.type === 'Int64' ) {
  526. txt = new Int32Array( content );
  527. if ( ele.attributes.format === 'binary' ) {
  528. txt = txt.filter( function ( el, idx ) {
  529. if ( idx % 2 !== 1 ) return true;
  530. } );
  531. }
  532. }
  533. } // endif ( ele.attributes.format === 'binary' && compressed )
  534. return txt;
  535. }
  536. // Main part
  537. // Get Dom
  538. const dom = new DOMParser().parseFromString( stringFile, 'application/xml' );
  539. // Get the doc
  540. const doc = dom.documentElement;
  541. // Convert to json
  542. const json = xmlToJson( doc );
  543. let points = [];
  544. let normals = [];
  545. let indices = [];
  546. if ( json.PolyData ) {
  547. const piece = json.PolyData.Piece;
  548. const compressed = json.attributes.hasOwnProperty( 'compressor' );
  549. // Can be optimized
  550. // Loop through the sections
  551. const sections = [ 'PointData', 'Points', 'Strips', 'Polys' ]; // +['CellData', 'Verts', 'Lines'];
  552. let sectionIndex = 0;
  553. const numberOfSections = sections.length;
  554. while ( sectionIndex < numberOfSections ) {
  555. const section = piece[ sections[ sectionIndex ] ];
  556. // If it has a DataArray in it
  557. if ( section && section.DataArray ) {
  558. // Depending on the number of DataArrays
  559. let arr;
  560. if ( Array.isArray( section.DataArray ) ) {
  561. arr = section.DataArray;
  562. } else {
  563. arr = [ section.DataArray ];
  564. }
  565. let dataArrayIndex = 0;
  566. const numberOfDataArrays = arr.length;
  567. while ( dataArrayIndex < numberOfDataArrays ) {
  568. // Parse the DataArray
  569. if ( '#text' in arr[ dataArrayIndex ] && arr[ dataArrayIndex ][ '#text' ].length > 0 ) {
  570. arr[ dataArrayIndex ].text = parseDataArray( arr[ dataArrayIndex ], compressed );
  571. }
  572. dataArrayIndex ++;
  573. }
  574. switch ( sections[ sectionIndex ] ) {
  575. // if iti is point data
  576. case 'PointData':
  577. {
  578. const numberOfPoints = parseInt( piece.attributes.NumberOfPoints );
  579. const normalsName = section.attributes.Normals;
  580. if ( numberOfPoints > 0 ) {
  581. for ( let i = 0, len = arr.length; i < len; i ++ ) {
  582. if ( normalsName === arr[ i ].attributes.Name ) {
  583. const components = arr[ i ].attributes.NumberOfComponents;
  584. normals = new Float32Array( numberOfPoints * components );
  585. normals.set( arr[ i ].text, 0 );
  586. }
  587. }
  588. }
  589. }
  590. break;
  591. // if it is points
  592. case 'Points':
  593. {
  594. const numberOfPoints = parseInt( piece.attributes.NumberOfPoints );
  595. if ( numberOfPoints > 0 ) {
  596. const components = section.DataArray.attributes.NumberOfComponents;
  597. points = new Float32Array( numberOfPoints * components );
  598. points.set( section.DataArray.text, 0 );
  599. }
  600. }
  601. break;
  602. // if it is strips
  603. case 'Strips':
  604. {
  605. const numberOfStrips = parseInt( piece.attributes.NumberOfStrips );
  606. if ( numberOfStrips > 0 ) {
  607. const connectivity = new Int32Array( section.DataArray[ 0 ].text.length );
  608. const offset = new Int32Array( section.DataArray[ 1 ].text.length );
  609. connectivity.set( section.DataArray[ 0 ].text, 0 );
  610. offset.set( section.DataArray[ 1 ].text, 0 );
  611. const size = numberOfStrips + connectivity.length;
  612. indices = new Uint32Array( 3 * size - 9 * numberOfStrips );
  613. let indicesIndex = 0;
  614. for ( let i = 0, len = numberOfStrips; i < len; i ++ ) {
  615. const strip = [];
  616. for ( let s = 0, len1 = offset[ i ], len0 = 0; s < len1 - len0; s ++ ) {
  617. strip.push( connectivity[ s ] );
  618. if ( i > 0 ) len0 = offset[ i - 1 ];
  619. }
  620. for ( let j = 0, len1 = offset[ i ], len0 = 0; j < len1 - len0 - 2; j ++ ) {
  621. if ( j % 2 ) {
  622. indices[ indicesIndex ++ ] = strip[ j ];
  623. indices[ indicesIndex ++ ] = strip[ j + 2 ];
  624. indices[ indicesIndex ++ ] = strip[ j + 1 ];
  625. } else {
  626. indices[ indicesIndex ++ ] = strip[ j ];
  627. indices[ indicesIndex ++ ] = strip[ j + 1 ];
  628. indices[ indicesIndex ++ ] = strip[ j + 2 ];
  629. }
  630. if ( i > 0 ) len0 = offset[ i - 1 ];
  631. }
  632. }
  633. }
  634. }
  635. break;
  636. // if it is polys
  637. case 'Polys':
  638. {
  639. const numberOfPolys = parseInt( piece.attributes.NumberOfPolys );
  640. if ( numberOfPolys > 0 ) {
  641. const connectivity = new Int32Array( section.DataArray[ 0 ].text.length );
  642. const offset = new Int32Array( section.DataArray[ 1 ].text.length );
  643. connectivity.set( section.DataArray[ 0 ].text, 0 );
  644. offset.set( section.DataArray[ 1 ].text, 0 );
  645. const size = numberOfPolys + connectivity.length;
  646. indices = new Uint32Array( 3 * size - 9 * numberOfPolys );
  647. let indicesIndex = 0,
  648. connectivityIndex = 0;
  649. let i = 0,
  650. len0 = 0;
  651. const len = numberOfPolys;
  652. while ( i < len ) {
  653. const poly = [];
  654. let s = 0;
  655. const len1 = offset[ i ];
  656. while ( s < len1 - len0 ) {
  657. poly.push( connectivity[ connectivityIndex ++ ] );
  658. s ++;
  659. }
  660. let j = 1;
  661. while ( j < len1 - len0 - 1 ) {
  662. indices[ indicesIndex ++ ] = poly[ 0 ];
  663. indices[ indicesIndex ++ ] = poly[ j ];
  664. indices[ indicesIndex ++ ] = poly[ j + 1 ];
  665. j ++;
  666. }
  667. i ++;
  668. len0 = offset[ i - 1 ];
  669. }
  670. }
  671. }
  672. break;
  673. default:
  674. break;
  675. }
  676. }
  677. sectionIndex ++;
  678. }
  679. const geometry = new THREE.BufferGeometry();
  680. geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
  681. geometry.setAttribute( 'position', new THREE.BufferAttribute( points, 3 ) );
  682. if ( normals.length === points.length ) {
  683. geometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
  684. }
  685. return geometry;
  686. } else {
  687. throw new Error( 'Unsupported DATASET type' );
  688. }
  689. }
  690. // get the 5 first lines of the files to check if there is the key word binary
  691. const meta = THREE.LoaderUtils.decodeText( new Uint8Array( data, 0, 250 ) ).split( '\n' );
  692. if ( meta[ 0 ].indexOf( 'xml' ) !== - 1 ) {
  693. return parseXML( THREE.LoaderUtils.decodeText( data ) );
  694. } else if ( meta[ 2 ].includes( 'ASCII' ) ) {
  695. return parseASCII( THREE.LoaderUtils.decodeText( data ) );
  696. } else {
  697. return parseBinary( data );
  698. }
  699. }
  700. }
  701. THREE.VTKLoader = VTKLoader;
  702. } )();