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