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