packages/miew/src/io/parsers/DSN6Parser.js

Summary

Maintainability
A
1 hr
Test Coverage
import Parser from './Parser'
import VolumeModel, { valueType } from './VolumeModel'
import { Vector3 } from 'three'

// http://www.uoxray.uoregon.edu/tnt/manual/node104.html
const DSN6Header = {
  nstart: [valueType.array, 'i16', 0],
  extent: [valueType.array, 'i16', 3],
  grid: [valueType.array, 'i16', 6],
  cellDims: [valueType.vector, 'i16', 9],
  angles: [valueType.array, 'i16', 12],
  div: [valueType.singular, 'i16', 15],
  adder: [valueType.singular, 'i16', 16],
  scaleFactor: [valueType.singular, 'i16', 17]
}

class DSN6Model extends VolumeModel {
  _parseHeader(_buffer) {
    this._buff = _buffer
    this._typedCheck()

    const arrays = {}
    arrays.i16 = new Int16Array(this._buff)

    // check and reverse if big endian
    if (arrays.i16[18] !== 100) {
      for (let i = 0, n = arrays.i16.length; i < n; ++i) {
        const val = arrays.i16[i]
        arrays.i16[i] = ((val & 0xff) << 8) | ((val >> 8) & 0xff)
      }
    }
    if (arrays.i16[18] !== 100) {
      throw new Error('DSN6: Incorrect format ')
    }

    const header = this._header

    this._fillHeader(DSN6Header, arrays)
    header.cellDims.multiplyScalar(1.0 / header.scaleFactor)
    header.angles.forEach((angle, i, a) => {
      a[i] *= Math.PI / 180.0 / header.scaleFactor
    })
    header.div /= 100
  }

  _setAxisIndices() {
    this._xyz2crs[0] = 0
    this._xyz2crs[1] = 1
    this._xyz2crs[2] = 2
  }

  _setOrigins() {
    const header = this._header
    const [xaxis, yaxis, zaxis] = this._getAxis()
    this._setAxisIndices()

    this._origin.addScaledVector(xaxis, header.nstart[0])
    this._origin.addScaledVector(yaxis, header.nstart[1])
    this._origin.addScaledVector(zaxis, header.nstart[2])

    xaxis.multiplyScalar(header.extent[0])
    yaxis.multiplyScalar(header.extent[1])
    zaxis.multiplyScalar(header.extent[2])

    this._setBoxParams(xaxis, yaxis, zaxis)
  }

  _pointCalculate(xyzData, byteBuffer, z, y, x, pos, i) {
    const header = this._header

    if (x < header.extent[0] && y < header.extent[1] && z < header.extent[2]) {
      const idx = x + header.extent[0] * (y + header.extent[1] * z)
      xyzData[idx] = (byteBuffer[pos.counter] - header.adder) / header.div
      ++pos.counter
    } else {
      pos.counter += 8 - i
      return false
    }
    return true
  }

  _blockCalculate(xyzData, byteBuffer, zBlock, yBlock, xBlock, pos) {
    for (let k = 0; k < 8; ++k) {
      const z = 8 * zBlock + k
      for (let j = 0; j < 8; ++j) {
        const y = 8 * yBlock + j
        let inRange = true
        let i = 0
        while (inRange && i < 8) {
          const x = 8 * xBlock + i
          inRange = this._pointCalculate(xyzData, byteBuffer, z, y, x, pos, i)
          i++
        }
      }
    }
  }

  _toXYZData() {
    const header = this._header
    const byteBuffer = new Uint8Array(this._buff)
    const xyzData = new Float32Array(
      header.extent[0] * header.extent[1] * header.extent[2]
    )

    const blocks = new Vector3(
      header.extent[0] / 8,
      header.extent[1] / 8,
      header.extent[2] / 8
    )

    const pos = {}
    pos.counter = 512

    for (let zBlock = 0; zBlock < blocks.z; ++zBlock) {
      for (let yBlock = 0; yBlock < blocks.y; ++yBlock) {
        for (let xBlock = 0; xBlock < blocks.x; ++xBlock) {
          this._blockCalculate(xyzData, byteBuffer, zBlock, yBlock, xBlock, pos)
        }
      }
    }
    this._calculateInfoParams(xyzData)
    return xyzData
  }

  _calculateInfoParams(xyzData) {
    this._header.dmean /= xyzData.length
    let dispersion = 0
    let minDensity = xyzData[0]
    let maxDensity = xyzData[0]
    for (let j = 0; j < xyzData.length; j++) {
      dispersion += (this._header.dmean - xyzData[j]) ** 2

      if (xyzData[j] < minDensity) {
        minDensity = xyzData[j]
      }
      if (xyzData[j] > maxDensity) {
        maxDensity = xyzData[j]
      }
    }
    this._header.sd = Math.sqrt(dispersion / xyzData.length)
    this._header.dmax = maxDensity
    this._header.dmin = minDensity
  }
}

class DSN6Parser extends Parser {
  constructor(data, options) {
    super(data, options)
    this._options.fileType = 'dsn6'
    this.model = new DSN6Model()
  }

  static canParse(data, options) {
    if (!data) {
      return false
    }
    return (
      data instanceof ArrayBuffer &&
      Parser.checkDataTypeOptions(options, 'dsn6')
    )
  }

  static canProbablyParse(_data) {
    return false
  }

  parseSync() {
    return this.model.parse(this._data)
  }
}

DSN6Parser.formats = ['dsn6']
DSN6Parser.extensions = ['.dsn6', '.omap']
DSN6Parser.binary = true

export default DSN6Parser