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