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 circular cone with its base
015   parallel to the xz-plane and its apex on the positive y-axis.
016<p>
017   See <a href="https://en.wikipedia.org/wiki/Cone" target="_top">
018                https://en.wikipedia.org/wiki/Cone</a>
019<p>
020   This model can also be used to create right k-sided polygonal pyramids.
021<p>
022   See <a href="https://en.wikipedia.org/wiki/Pyramid_(geometry)" target="_top">
023                https://en.wikipedia.org/wiki/Pyramid_(geometry)</a>
024
025   @see ConeFrustum
026*/
027public class Cone extends Model implements MeshMaker
028{
029   public final double r;
030   public final double h;
031   public final int n;
032   public final int k;
033
034   /**
035      Create a right circular cone with its base in the xz-plane,
036      a base radius of 1, height 1, and apex on the positive y-axis.
037   */
038   public Cone( )
039   {
040      this(1, 1, 15, 16);
041   }
042
043
044   /**
045      Create a right circular cone with its base in the xz-plane,
046      a base radius of {@code r}, height {@code h}, and apex on
047      the y-axis.
048
049      @param r  radius of the base in the xz-plane
050      @param h  height of the apex on the y-axis
051   */
052   public Cone(final double r, final double h)
053   {
054      this(r, h, 15, 16);
055   }
056
057
058   /**
059      Create a right circular cone with its base in the xz-plane,
060      a base radius of {@code r}, height {@code h}, and apex on
061      the y-axis.
062   <p>
063      The last two parameters determine the number of lines of longitude
064      and the number of circles of latitude in the model.
065   <p>
066      If there are {@code n} circles of latitude in the model (including
067      the bottom edge), then each line of longitude will have {@code n+1}
068      line segments. If there are {@code k} lines of longitude, then each
069      circle of latitude will have {@code k} line segments.
070   <p>
071      There must be at least three lines of longitude and at least
072      one circle of latitude.
073   <p>
074      By setting {@code k} to be a small integer, this model can also
075      be used to create k-sided polygonal pyramids.
076
077      @param r  radius of the base in the xz-plane
078      @param h  height of the apex on the y-axis
079      @param n  number of circles of latitude around the cone
080      @param k  number of lines of longitude
081      @throws IllegalArgumentException if {@code n} is less than 2
082      @throws IllegalArgumentException if {@code k} is less than 4
083   */
084   public Cone(final double r, final double h, final int n, final int k)
085   {
086      super(String.format("Cone(%.2f,%.2f,%d,%d)",
087                                r,   h,   n, k));
088
089      if (n < 2)
090         throw new IllegalArgumentException("n must be greater than 1");
091      if (k < 4)
092         throw new IllegalArgumentException("k must be greater than 3");
093
094      this.r = r;
095      this.h = h;
096      this.n = n;
097      this.k = k;
098
099      // Create the cone's geometry.
100
101      final double deltaH = h / (n - 1),
102                   deltaTheta = (2.0*Math.PI) / (k - 1);
103
104      // An array of indexes to be used to create line segments.
105      final int[][] indexes = new int[n][k];
106
107      // Create all the vertices (from the bottom up).
108      int index = 0;
109      for (int j = 0; j < k; ++j) // choose an angle of longitude
110      {
111         final double c = Math.cos(j * deltaTheta),
112                      s = Math.sin(j * deltaTheta);
113         for (int i = 0; i < n; ++i) // choose a circle of latitude
114         {
115            final double slantRadius = r * (1 - i * deltaH / h);
116            addVertex( new Vertex(slantRadius * c,
117                                  i * deltaH,
118                                  slantRadius * s) );
119            indexes[i][j] = index++;
120         }
121      }
122      addVertex( new Vertex(0, h, 0) ); // apex
123      final int apexIndex = index;
124      ++index;
125      addVertex( new Vertex(0, 0, 0) ); // bottom center
126      final int bottomCenterIndex = index;
127      ++index;
128
129      // Create all the horizontal circles of latitude around the cone.
130      for (int i = 0; i < n; ++i)
131      {
132         for (int j = 0; j < k - 1; ++j)
133         {
134            addPrimitive(new LineSegment(indexes[i][j], indexes[i][j+1]));
135         }
136      }
137
138      // Create the slanted lines of longitude from the base to the
139      // apex, and the triangle fan in the base.
140      for (int j = 0; j < k; ++j)
141      {
142         addPrimitive(new LineSegment(bottomCenterIndex, indexes[0][j]));
143
144         for (int i = 0; i < n - 1; ++i)
145         {
146            addPrimitive(new LineSegment(indexes[i][j], indexes[i+1][j]));
147         }
148
149         addPrimitive(new LineSegment(indexes[n-1][j], apexIndex));
150      }
151   }
152
153
154
155   // Implement the MeshMaker interface (three methods).
156   @Override public int getHorzCount() {return n;}
157
158   @Override public int getVertCount() {return k;}
159
160   @Override
161   public Cone remake(final int n, final int k)
162   {
163      return new Cone(this.r,
164                      this.h,
165                      n, k);
166   }
167}//Cone