001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.nbio.structure.symmetry.core;
022
023import org.biojava.nbio.structure.symmetry.geometry.MomentsOfInertia;
024import org.biojava.nbio.structure.symmetry.geometry.SuperPosition;
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028import javax.vecmath.*;
029import java.util.*;
030
031public class HelixAxisAligner extends AxisAligner {
032        
033        private static final Logger LOGGER = LoggerFactory.getLogger(HelixAxisAligner.class);
034        
035        private static final Vector3d Y_AXIS = new Vector3d(0,1,0);
036        private static final Vector3d Z_AXIS = new Vector3d(0,0,1);
037
038        private Subunits subunits = null;
039        private HelixLayers helixLayers = null;
040
041        private Matrix4d transformationMatrix = new Matrix4d();
042        private Matrix4d reverseTransformationMatrix = new Matrix4d();
043        private Vector3d referenceVector = new Vector3d();
044        private Vector3d principalRotationVector = new Vector3d();
045        private Vector3d[] principalAxesOfInertia = null;
046        private List<List<Integer>> alignedOrbits = null;
047
048        private Vector3d minBoundary = new Vector3d();
049        private Vector3d maxBoundary = new Vector3d();
050        private double xzRadiusMax = Double.MIN_VALUE;
051
052        boolean modified = true;
053
054        public HelixAxisAligner(QuatSymmetryResults results) {
055                this.subunits = results.getSubunits();
056                this.helixLayers = results.getHelixLayers();
057                if (subunits == null) {
058                        throw new IllegalArgumentException("HelixAxisTransformation: Subunits are null");
059                } else if (helixLayers == null) {
060                        throw new IllegalArgumentException("HelixAxisTransformation: HelixLayers is null");
061                } else if (subunits.getSubunitCount() == 0) {
062                        throw new IllegalArgumentException("HelixAxisTransformation: Subunits is empty");
063                } else if (helixLayers.size() == 0) {
064                        throw new IllegalArgumentException("HelixAxisTransformation: HelixLayers is empty");
065                }
066        }
067
068
069        /* (non-Javadoc)
070         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getTransformation()
071         */
072        @Override
073        public String getSymmetry() {
074                run();
075                return "H";
076        }
077
078        /* (non-Javadoc)
079         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getTransformation()
080         */
081        @Override
082        public Matrix4d getTransformation() {
083                run();
084                return transformationMatrix;
085        }
086
087        /* (non-Javadoc)
088         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getRotationMatrix()
089         */
090        @Override
091        public Matrix3d getRotationMatrix() {
092                run();
093                Matrix3d m = new Matrix3d();
094                transformationMatrix.getRotationScale(m);
095                return m;
096        }
097
098        /* (non-Javadoc)
099         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getReverseTransformation()
100         */
101        @Override
102        public Matrix4d getReverseTransformation() {
103                run();
104                return reverseTransformationMatrix;
105        }
106
107        /* (non-Javadoc)
108         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getPrincipalRotationAxis()
109         */
110        @Override
111        public Vector3d getPrincipalRotationAxis() {
112                run();
113                return principalRotationVector;
114        }
115
116        /* (non-Javadoc)
117         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getRotationReferenceAxis()
118         */
119        @Override
120        public Vector3d getRotationReferenceAxis() {
121                run();
122                return referenceVector;
123        }
124
125        /* (non-Javadoc)
126         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getPrincipalAxesOfInertia()
127         */
128        @Override
129        public Vector3d[] getPrincipalAxesOfInertia() {
130                run();
131                return principalAxesOfInertia;
132        }
133
134        /* (non-Javadoc)
135         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getDimension()
136         */
137        @Override
138        public Vector3d getDimension() {
139                run();
140                Vector3d dimension = new Vector3d();
141                dimension.sub(maxBoundary, minBoundary);
142                dimension.scale(0.5);
143                return dimension;
144        }
145
146        /* (non-Javadoc)
147         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getXYRadius()
148         */
149        @Override
150        public double getRadius() {
151                run();
152                return xzRadiusMax;
153        }
154
155        /* (non-Javadoc)
156         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getGeometicCenterTransformation()
157         */
158        @Override
159        public Matrix4d getGeometicCenterTransformation() {
160                run();
161
162                Matrix4d geometricCentered = new Matrix4d(reverseTransformationMatrix);
163                geometricCentered.setTranslation(new Vector3d(getGeometricCenter()));
164
165                return geometricCentered;
166        }
167
168        /* (non-Javadoc)
169         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getGeometricCenter()
170         */
171        @Override
172        public Point3d getGeometricCenter() {
173                run();
174
175                Point3d geometricCenter = new Point3d();
176                Vector3d translation = new Vector3d();
177//              reverseTransformationMatrix.get(translation);
178
179                // TODO does this apply to the helic case?
180                // calculate adjustment around z-axis and transform adjustment to
181                //  original coordinate frame with the reverse transformation
182
183//              Vector3d corr = new Vector3d(0,minBoundary.y+getDimension().y, 0);
184//              reverseTransformationMatrix.transform(corr);
185//              geometricCenter.set(corr);
186
187                reverseTransformationMatrix.transform(translation);
188                geometricCenter.add(translation);
189                return geometricCenter;
190        }
191
192        /* (non-Javadoc)
193         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getCentroid()
194         */
195        @Override
196        public Point3d getCentroid() {
197                return new Point3d(subunits.getCentroid());
198        }
199
200        /* (non-Javadoc)
201         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getSubunits()
202         */
203        @Override
204        public Subunits getSubunits() {
205                return subunits;
206        }
207
208        public HelixLayers getHelixLayers() {
209                run();
210                return helixLayers;
211        }
212
213        /* (non-Javadoc)
214         * @see org.biojava.nbio.structure.quaternary.core.AxisAligner#getOrbits()
215         */
216        @Override
217        public List<List<Integer>> getOrbits() {
218                run();
219                return alignedOrbits;
220        }
221
222        /**
223         * @return
224         */
225
226        private void run () {
227                if (modified) {
228                        calcPrincipalRotationVector();
229                        calcPrincipalAxes();
230                        calcReferenceVector();
231                        calcTransformation();
232                        // orient helix along Y axis by rotating 90 degrees around X-axis
233                        transformationMatrix = reorientHelix(0);
234
235                        calcReverseTransformation();
236                        calcBoundaries();
237                        calcAlignedOrbits();
238                        calcCenterOfRotation();
239
240                        // orient helix along Y axis by rotating 90 degrees around X-axis
241//                      transformationMatrix = reorientHelix(0);
242//                      calcReverseTransformation();
243
244                        modified = false;
245                }
246        }
247
248        public Point3d calcCenterOfRotation() {
249                List<Integer> line = getLongestLayerLine();
250
251                // can't determine center of rotation if there are only 2 points
252                // TODO does this ever happen??
253                if (line.size() < 3) {
254                        return subunits.getCentroid();
255                }
256
257                Point3d centerOfRotation = new Point3d();
258                List<Point3d> centers = subunits.getOriginalCenters();
259
260                // calculate helix mid points for each set of 3 adjacent subunits
261                for (int i = 0; i < line.size()-2; i++) {
262                        Point3d p1 = new Point3d(centers.get(line.get(i)));
263                        Point3d p2 = new Point3d(centers.get(line.get(i+1)));
264                        Point3d p3 = new Point3d(centers.get(line.get(i+2)));
265                        transformationMatrix.transform(p1);
266                        transformationMatrix.transform(p2);
267                        transformationMatrix.transform(p3);
268                        centerOfRotation.add(getMidPoint(p1, p2, p3));
269                }
270
271                // average over all midpoints to find best center of rotation
272                centerOfRotation.scale(1/(line.size()-2));
273                // since helix is aligned along the y-axis, with an origin at y = 0, place the center of rotation there
274                centerOfRotation.y = 0;
275                // transform center of rotation to the original coordinate frame
276                reverseTransformationMatrix.transform(centerOfRotation);
277//              System.out.println("center of rotation: " + centerOfRotation);
278                return centerOfRotation;
279        }
280
281        private List<Integer> getLongestLayerLine() {
282                int len = 0;
283                int index = 0;
284
285                Helix helix = helixLayers.getByLargestContacts();
286                List<List<Integer>> layerLines = helix.getLayerLines();
287                for (int i = 0; i < layerLines.size(); i++) {
288                        if (layerLines.get(i).size() > len) {
289                                len = layerLines.get(i).size();
290                                index = i;
291                        }
292                }
293                return layerLines.get(index);
294        }
295
296        /**
297         * Return a midpoint of a helix, calculated from three positions
298         * of three adjacent subunit centers.
299         * @param p1 center of first subunit
300         * @param p2 center of second subunit
301         * @param p3 center of third subunit
302         * @return midpoint of helix
303         */
304        private Point3d getMidPoint(Point3d p1, Point3d p2, Point3d p3) {
305                Vector3d v1 = new Vector3d();
306                v1.sub(p1, p2);
307                Vector3d v2 = new Vector3d();
308                v2.sub(p3, p2);
309                Vector3d v3 = new Vector3d();
310                v3.add(v1);
311                v3.add(v2);
312                v3.normalize();
313
314                // calculat the total distance between to subunits
315                double dTotal = v1.length();
316                // calculate the rise along the y-axis. The helix axis is aligned with y-axis,
317                // therfore, the rise between subunits is the y-distance
318                double rise = p2.y - p1.y;
319                // use phythagorean theoremm to calculate chord length between two subunit centers
320                double chord = Math.sqrt(dTotal*dTotal - rise*rise);
321//              System.out.println("Chord d: " + dTotal + " rise: " + rise + "chord: " + chord);
322                double angle = helixLayers.getByLargestContacts().getAxisAngle().angle;
323
324                // using the axis angle and the chord length, we can calculate the radius of the helix
325                // http://en.wikipedia.org/wiki/Chord_%28geometry%29
326                double radius = chord/Math.sin(angle/2)/2; // can this go to zero?
327//              System.out.println("Radius: " + radius);
328
329                // project the radius onto the vector that points toward the helix axis
330                v3.scale(radius);
331                v3.add(p2);
332//              System.out.println("Angle: " + Math.toDegrees(helixLayers.getByLowestAngle().getAxisAngle().angle));
333                Point3d cor = new Point3d(v3);
334                return cor;
335        }
336
337        private Matrix4d reorientHelix(int index) {
338                Matrix4d matrix = new Matrix4d();
339                matrix.setIdentity();
340                matrix.setRotation(new AxisAngle4d(1,0,0,Math.PI/2*(index+1)));
341                matrix.mul(transformationMatrix);
342        return matrix;
343        }
344
345        /**
346         * Returns a list of orbits (an orbit is a cyclic permutation of subunit indices that are related
347         * by a rotation around the principal rotation axis) ordered from the +z direction to the -z direction (z-depth).
348         * Within an orbit, subunit indices are ordered with a clockwise rotation around the z-axis.
349         * @return list of orbits ordered by z-depth
350         */
351        private void calcAlignedOrbits() {
352                Map<Double, List<Integer>> depthMap = new TreeMap<Double, List<Integer>>();
353                double[] depth = getSubunitZDepth();
354                alignedOrbits = calcOrbits();
355
356                // calculate the mean depth of orbit along z-axis
357                for (List<Integer> orbit: alignedOrbits) {
358                        // calculate the mean depth along z-axis for each orbit
359                        double meanDepth = 0;
360                        for (int subunit: orbit) {
361                                meanDepth += depth[subunit];
362                        }
363                        meanDepth /= orbit.size();
364
365                        if (depthMap.get(meanDepth) != null) {
366                                meanDepth += 0.01;
367                        }
368//                      System.out.println("calcAlignedOrbits: " + meanDepth + " orbit: " + orbit);
369                        depthMap.put(meanDepth, orbit);
370                }
371
372                // now fill orbits back into list ordered by depth
373                alignedOrbits.clear();
374                for (List<Integer> orbit: depthMap.values()) {
375                        // order subunit in a clockwise rotation around the z-axis
376                        /// starting at the 12 O-clock position (+y position)
377                        // TODO how should this be aligned??
378        //              alignWithReferenceAxis(orbit);
379                        alignedOrbits.add(orbit);
380                }
381        }
382
383
384        private void calcTransformation() {
385                calcTransformationBySymmetryAxes();
386                // make sure this value is zero. On Linux this value is 0.
387                transformationMatrix.setElement(3, 3, 1.0);
388        }
389
390        private void calcReverseTransformation() {
391                reverseTransformationMatrix.invert(transformationMatrix);
392        }
393
394        private void calcTransformationBySymmetryAxes() {
395                Vector3d[] axisVectors = new Vector3d[2];
396                axisVectors[0] = new Vector3d(principalRotationVector);
397                axisVectors[1] = new Vector3d(referenceVector);
398
399                //  y,z axis centered at the centroid of the subunits
400                Vector3d[] referenceVectors = new Vector3d[2];
401                referenceVectors[0] = new Vector3d(Z_AXIS);
402                referenceVectors[1] = new Vector3d(Y_AXIS);
403
404                transformationMatrix = alignAxes(axisVectors, referenceVectors);
405
406                // combine with translation
407                Matrix4d combined = new Matrix4d();
408                combined.setIdentity();
409                Vector3d trans = new Vector3d(subunits.getCentroid());
410                trans.negate();
411                combined.setTranslation(trans);
412                transformationMatrix.mul(combined);
413
414                // for helical geometry, set a canonical view for the Z direction
415                calcZDirection();
416        }
417
418        /**
419         * Returns a transformation matrix that rotates refPoints to match
420         * coordPoints
421         * @param refPoints the points to be aligned
422         * @param referenceVectors
423         * @return
424         */
425        private Matrix4d alignAxes(Vector3d[] axisVectors, Vector3d[] referenceVectors) {
426                Matrix4d m1 = new Matrix4d();
427                AxisAngle4d a = new AxisAngle4d();
428                Vector3d axis = new Vector3d();
429
430                // calculate rotation matrix to rotate refPoints[0] into coordPoints[0]
431                Vector3d v1 = new Vector3d(axisVectors[0]);
432                Vector3d v2 = new Vector3d(referenceVectors[0]);
433                double dot = v1.dot(v2);
434                if (Math.abs(dot) < 0.999) {
435                        axis.cross(v1,v2);
436                        axis.normalize();
437                        a.set(axis, v1.angle(v2));
438                        m1.set(a);
439                        // make sure matrix element m33 is 1.0. It's 0 on Linux.
440                        m1.setElement(3,  3, 1.0);
441                } else if (dot > 0) {
442                        // parallel axis, nothing to do -> identity matrix
443                        m1.setIdentity();
444                } else if (dot < 0) {
445                        // anti-parallel axis, flip around x-axis
446                        m1.set(flipX());
447                }
448
449                // apply transformation matrix to all refPoints
450                m1.transform(axisVectors[0]);
451                m1.transform(axisVectors[1]);
452
453                // calculate rotation matrix to rotate refPoints[1] into coordPoints[1]
454                v1 = new Vector3d(axisVectors[1]);
455                v2 = new Vector3d(referenceVectors[1]);
456                Matrix4d m2 = new Matrix4d();
457                dot = v1.dot(v2);
458                if (Math.abs(dot) < 0.999) {
459                        axis.cross(v1,v2);
460                        axis.normalize();
461                        a.set(axis, v1.angle(v2));
462                        m2.set(a);
463                        // make sure matrix element m33 is 1.0. It's 0 on Linux.
464                        m2.setElement(3,  3, 1.0);
465                } else if (dot > 0) {
466                        // parallel axis, nothing to do -> identity matrix
467                        m2.setIdentity();
468                } else if (dot < 0) {
469                        // anti-parallel axis, flip around z-axis
470                        m2.set(flipZ());
471                }
472
473                // apply transformation matrix to all refPoints
474                m2.transform(axisVectors[0]);
475                m2.transform(axisVectors[1]);
476
477                // combine the two rotation matrices
478                m2.mul(m1);
479
480                // the RMSD should be close to zero
481                Point3d[] axes = new Point3d[2];
482                axes[0] = new Point3d(axisVectors[0]);
483                axes[1] = new Point3d(axisVectors[1]);
484                Point3d[] ref = new Point3d[2];
485                ref[0] = new Point3d(referenceVectors[0]);
486                ref[1] = new Point3d(referenceVectors[1]);
487                if (SuperPosition.rmsd(axes, ref) > 0.1) {
488                        LOGGER.info("AxisTransformation: axes alignment is off. RMSD: " + SuperPosition.rmsd(axes, ref));
489                }
490
491                return m2;
492        }
493
494        private void calcPrincipalAxes() {
495                MomentsOfInertia moi = new MomentsOfInertia();
496
497                for (Point3d[] list: subunits.getTraces()) {
498                        for (Point3d p: list) {
499                                moi.addPoint(p, 1.0);
500                        }
501                }
502                principalAxesOfInertia = moi.getPrincipalAxes();
503        }
504
505        /**
506         * Calculates the min and max boundaries of the structure after it has been
507         * transformed into its canonical orientation.
508         */
509        private void calcBoundaries() {
510                minBoundary.x = Double.MAX_VALUE;
511                maxBoundary.x = Double.MIN_VALUE;
512                minBoundary.y = Double.MAX_VALUE;
513                maxBoundary.x = Double.MIN_VALUE;
514                minBoundary.z = Double.MAX_VALUE;
515                maxBoundary.z = Double.MIN_VALUE;
516                xzRadiusMax = Double.MIN_VALUE;
517
518                Point3d probe = new Point3d();
519
520                for (Point3d[] list: subunits.getTraces()) {
521                        for (Point3d p: list) {
522                                probe.set(p);
523                                transformationMatrix.transform(probe);
524
525                                minBoundary.x = Math.min(minBoundary.x, probe.x);
526                                maxBoundary.x = Math.max(maxBoundary.x, probe.x);
527                                minBoundary.y = Math.min(minBoundary.y, probe.y);
528                                maxBoundary.y = Math.max(maxBoundary.y, probe.y);
529                                minBoundary.z = Math.min(minBoundary.z, probe.z);
530                                maxBoundary.z = Math.max(maxBoundary.z, probe.z);
531                                xzRadiusMax = Math.max(xzRadiusMax, Math.sqrt(probe.x*probe.x + probe.z * probe.z));
532                        }
533                }
534//              System.out.println("MinBoundary: " + minBoundary);
535//              System.out.println("MaxBoundary: " + maxBoundary);
536//              System.out.println("zxRadius: " + xzRadiusMax);
537        }
538
539        /*
540         * Modifies the rotation part of the transformation axis for
541         * a Cn symmetric complex, so that the narrower end faces the
542         * viewer, and the wider end faces away from the viewer. Example: 3LSV
543         */
544        private void calcZDirection() {
545                calcBoundaries();
546
547                // if the longer part of the structure faces towards the back (-z direction),
548                // rotate around y-axis so the longer part faces the viewer (+z direction)
549                if (Math.abs(minBoundary.z) > Math.abs(maxBoundary.z)) {
550                        Matrix4d rot = flipY();
551                        rot.mul(transformationMatrix);
552                        transformationMatrix.set(rot);
553                }
554        }
555
556        private double[] getSubunitZDepth() {
557                int n = subunits.getSubunitCount();
558                double[] depth = new double[n];
559                Point3d probe = new Point3d();
560
561                // transform subunit centers into z-aligned position and calculate
562                // z-coordinates (depth) along the z-axis.
563                for (int i = 0; i < n; i++) {
564                        Point3d p= subunits.getCenters().get(i);
565                        probe.set(p);
566                        transformationMatrix.transform(probe);
567                        depth[i] = probe.z;
568                }
569                return depth;
570        }
571
572        /**
573         * Returns a list of list of subunit ids that form an "orbit", i.e. they
574         * are transformed into each other during a rotation around the principal symmetry axis (z-axis)
575         * @return
576         */
577        private List<List<Integer>> calcOrbits() {
578                int n = subunits.getSubunitCount();
579
580                List<List<Integer>> orbits = new ArrayList<List<Integer>>();
581                for (int i = 0; i < n; i++) {
582                        orbits.add(Collections.singletonList(i));
583                }
584
585                return orbits;
586        }
587
588        /**
589         * Returns a vector along the principal rotation axis for the
590         * alignment of structures along the z-axis
591         * @return principal rotation vector
592         */
593        private void calcPrincipalRotationVector() {
594//              AxisAngle4d axisAngle = helixLayers.getByLowestAngle().getAxisAngle();
595                AxisAngle4d axisAngle = helixLayers.getByLargestContacts().getAxisAngle();
596                principalRotationVector = new Vector3d(axisAngle.x, axisAngle.y, axisAngle.z);
597        }
598
599        /**
600         * Returns a vector perpendicular to the principal rotation vector
601         * for the alignment of structures in the xy-plane
602         * @return reference vector
603         */
604        private void calcReferenceVector() {
605                referenceVector = getReferenceAxisCylic();
606
607                if (referenceVector == null) {
608                        LOGGER.warn("no reference vector found. Using y-axis.");
609                        referenceVector = new Vector3d(Y_AXIS);
610                }
611                // make sure reference vector is perpendicular principal roation vector
612                referenceVector = orthogonalize(principalRotationVector, referenceVector);
613        }
614
615        private Vector3d orthogonalize(Vector3d vector1, Vector3d vector2) {
616                double dot = vector1.dot(vector2);
617                Vector3d ref = new Vector3d(vector2);
618//              System.out.println("p.r: " + dot);
619//              System.out.println("Orig refVector: " + referenceVector);
620                if (dot < 0) {
621                        vector2.negate();
622                }
623                if (Math.abs(dot) < 0.00001) {
624                        LOGGER.info("HelixAxisAligner: reference axis parallel");
625                }
626                vector2.cross(vector1, vector2);
627//              System.out.println("Intermed. refVector: " + vector2);
628                vector2.normalize();
629//              referenceVector.cross(referenceVector, principalRotationVector);
630                vector2.cross(vector1, vector2);
631                vector2.normalize();
632                if (ref.dot(vector2) < 0) {
633                        vector2.negate();
634                }
635//              System.out.println("Mod. refVector: " + vector2);
636                return vector2;
637        }
638        /**
639         * Returns the default reference vector for the alignment of Cn structures
640         * @return
641         */
642        private Vector3d getReferenceAxisCylic() {
643                // get principal axis vector that is perpendicular to the principal
644                // rotation vector
645                Vector3d vmin = null;
646                double dotMin = 1.0;
647                for (Vector3d v: principalAxesOfInertia) {
648                        if (Math.abs(principalRotationVector.dot(v)) < dotMin) {
649                                dotMin = Math.abs(principalRotationVector.dot(v));
650                                vmin = new Vector3d(v);
651                        }
652                }
653                if (principalRotationVector.dot(vmin) < 0) {
654                        vmin.negate();
655                }
656
657                return vmin;
658        }
659
660        private static Matrix4d flipX() {
661                Matrix4d rot = new Matrix4d();
662                rot.m00 = 1;
663                rot.m11 = -1;
664                rot.m22 = -1;
665                rot.m33 = 1;
666                return rot;
667        }
668
669        private static Matrix4d flipY() {
670                Matrix4d rot = new Matrix4d();
671                rot.m00 = -1;
672                rot.m11 = 1;
673                rot.m22 = -1;
674                rot.m33 = 1;
675                return rot;
676        }
677
678        private static Matrix4d flipZ() {
679                Matrix4d rot = new Matrix4d();
680                rot.m00 = -1;
681                rot.m11 = -1;
682                rot.m22 = 1;
683                rot.m33 = 1;
684                return rot;
685        }
686
687}