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