org.biojava.nbio.structure.symmetry.geometry.SphereSampler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of biojava-structure Show documentation
Show all versions of biojava-structure Show documentation
The protein structure modules of BioJava.
/*
* BioJava development code
*
* This code may be freely distributed and modified under the
* terms of the GNU Lesser General Public Licence. This should
* be distributed with the code. If you do not have a copy,
* see:
*
* http://www.gnu.org/copyleft/lesser.html
*
* Copyright for this code is held jointly by the individual
* authors. These should be listed in @author doc comments.
*
* For more information on the BioJava project and its aims,
* or to join the biojava-l mailing list, visit the home page
* at:
*
* http://www.biojava.org/
*
*/
package org.biojava.nbio.structure.symmetry.geometry;
import javax.vecmath.*;
import java.util.ArrayList;
import java.util.List;
/**
* Generate the permutations and sign changes for a Triple.
* For instance, (a,0,0) becomes (±a,0,0),(0,±a,0),(0,0,±a). In the general
* case 48 tuples are returned.
*/
class Permute {
private List triples = new ArrayList();
Permute(Point3i t) {
Point3i tmp = new Point3i();
tmp.x = t.x;
tmp.y = t.y;
tmp.z = t.z;
triples.add(tmp);
int n = 1;
// Unpermuted ±x,±y,±z
if (t.x != 0) {
for (int i = 0; i < n; ++i) {
Tuple3i m = triples.get(i);
triples.add(new Point3i(-m.x, m.y, m.z));
}
n *= 2;
}
if (t.y != 0) {
for (int i = 0; i < n; ++i) {
Point3i m = triples.get(i);
triples.add(new Point3i(m.x, -m.y, m.z));
}
n *= 2;
}
if (t.z != 0) {
for (int i = 0; i < n; ++i) {
Point3i m = triples.get(i);
triples.add(new Point3i(m.x, m.y, -m.z));
}
n *= 2;
}
if (t.x == t.y && t.y == t.z) {
return;
}
// At least one differing value
for (int i = 0; i < n; ++i) {
Point3i m = triples.get(i);
triples.add(new Point3i(m.y, m.z, m.x));
triples.add(new Point3i(m.z, m.x, m.y));
}
n *= 3;
if (t.x == t.y || t.y == t.z) {
return;
}
// At least two differing values
for (int i = 0; i < n; ++i) {
Point3i m = triples.get(i);
triples.add(new Point3i(m.y, m.x, m.z));
}
n *= 2;
}
public int size() {
return triples.size();
}
public Point3i get(int i) {
return triples.get(i);
}
};
/**
* Sample possible orientations. An orientation can be represented as a
* quaternion or as a rotation axis and angle. The orientations are somewhat
* evenly distributed over orientation space, but the current implementation
* is not suited for statistically uniform sampling.
* @author Peter
*/
public final class SphereSampler {
private static final List orientations ;
// The rotational symmetries of the cube. (Not normalized, since
// PackSet.Add does this.)
private static final double[][] cubeSyms = {
{ 1, 0, 0, 0 },
// 180 deg rotations about 3 axes
{ 0, 1, 0, 0 },
{ 0, 0, 1, 0 },
{ 0, 0, 0, 1 },
// +/- 120 degree rotations about 4 leading diagonals
{ 1, 1, 1, 1 }, { 1, 1, 1, -1 }, { 1, 1, -1, 1 }, { 1, 1, -1, -1 },
{ 1, -1, 1, 1 }, { 1, -1, 1, -1 },
{ 1, -1, -1, 1 },
{ 1, -1, -1, -1 },
// +/- 90 degree rotations about 3 axes
{ 1, 1, 0, 0 }, { 1, -1, 0, 0 }, { 1, 0, 1, 0 }, { 1, 0, -1, 0 },
{ 1, 0, 0, 1 }, { 1, 0, 0, -1 },
// 180 degree rotations about 6 face diagonals
{ 0, 1, 1, 0 }, { 0, 1, -1, 0 }, { 0, 1, 0, 1 }, { 0, 1, 0, -1 },
{ 0, 0, 1, 1 }, { 0, 0, 1, -1 }, };
// Approximate spacing between grid points
private static final double delta = 0.15846;
// Amount of distortion towards grid corners to account for the projection back to a 4-sphere
private static final double sigma = 0.00;
private static final int ntot = 7416; // total number of grid points over the 24 cells
private static final int ncell = 309; // number of grid points per cell
private static final int nent = 18; // number of distinct grid points (with k>=l>=m)
// Why not (5,5,3) and (5,5,5)? Would they overlap with other cells? -SB
private static final int[] k = { 0, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5};
private static final int[] l = { 0, 1, 0, 2, 2, 1, 3, 3, 0, 2, 2, 4, 4, 4, 1, 3, 3, 5};
private static final int[] m = { 0, 1, 0, 0, 2, 1, 1, 3, 0, 0, 2, 0, 2, 4, 1, 1, 3, 1};
// number of permutations to expect for each (k,l,m) tuple
private static final int[] mult = { 1, 8, 6, 12, 8, 24, 24, 8, 6, 24, 24, 12, 24, 8, 24, 48, 24, 24};
static
{
List myorientations = new ArrayList();
// 60 equally spaced grid points covering all quaternions
// This could be removed, since the 48-cell bcc lattice has lower angle error -SB
for (int i = 0; i < IcosahedralSampler.getSphereCount(); i++) {
myorientations.add(IcosahedralSampler.getQuat4d(i));
}
// SB's interpretation of this code:
// Directly form a 4D grid over the 4-sphere of orientations. Start by
// making a 3D body-centered lattice with w=1. Then use the hypercube symmetries
// to extend this grid over the whole surface.
// The permuted (k,l,m) values together make a diagonal grid out to ±(5,5,5).
// The point spacing is distorted by the pind() function so that the
// projection of the points back to the 4-sphere will be more even.
// This is the c48u309 lattice from Karney 2006, with a max error of 10.07
// degrees.
List grid = new ArrayList();
int ncell1 = 0;
for (int n = 0; n < nent; ++n) { // for each tuple (k,l,m) above
Permute p = new Permute(new Point3i(k[n], l[n], m[n]));
assert (mult[n] == p.size());
for (int i = 0; i < mult[n]; ++i) { // for each permutation
Point3i t = p.get(i);
grid.add(new Quat4d(1.0, pind(0.5 * t.x, delta, sigma), pind(
0.5 * t.y, delta, sigma), pind(0.5 * t.z, delta, sigma)));
}
ncell1 += mult[n];
}
assert (ncell1 == ncell);
// Apply symmetry operations to distribute grid over all values of w
int nc = grid.size();
assert (nc == ncell);
for (int n = 1; n < 24; ++n) {
Quat4d q = new Quat4d(cubeSyms[n][0], cubeSyms[n][1],
cubeSyms[n][2], cubeSyms[n][3]);
for (int i = 0; i < nc; ++i) {
Quat4d qs = new Quat4d();
qs.mul(q, grid.get(i));
grid.add(qs);
// s.add(times(q, s.getOrientation(i)), s.getWeight(i)); // this data set has no weights
// Actually, it does have weights but we don't care since we don't need uniform sampling. -SB
}
}
assert (grid.size() == ntot);
myorientations.addAll(grid);
orientations = myorientations;
}
// this class cannot be instantiated
private SphereSampler() {
};
public static int getSphereCount() {
return orientations.size();
}
public static Quat4d getQuat4d(int index) {
return orientations.get(index);
}
public static void getAxisAngle(int index, AxisAngle4f axisAngle) {
axisAngle.set(orientations.get(index));
}
/**
* @param index Index between 0 and {@link #getSphereCount()}-1
* @param axisAngle (Output) modified to contain the index-th sampled orientation
*/
public static void getAxisAngle(int index, AxisAngle4d axisAngle) {
axisAngle.set(orientations.get(index));
}
// Convert from index to position. The sinh scaling tries to compensate
// for the bunching up that occurs when [1 x y z] is projected onto the
// unit sphere.
private static double pind(double ind, double delta, double sigma) {
return (sigma == 0) ? ind * delta : Math.sinh(sigma * ind * delta)
/ sigma;
}
}