three-stdlib
Version: 
stand-alone library of threejs examples
404 lines (403 loc) • 11.7 kB
JavaScript
import { Loader, FileLoader, Vector3, Matrix4 } from "three";
import { gunzipSync } from "fflate";
import { Volume } from "../misc/Volume.js";
class NRRDLoader extends Loader {
  constructor(manager) {
    super(manager);
  }
  load(url, onLoad, onProgress, onError) {
    const scope = this;
    const loader = new 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
    );
  }
  parse(data) {
    let _data = data;
    let _dataPointer = 0;
    const _nativeLittleEndian = new Int8Array(new Int16Array([1]).buffer)[0] > 0;
    const _littleEndian = true;
    const headerObject = {};
    function scan(type, chunks) {
      if (chunks === void 0 || chunks === null) {
        chunks = 1;
      }
      let _chunkSize = 1;
      let _array_type = Uint8Array;
      switch (type) {
        case "uchar":
          break;
        case "schar":
          _array_type = Int8Array;
          break;
        case "ushort":
          _array_type = Uint16Array;
          _chunkSize = 2;
          break;
        case "sshort":
          _array_type = Int16Array;
          _chunkSize = 2;
          break;
        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;
      }
      let _bytes2 = new _array_type(_data.slice(_dataPointer, _dataPointer += chunks * _chunkSize));
      if (_nativeLittleEndian != _littleEndian) {
        _bytes2 = flipEndianness(_bytes2, _chunkSize);
      }
      if (chunks == 1) {
        return _bytes2[0];
      }
      return _bytes2;
    }
    function flipEndianness(array, chunkSize) {
      const u8 = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
      for (let i2 = 0; i2 < array.byteLength; i2 += chunkSize) {
        for (let j = i2 + chunkSize - 1, k = i2; j > k; j--, k++) {
          const tmp = u8[k];
          u8[k] = u8[j];
          u8[j] = tmp;
        }
      }
      return array;
    }
    function parseHeader(header) {
      let data2, field, fn, i2, l, m, _i, _len;
      const 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();
          data2 = m[2].trim();
          fn = _fieldFunctions[field];
          if (fn) {
            fn.call(headerObject, data2);
          } else {
            headerObject[field] = data2;
          }
        }
      }
      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) {
        headerObject.vectors = [new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1)];
        if (headerObject.spacings) {
          for (i2 = 0; i2 <= 2; i2++) {
            if (!isNaN(headerObject.spacings[i2])) {
              headerObject.vectors[i2].multiplyScalar(headerObject.spacings[i2]);
            }
          }
        }
      }
    }
    function parseDataAsText(data2, start, end) {
      let number = "";
      start = start || 0;
      end = end || data2.length;
      let value;
      const lengthOfTheResult = headerObject.sizes.reduce(function(previous, current) {
        return previous * current;
      }, 1);
      let base = 10;
      if (headerObject.encoding === "hex") {
        base = 16;
      }
      const result = new headerObject.__array(lengthOfTheResult);
      let resultIndex = 0;
      let parsingFunction = parseInt;
      if (headerObject.__array === Float32Array || headerObject.__array === Float64Array) {
        parsingFunction = parseFloat;
      }
      for (let i2 = start; i2 < end; i2++) {
        value = data2[i2];
        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;
    }
    const _bytes = scan("uchar", data.byteLength);
    const _length = _bytes.length;
    let _header = null;
    let _data_start = 0;
    let i;
    for (i = 1; i < _length; i++) {
      if (_bytes[i - 1] == 10 && _bytes[i] == 10) {
        _header = this.parseChars(_bytes, 0, i - 2);
        _data_start = i + 1;
        break;
      }
    }
    parseHeader(_header);
    _data = _bytes.subarray(_data_start);
    if (headerObject.encoding.substring(0, 2) === "gz") {
      _data = gunzipSync(new Uint8Array(_data));
    } else if (headerObject.encoding === "ascii" || headerObject.encoding === "text" || headerObject.encoding === "txt" || headerObject.encoding === "hex") {
      _data = parseDataAsText(_data);
    } else if (headerObject.encoding === "raw") {
      const _copy = new Uint8Array(_data.length);
      for (let i2 = 0; i2 < _data.length; i2++) {
        _copy[i2] = _data[i2];
      }
      _data = _copy;
    }
    _data = _data.buffer;
    const volume = new Volume();
    volume.header = headerObject;
    volume.data = new headerObject.__array(_data);
    const min_max = volume.computeMinMax();
    const min = min_max[0];
    const max = min_max[1];
    volume.windowLow = min;
    volume.windowHigh = max;
    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];
    const spacingX = new Vector3(
      headerObject.vectors[0][0],
      headerObject.vectors[0][1],
      headerObject.vectors[0][2]
    ).length();
    const spacingY = new Vector3(
      headerObject.vectors[1][0],
      headerObject.vectors[1][1],
      headerObject.vectors[1][2]
    ).length();
    const spacingZ = new Vector3(
      headerObject.vectors[2][0],
      headerObject.vectors[2][1],
      headerObject.vectors[2][2]
    ).length();
    volume.spacing = [spacingX, spacingY, spacingZ];
    volume.matrix = new Matrix4();
    let _spaceX = 1;
    let _spaceY = 1;
    const _spaceZ = 1;
    if (headerObject.space == "left-posterior-superior") {
      _spaceX = -1;
      _spaceY = -1;
    } else if (headerObject.space === "left-anterior-superior") {
      _spaceX = -1;
    }
    if (!headerObject.vectors) {
      volume.matrix.set(_spaceX, 0, 0, 0, 0, _spaceY, 0, 0, 0, 0, _spaceZ, 0, 0, 0, 0, 1);
    } else {
      const v = headerObject.vectors;
      volume.matrix.set(
        _spaceX * v[0][0],
        _spaceX * v[1][0],
        _spaceX * v[2][0],
        0,
        _spaceY * v[0][1],
        _spaceY * v[1][1],
        _spaceY * v[2][1],
        0,
        _spaceZ * v[0][2],
        _spaceZ * v[1][2],
        _spaceZ * v[2][2],
        0,
        0,
        0,
        0,
        1
      );
    }
    volume.inverseMatrix = new Matrix4();
    volume.inverseMatrix.copy(volume.matrix).invert();
    volume.RASDimensions = new Vector3(volume.xLength, volume.yLength, volume.zLength).applyMatrix4(volume.matrix).round().toArray().map(Math.abs);
    if (volume.lowerThreshold === -Infinity) {
      volume.lowerThreshold = min;
    }
    if (volume.upperThreshold === Infinity) {
      volume.upperThreshold = max;
    }
    return volume;
  }
  parseChars(array, start, end) {
    if (start === void 0) {
      start = 0;
    }
    if (end === void 0) {
      end = array.length;
    }
    let output = "";
    let i = 0;
    for (i = start; i < end; ++i) {
      output += String.fromCharCode(array[i]);
    }
    return output;
  }
}
const _fieldFunctions = {
  type: function(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(data) {
    return this.endian = data;
  },
  encoding: function(data) {
    return this.encoding = data;
  },
  dimension: function(data) {
    return this.dim = parseInt(data, 10);
  },
  sizes: function(data) {
    let i;
    return this.sizes = function() {
      const _ref = data.split(/\s+/);
      const _results = [];
      for (let _i = 0, _len = _ref.length; _i < _len; _i++) {
        i = _ref[_i];
        _results.push(parseInt(i, 10));
      }
      return _results;
    }();
  },
  space: function(data) {
    return this.space = data;
  },
  "space origin": function(data) {
    return this.space_origin = data.split("(")[1].split(")")[0].split(",");
  },
  "space directions": function(data) {
    let f, v;
    const parts = data.match(/\(.*?\)/g);
    return this.vectors = function() {
      const _results = [];
      for (let _i = 0, _len = parts.length; _i < _len; _i++) {
        v = parts[_i];
        _results.push(
          function() {
            const _ref = v.slice(1, -1).split(/,/);
            const _results2 = [];
            for (let _j = 0, _len2 = _ref.length; _j < _len2; _j++) {
              f = _ref[_j];
              _results2.push(parseFloat(f));
            }
            return _results2;
          }()
        );
      }
      return _results;
    }();
  },
  spacings: function(data) {
    let f;
    const parts = data.split(/\s+/);
    return this.spacings = function() {
      const _results = [];
      for (let _i = 0, _len = parts.length; _i < _len; _i++) {
        f = parts[_i];
        _results.push(parseFloat(f));
      }
      return _results;
    }();
  }
};
export {
  NRRDLoader
};
//# sourceMappingURL=NRRDLoader.js.map