Skip to content

Commit 87586f6

Browse files
committed
Refactor SymmetryAxis class to encapsulate individual axes
1 parent 3f3a2c5 commit 87586f6

2 files changed

Lines changed: 125 additions & 67 deletions

File tree

biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/internal/SymmetryAxes.java

Lines changed: 112 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
import java.util.List;
2626

2727
import javax.vecmath.Matrix4d;
28+
import javax.vecmath.Point3d;
2829

2930
import org.biojava.nbio.structure.align.multiple.MultipleAlignment;
31+
import org.biojava.nbio.structure.align.util.RotationAxis;
3032
import org.biojava.nbio.structure.symmetry.internal.CESymmParameters.SymmetryType;
3133

3234
/**
@@ -62,34 +64,93 @@ public class SymmetryAxes {
6264
/*
6365
* Implementation note: The tree is a nice explanation and a good image
6466
* for developing algorithms, but it is not constructed explicitly.
65-
* Instead, we just store the axis and order for each level and reconstruct
67+
* Instead, we just store one elementary axis for each level and reconstruct
6668
* which operators apply to a particular leaf based on that leaf's index.
6769
*/
70+
71+
/**
72+
* Represents an axis of symmetry
73+
* @author Spencer Bliven
74+
*
75+
*/
76+
public static class Axis {
77+
private Matrix4d operator;
78+
private int order;
79+
private SymmetryType symmType;
80+
private int level;
81+
82+
public Axis(Matrix4d operator, int order, SymmetryType type) {
83+
if (order < 2) {
84+
throw new IllegalArgumentException("A symmetry axis should divide a structure in > 2 parts");
85+
}
86+
if(type != SymmetryType.OPEN && type != SymmetryType.CLOSED) {
87+
throw new IllegalArgumentException("Invalid symmetry type. Only OPEN and CLOSED are allowed");
88+
}
89+
90+
this.operator = operator;
91+
this.order = order;
92+
this.symmType = type;
93+
this.level = -1;
94+
}
95+
/**
96+
* Get the transformation operator for this axis as an homogeneous matrix
97+
* @return the transformation operator
98+
*/
99+
public Matrix4d getOperator() {
100+
return operator;
101+
}
102+
public void setOperator(Matrix4d op) {
103+
this.operator = op;
104+
}
105+
/**
106+
* Get the order of this axis (closed symm) or the number of repeats
107+
* (open symm)
108+
* @return the order
109+
*/
110+
public int getOrder() {
111+
return order;
112+
}
113+
/**
114+
* @return the symmType (OPEN or CLOSED only)
115+
*/
116+
public SymmetryType getSymmType() {
117+
return symmType;
118+
}
119+
120+
/**
121+
* Get the transformation operator as a rotation axis. For open
122+
* symmetry this will have a non-zero screw component.
123+
* @return a RotationAxis for this Axis
124+
*/
125+
public RotationAxis getRotationAxis() {
126+
return new RotationAxis(operator);
127+
}
128+
/**
129+
* @return The level of this axis within it's parent hierarchy, or -1 if unset
130+
*/
131+
public int getLevel() {
132+
return level;
133+
}
134+
public void setLevel(int level) {
135+
if(level < 0) throw new IndexOutOfBoundsException("Level must be positive");
136+
this.level = level;
137+
}
138+
}
139+
68140
/**
69141
* List of all symmetry axis. They are sorted from higher to lower
70142
* in the symmetry hierarchy, where higher means that they apply
71143
* more globally and lower means that they apply to a local region
72144
* of the higher axis division.
73-
*
74-
* (Operator for each level in the hierarchy)
75-
*/
76-
private final List<Matrix4d> axes;
77-
78-
/**
79-
* Degree (branching factor) for each level in the hierarchy. This should
80-
* be the order of the corresponding transformation operator
81145
*/
82-
private final List<Integer> degrees;
146+
private final List<Axis> axes;
83147

84-
private final List<SymmetryType> symmTypes;
85148
/**
86149
* Constructor.
87150
* Initializes variables only.
88151
*/
89152
public SymmetryAxes(){
90153
axes = new ArrayList<>();
91-
degrees = new ArrayList<>();
92-
symmTypes = new ArrayList<>();
93154
}
94155

95156
/**
@@ -139,16 +200,7 @@ public void addAxis(Matrix4d axis, List<List<Integer>> superposition,
139200
* @param type indicates whether the axis has OPEN or CLOSED symmetry
140201
*/
141202
public void addAxis(Matrix4d axis, int order, SymmetryType type) {
142-
if (order < 2) {
143-
throw new IllegalArgumentException("A symmetry axis should divide a structure in > 2 parts");
144-
}
145-
if(type != SymmetryType.OPEN && type != SymmetryType.CLOSED) {
146-
throw new IllegalArgumentException("Invalid symmetry type. Only OPEN and CLOSED are allowed");
147-
}
148-
149-
axes.add(axis);
150-
degrees.add(order);
151-
symmTypes.add(type);
203+
axes.add(new Axis(axis,order,type));
152204
}
153205

154206
/**
@@ -157,17 +209,17 @@ public void addAxis(Matrix4d axis, int order, SymmetryType type) {
157209
* <P>
158210
* For instance, for a D3 case <tt>getAxisCounts(4)</tt> would return [2,0],
159211
* indicating that repeat 4 is generated by two applications of the 3-fold
160-
* axis followed by 0 appications of the two-fold axis.
212+
* axis followed by 0 applications of the two-fold axis.
161213
*
162214
* @param repeat Index of the desired repeat
163215
* @return array of the same length as axes giving the number of times
164216
* to apply each axis.
165217
*/
166218
private int[] getAxisCounts(int repeat) {
167-
int[] counts = new int[degrees.size()];
219+
int[] counts = new int[getNumLevels()];
168220

169221
for(int i = counts.length-1; i >= 0; i--) {
170-
int d = degrees.get(i);
222+
int d = axes.get(i).getOrder();
171223
counts[i] = repeat % d;
172224
repeat /= d;
173225
}
@@ -184,7 +236,7 @@ private int[] getAxisCounts(int repeat) {
184236
private int getRepeatIndex(int[] counts) {
185237
int repeat = 0;
186238
for(int i = 0; i< counts.length; i++) {
187-
repeat += counts[i]*degrees.get(i);
239+
repeat += counts[i]*axes.get(i).getOrder();
188240
}
189241
return repeat;
190242
}
@@ -195,7 +247,7 @@ private int getRepeatIndex(int[] counts) {
195247
* @param newAxis
196248
*/
197249
public void updateAxis(Integer index, Matrix4d newAxis){
198-
axes.set(index, newAxis);
250+
axes.get(index).setOperator(newAxis);
199251
}
200252

201253
/**
@@ -206,7 +258,11 @@ public void updateAxis(Integer index, Matrix4d newAxis){
206258
* @return axes elementary axes of symmetry.
207259
*/
208260
public List<Matrix4d> getElementaryAxes(){
209-
return axes;
261+
List<Matrix4d> ops = new ArrayList<Matrix4d>(getNumLevels());
262+
for(Axis axis : axes) {
263+
ops.add(axis.getOperator());
264+
}
265+
return ops;
210266
}
211267

212268
/**
@@ -221,10 +277,11 @@ public List<Matrix4d> getElementaryAxes(){
221277
* level is invalid
222278
*/
223279
public List<List<Integer>> getRepeatRelation(int level){
280+
Axis axis = axes.get(level);
224281
int m = getNumRepeats(level+1);//size of the children
225-
int d = degrees.get(level); // degree of this node
282+
int d = axis.getOrder(); // degree of this node
226283
int n = m*d; // number of repeats included
227-
if(symmTypes.get(level) == SymmetryType.OPEN) {
284+
if(axis.getSymmType() == SymmetryType.OPEN) {
228285
n -= m; // leave off last child for open symm
229286
}
230287
List<Integer> repeats = new ArrayList<>(n);
@@ -254,7 +311,7 @@ public Matrix4d getRepeatTransform(int repeat){
254311
for(int t = counts.length-1; t>=0; t--) {
255312
if( counts[t] == 0 )
256313
continue;
257-
Matrix4d axis = new Matrix4d(axes.get(t));
314+
Matrix4d axis = new Matrix4d(axes.get(t).getOperator());
258315
for(int i=0;i<counts[t];i++) {
259316
transform.mul(axis);
260317
}
@@ -269,9 +326,9 @@ public Matrix4d getRepeatTransform(int repeat){
269326
* degrees.
270327
* @return axes all symmetry axes of the structure.
271328
*/
272-
public List<Matrix4d> getSymmetryAxes(){
329+
public List<Axis> getSymmetryAxes(){
273330

274-
List<Matrix4d> symmAxes = new ArrayList<Matrix4d>();
331+
List<Axis> symmAxes = new ArrayList<>();
275332

276333
Matrix4d prior = new Matrix4d();
277334
prior.setIdentity();
@@ -287,30 +344,34 @@ public List<Matrix4d> getSymmetryAxes(){
287344
* @param prior transformation aligning the first repeat of this axis with the first overall
288345
* @param level current level
289346
*/
290-
private void getSymmetryAxes(List<Matrix4d> symmAxes, Matrix4d prior, int level) {
291-
if(level >= degrees.size() ) {
347+
private void getSymmetryAxes(List<Axis> symmAxes, Matrix4d prior, int level) {
348+
if(level >= getNumLevels() ) {
292349
return;
293350
}
294-
295-
Matrix4d elementary = axes.get(level);
351+
352+
Axis elem = axes.get(level);
353+
Matrix4d elemOp = elem.getOperator();
296354

297355
// Current axis:
298356
// elementary maps B -> A
299357
// prior maps I -> A and J -> B
300358
// want J -> I = J -> B -> A <- I= inv(prior) * elementary * prior
301359
Matrix4d invPrior = new Matrix4d(prior);
302360
invPrior.invert();
303-
Matrix4d currAxis = new Matrix4d(prior);
304-
currAxis.mul(elementary);
305-
Matrix4d newPrior = new Matrix4d(currAxis);//save intermediate for later
306-
currAxis.mul(invPrior);
361+
Matrix4d currAxisOp = new Matrix4d(prior);
362+
currAxisOp.mul(elemOp);
363+
Matrix4d newPrior = new Matrix4d(currAxisOp);//save intermediate for later
364+
currAxisOp.mul(invPrior);
365+
Axis currAxis = new Axis(currAxisOp,elem.getOrder(),elem.getSymmType());
366+
currAxis.setLevel(level);
307367
symmAxes.add(currAxis);
368+
308369
//New prior is elementary^d*prior
309370
//Remember that all degrees are at least 2
310371
getSymmetryAxes(symmAxes,prior,level+1);
311372
getSymmetryAxes(symmAxes,newPrior,level+1);
312-
for(int d=2;d<degrees.get(level);d++) {
313-
newPrior.mul(elementary);
373+
for(int d=2;d<elem.getOrder();d++) {
374+
newPrior.mul(elemOp);
314375
getSymmetryAxes(symmAxes,newPrior,level+1);
315376
}
316377
}
@@ -343,23 +404,19 @@ public int getNumRepeats() {
343404
private int getNumRepeats(int level) {
344405
int size = 1;
345406
// Return 1 for illegally high level
346-
if(level < degrees.size()) {
347-
for(int order : degrees.subList(level, degrees.size())) {
348-
size *= order;
407+
if(level < getNumLevels()) {
408+
for(Axis axis : axes.subList(level, getNumLevels())) {
409+
size *= axis.getOrder();
349410
}
350411
}
351412
return size;
352413
}
353-
354-
public SymmetryType getSymmetryType(int level) {
355-
return symmTypes.get(level);
356-
}
357-
358-
public Matrix4d getElementaryAxis(int level) {
414+
415+
public Axis getElementaryAxis(int level) {
359416
return axes.get(level);
360417
}
361-
418+
362419
public int getNumLevels() {
363-
return degrees.size();
420+
return axes.size();
364421
}
365422
}

biojava-structure/src/test/java/org/biojava/nbio/structure/symmetry/internal/TestSymmetryAxes.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import javax.vecmath.Vector3d;
1212

1313
import org.biojava.nbio.structure.symmetry.internal.CESymmParameters.SymmetryType;
14+
import org.biojava.nbio.structure.symmetry.internal.SymmetryAxes.Axis;
1415
import org.junit.Test;
1516

1617

@@ -96,32 +97,32 @@ public void testClosedCase() {
9697

9798
Point3d x;
9899

99-
List<Matrix4d> symmetryAxes = axes.getSymmetryAxes();
100+
List<Axis> symmetryAxes = axes.getSymmetryAxes();
100101
assertEquals(5,symmetryAxes.size());
101102
int axisNum = 0;
102103
// Repeat 2 -> 0 (90 deg around z)
103104
x = new Point3d(repeats[2]);
104-
symmetryAxes.get(axisNum).transform(x);
105+
symmetryAxes.get(axisNum).getOperator().transform(x);
105106
assertTrue(String.format("SymmAxis %d of %s=%s not %s",axisNum,round(repeats[2]),round(x),round(repeats[0])),x.epsilonEquals(repeats[0], 1e-5));
106107
axisNum++;
107108
// Repeat 1 -> 0 (180 deg around x)
108109
x = new Point3d(repeats[1]);
109-
symmetryAxes.get(axisNum).transform(x);
110+
symmetryAxes.get(axisNum).getOperator().transform(x);
110111
assertTrue(String.format("SymmAxis %d of %s=%s not %s",axisNum,round(repeats[1]),round(x),round(repeats[0])),x.epsilonEquals(repeats[0], 1e-5));
111112
axisNum++;
112113
// Repeat 3 -> 2 (180 deg around y)
113114
x = new Point3d(repeats[3]);
114-
symmetryAxes.get(axisNum).transform(x);
115+
symmetryAxes.get(axisNum).getOperator().transform(x);
115116
assertTrue(String.format("SymmAxis %d of %s=%s not %s",axisNum,round(repeats[3]),round(x),round(repeats[2])),x.epsilonEquals(repeats[2], 1e-5));
116117
axisNum++;
117118
// Repeat 5 -> 4 (180 deg around x)
118119
x = new Point3d(repeats[5]);
119-
symmetryAxes.get(axisNum).transform(x);
120+
symmetryAxes.get(axisNum).getOperator().transform(x);
120121
assertTrue(String.format("SymmAxis %d of %s=%s not %s",axisNum,round(repeats[5]),round(x),round(repeats[4])),x.epsilonEquals(repeats[4], 1e-5));
121122
axisNum++;
122123
// Repeat 7 -> 6 (180 deg around y)
123124
x = new Point3d(repeats[7]);
124-
symmetryAxes.get(axisNum).transform(x);
125+
symmetryAxes.get(axisNum).getOperator().transform(x);
125126
assertTrue(String.format("SymmAxis %d of %s=%s not %s",axisNum,round(repeats[7]),round(x),round(repeats[6])),x.epsilonEquals(repeats[6], 1e-5));
126127
axisNum++;
127128
}
@@ -209,33 +210,33 @@ public void testOpenCase() {
209210

210211
Point3d x;
211212

212-
List<Matrix4d> symmetryAxes = axes.getSymmetryAxes();
213+
List<Axis> symmetryAxes = axes.getSymmetryAxes();
213214
assertEquals(5,symmetryAxes.size());
214215
int axisNum = 0;
215216
// Repeat 2 -> 0 (shift 1)
216217
x = new Point3d(repeats[2]);
217-
symmetryAxes.get(axisNum).transform(x);
218+
symmetryAxes.get(axisNum).getOperator().transform(x);
218219
assertTrue(String.format("SymmAxis %d of %s=%s not %s",axisNum,round(repeats[2]),round(x),round(repeats[0])),x.epsilonEquals(repeats[0], 1e-5));
219220
axisNum++;
220221
// All of these are actually equivalent
221222
// Repeat 1 -> 0 (180 deg around x)
222223
x = new Point3d(repeats[1]);
223-
symmetryAxes.get(axisNum).transform(x);
224+
symmetryAxes.get(axisNum).getOperator().transform(x);
224225
assertTrue(String.format("SymmAxis %d of %s=%s not %s",axisNum,round(repeats[1]),round(x),round(repeats[0])),x.epsilonEquals(repeats[0], 1e-5));
225226
axisNum++;
226227
// Repeat 3 -> 2 (180 deg around x)
227228
x = new Point3d(repeats[3]);
228-
symmetryAxes.get(axisNum).transform(x);
229+
symmetryAxes.get(axisNum).getOperator().transform(x);
229230
assertTrue(String.format("SymmAxis %d of %s=%s not %s",axisNum,round(repeats[3]),round(x),round(repeats[2])),x.epsilonEquals(repeats[2], 1e-5));
230231
axisNum++;
231232
// Repeat 5 -> 4 (180 deg around x)
232233
x = new Point3d(repeats[5]);
233-
symmetryAxes.get(axisNum).transform(x);
234+
symmetryAxes.get(axisNum).getOperator().transform(x);
234235
assertTrue(String.format("SymmAxis %d of %s=%s not %s",axisNum,round(repeats[5]),round(x),round(repeats[4])),x.epsilonEquals(repeats[4], 1e-5));
235236
axisNum++;
236237
// Repeat 7 -> 6 (180 deg around x)
237238
x = new Point3d(repeats[7]);
238-
symmetryAxes.get(axisNum).transform(x);
239+
symmetryAxes.get(axisNum).getOperator().transform(x);
239240
assertTrue(String.format("SymmAxis %d of %s=%s not %s",axisNum,round(repeats[7]),round(x),round(repeats[6])),x.epsilonEquals(repeats[6], 1e-5));
240241
axisNum++;
241242
}

0 commit comments

Comments
 (0)