(function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports", "three", "../libs/fflate.module.js", "../misc/Volume.js"], factory); } else if (typeof exports !== "undefined") { factory(exports, require("three"), require("../libs/fflate.module.js"), require("../misc/Volume.js")); } else { var mod = { exports: {} }; factory(mod.exports, global.three, global.fflateModule, global.Volume); global.NRRDLoader = mod.exports; } })(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _three, fflate, _Volume) { "use strict"; Object.defineProperty(_exports, "__esModule", { value: true }); _exports.NRRDLoader = void 0; fflate = _interopRequireWildcard(fflate); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } Object.defineProperty(subClass, "prototype", { value: Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }), writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } var NRRDLoader = /*#__PURE__*/function (_Loader) { _inherits(NRRDLoader, _Loader); var _super = _createSuper(NRRDLoader); function NRRDLoader(manager) { _classCallCheck(this, NRRDLoader); return _super.call(this, manager); } _createClass(NRRDLoader, [{ key: "load", value: function load(url, onLoad, onProgress, onError) { var scope = this; var loader = new _three.FileLoader(scope.manager); loader.setPath(scope.path); loader.setResponseType('arraybuffer'); loader.setRequestHeader(scope.requestHeader); loader.setWithCredentials(scope.withCredentials); loader.load(url, function (data) { try { onLoad(scope.parse(data)); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); } }, { key: "parse", value: function parse(data) { // this parser is largely inspired from the XTK NRRD parser : https://github.com/xtk/X var _data = data; var _dataPointer = 0; var _nativeLittleEndian = new Int8Array(new Int16Array([1]).buffer)[0] > 0; var _littleEndian = true; var headerObject = {}; function scan(type, chunks) { if (chunks === undefined || chunks === null) { chunks = 1; } var _chunkSize = 1; var _array_type = Uint8Array; switch (type) { // 1 byte data types case 'uchar': break; case 'schar': _array_type = Int8Array; break; // 2 byte data types case 'ushort': _array_type = Uint16Array; _chunkSize = 2; break; case 'sshort': _array_type = Int16Array; _chunkSize = 2; break; // 4 byte data types case 'uint': _array_type = Uint32Array; _chunkSize = 4; break; case 'sint': _array_type = Int32Array; _chunkSize = 4; break; case 'float': _array_type = Float32Array; _chunkSize = 4; break; case 'complex': _array_type = Float64Array; _chunkSize = 8; break; case 'double': _array_type = Float64Array; _chunkSize = 8; break; } // increase the data pointer in-place var _bytes = new _array_type(_data.slice(_dataPointer, _dataPointer += chunks * _chunkSize)); // if required, flip the endianness of the bytes if (_nativeLittleEndian != _littleEndian) { // we need to flip here since the format doesn't match the native endianness _bytes = flipEndianness(_bytes, _chunkSize); } if (chunks == 1) { // if only one chunk was requested, just return one value return _bytes[0]; } // return the byte array return _bytes; } //Flips typed array endianness in-place. Based on https://github.com/kig/DataStream.js/blob/master/DataStream.js. function flipEndianness(array, chunkSize) { var u8 = new Uint8Array(array.buffer, array.byteOffset, array.byteLength); for (var _i2 = 0; _i2 < array.byteLength; _i2 += chunkSize) { for (var j = _i2 + chunkSize - 1, k = _i2; j > k; j--, k++) { var tmp = u8[k]; u8[k] = u8[j]; u8[j] = tmp; } } return array; } //parse the header function parseHeader(header) { var data, field, fn, i, l, m, _i, _len; var lines = header.split(/\r?\n/); for (_i = 0, _len = lines.length; _i < _len; _i++) { l = lines[_i]; if (l.match(/NRRD\d+/)) { headerObject.isNrrd = true; } else if (l.match(/^#/)) {} else if (m = l.match(/(.*):(.*)/)) { field = m[1].trim(); data = m[2].trim(); fn = _fieldFunctions[field]; if (fn) { fn.call(headerObject, data); } else { headerObject[field] = data; } } } if (!headerObject.isNrrd) { throw new Error('Not an NRRD file'); } if (headerObject.encoding === 'bz2' || headerObject.encoding === 'bzip2') { throw new Error('Bzip is not supported'); } if (!headerObject.vectors) { //if no space direction is set, let's use the identity headerObject.vectors = [new _three.Vector3(1, 0, 0), new _three.Vector3(0, 1, 0), new _three.Vector3(0, 0, 1)]; //apply spacing if defined if (headerObject.spacings) { for (i = 0; i <= 2; i++) { if (!isNaN(headerObject.spacings[i])) { headerObject.vectors[i].multiplyScalar(headerObject.spacings[i]); } } } } } //parse the data when registred as one of this type : 'text', 'ascii', 'txt' function parseDataAsText(data, start, end) { var number = ''; start = start || 0; end = end || data.length; var value; //length of the result is the product of the sizes var lengthOfTheResult = headerObject.sizes.reduce(function (previous, current) { return previous * current; }, 1); var base = 10; if (headerObject.encoding === 'hex') { base = 16; } var result = new headerObject.__array(lengthOfTheResult); var resultIndex = 0; var parsingFunction = parseInt; if (headerObject.__array === Float32Array || headerObject.__array === Float64Array) { parsingFunction = parseFloat; } for (var _i3 = start; _i3 < end; _i3++) { value = data[_i3]; //if value is not a space if ((value < 9 || value > 13) && value !== 32) { number += String.fromCharCode(value); } else { if (number !== '') { result[resultIndex] = parsingFunction(number, base); resultIndex++; } number = ''; } } if (number !== '') { result[resultIndex] = parsingFunction(number, base); resultIndex++; } return result; } var _bytes = scan('uchar', data.byteLength); var _length = _bytes.length; var _header = null; var _data_start = 0; var i; for (i = 1; i < _length; i++) { if (_bytes[i - 1] == 10 && _bytes[i] == 10) { // we found two line breaks in a row // now we know what the header is _header = this.parseChars(_bytes, 0, i - 2); // this is were the data starts _data_start = i + 1; break; } } // parse the header parseHeader(_header); _data = _bytes.subarray(_data_start); // the data without header if (headerObject.encoding.substring(0, 2) === 'gz') { // we need to decompress the datastream // here we start the unzipping and get a typed Uint8Array back _data = fflate.gunzipSync(new Uint8Array(_data)); // eslint-disable-line no-undef } else if (headerObject.encoding === 'ascii' || headerObject.encoding === 'text' || headerObject.encoding === 'txt' || headerObject.encoding === 'hex') { _data = parseDataAsText(_data); } else if (headerObject.encoding === 'raw') { //we need to copy the array to create a new array buffer, else we retrieve the original arraybuffer with the header var _copy = new Uint8Array(_data.length); for (var _i4 = 0; _i4 < _data.length; _i4++) { _copy[_i4] = _data[_i4]; } _data = _copy; } // .. let's use the underlying array buffer _data = _data.buffer; var volume = new _Volume.Volume(); volume.header = headerObject; // // parse the (unzipped) data to a datastream of the correct type // volume.data = new headerObject.__array(_data); // get the min and max intensities var min_max = volume.computeMinMax(); var min = min_max[0]; var max = min_max[1]; // attach the scalar range to the volume volume.windowLow = min; volume.windowHigh = max; // get the image dimensions volume.dimensions = [headerObject.sizes[0], headerObject.sizes[1], headerObject.sizes[2]]; volume.xLength = volume.dimensions[0]; volume.yLength = volume.dimensions[1]; volume.zLength = volume.dimensions[2]; // Identify axis order in the space-directions matrix from the header if possible. if (headerObject.vectors) { var xIndex = headerObject.vectors.findIndex(function (vector) { return vector[0] !== 0; }); var yIndex = headerObject.vectors.findIndex(function (vector) { return vector[1] !== 0; }); var zIndex = headerObject.vectors.findIndex(function (vector) { return vector[2] !== 0; }); var axisOrder = []; axisOrder[xIndex] = 'x'; axisOrder[yIndex] = 'y'; axisOrder[zIndex] = 'z'; volume.axisOrder = axisOrder; } else { volume.axisOrder = ['x', 'y', 'z']; } // spacing var spacingX = new _three.Vector3().fromArray(headerObject.vectors[0]).length(); var spacingY = new _three.Vector3().fromArray(headerObject.vectors[1]).length(); var spacingZ = new _three.Vector3().fromArray(headerObject.vectors[2]).length(); volume.spacing = [spacingX, spacingY, spacingZ]; // Create IJKtoRAS matrix volume.matrix = new _three.Matrix4(); var transitionMatrix = new _three.Matrix4(); if (headerObject.space === 'left-posterior-superior') { transitionMatrix.set(-1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } else if (headerObject.space === 'left-anterior-superior') { transitionMatrix.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1); } if (!headerObject.vectors) { volume.matrix.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); } else { var v = headerObject.vectors; var ijk_to_transition = new _three.Matrix4().set(v[0][0], v[1][0], v[2][0], 0, v[0][1], v[1][1], v[2][1], 0, v[0][2], v[1][2], v[2][2], 0, 0, 0, 0, 1); var transition_to_ras = new _three.Matrix4().multiplyMatrices(ijk_to_transition, transitionMatrix); volume.matrix = transition_to_ras; } volume.inverseMatrix = new _three.Matrix4(); volume.inverseMatrix.copy(volume.matrix).invert(); volume.RASDimensions = new _three.Vector3(volume.xLength, volume.yLength, volume.zLength).applyMatrix4(volume.matrix).round().toArray().map(Math.abs); // .. and set the default threshold // only if the threshold was not already set if (volume.lowerThreshold === -Infinity) { volume.lowerThreshold = min; } if (volume.upperThreshold === Infinity) { volume.upperThreshold = max; } return volume; } }, { key: "parseChars", value: function parseChars(array, start, end) { // without borders, use the whole array if (start === undefined) { start = 0; } if (end === undefined) { end = array.length; } var output = ''; // create and append the chars var i = 0; for (i = start; i < end; ++i) { output += String.fromCharCode(array[i]); } return output; } }]); return NRRDLoader; }(_three.Loader); _exports.NRRDLoader = NRRDLoader; var _fieldFunctions = { type: function type(data) { switch (data) { case 'uchar': case 'unsigned char': case 'uint8': case 'uint8_t': this.__array = Uint8Array; break; case 'signed char': case 'int8': case 'int8_t': this.__array = Int8Array; break; case 'short': case 'short int': case 'signed short': case 'signed short int': case 'int16': case 'int16_t': this.__array = Int16Array; break; case 'ushort': case 'unsigned short': case 'unsigned short int': case 'uint16': case 'uint16_t': this.__array = Uint16Array; break; case 'int': case 'signed int': case 'int32': case 'int32_t': this.__array = Int32Array; break; case 'uint': case 'unsigned int': case 'uint32': case 'uint32_t': this.__array = Uint32Array; break; case 'float': this.__array = Float32Array; break; case 'double': this.__array = Float64Array; break; default: throw new Error('Unsupported NRRD data type: ' + data); } return this.type = data; }, endian: function endian(data) { return this.endian = data; }, encoding: function encoding(data) { return this.encoding = data; }, dimension: function dimension(data) { return this.dim = parseInt(data, 10); }, sizes: function sizes(data) { var i; return this.sizes = function () { var _ref = data.split(/\s+/); var _results = []; for (var _i = 0, _len = _ref.length; _i < _len; _i++) { i = _ref[_i]; _results.push(parseInt(i, 10)); } return _results; }(); }, space: function space(data) { return this.space = data; }, 'space origin': function spaceOrigin(data) { return this.space_origin = data.split('(')[1].split(')')[0].split(','); }, 'space directions': function spaceDirections(data) { var f, v; var parts = data.match(/\(.*?\)/g); return this.vectors = function () { var _results = []; for (var _i = 0, _len = parts.length; _i < _len; _i++) { v = parts[_i]; _results.push(function () { var _ref = v.slice(1, -1).split(/,/); var _results2 = []; for (var _j = 0, _len2 = _ref.length; _j < _len2; _j++) { f = _ref[_j]; _results2.push(parseFloat(f)); } return _results2; }()); } return _results; }(); }, spacings: function spacings(data) { var f; var parts = data.split(/\s+/); return this.spacings = function () { var _results = []; for (var _i = 0, _len = parts.length; _i < _len; _i++) { f = parts[_i]; _results.push(parseFloat(f)); } return _results; }(); } }; });