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