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