NGL@1.0.0-beta.7 Home Manual Reference Source Gallery

src/surface/filtered-volume.js

/**
 * @file Filtered Volume
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 * @private
 */

import { defaults } from '../utils.js'
import Volume from './volume.js'

class FilteredVolume {
  constructor (volume, minValue, maxValue, outside) {
    this.volume = volume
    this.setFilter(minValue, maxValue, outside)
  }

  get header () { return this.volume.header }
  get matrix () { return this.volume.matrix }
  get normalMatrix () { return this.volume.normalMatrix }
  get inverseMatrix () { return this.volume.inverseMatrix }
  get center () { return this.volume.center }
  get boundingBox () { return this.volume.boundingBox }
  get min () { return this.volume.min }
  get max () { return this.volume.max }
  get mean () { return this.volume.mean }
  get rms () { return this.volume.rms }

  _getFilterHash (minValue, maxValue, outside) {
    return JSON.stringify([ minValue, maxValue, outside ])
  }

  setFilter (minValue, maxValue, outside) {
    if (isNaN(minValue) && this.header) {
      minValue = this.header.DMEAN + 2.0 * this.header.ARMS
    }

    minValue = (minValue !== undefined && !isNaN(minValue)) ? minValue : -Infinity
    maxValue = defaults(maxValue, Infinity)
    outside = defaults(outside, false)

    const data = this.volume.data
    const position = this.volume.position
    const atomindex = this.volume.atomindex

    const filterHash = this._getFilterHash(minValue, maxValue, outside)

    if (filterHash === this._filterHash) {
      // already filtered
      return
    } else if (minValue === -Infinity && maxValue === Infinity) {
      this.data = data
      this.position = position
      this.atomindex = atomindex
    } else {
      const n = data.length

      if (!this._dataBuffer) {
        // ArrayBuffer for re-use as Float32Array backend

        this._dataBuffer = new ArrayBuffer(n * 4)
        this._positionBuffer = new ArrayBuffer(n * 3 * 4)
        if (atomindex) this._atomindexBuffer = new ArrayBuffer(n * 4)
      }

      const filteredData = new Float32Array(this._dataBuffer)
      const filteredPosition = new Float32Array(this._positionBuffer)
      let filteredAtomindex
      if (atomindex) filteredAtomindex = new Uint32Array(this._atomindexBuffer)

      let j = 0

      for (let i = 0; i < n; ++i) {
        const i3 = i * 3
        const v = data[ i ]

        if ((!outside && v >= minValue && v <= maxValue) ||
            (outside && (v < minValue || v > maxValue))
        ) {
          const j3 = j * 3

          filteredData[ j ] = v

          filteredPosition[ j3 + 0 ] = position[ i3 + 0 ]
          filteredPosition[ j3 + 1 ] = position[ i3 + 1 ]
          filteredPosition[ j3 + 2 ] = position[ i3 + 2 ]

          if (atomindex) filteredAtomindex[ j ] = atomindex[ i ]

          j += 1
        }
      }

      // set views

      this.data = new Float32Array(this._dataBuffer, 0, j)
      this.position = new Float32Array(this._positionBuffer, 0, j * 3)
      if (atomindex) this.atomindex = new Float32Array(this._atomindexBuffer, 0, j)
    }

    this._filterHash = filterHash
  }
}

FilteredVolume.prototype.getValueForSigma = Volume.prototype.getValueForSigma
FilteredVolume.prototype.getSigmaForValue = Volume.prototype.getSigmaForValue

FilteredVolume.prototype.getDataAtomindex = Volume.prototype.getDataAtomindex
FilteredVolume.prototype.getDataPosition = Volume.prototype.getDataPosition
FilteredVolume.prototype.getDataColor = Volume.prototype.getDataColor
FilteredVolume.prototype.getDataPicking = Volume.prototype.getDataPicking
FilteredVolume.prototype.getDataSize = Volume.prototype.getDataSize

export default FilteredVolume