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