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

src/align/superposition.js

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

import { Debug, Log } from '../globals.js'
import {
    Matrix, svd, meanRows, subRows, addRows, transpose,
    multiplyABt, invert3x3, multiply3x3, mat3x3determinant
} from '../math/matrix-utils.js'

class Superposition {
  constructor (atoms1, atoms2) {
    // allocate & init data structures

    var n1
    if (typeof atoms1.eachAtom === 'function') {
      n1 = atoms1.atomCount
    } else if (atoms1 instanceof Float32Array) {
      n1 = atoms1.length / 3
    }

    var n2
    if (typeof atoms2.eachAtom === 'function') {
      n2 = atoms2.atomCount
    } else if (atoms1 instanceof Float32Array) {
      n2 = atoms2.length / 3
    }

    var n = Math.min(n1, n2)

    var coords1 = new Matrix(3, n)
    var coords2 = new Matrix(3, n)

    this.coords1t = new Matrix(n, 3)
    this.coords2t = new Matrix(n, 3)

    this.A = new Matrix(3, 3)
    this.W = new Matrix(1, 3)
    this.U = new Matrix(3, 3)
    this.V = new Matrix(3, 3)
    this.VH = new Matrix(3, 3)
    this.R = new Matrix(3, 3)

    this.tmp = new Matrix(3, 3)
    this.c = new Matrix(3, 3)
    this.c.data.set([ 1, 0, 0, 0, 1, 0, 0, 0, -1 ])

    // prep coords

    this.prepCoords(atoms1, coords1, n)
    this.prepCoords(atoms2, coords2, n)

    // superpose

    this._superpose(coords1, coords2)
  }

  _superpose (coords1, coords2) {
    this.mean1 = meanRows(coords1)
    this.mean2 = meanRows(coords2)

    subRows(coords1, this.mean1)
    subRows(coords2, this.mean2)

    transpose(this.coords1t, coords1)
    transpose(this.coords2t, coords2)

    multiplyABt(this.A, this.coords2t, this.coords1t)

    svd(this.A, this.W, this.U, this.V)

    invert3x3(this.V, this.VH)
    multiply3x3(this.R, this.U, this.VH)

    if (mat3x3determinant(this.R) < 0.0) {
      if (Debug) Log.log('R not a right handed system')

      multiply3x3(this.tmp, this.c, this.VH)
      multiply3x3(this.R, this.U, this.tmp)
    }
  }

  prepCoords (atoms, coords, n) {
    var i = 0
    var n3 = n * 3
    var cd = coords.data

    if (typeof atoms.eachAtom === 'function') {
      atoms.eachAtom(function (a) {
        if (i < n3) {
          cd[ i + 0 ] = a.x
          cd[ i + 1 ] = a.y
          cd[ i + 2 ] = a.z

          i += 3
        }
      })
    } else if (atoms instanceof Float32Array) {
      cd.set(atoms.subarray(0, n3))
    } else {
      Log.warn('prepCoords: input type unknown')
    }
  }

  transform (atoms) {
    // allocate data structures

    var n
    if (typeof atoms.eachAtom === 'function') {
      n = atoms.atomCount
    } else if (atoms instanceof Float32Array) {
      n = atoms.length / 3
    }

    var coords = new Matrix(3, n)
    var tmp = new Matrix(n, 3)

    // prep coords

    this.prepCoords(atoms, coords, n)

    // do transform

    subRows(coords, this.mean1)
    multiplyABt(tmp, this.R, coords)
    transpose(coords, tmp)
    addRows(coords, this.mean2)

    var i = 0
    var cd = coords.data

    if (typeof atoms.eachAtom === 'function') {
      atoms.eachAtom(function (a) {
        a.x = cd[ i + 0 ]
        a.y = cd[ i + 1 ]
        a.z = cd[ i + 2 ]

        i += 3
      })
    } else if (atoms instanceof Float32Array) {
      atoms.set(cd.subarray(0, n * 3))
    } else {
      Log.warn('transform: input type unknown')
    }
  }
}

export default Superposition