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 torus.
015<p>
016   See <a href="https://en.wikipedia.org/wiki/Torus" target="_top">
017                https://en.wikipedia.org/wiki/Torus</a>
018<p>
019   This partial torus is the surface of revolution generated by revolving
020   a sector of the circle in the xy-plane with radius {@code r2} and center
021   {@code (r1,0,0)} part way around the y-axis. We are assuming that
022   {@code r1 > r2}.
023<p>
024   The whole torus is the surface of revolution generated by revolving
025   the whole circle in the xy-plane with radius {@code r2} and center
026   {@code (r1,0,0)} all the way around the y-axis.
027<p>
028   Here are parametric equations for the circle in the xy-plane with
029   radius {@code r2} and center {@code (r1,0,0)} and parameterized
030   starting from the top, with parameter {@code 0 <= phi <= 2*PI}.
031   <pre>{@code
032      x(phi) = r1 + r2 * sin(phi)
033      y(phi) =      r2 * cos(phi)
034      z(phi) = 0
035   }</pre>
036   Here is the 3D rotation matrix that rotates around the y-axis
037   by {@code theta} radians with {@code 0 <= theta <= 2*PI}.
038   <pre>{@code
039      [ cos(theta)   0   sin(theta)]
040      [     0        1       0     ]
041      [-sin(theta)   0   cos(theta)]
042   }</pre>
043   If we multiply the rotation matrix with the circle parameterization,
044   we get a parameterization of the torus.
045   <pre>{@code
046      [ cos(theta)   0   sin(theta)]   [r1 + r2 * sin(phi)]
047      [     0        1       0     ] * [     r2 * cos(phi)]
048      [-sin(theta)   0   cos(theta)]   [        0         ]
049
050      = ( r1*cos(theta) + r2*cos(theta)*sin(phi).
051          r2*cos(phi),
052         -r1*sin(theta) - r2*sin(theta)*sin(phi) )
053
054      = ( (r1 + r2*sin(phi)) * cos(theta),
055                r2*cos(phi),
056         -(r1 + r2*sin(phi)) * sin(theta) )
057   }</pre>
058   See
059     <a href="https://en.wikipedia.org/wiki/Torus#Geometry" target="_top">
060              https://en.wikipedia.org/wiki/Torus#Geometry</a>
061
062   @see Torus
063*/
064public class TorusSector extends Model implements MeshMaker
065{
066   public final double r1;
067   public final double r2;
068   final double theta1;
069   final double theta2;
070   final double phi1;
071   final double phi2;
072   public final int n;
073   public final int k;
074
075   /**
076      Create a partial torus with half the circle of revolution with radius 3/4
077      and a cross section that is half the circle of longitude with radius 1/4.
078   */
079   public TorusSector( )
080   {
081      this(0.75, 0.25, Math.PI/2, 3*Math.PI/2, Math.PI, 2*Math.PI, 6, 8);
082   }
083
084
085   /**
086      Create a partial torus with a partial circle of revolution
087      with radius {@code r1} and a cross section circle (circle of
088      longitude) with radius {@code r2}.
089   <p>
090      If {@code theta1 > 0} or {@code theta2 < 2*PI}, then the (partial)
091      circle of revolution is the circular sector from angle {@code theta1}
092      to angle {@code theta2}. In other words, the (partial) circles of
093      latitude in the model extend from angle {@code theta1} to angle
094      {@code theta2}.
095   <p>
096      The last two parameters determine the number of circles of longitude
097      and the number of (partial) circles of latitude in the model.
098   <p>
099      If there are {@code k} circles of longitude, then each (partial)
100      circle of latitude will have {@code k-1} line segments.
101      If there are {@code n} (partial) circles of latitude, then each
102      circle of longitude will have {@code n} line segments.
103   <p>
104      There must be at least four circles of longitude and at least
105      three circles of latitude.
106
107      @param r1      radius of the circle of revolution
108      @param r2      radius of the cross section circle (circle of longitude)
109      @param theta1  beginning longitude angle for the circle of revolution
110      @param theta2  ending longitude angle for the circle of revolution
111      @param n       number of circles of latitude
112      @param k       number of circles of longitude
113      @throws IllegalArgumentException if {@code n} is less than 4
114      @throws IllegalArgumentException if {@code k} is less than 4
115   */
116   public TorusSector(final double r1, final double r2,
117                      final double theta1, final double theta2,
118                      final int n, final int k)
119   {
120      this(r1, r2, theta1, theta2, 0, 2*Math.PI, n+1, k);
121   }
122
123
124   /**
125      Create a partial torus with a partial circle of revolution with
126      radius {@code r1} and a partial cross section circle with radius
127      {@code r2}.
128   <p>
129      If {@code phi1 > 0} or {@code phi2 < 2*PI}, then the (partial) cross
130      section circle is the circular sector from angle {@code phi1} to angle
131      {@code phi2}. In other words, the (partial) circles of longitude in the
132      model extend from angle {@code phi1} to angle {@code phi2}.
133   <p>
134      If {@code theta1 > 0} or {@code theta2 < 2*PI}, then the (partial) circle
135      of revolution is the circular sector from angle {@code theta1} to angle
136      {@code theta2}. In other words, the (partial) circles of latitude in
137      the model extend from angle {@code theta1} to angle {@code theta2}.
138   <p>
139      The last two parameters determine the number of (partial) circles of
140      longitude and the number of (partial) circles of latitude in the model.
141   <p>
142      If there are {@code k} circles of longitude, then each (partial)
143      circle of latitude will have {@code k-1} line segments.
144      If there are {@code n} (partial) circles of latitude, then each
145      circle of longitude will have {@code n-1} line segments.
146   <p>
147      There must be at least four circles of longitude and at least
148      four circles of latitude.
149
150      @param r1      radius of the circle of revolution
151      @param r2      radius of the cross section circle (circle of longitude)
152      @param theta1  beginning longitude angle for the circle of revolution
153      @param theta2  ending longitude angle for the circle of revolution
154      @param phi1    beginning latitude angle for the cross section circle
155      @param phi2    ending latitude angle for the cross section circle
156      @param n       number of circles of latitude
157      @param k       number of circles of longitude
158      @throws IllegalArgumentException if {@code n} is less than 4
159      @throws IllegalArgumentException if {@code k} is less than 4
160   */
161   public TorusSector(final double r1,     final double r2,
162                      final double theta1, final double theta2,
163                      final double phi1,   final double phi2,
164                      final int n, final int k)
165   {
166      super(String.format("Torus Sector(%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%d,%d)",
167                           r1, r2, theta1, theta2, phi1, phi2, n, k));
168
169      if (n < 4)
170         throw new IllegalArgumentException("n must be greater than 3");
171      if (k < 4)
172         throw new IllegalArgumentException("k must be greater than 3");
173
174      this.r1 = r1;
175      this.r2 = r2;
176      this.theta1 = theta1;
177      this.theta2 = theta2;
178      this.phi1 = phi1;
179      this.phi2 = phi2;
180      this.n = n;
181      this.k = k;
182
183      // Create the torus's geometry.
184
185      final double deltaPhi = (phi2 - phi1) / (n - 1),
186                   deltaTheta = (theta2 - theta1) / (k - 1);
187
188      // An array of vertices to be used to create line segments.
189      final Vertex[][] v = new Vertex[n][k];
190
191      // Create all the vertices.
192      for (int j = 0; j < k; ++j) // choose a rotation around the y-axis
193      {
194         final double c1 = Math.cos(theta1 + j * deltaTheta),
195                      s1 = Math.sin(theta1 + j * deltaTheta);
196         for (int i = 0; i < n; ++i)  // go around a cross section circle
197         {
198            final double c2 = Math.cos(phi1 + i * deltaPhi),
199                         s2 = Math.sin(phi1 + i * deltaPhi);
200            v[i][j] = new Vertex( (r1 + r2*s2) * c1,
201                                        r2*c2,
202                                 -(r1 + r2*s2) * s1 );
203         }
204      }
205
206      // Add all of the vertices to this model.
207      for (int i = 0; i < n; ++i)
208      {
209         for (int j = 0; j < k; ++j)
210         {
211            addVertex( v[i][j] );
212         }
213      }
214
215      // Create the vertical (partial) cross-section circles.
216      for (int j = 0; j < k; ++j) // choose a rotation around the y-axis
217      {
218         for (int i = 0; i < n - 1; ++i) // go around a cross section circle
219         {  //                                 v[i][j]      v[i+1][j]
220            addPrimitive(new LineSegment( (i * k) + j, ((i+1) * k) + j ));
221         }
222      }
223
224      // Create all the horizontal (partial) circles around the torus.
225      for (int i = 0; i < n; ++i) //choose a rotation around the cross section
226      {
227         for (int j = 0; j < k - 1; ++j) // go around a horizontal circle
228         {  //                                v[i][j]       v[i][j+1]
229            addPrimitive(new LineSegment( (i * k) + j, (i * k) + (j+1) ));
230         }
231      }
232   }
233
234
235
236   // Implement the MeshMaker interface (three methods).
237   @Override public int getHorzCount() {return n;}
238
239   @Override public int getVertCount() {return k;}
240
241   @Override
242   public TorusSector remake(final int n, final int k)
243   {
244      return new TorusSector(this.r1,     this.r2,
245                             this.theta1, this.theta2,
246                             this.phi1,   this.phi2,
247                             n, k);
248   }
249}//TorusSector