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 frustum of a right circular cone 015 with its base in the xz-plane. 016<p> 017 See <a href="https://en.wikipedia.org/wiki/Frustum" target="_top"> 018 https://en.wikipedia.org/wiki/Frustum</a> 019 020 @see Cone 021 @see ConeSector 022*/ 023public class ConeFrustum extends Model implements MeshMaker 024{ 025 public final double r1; 026 public final double r2; 027 public final double h; 028 public final int n; 029 public final int k; 030 031 /** 032 Create a frustum of a right circular cone with its base in the 033 xz-plane, a base radius of 1, top radius of 1/2, and height 1/2. 034 */ 035 public ConeFrustum( ) 036 { 037 this(1.0, 0.5, 0.5, 7, 16); 038 } 039 040 041 /** 042 Create a frustum of a right circular cone with its base in the 043 xz-plane, a base radius of {@code r}, top of the frustum at 044 height {@code h}, and with the cone's apex on the y-axis at 045 height {@code a}. 046 <p> 047 There must be at least three lines of longitude and at least 048 two circles of latitude. 049 050 @param n number of circles of latitude 051 @param k number of lines of longitude 052 @param r radius of the base in the xz-plane 053 @param h height of the frustum 054 @param a height of the apex of the cone 055 @throws IllegalArgumentException if {@code n} is less than 2 056 @throws IllegalArgumentException if {@code k} is less than 3 057 */ 058 public ConeFrustum(final int n, final int k, 059 final double r, final double h, final double a) 060 { 061 this(r, (1 - h/a)*r, h, n, k); 062 } 063 064 065 /** 066 Create a frustum of a right circular cone with its base in the 067 xz-plane, a base radius of {@code r1}, top radius of {@code r2}, 068 and height {@code h}. 069 <p> 070 This model works with either {@code r1 > r2} or {@code r1 < r2}. 071 In other words, the frustum can have its "apex" either above or 072 below the xz-plane. 073 <p> 074 There must be at least three lines of longitude and at least 075 two circles of latitude. 076 077 @param r1 radius of the base of the frustum 078 @param h height of the frustum 079 @param r2 radius of the top of the frustum 080 @param n number of circles of latitude 081 @param k number of lines of longitude 082 @throws IllegalArgumentException if {@code n} is less than 2 083 @throws IllegalArgumentException if {@code k} is less than 3 084 */ 085 public ConeFrustum(final double r1, final double h, final double r2, 086 int n, int k) 087 { 088 super(String.format("Cone Frustum(%.2f,%.2f,%.2f,%d,%d)", 089 r1, h, r2, n, k)); 090 091 if (n < 2) 092 throw new IllegalArgumentException("n must be greater than 1"); 093 if (k < 3) 094 throw new IllegalArgumentException("k must be greater than 2"); 095 096 this.r1 = r1; 097 this.r2 = r2; 098 this.h = h; 099 this.n = n; 100 this.k = k; 101 102 // Create the frustum's geometry. 103 104 final double deltaH = h / (n - 1), 105 deltaTheta = (2 * Math.PI) / k; 106 107 // An array of indexes to be used to create line segments. 108 final int[][] indexes = new int[n][k]; 109 110 // Create all the vertices (from the top down). 111 int index = 0; 112 for (int j = 0; j < k; ++j) // choose an angle of longitude 113 { 114 final double c = Math.cos(j * deltaTheta), 115 s = Math.sin(j * deltaTheta); 116 for (int i = 0; i < n; ++i) // choose a circle of latitude 117 { 118 final double slantRadius = (i/(n - 1.0)) * r1 + (1.0 - i/(n - 1.0)) * r2; 119 addVertex( new Vertex(slantRadius * c, 120 h - i * deltaH, 121 slantRadius * s) ); 122 indexes[i][j] = index; 123 ++index; 124 } 125 } 126 addVertex( new Vertex(0, h, 0) ); // top center 127 final int topCenterIndex = index; 128 ++index; 129 addVertex( new Vertex(0, 0, 0) ); // bottom center 130 final int bottomCenterIndex = index; 131 ++index; 132 133 // Create all the horizontal circles of latitude around the frustum wall. 134 for (int i = 0; i < n; ++i) 135 { 136 for (int j = 0; j < k-1; ++j) 137 { 138 addPrimitive(new LineSegment(indexes[i][j], indexes[i][j+1])); 139 } 140 // close the circle 141 addPrimitive(new LineSegment(indexes[i][k-1], indexes[i][0])); 142 } 143 144 // Create the vertical half-trapazoids of longitude from north to south pole. 145 for (int j = 0; j < k; ++j) 146 { 147 // Create the triangle fan at the top. 148 addPrimitive(new LineSegment(topCenterIndex, indexes[0][j])); 149 // Create the slant lines from the top to the base. 150 addPrimitive(new LineSegment(indexes[0][j], indexes[n-1][j])); 151 // Create the triangle fan at the base. 152 addPrimitive(new LineSegment(indexes[n-1][j], bottomCenterIndex)); 153 } 154 } 155 156 157 158 // Implement the MeshMaker interface (three methods). 159 @Override public int getHorzCount() {return n;} 160 161 @Override public int getVertCount() {return k;} 162 163 @Override 164 public ConeFrustum remake(final int n, final int k) 165 { 166 return new ConeFrustum(this.r1, this.h, this.r2, 167 n, k); 168 } 169}//ConeFrustum