001/*
002 * Renderer Models. The MIT License.
003 * Copyright (c) 2022 rlkraft@pnw.edu
004 * See LICENSE for details.
005*/
006
007package renderer.models_L;
008
009import renderer.scene.*;
010import renderer.scene.primitives.*;
011
012/**
013   Create a wireframe model of a sphere centered at the origin
014   by recursively subdividing the faces of a tetrahedron.
015<p>
016   Also use this subdivision process to draw Sierpinski triangles
017   on the surface of the sphere.
018*/
019public class SphereSubdivided extends Model
020{
021   /**
022      Create a sphere centered at the origin by recursively
023      subdividing the faces of a tetrahedron four times.
024   */
025   public SphereSubdivided()
026   {
027      this(4);
028   }
029
030
031   /**
032      Create a sphere centered at the origin by recursively
033      subdividing the faces of a tetrahedron {@code n} times.
034
035      @param n  number of recursive subdivisions
036      @throws IllegalArgumentException if {@code n} is less than 0
037   */
038   public SphereSubdivided(final int n)
039   {
040      this(n, false, false);
041   }
042
043
044   /**
045      Create a sphere centered at the origin by recursively
046      subdividing the faces of a tetrahedron {@code n} times.
047   <p>
048      The {@code hole} parameter leaves out one of the original
049      four triangle faces of the tetrahedron. This creates a hole
050      in the final sphere that is useful for looking at the back
051      side of the sphere.
052   <p>
053      The {@code sierpinski} parameter creates Sierpinski triangles
054      on the sphere.
055
056      @param n           number of recursive subdivisions
057      @param hole        do not render one of the four triangles of the tetrahedron
058      @param sierpinski  create Sierpinski triangles
059      @throws IllegalArgumentException if {@code n} is less than 0
060   */
061   public SphereSubdivided(final int n, final boolean hole, final boolean sierpinski)
062   {
063      super(String.format("Sphere Subdivided(%d)", n));
064
065      if (n < 0)
066         throw new IllegalArgumentException("n must be greater than or equal to 0");
067
068      // Start with the tetrahedron's geometry.
069      final double sqr3inv = 1.0/Math.sqrt(3);
070      final Vertex v0 = new Vertex( sqr3inv,  sqr3inv,  sqr3inv),
071                   v1 = new Vertex(-sqr3inv,  sqr3inv, -sqr3inv),
072                   v2 = new Vertex( sqr3inv, -sqr3inv, -sqr3inv),
073                   v3 = new Vertex(-sqr3inv, -sqr3inv,  sqr3inv);
074
075      // Subdivide each of the tetrahedron's four triangles.
076      sub(n, v0, v1, v2, sierpinski);
077      sub(n, v1, v3, v2, sierpinski);
078      sub(n, v2, v3, v0, sierpinski);
079      if (! hole) sub(n, v3, v1, v0, sierpinski);
080   }
081
082
083   /**
084      Recursive helper function.
085
086      @param n           number of recursive subdivisions
087      @param v0          vertex of a triangle on the sphere
088      @param v1          vertex of a triangle on the sphere
089      @param v2          vertex of a triangle on the sphere
090      @param sierpinski  create Sierpinski triangles
091   */
092   private void sub(final int n,
093                    final Vertex v0, final Vertex v1, final Vertex v2,
094                    final boolean sierpinski)
095   {
096      assert (n >= 0);
097
098      if (0 == n)
099      {
100         final int index = vertexList.size();
101         addVertex(v0, v1, v2);
102         addPrimitive(new LineSegment(index+0, index+1),  // v0, v1
103                      new LineSegment(index+1, index+2),  // v1, v2
104                      new LineSegment(index+2, index+0)); // v2, v0
105      }
106      else
107      {
108         // Subdivide each of the three edges.
109         final Vertex v3 = new Vertex(0.5*(v0.x + v1.x),
110                                      0.5*(v0.y + v1.y),
111                                      0.5*(v0.z + v1.z));
112         final Vertex v4 = new Vertex(0.5*(v1.x + v2.x),
113                                      0.5*(v1.y + v2.y),
114                                      0.5*(v1.z + v2.z));
115         final Vertex v5 = new Vertex(0.5*(v2.x + v0.x),
116                                      0.5*(v2.y + v0.y),
117                                      0.5*(v2.z + v0.z));
118         // Normalize the subdivision points.
119         final double L3 = Math.sqrt(v3.x * v3.x + v3.y * v3.y + v3.z * v3.z),
120                      L4 = Math.sqrt(v4.x * v4.x + v4.y * v4.y + v4.z * v4.z),
121                      L5 = Math.sqrt(v5.x * v5.x + v5.y * v5.y + v5.z * v5.z);
122         final Vertex v3n = new Vertex(v3.x / L3, v3.y / L3, v3.z / L3),
123                      v4n = new Vertex(v4.x / L4, v4.y / L4, v4.z / L4),
124                      v5n = new Vertex(v5.x / L5, v5.y / L5, v5.z / L5);
125         // Recursively do another subdivision.
126         sub(n-1, v0,  v3n, v5n, sierpinski);
127         sub(n-1, v5n, v4n, v2,  sierpinski);
128         sub(n-1, v3n, v1,  v4n, sierpinski);
129         if (! sierpinski) sub(n-1, v3n, v4n, v5n, sierpinski);
130      }
131   }
132}//SphereSubdivided