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 barycentricly subdivided 015 equilateral triangle. 016 017 See <a href="https://en.wikipedia.org/wiki/Barycentric_subdivision" target="_top"> 018 https://en.wikipedia.org/wiki/Barycentric_subdivision</a> 019*/ 020public class BarycentricTriangle extends Model implements MeshMaker 021{ 022 public final double theta; 023 public final int n; 024 025 /** 026 Create a barycentricly subdivided equilateral triangle 027 in the xy-plane with corners on the unit circle. 028 <p> 029 The value of {@code n} should be less than 8. 030 031 @param n number of barycentric subdivisions of the triangle 032 @throws IllegalArgumentException if {@code n} is less than 0 033 */ 034 public BarycentricTriangle(final int n) 035 { 036 this(0, n); 037 } 038 039 040 /** 041 Create a barycentricly subdivided equilateral triangle 042 in the xy-plane with corners on the unit circle and 043 rotated by angle {@code theta} degrees. 044 <p> 045 The value of {@code n} should be less than 8. 046 047 @param theta rotation (in degrees) of the equilateral triangle 048 @param n number of barycentric subdivisions of this triangle 049 @throws IllegalArgumentException if {@code n} is less than 0 050 */ 051 public BarycentricTriangle(final double theta, final int n) 052 { 053 super(String.format("Barycentric Triangle(%.2f,%d)", theta, n)); 054 055 if (n < 0) 056 throw new IllegalArgumentException("n must be greater than or equal to0"); 057 058 this.theta = theta; 059 this.n = n; 060 061 final double theta1 = theta * Math.PI/180.0, 062 theta2 = 2.0 * Math.PI / 3.0; 063 addVertex(new Vertex(Math.cos(theta1), 064 Math.sin(theta1), 065 0.0), 066 new Vertex(Math.cos(theta1 + theta2), 067 Math.sin(theta1 + theta2), 068 0.0), 069 new Vertex(Math.cos(theta1 + 2*theta2), 070 Math.sin(theta1 + 2*theta2), 071 0.0)); 072 addPrimitive(new LineSegment(0, 1), 073 new LineSegment(1, 2), 074 new LineSegment(2, 0)); 075 076 if (n > 0) 077 barycentric(0, 1, 2, n); 078 } 079 080 081 /** 082 Recursively use barycentric subdivision to put into this 083 {@link Model} vertices and line segments that subdivide 084 the triangle whose vertices are indexed by {@code vIndex0}, 085 {@code vIndex1} and {@code vIndex2}. 086 <p> 087 The value of {@code n} should be less than 8. 088 089 @param vIndex0 index of a {link Vertex} of a triangle 090 @param vIndex1 index of a {link Vertex} of a triangle 091 @param vIndex2 index of a {link Vertex} of a triangle 092 @param n number of barycentric subdivisions of this triangle 093 */ 094 public void barycentric(final int vIndex0, 095 final int vIndex1, 096 final int vIndex2, 097 final int n) 098 { 099 final Vertex v0 = vertexList.get(vIndex0), 100 v1 = vertexList.get(vIndex1), 101 v2 = vertexList.get(vIndex2); 102 final int index = vertexList.size(); 103 104 if (n > 0) 105 { 106 // Barycentric subdivision. 107 // https://en.wikipedia.org/wiki/Barycentric_subdivision 108 109 // Add four vertices to the model. 110 addVertex(new Vertex( 111 // (1/3)*v0 + (1/3)*v1 + (1/3)*v2 112 (v0.x + v1.x + v2.x)/3.0, 113 (v0.y + v1.y + v2.y)/3.0, 114 (v0.z + v1.z + v2.z)/3.0)); 115 addVertex(new Vertex( 116 // (1/2)*v0 + (1/2)*v1 117 (v0.x + v1.x)/2.0, 118 (v0.y + v1.y)/2.0, 119 (v0.z + v1.z)/2.0)); 120 addVertex(new Vertex( 121 // (1/2)*v1 + (1/2)*v2 122 (v1.x + v2.x)/2.0, 123 (v1.y + v2.y)/2.0, 124 (v1.z + v2.z)/2.0)); 125 addVertex(new Vertex( 126 // (1/2)*v2 + (1/2)*v0 127 (v2.x + v0.x)/2.0, 128 (v2.y + v0.y)/2.0, 129 (v2.z + v0.z)/2.0)); 130 // Give a name to the index of each of the four new vertices. 131 final int vIndexCenter = index, 132 vIndex01 = index + 1, 133 vIndex12 = index + 2, 134 vIndex20 = index + 3; 135 // 6 new line segments 136 addPrimitive(new LineSegment(vIndex0, vIndexCenter), 137 new LineSegment(vIndex1, vIndexCenter), 138 new LineSegment(vIndex2, vIndexCenter), 139 new LineSegment(vIndex01, vIndexCenter), 140 new LineSegment(vIndex12, vIndexCenter), 141 new LineSegment(vIndex20, vIndexCenter)); 142 143 barycentric(vIndex0, vIndex01, vIndexCenter, n-1); 144 barycentric(vIndex0, vIndex20, vIndexCenter, n-1); 145 barycentric(vIndex1, vIndex01, vIndexCenter, n-1); 146 barycentric(vIndex1, vIndex12, vIndexCenter, n-1); 147 barycentric(vIndex2, vIndex12, vIndexCenter, n-1); 148 barycentric(vIndex2, vIndex20, vIndexCenter, n-1); 149 } 150 } 151 152 153 154 // Implement the MeshMaker interface (three methods). 155 @Override public int getHorzCount() {return this.n;} 156 157 @Override public int getVertCount() {return (int)Math.round(theta);} 158 159 @Override 160 public BarycentricTriangle remake(final int n, final int k) 161 { 162 return new BarycentricTriangle(k, n); 163 } 164}//BarycentricTriangle