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 right square pyramid with its 015 base in the xz-plane and its apex on the positive y-axis. 016<p> 017 See <a href="https://en.wikipedia.org/wiki/Pyramid_(geometry)" target="_top"> 018 https://en.wikipedia.org/wiki/Pyramid_(geometry)</a> 019 020 @see PyramidFrustum 021*/ 022public class Pyramid extends Model implements MeshMaker 023{ 024 public final double s; 025 public final double h; 026 public final int n; 027 public final int k; 028 029 /** 030 Create a right square pyramid with its base in the xz-plane, 031 a base side length of 2, height 1, and apex on the positive y-axis. 032 */ 033 public Pyramid( ) 034 { 035 this(2.0, 1.0, 15, 4); 036 } 037 038 039 /** 040 Create a right square pyramid with its base in the xz-plane, 041 a base length of {@code s}, height {@code h}, and apex on the 042 positive y-axis. 043 044 @param s side length of the base in the xz-plane 045 @param h height of the apex on the y-axis 046 */ 047 public Pyramid(final double s, final double h) 048 { 049 super(String.format("Pyramid(%.2f,%.2f)", s, h)); 050 051 this.s = s; 052 this.h = h; 053 this.n = 1; 054 this.k = 1; 055 056 // Create the pyramid's geometry. 057 addVertex(new Vertex(-s/2, 0, -s/2), // base 058 new Vertex(-s/2, 0, s/2), 059 new Vertex( s/2, 0, s/2), 060 new Vertex( s/2, 0, -s/2), 061 new Vertex( 0, h, 0)); // apex 062 063 // Create 8 line segments for 5 faces. 064 addPrimitive(new LineSegment(0, 1), // base 065 new LineSegment(1, 2), 066 new LineSegment(2, 3), 067 new LineSegment(3, 0), 068 new LineSegment(4, 0), // 4 sides 069 new LineSegment(4, 1), 070 new LineSegment(4, 2), 071 new LineSegment(4, 3)); 072 } 073 074 075 /** 076 Create a right square pyramid with its base in the xz-plane, 077 a base length of {@code s}, height {@code h}, and apex on the 078 positive y-axis. 079 080 @param s side length of the base in the xz-plane 081 @param h height of the apex on the y-axis 082 @param n number of lines of latitude around the body of the pyramid 083 @param k number of triangles in the triangle fan at the top of each side 084 @throws IllegalArgumentException if {@code n} is less than 1 085 @throws IllegalArgumentException if {@code k} is less than 1 086 */ 087 public Pyramid(final double s, final double h, 088 final int n, final int k) 089 { 090 this(s, h, n, k, false); 091 } 092 093 094 /** 095 Create a right square pyramid with its base in the xz-plane, 096 a base length of {@code s}, height {@code h}, and apex on the 097 positive y-axis. 098 <p> 099 The last parameter provides a choice between having a square 100 grid of lines or a line fan in the base of the pyramid. 101 102 @param s side length of the base in the xz-plane 103 @param h height of the apex on the y-axis 104 @param n number of lines of latitude around the body of the pyramid 105 @param k number of triangles in the triangle fan at the top of each side 106 @param grid choose either a square grid or a line fan in the base 107 @throws IllegalArgumentException if {@code n} is less than 1 108 @throws IllegalArgumentException if {@code k} is less than 1 109 */ 110 public Pyramid(double s, double h, 111 final int n, final int k, 112 final boolean grid) 113 { 114 super(String.format("Pyramid(%.2f,%.2f,%d,%d)", s, h, n, k)); 115 116 if (n < 1) 117 throw new IllegalArgumentException("n must be greater than 0"); 118 if (k < 1) 119 throw new IllegalArgumentException("k must be greater than 0"); 120 121 this.s = s; 122 this.h = h; 123 this.n = n; 124 this.k = k; 125 126 // Create the pyramid's geometry. 127 addVertex(new Vertex(0, h, 0)); 128 final int apexIndex = 0; 129 int index = 1; 130 131 // Create all the lines of "longitude" from the apex, down 132 // to the base, across the base, and then back up to the apex. 133 s = s/2; 134 final double delta = (2 * s) / k; 135 // lines of "longitude" perpendicular to the x-axis 136 for (int j = 0; j < k + 1; ++j) 137 { 138 final double d = j * delta; 139 if (grid) 140 { 141 addVertex(new Vertex(-s+d, 0, -s), 142 new Vertex(-s+d, 0, s)); 143 } 144 else // a fan in the base 145 { 146 addVertex(new Vertex(-s+d, 0, -s), 147 new Vertex( s-d, 0, s)); 148 } 149 addPrimitive(new LineSegment(apexIndex, index+0), 150 new LineSegment(index+0, index+1), 151 new LineSegment(index+1, apexIndex)); 152 index += 2; 153 } 154 // lines of "longitude" perpendicular to the z-axis 155 for (int j = 1; j < k; ++j) 156 { 157 final double d = j * delta; 158 if (grid) 159 { 160 addVertex(new Vertex( s, 0, -s+d), 161 new Vertex(-s, 0, -s+d)); 162 } 163 else // a fan in the base 164 { 165 addVertex(new Vertex( s, 0, -s+d), 166 new Vertex(-s, 0, s-d)); 167 } 168 addPrimitive(new LineSegment(apexIndex, index+0), 169 new LineSegment(index+0, index+1), 170 new LineSegment(index+1, apexIndex)); 171 index += 2; 172 } 173 // Create all the lines of "latitude" around the pyramid, starting 174 // from the base and working upwards. 175 final double deltaH = h / n, 176 deltaS = s / n; 177 for (int i = 0; i < n; ++i) 178 { 179 h = i * deltaH; 180 addVertex(new Vertex( s, h, s), 181 new Vertex( s, h, -s), 182 new Vertex(-s, h, -s), 183 new Vertex(-s, h, s)); 184 addPrimitive(new LineSegment(index+0, index+1), 185 new LineSegment(index+1, index+2), 186 new LineSegment(index+2, index+3), 187 new LineSegment(index+3, index+0)); 188 s -= deltaS; 189 index += 4; 190 } 191 } 192 193 194 195 // Implement the MeshMaker interface (three methods). 196 @Override public int getHorzCount() {return n;} 197 198 @Override public int getVertCount() {return k;} 199 200 @Override 201 public Pyramid remake(final int n, final int k) 202 { 203 return new Pyramid(this.s, this.h, 204 n, k); 205 } 206}//Pyramid