src/symmetry/unitcell.js
/**
* @file Unitcell
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @private
*/
import { Color, Vector3, Matrix4 } from '../../lib/three.es6.js'
import { defaults } from '../utils.js'
import { degToRad } from '../math/math-utils.js'
import {
uniformArray, uniformArray3, centerArray3
} from '../math/array-utils.js'
import { UnitcellPicker } from '../utils/picker.js'
/**
* Unitcell class
*/
class Unitcell {
/**
* @param {Object} params - unitcell parameters
* @param {Number} params.a - length a
* @param {Number} params.b - length b
* @param {Number} params.c - length c
* @param {Number} params.alpha - angle alpha
* @param {Number} params.beta - angle beta
* @param {Number} params.gamma - angle gamma
* @param {String} params.spacegroup - spacegroup
* @param {Matrix4} [params.cartToFrac] - transformation matrix from
* cartesian to fractional coordinates
* @param {Matrix4} [params.scale] - alias for `params.cartToFrac`
*/
constructor (params) {
const p = params || {}
/**
* @type {Number}
*/
this.a = p.a || 1
/**
* @type {Number}
*/
this.b = p.b || 1
/**
* @type {Number}
*/
this.c = p.c || 1
/**
* @type {Number}
*/
this.alpha = p.alpha || 90
/**
* @type {Number}
*/
this.beta = p.beta || 90
/**
* @type {Number}
*/
this.gamma = p.gamma || 90
/**
* @type {String}
*/
this.spacegroup = p.spacegroup || 'P 1'
/**
* @type {Matrix4}
*/
this.cartToFrac = p.cartToFrac || p.scale
/**
* @type {Matrix4}
*/
this.fracToCart = new Matrix4()
//
const alphaRad = degToRad(this.alpha)
const betaRad = degToRad(this.beta)
const gammaRad = degToRad(this.gamma)
const cosAlpha = Math.cos(alphaRad)
const cosBeta = Math.cos(betaRad)
const cosGamma = Math.cos(gammaRad)
const sinBeta = Math.sin(betaRad)
const sinGamma = Math.sin(gammaRad)
/**
* @type {Number}
*/
this.volume = (
this.a * this.b * this.c *
Math.sqrt(
1 - cosAlpha * cosAlpha - cosBeta * cosBeta - cosGamma * cosGamma +
2.0 * cosAlpha * cosBeta * cosGamma
)
)
//
if (this.cartToFrac === undefined) {
// https://github.com/biojava/biojava/blob/master/biojava-structure/src/main/java/org/biojava/nbio/structure/xtal/CrystalCell.java
const cStar = (this.a * this.b * sinGamma) / this.volume
const cosAlphaStar = (
(cosBeta * cosGamma - cosAlpha) / (sinBeta * sinGamma)
)
this.fracToCart.set(
this.a, 0, 0, 0,
this.b * cosGamma, this.b * sinGamma, 0, 0,
this.c * cosBeta, -this.c * sinBeta * cosAlphaStar, 1.0 / cStar, 0,
0, 0, 0, 1
).transpose()
this.cartToFrac = new Matrix4().getInverse(this.fracToCart)
} else {
this.fracToCart.getInverse(this.cartToFrac)
}
}
getPosition (structure) {
const vertexPosition = new Float32Array(3 * 8)
const uc = structure.unitcell
const centerFrac = structure.center.clone()
.applyMatrix4(uc.cartToFrac)
.floor().multiplyScalar(2).addScalar(1)
const v = new Vector3()
let cornerOffset = 0
function addCorner (x, y, z) {
v.set(x, y, z)
.multiply(centerFrac)
.applyMatrix4(uc.fracToCart)
.toArray(vertexPosition, cornerOffset)
cornerOffset += 3
}
addCorner(0, 0, 0)
addCorner(1, 0, 0)
addCorner(0, 1, 0)
addCorner(0, 0, 1)
addCorner(1, 1, 0)
addCorner(1, 0, 1)
addCorner(0, 1, 1)
addCorner(1, 1, 1)
return vertexPosition
}
getCenter (structure) {
return centerArray3(this.getPosition(structure))
}
getData (structure, params) {
const p = params || {}
const colorValue = defaults(p.colorValue, 'orange')
const radius = defaults(p.radius, Math.cbrt(this.volume) / 200)
const c = new Color(colorValue)
const v = new Vector3()
const vertexPosition = this.getPosition(structure)
const vertexColor = uniformArray3(8, c.r, c.g, c.b)
const vertexRadius = uniformArray(8, radius)
const edgePosition1 = new Float32Array(3 * 12)
const edgePosition2 = new Float32Array(3 * 12)
const edgeColor = uniformArray3(12, c.r, c.g, c.b)
const edgeRadius = uniformArray(12, radius)
let edgeOffset = 0
function addEdge (a, b) {
v.fromArray(vertexPosition, a * 3)
.toArray(edgePosition1, edgeOffset)
v.fromArray(vertexPosition, b * 3)
.toArray(edgePosition2, edgeOffset)
edgeOffset += 3
}
addEdge(0, 1)
addEdge(0, 2)
addEdge(0, 3)
addEdge(1, 4)
addEdge(1, 5)
addEdge(2, 6)
addEdge(3, 5)
addEdge(4, 7)
addEdge(5, 7)
addEdge(2, 4)
addEdge(7, 6)
addEdge(3, 6)
const picker = new UnitcellPicker(this, structure)
return {
vertex: {
position: vertexPosition,
color: vertexColor,
radius: vertexRadius,
picking: picker
},
edge: {
position1: edgePosition1,
position2: edgePosition2,
color: edgeColor,
color2: edgeColor,
radius: edgeRadius,
picking: picker
}
}
}
}
export default Unitcell