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