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.*; 011import renderer.scene.util.MeshMaker; 012 013/** 014 Create a wireframe model of a tetrahedron as a 015 triangular pyramid with an equilateral triangle 016 base (centered at the origin in the xz-plane) 017 whose three vertices are connected to a 4th vertex 018 on the positive y-axis. 019 020 @see Tetrahedron 021*/ 022public class TriangularPyramid extends Model implements MeshMaker 023{ 024 public final double r; 025 public final double h; 026 public final int n; 027 public final int k; 028 029 /** 030 Create a regular tetrahedron having side length 031 {@code sqrt(3)/sqrt(2)}, with one face in the 032 xz-plane with its center at the origin, and the 033 4th vertex on the positive y-axis at height 1. 034 */ 035 public TriangularPyramid( ) 036 { 037 this(Math.sqrt(3)/Math.sqrt(2)); // makes the height = 1 038 //or 039 //this(Math.sqrt(3)); // make the height = sqrt(2) > 1 040 } 041 042 043 /** 044 Create a regular tetrahedron having side length {@code s}, 045 with one face in the xz-plane with its center at the origin, 046 and with the 4th vertex on the positive y-axis at 047 height {@code s*sqrt(2)/sqrt(3)}. 048 049 @param s the length of the regular tetrahedron's sides 050 */ 051 public TriangularPyramid(final double s) 052 { 053 this(s/Math.sqrt(3), s*Math.sqrt(2)/Math.sqrt(3)); 054 } 055 056 057 /** 058 Create a tetrahedron with one face being an equilateral triangle 059 inscribed in a circle of radius {@code r} centered at the origin 060 of the xz-plane and with the 4th vertex on the y-axis at height 061 {@code h}. 062 <p> 063 If {@code h = r * sqrt(2)}, then the tetrahedron is a regular tetrahedron. 064 with side length {@code s = r * sqrt(3)}. 065 <p> 066 Another way to state this is, if an equilateral triangle is inscribed 067 in a circle of radius {@code r}, then the edge length of the triangle 068 is {@code r*sqrt(3)} and the height of the regular tetrahedron made 069 from the triangle is {@code r*sqrt(2)}. 070 071 @param r radius of circle in xz-plane that the equilateral base is inscribed in 072 @param h coordinate on the y-axis of the apex 073 */ 074 public TriangularPyramid(final double r, final double h) 075 { 076 super(String.format("Triangular_Pyramid(%.2f,%.2f)", r, h)); 077 078 this.r = r; 079 this.h = h; 080 this.n = 1; 081 this.k = 1; 082 083 // Create the tetrahedron's geometry. 084 final double sqrt3 = Math.sqrt(3.0); 085 addVertex(new Vertex( r, 0, 0), // three vertices around the bottom face 086 new Vertex(-r/2, 0, r*0.5*sqrt3), 087 new Vertex(-r/2, 0, -r*0.5*sqrt3), 088 new Vertex( 0, h, 0)); // vertex at the top 089 090 // Create 6 line segments for 3 faces. 091 addPrimitive(new LineSegment(0, 1), // bottom face 092 new LineSegment(1, 2), 093 new LineSegment(2, 0), 094 new LineSegment(0, 3), // edge 1 095 new LineSegment(1, 3), // edge 2 096 new LineSegment(2, 3)); // edge 3 097 } 098 099 100 /** 101 Create a tetrahedron with one face being an equilateral triangle 102 inscribed in a circle of radius {@code r} centered at the origin 103 of the xz-plane and with the 4th vertex on the y-axis at height 104 {@code h}. 105 <p> 106 If {@code h = r * sqrt(2)}, then the tetrahedron is a regular tetrahedron. 107 with side length {@code s = r * sqrt(3)}. 108 <p> 109 Another way to state this is, if an equilateral triangle is inscribed 110 in a circle of radius {@code r}, then the edge length of the triangle 111 is {@code r*sqrt(3)} and the height of the regular tetrahedron made 112 from the triangle is {@code r*sqrt(2)}. 113 114 @param r radius of circle in xz-plane that the equilateral base is inscribed in 115 @param h coordinate on the y-axis of the apex 116 @param n number of lines of latitude around the body of the pyramid 117 @param k number of triangles in the triangle fan at the top of each side 118 @throws IllegalArgumentException if {@code n} is less than 1 119 @throws IllegalArgumentException if {@code k} is less than 1 120 */ 121 public TriangularPyramid(final double r, final double h, 122 final int n, final int k) 123 { 124 super(String.format("Triangular_Pyramid(%.2f,%.2f,%d,%d)", 125 r, h, n, k)); 126 127 if (n < 1) 128 throw new IllegalArgumentException("n must be greater than 0"); 129 if (k < 1) 130 throw new IllegalArgumentException("k must be greater than 0"); 131 132 this.r = r; 133 this.h = h; 134 this.n = n; 135 this.k = k; 136 137 // Create the pyramid's geometry. 138 final Vertex apex = new Vertex(0, h, 0), 139 centerVertex = new Vertex(0, 0, 0); 140 addVertex(apex, 141 centerVertex); 142 final int apexIndex = 0, 143 centerIndex = 1; 144 int index = 2; 145 146 // Create all the lines of "longitude" from the apex, down 147 // to the base, and then to the center of the base. 148 final double sqrt3 = Math.sqrt(3.0); 149 // Three vertices around the bottom face. 150 final Vertex v0 = new Vertex( r, 0, 0), 151 v1 = new Vertex(-r/2, 0, r*0.5*sqrt3), 152 v2 = new Vertex(-r/2, 0, -r*0.5*sqrt3); 153 for (int j = 0; j < k; ++j) 154 { 155 final double t = j * (1.0 / k); 156 // use linear interpolation (lerp) 157 addVertex( new Vertex( 158 // (1-t)*v0 + t*v1 159 (1-t)*v0.x + t*v1.x, 160 (1-t)*v0.y + t*v1.y, 161 (1-t)*v0.z + t*v1.z )); 162 addVertex( new Vertex( 163 // (1-t)*v1 + t*v2 164 (1-t)*v1.x + t*v2.x, 165 (1-t)*v1.y + t*v2.y, 166 (1-t)*v1.z + t*v2.z )); 167 addVertex( new Vertex( 168 // (1-t)*v2 + t*v0 169 (1-t)*v2.x + t*v0.x, 170 (1-t)*v2.y + t*v0.y, 171 (1-t)*v2.z + t*v0.z )); 172 173 // first side 174 addPrimitive(new LineSegment(apexIndex, index+0), 175 new LineSegment(index+0, centerIndex)); 176 // second side 177 addPrimitive(new LineSegment(apexIndex, index+1), 178 new LineSegment(index+1, centerIndex)); 179 // third side 180 addPrimitive(new LineSegment(apexIndex, index+2), 181 new LineSegment(index+2, centerIndex)); 182 183 index += 3; 184 } 185 // Create all the lines of "latitude" around the pyramid, starting 186 // from the base and working upwards. 187 for (int i = 0; i < n; ++i) 188 { 189 final double t = i * (1.0 / n); 190 // Use linear interpolation (lerp). 191 addVertex( new Vertex( 192 // (1-t)*v0 + t*apex 193 (1-t)*v0.x + t*apex.x, 194 (1-t)*v0.y + t*apex.y, 195 (1-t)*v0.z + t*apex.z )); 196 addVertex( new Vertex( 197 // (1-t)*v1 + t*apex 198 (1-t)*v1.x + t*apex.x, 199 (1-t)*v1.y + t*apex.y, 200 (1-t)*v1.z + t*apex.z )); 201 addVertex( new Vertex( 202 // (1-t)*v2 + t*apex 203 (1-t)*v2.x + t*apex.x, 204 (1-t)*v2.y + t*apex.y, 205 (1-t)*v2.z + t*apex.z )); 206 207 addPrimitive(new LineSegment(index+0, index+1), 208 new LineSegment(index+1, index+2), 209 new LineSegment(index+2, index+0)); 210 211 index += 3; 212 } 213 } 214 215 216 217 // Implement the MeshMaker interface (three methods). 218 @Override public int getHorzCount() {return n;} 219 220 @Override public int getVertCount() {return k;} 221 222 @Override 223 public TriangularPyramid remake(final int n, final int k) 224 { 225 return new TriangularPyramid(this.r, this.h, 226 n, k); 227 } 228}//TriangularPyramid