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 sector of a disk
015   in the xy-plane centered at the origin.
016<p>
017   See <a href="https://en.wikipedia.org/wiki/Circular_sector" target="_top">
018                https://en.wikipedia.org/wiki/Circular_sector</a>
019
020   @see Disk
021   @see CircleSector
022   @see RingSector
023   @see ConeSector
024   @see CylinderSector
025   @see SphereSector
026   @see TorusSector
027*/
028public class DiskSector extends Model implements MeshMaker
029{
030   public final double r;
031   public final double theta1;
032   public final double theta2;
033   public final int n;
034   public final int k;
035
036   /**
037      Create half a disk in the xy-plane with radius 1,
038      with 8 spokes coming out of the center, and
039      with 6 concentric circles around the disk.
040   */
041   public DiskSector( )
042   {
043      this(1, 0, Math.PI, 6, 8);
044   }
045
046
047   /**
048      Create a sector of a disk in the xy-plane with radius {@code r},
049      starting angle {@code theta1}, ending angle {@code theta2},
050      with {@code k} spokes coming out of the center, and with
051      {@code n} concentric circles around the disk.
052   <p>
053      If there are {@code k} spokes, then each (partial) circle
054      around the center will have {@code k-1} line segments.
055      If there are {@code n} concentric circles around the center,
056      then each spoke will have {@code n} line segments.
057   <p>
058      There must be at least four spokes and at least one concentric circle.
059
060      @param r       radius of the disk
061      @param theta1  beginning angle of the sector (in radians)
062      @param theta2  ending angle of the sector (in radians)
063      @param n       number of concentric circles
064      @param k       number of spokes in the disk
065      @throws IllegalArgumentException if {@code n} is less than 1
066      @throws IllegalArgumentException if {@code k} is less than 4
067   */
068   public DiskSector(final double r,
069                     double theta1, double theta2,
070                     final int n, final int k)
071   {
072      super(String.format("Disk Sector(%.2f,%.2f,%.2f,%d,%d)",
073                                       r, theta1, theta2, n, k));
074
075      if (n < 1)
076         throw new IllegalArgumentException("n must be greater than 0");
077      if (k < 4)
078         throw new IllegalArgumentException("k must be greater than 3");
079
080      theta1 = theta1 % (2*Math.PI);
081      theta2 = theta2 % (2*Math.PI);
082      if (theta1 < 0) theta1 = 2*Math.PI + theta1;
083      if (theta2 < 0) theta2 = 2*Math.PI + theta2;
084      if (theta2 <= theta1) theta2 = theta2 + 2*Math.PI;
085
086      this.r = r;
087      this.theta1 = theta1;
088      this.theta2 = theta2;
089      this.n = n;
090      this.k = k;
091
092      // Create the disk's geometry.
093
094      final double deltaR = r / n,
095                   deltaTheta = (theta2 - theta1) / (k - 1);
096
097      // An array of vertices to be used to create line segments.
098      final Vertex[][] v = new Vertex[n][k];
099
100      // Create all the vertices.
101      for (int j = 0; j < k; ++j) // choose a spoke (an angle)
102      {
103         final double c = Math.cos(theta1 + j * deltaTheta),
104                      s = Math.sin(theta1 + j * deltaTheta);
105         for (int i = 0; i < n; ++i) // move along the spoke
106         {
107            final double ri = (i + 1) * deltaR;
108            v[i][j] = new Vertex( ri * c,
109                                  ri * s,
110                                  0 );
111         }
112      }
113      final Vertex center = new Vertex(0,0,0);
114
115      // Add all of the vertices to this model.
116      for (int i = 0; i < n; ++i)
117      {
118         for (int j = 0; j < k; ++j)
119         {
120            addVertex( v[i][j] );
121         }
122      }
123      addVertex( center );
124      final int centerIndex = n * k;
125
126      // Create the spokes connecting the center to the outer circle.
127      for (int j = 0; j < k; ++j) // choose a spoke
128      {  //                                             v[0][j]
129         addPrimitive(new LineSegment( centerIndex, (0 * k) + j ));
130
131         for (int i = 0; i < n - 1; ++i)
132         {  //                                 v[i][j]        v[i+1][j]
133            addPrimitive(new LineSegment( (i * k) + j, ((i+1) * k) + j ));
134         }
135      }
136
137      // Create the line segments around each (partial) concentric circle.
138      for (int i = 0; i < n; ++i)  // choose a circle
139      {
140         for (int j = 0; j < k - 1; ++j)
141         {  //                                v[i][j]        v[i][j+1]
142            addPrimitive(new LineSegment( (i * k) + j, (i * k) + (j + 1) ));
143         }
144      }
145   }
146
147
148
149   // Implement the MeshMaker interface (three methods).
150   @Override public int getHorzCount() {return n;}
151
152   @Override public int getVertCount() {return k;}
153
154   @Override
155   public DiskSector remake(final int n, final int k)
156   {
157      return new DiskSector(this.r,
158                            this.theta1, this.theta2,
159                            n, k);
160   }
161}//DiskSector