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}