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