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