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.geometry;
022
023import javax.vecmath.Matrix3d;
024import javax.vecmath.Point3d;
025import java.util.Arrays;
026import java.util.List;
027
028
029public class Icosahedron implements Polyhedron {
030        private static int[] lineLoop1 = {4,0,1,2,3,4,5,1};
031        private static int[] lineLoop2 = {3,0,2};
032        private static int[] lineLoop3 = {0,5};
033        private static int[] lineLoop4 = {11,3,7,4,8,6,7,8,9,10,11,7};
034        private static int[] lineLoop5 = {6,11,2,10,1,9,6,10};
035        private static int[] lineLoop6 = {8,5,9};
036
037        private double circumscribedRadius = 1.0;
038
039        /**
040         * Returns the radius of a circumscribed sphere, that goes
041         * through all vertices
042         * @return the cirumscribedRadius
043         */
044        @Override
045        public double getCirumscribedRadius() {
046                return circumscribedRadius;
047        }
048
049        /**
050         * Set the radius of a circumscribed sphere, that goes
051         * through all vertices
052         * @param cirumscribedRadius the cirumscribedRadius to set
053         */
054        public void setCirumscribedRadius(double cirumscribedRadius) {
055                this.circumscribedRadius = cirumscribedRadius;
056        }
057        /**
058         * Returns the radius of an inscribed sphere, that is tangent to each
059         * of the icosahedron's faces
060         * @return the inscribedRadius
061         */
062        public double getInscribedRadius() {
063                double side = getSideLengthFromCircumscribedRadius(circumscribedRadius);
064                return getInscribedRadiusFromSideLength(side);
065        }
066
067        /**
068         * Sets the radius of an inscribed sphere, that is tangent to each
069         * of the icosahedron's faces
070         * @param inscribedRadius the inscribedRadius to set
071         */
072        public void setInscribedRadius(double radius) {
073                double side = getSideLengthFromInscribedRadius(radius);
074                this.circumscribedRadius = getCircumscribedRadiusFromSideLength(side);
075        }
076
077        /**
078         * Returns the radius of a sphere, that is tangent to each
079         * of the icosahedron's edges
080         *
081         * @return the midRadius
082         */
083        public double getMidRadius() {
084                double side = getSideLengthFromCircumscribedRadius(circumscribedRadius);
085                return getMiddleRadiusFromSideLength(side);
086        }
087
088        /**
089         * Sets the radius of radius of a sphere, that is tangent to each
090         * of the icosahedron's edges
091         * @param midRadius the midRadius to set
092         */
093        public void setMidRadius(double radius) {
094                double side = getSideLengthFromMiddleRadius(radius);
095                this.circumscribedRadius = getCircumscribedRadiusFromSideLength(side);
096        }
097
098        @Override
099        public Point3d[] getVertices() {
100                Point3d[] icosahedron = new Point3d[12];
101                // see http://answers.yahoo.com/question/index?qid=20080108041441AAJCjEu
102                double c = circumscribedRadius * 1 / Math.sqrt(5);
103                double s = 2 * c; // golden ratio
104                double c1 = Math.sqrt((3-Math.sqrt(5))/8); // cos(2Pi/5)
105                double s1 = Math.sqrt((5+Math.sqrt(5))/8); // sin(2Pi/5)
106                double c2 = Math.sqrt((3+Math.sqrt(5))/8); // cos(Pi/5)
107                double s2 = Math.sqrt((5-Math.sqrt(5))/8); // sin(Pi/5)
108
109                icosahedron[0] = new Point3d(0, 0, circumscribedRadius);
110                icosahedron[1] = new Point3d(s, 0, c);
111                icosahedron[2] = new Point3d(s*c1, s*s1, c);
112                icosahedron[3] = new Point3d(-s*c2, s*s2, c);
113                icosahedron[4] = new Point3d(-s*c2, -s*s2, c);
114                icosahedron[5] = new Point3d(s*c1, -s*s1, c);
115                for (int i = 0; i < 6; i++) {
116                        icosahedron[i+6] = new Point3d(icosahedron[i]);
117                        icosahedron[i+6].negate();
118                }
119
120                Matrix3d m = new Matrix3d();
121                m.rotZ(Math.PI/10);
122                for (Point3d p: icosahedron) {
123                        m.transform(p);
124                }
125
126                return icosahedron;
127        };
128
129        @Override
130        public List<int[]> getLineLoops() {
131                return Arrays.asList(lineLoop1, lineLoop2, lineLoop3, lineLoop4, lineLoop5, lineLoop6);
132        }
133
134        @Override
135        public int getViewCount() {
136                return 3;
137        }
138
139        @Override
140        public String getViewName(int index) {
141                String name;
142                switch (index) {
143                case 0:  name = "C5 axis vertex-centered";
144                break;
145                case 1:  name = "C3 axis face-centered";
146                break;
147                case 2:  name = "C2 axis edge-centered";
148                break;
149                default: throw new IllegalArgumentException("getViewMatrix: index out of range:" + index);
150                }
151                return name;
152        }
153
154        @Override
155        public Matrix3d getViewMatrix(int index) {
156                Matrix3d m = new Matrix3d();
157                switch (index) {
158                case 0:
159                        m.setIdentity(); // front vertex-centered
160                        break;
161                case 1:
162                        m.rotX(-0.6523581397843639); // back face-centered -0.5535743588970415 m.rotX(Math.toRadians(-26));
163                        break;
164                case 2:
165                        m.rotZ(Math.PI/2);
166                        Matrix3d m1 = new Matrix3d();
167                        m1.rotX(-1.0172219678978445);
168                        m.mul(m1);
169                        break;
170                default:
171                        throw new IllegalArgumentException("getViewMatrix: index out of range:" + index);
172                }
173                return m;
174        }
175
176        private static double getSideLengthFromInscribedRadius(double radius) {
177                return radius / (Math.sqrt(3)/12 * (3 + Math.sqrt(5)));
178        }
179
180        private static double getInscribedRadiusFromSideLength(double sideLength) {
181                return sideLength * (Math.sqrt(3)/12 * (3 + Math.sqrt(5)));
182        }
183
184        private static double getSideLengthFromMiddleRadius(double radius) {
185                return radius * 4 /(1 + Math.sqrt(5));
186        }
187
188        private static double getMiddleRadiusFromSideLength(double sideLength) {
189                return sideLength / 4 * (1 + Math.sqrt(5));
190        }
191
192        private static double getSideLengthFromCircumscribedRadius(double radius) {
193                return radius * 4 / Math.sqrt(10 + 2 * Math.sqrt(5));
194        }
195
196        private static double getCircumscribedRadiusFromSideLength(double sideLength) {
197                return sideLength / 4 * Math.sqrt(10 + 2 * Math.sqrt(5));
198        }
199
200}