001/* 002 003*/ 004 005package renderer.models; 006import renderer.scene.*; 007 008/** 009 Create a wireframe model of a sphere centered at the origin 010 by recursively subdividing the faces of a tetrahedron. 011 <p> 012 Also use this subdivision process to draw Sierpinski triangles 013 on the surface of the sphere. 014*/ 015public class SphereSubdivided extends Model 016{ 017 /** 018 Create a sphere centered at the origin by recursively 019 subdividing the faces of a tetrahedron four times. 020 */ 021 public SphereSubdivided() 022 { 023 this(4); 024 } 025 026 027 /** 028 Create a sphere centered at the origin by recursively 029 subdividing the faces of a tetrahedron {@code n} times. 030 031 @param n number of recursive subdivisions 032 */ 033 public SphereSubdivided(int n) 034 { 035 this(n, false, false); 036 } 037 038 039 /** 040 Create a sphere centered at the origin by recursively 041 subdividing the faces of a tetrahedron {@code n} times. 042 <p> 043 The {@code hole} parameter leaves out one of the original 044 four triangle faces of the tetrahedron. This creates a hole 045 in the final sphere that is useful for looking at the back 046 side of the sphere. 047 <p> 048 The {@code sierpinski} parameter creates Sierpinski triangles 049 on the sphere. 050 051 @param n number of recursive subdivisions 052 @param hole do not render one of the four triangles of the tetrahedron 053 @param sierpinski create Sierpinski triangles 054 */ 055 public SphereSubdivided(int n, boolean hole, boolean sierpinski) 056 { 057 super(); 058 059 // Start with the tetrahedron's geometry. 060 double sqr3inv = 1.0/Math.sqrt(3); 061 Vertex v0 = new Vertex( sqr3inv, sqr3inv, sqr3inv); 062 Vertex v1 = new Vertex(-sqr3inv, sqr3inv, -sqr3inv); 063 Vertex v2 = new Vertex( sqr3inv, -sqr3inv, -sqr3inv); 064 Vertex v3 = new Vertex(-sqr3inv, -sqr3inv, sqr3inv); 065 066 // Subdivide each of the tetrahedron's four triangles. 067 subA(n, v0, v1, v2, sierpinski); 068 subA(n, v1, v3, v2, sierpinski); 069 subB(n, v2, v3, v0, sierpinski); 070 if (! hole) subB(n, v3, v1, v0, sierpinski); 071 } 072 073 074 /** 075 Recursive helper function. 076 <p> 077 The reason for two helper functions is to try 078 and reduce the number of repeated edges in the 079 final subdivision geometry. A single recursive 080 subdivision function would end up drawing every 081 edge two times. 082 <p> 083 This function does recursion on three of the four 084 triangle subdivisions and it calls the other helper 085 function on the fourth subdivision. 086 <p> 087 This helper function is responsible for drawing two 088 of the three edges of its triangle. 089 090 @param n number of recursive subdivisions 091 @param v0 vertex of a triangle on the sphere 092 @param v1 vertex of a triangle on the sphere 093 @param v2 vertex of a triangle on the sphere 094 @param sierpinski create Sierpinski triangles 095 */ 096 private void subA(int n, Vertex v0, Vertex v1, Vertex v2, boolean sierpinski) 097 { 098 if (0 == n) 099 { 100 addLineSegment(new LineSegment(new Vertex(v0), new Vertex(v1))); 101 addLineSegment(new LineSegment(new Vertex(v1), new Vertex(v2))); 102 if (sierpinski) 103 { 104 addLineSegment(new LineSegment(new Vertex(v2), new Vertex(v0))); 105 } 106 } 107 else 108 { 109 // Subdivide each of the three edges. 110 Vertex v3 = new Vertex(0.5*(v0.x + v1.x), 0.5*(v0.y + v1.y), 0.5*(v0.z + v1.z)); 111 Vertex v4 = new Vertex(0.5*(v1.x + v2.x), 0.5*(v1.y + v2.y), 0.5*(v1.z + v2.z)); 112 Vertex v5 = new Vertex(0.5*(v2.x + v0.x), 0.5*(v2.y + v0.y), 0.5*(v2.z + v0.z)); 113 // Normalize the subdivision points. 114 double L3 = Math.sqrt(v3.x * v3.x + v3.y * v3.y + v3.z * v3.z); 115 double L4 = Math.sqrt(v4.x * v4.x + v4.y * v4.y + v4.z * v4.z); 116 double L5 = Math.sqrt(v5.x * v5.x + v5.y * v5.y + v5.z * v5.z); 117 v3.set(v3.x / L3, v3.y / L3, v3.z / L3); 118 v4.set(v4.x / L4, v4.y / L4, v4.z / L4); 119 v5.set(v5.x / L5, v5.y / L5, v5.z / L5); 120 // Recursively do another subdivision. 121 subA(n-1, v0, v3, v5, sierpinski); 122 subA(n-1, v5, v4, v2, sierpinski); 123 subA(n-1, v3, v1, v4, sierpinski); 124 if (! sierpinski) subB(n-1, v3, v4, v5, sierpinski); 125 } 126 } 127 128 /** 129 Recursive helper function. 130 <p> 131 The reason for two helper functions is to try 132 and reduce the number of repeated edges in the 133 final subdivision geometry. A single recursive 134 subdivision function would end up drawing every 135 edge two times. 136 <p> 137 This function does recursion on three of the four 138 triangle subdivisions and it calls the other helper 139 function on the fourth subdivision. 140 <p> 141 This helper function is responsible for drawing only 142 one of the three edges of its triangle. 143 144 @param n number of recursive subdivisions 145 @param v0 vertex of a triangle on the sphere 146 @param v1 vertex of a triangle on the sphere 147 @param v2 vertex of a triangle on the sphere 148 @param sierpinski create Sierpinski triangles 149 */ 150 private void subB(int n, Vertex v0, Vertex v1, Vertex v2, boolean sierpinski) 151 { 152 if (0 == n) 153 { 154 addLineSegment(new LineSegment(new Vertex(v2), new Vertex(v0))); 155 if (sierpinski) 156 { 157 addLineSegment(new LineSegment(new Vertex(v0), new Vertex(v1))); 158 addLineSegment(new LineSegment(new Vertex(v1), new Vertex(v2))); 159 } 160 } 161 else 162 { 163 // Subdivide each of the three edges. 164 Vertex v3 = new Vertex(0.5*(v0.x + v1.x), 0.5*(v0.y + v1.y), 0.5*(v0.z + v1.z)); 165 Vertex v4 = new Vertex(0.5*(v1.x + v2.x), 0.5*(v1.y + v2.y), 0.5*(v1.z + v2.z)); 166 Vertex v5 = new Vertex(0.5*(v2.x + v0.x), 0.5*(v2.y + v0.y), 0.5*(v2.z + v0.z)); 167 double L3 = Math.sqrt(v3.x * v3.x + v3.y * v3.y + v3.z * v3.z); 168 double L4 = Math.sqrt(v4.x * v4.x + v4.y * v4.y + v4.z * v4.z); 169 double L5 = Math.sqrt(v5.x * v5.x + v5.y * v5.y + v5.z * v5.z); 170 // Normalize the subdivision points. 171 v3.set(v3.x / L3, v3.y / L3, v3.z / L3); 172 v4.set(v4.x / L4, v4.y / L4, v4.z / L4); 173 v5.set(v5.x / L5, v5.y / L5, v5.z / L5); 174 // Recursively do another subdivision. 175 subB(n-1, v0, v3, v5, sierpinski); 176 subB(n-1, v5, v4, v2, sierpinski); 177 subB(n-1, v3, v1, v4, sierpinski); 178 if (! sierpinski) subA(n-1, v3, v4, v5, sierpinski); 179 } 180 } 181}//SphereSubdivided