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