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