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

Summary

Maintainability
B
4 hrs
Test Coverage
import Volume from '../../chem/Volume'
import { Box3, Vector3 } from 'three'
import { isArrayBuffer, isTypedArray, pick } from 'lodash'

export const valueType = {
  singular: 0,
  vector: 1,
  array: 2,
  buffer: 3
}

class VolumeModel {
  _xyz2crs = []

  _origin = new Vector3(0, 0, 0)

  constructor() {
    this._header = {}
    this._boxSize = new Vector3()
    this._boxStart = new Vector3()
    this._header.delta = {}
    this._header.extent = []
    this._header.nstart = []
    this._header.grid = []
    this._header.crs2xyz = []
    this._header.cellDims = new Vector3()
    this._header.angles = []
    this._header.origin = new Vector3(0, 0, 0)
    this._header.dmin = 0
    this._header.dmean = 0
    this._header.dmax = 0
  }

  _typedCheck() {
    if (isTypedArray(this._buff)) {
      this._buff = this._buff.buffer
    } else if (!isArrayBuffer(this._buff)) {
      throw new TypeError('Expected ArrayBuffer or TypedArray')
    }
  }

  _fillHeader(headerFormat, arrays) {
    for (const key in headerFormat) {
      if (Object.hasOwn(headerFormat, key)) {
        switch (headerFormat[key][0]) {
          case valueType.singular:
            this._header[key] =
              arrays[headerFormat[key][1]][headerFormat[key][2]]
            break

          case valueType.array:
            this._parseArray(
              this._header[key],
              arrays[headerFormat[key][1]],
              headerFormat[key][2]
            )
            break

          case valueType.vector:
            this._parseVector(
              this._header[key],
              arrays[headerFormat[key][1]],
              headerFormat[key][2]
            )
            break

          case valueType.buffer:
            this._header[key] = new Uint8Array(
              arrays[headerFormat[key][1]],
              [headerFormat[key][2]] * 4,
              [headerFormat[key][3]] * 4
            )
            break

          default:
            break
        }
      }
    }
  }

  _parseVector(vector, arr, pos) {
    ;[vector.x, vector.y, vector.z] = [arr[pos], arr[pos + 1], arr[pos + 2]]
  }

  _parseArray(vector, arr, pos) {
    vector[0] = arr[pos]
    vector[1] = arr[pos + 1]
    vector[2] = arr[pos + 2]
  }

  _parseHeader(_buffer) {}

  _setAxisIndices() {}

  _setOrigins() {}

  _getAxis() {
    const header = this._header

    const xScale = header.cellDims.x / header.grid[0]
    const yScale = header.cellDims.y / header.grid[1]
    const zScale = header.cellDims.z / header.grid[2]

    const [alpha, beta, gamma] = header.angles

    const z1 = Math.cos(beta)
    const z2 =
      (Math.cos(alpha) - Math.cos(beta) * Math.cos(gamma)) / Math.sin(gamma)
    const z3 = Math.sqrt(1.0 - z1 * z1 - z2 * z2)

    const xaxis = new Vector3(xScale, 0, 0)
    const yaxis = new Vector3(
      Math.cos(gamma) * yScale,
      Math.sin(gamma) * yScale,
      0
    )
    const zaxis = new Vector3(z1 * zScale, z2 * zScale, z3 * zScale)

    return [xaxis, yaxis, zaxis]
  }

  _getXYZdim() {
    return [
      this._header.extent[this._xyz2crs[0]],
      this._header.extent[this._xyz2crs[1]],
      this._header.extent[this._xyz2crs[2]]
    ]
  }

  _getVolumeInfo() {
    const volInfo = pick(this._header, ['dmean', 'dmin', 'dmax', 'sd', 'delta'])
    volInfo.obtuseAngle = this._header.angles.map((angle) =>
      Number(angle >= Math.PI / 2)
    )
    return volInfo
  }

  _setBoxParams(xaxis, yaxis, zaxis) {
    // if axes are not orthogonal, the origins might not match with box coordinates - need to make shift
    let shiftX = 0
    let shiftY = 0
    const [alpha, beta, gamma] = this._header.angles

    if (gamma >= Math.PI / 2) {
      shiftX += Math.abs(yaxis.x)
    }
    if (beta >= Math.PI / 2) {
      shiftX += Math.abs(zaxis.x)
    }
    if (alpha >= Math.PI / 2) {
      shiftY += Math.abs(zaxis.y)
    }

    this._boxStart = new Vector3(
      this._origin.x - shiftX,
      this._origin.y - shiftY,
      this._origin.z
    )
    this._boxSize = new Vector3(
      Math.abs(xaxis.x) + Math.abs(yaxis.x) + Math.abs(zaxis.x),
      Math.abs(yaxis.y) + Math.abs(zaxis.y),
      Math.abs(zaxis.z)
    )

    const delta = (axe, proj) => Math.abs(axe[proj]) / this._boxSize[proj]
    this._header.delta.x = delta(yaxis, 'x')
    this._header.delta.y = delta(zaxis, 'x')
    this._header.delta.z = delta(zaxis, 'y')
  }

  _getXYZbox() {
    return new Box3(
      this._boxStart.clone(),
      this._boxStart.clone().add(this._boxSize)
    )
  }

  _toXYZData() {}

  parse(data) {
    this._parseHeader(data)
    this._setOrigins()
    return new Volume(
      Float32Array,
      this._getXYZdim(),
      this._getXYZbox(),
      1,
      this._toXYZData(),
      this._getVolumeInfo()
    )
  }
}

export default VolumeModel