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