1 module beziermeshmaker.datastructures.quadcell; 2 3 import std.math; 4 import std.stdio; 5 6 import beziermeshmaker.datastructures.meshpoint; 7 import beziermeshmaker.datastructures.vec3; 8 import beziermeshmaker.datastructures.biquarticbeziersurface; 9 10 class QuadCell { 11 immutable static string VERTEX_METADATA_KEY = "originalVertexNum"; 12 13 //Vertices should be defined so vertices[0] is the P vertex associated with an original mesh vertex 14 //This means that vertices[2] is the P vertex associated with the centroid of a cell in the original mesh 15 //Note that we can't depend on which order vertices[1] and vertices[3] are in, since that depends on the order of the original polygon's vertices (or equivalently, on its normal direction) 16 MeshPoint[4] vertices; 17 private QuadCell[4] neighbors; 18 19 float a1Blend; 20 float a2Blend; 21 22 vec3 center; //Ci in the paper 23 24 vec3[MeshPoint] pPrime; 25 vec3[MeshPoint] cPrime; 26 27 bool border = false; //Set to true if this is an extra cell added to extend the border, that should be trimmed later. 28 BiquarticBezierSurface surfacePatch; 29 30 //Used to carry forward information from the initial polygons to the output surfaces 31 string[string] metadata; 32 33 this() { 34 this(0.5, 0.5); //Defaults suggested by the paper 35 } 36 this(float a1Blend, float a2Blend) { 37 this.a1Blend = a1Blend; 38 this.a2Blend = a2Blend; 39 surfacePatch = new BiquarticBezierSurface(); 40 } 41 42 //The following methods are to be called once everything is properly linked. 43 //However, each of them needs to be called on all quads in order before moving on to the next, since each depends on the previous. 44 public void calculateCenter() { 45 center = (1 - a1Blend) * (1 - a2Blend) * vertices[0].pt + 46 (1 - a1Blend) * a2Blend * vertices[1].pt + 47 a1Blend * a2Blend * vertices[2].pt + 48 a1Blend * (1 - a2Blend) * vertices[3].pt; 49 } 50 public void calculatePrimes() { 51 pPrime[vertices[0]] = getPPrime(vertices[0]); 52 pPrime[vertices[2]] = getPPrime(vertices[2]); 53 54 cPrime[vertices[0]] = getCPrime(vertices[0], pPrime[vertices[0]]); 55 cPrime[vertices[2]] = getCPrime(vertices[2], pPrime[vertices[2]]); 56 } 57 58 //Helpers for the above 59 private vec3 getPPrime(MeshPoint p) { 60 vec3 sum = new vec3(0, 0, 0); 61 foreach (QuadCell cell ; p.neighbors) { 62 sum = sum + cell.center; 63 } 64 sum = sum / p.neighbors.length; 65 66 return (1 - p.betaBlend) * p.pt + p.betaBlend * sum; 67 } 68 private vec3 getCPrime(MeshPoint p, vec3 pPrime) { 69 int currentQuadIndex = p.getIndex(this); 70 vec3 sum = new vec3(0, 0, 0); 71 for (int i = 1; i <= p.neighbors.length; i++) { 72 float cosTerm = cos( (PI * 2 * i) / p.neighbors.length); 73 vec3 vertex = p.neighbors[ (currentQuadIndex + i) % p.neighbors.length].center; 74 75 sum = sum + (cosTerm * vertex); 76 } 77 78 return pPrime + (p.alphaBlend / p.neighbors.length) * sum; 79 } 80 81 /* 82 * The intermediate control points b1 and b2 are generated in the paper by averaging over the C and C' of neighboring cells around P 83 * To avoid having to sort the neighbors of the P points consistently, we instead calculate b1 & b2 relative to the edge between P and M 84 * This is a little redundant, but it means the neighbor order doesn't matter, which drastically simplifies things. 85 */ 86 public vec3 getB1(MeshPoint pPoint, MeshPoint mPoint) { 87 return (cPrime[pPoint] + getNeighborSharingBoth(pPoint, mPoint).cPrime[pPoint]) / 2; 88 } 89 public vec3 getB2(MeshPoint pPoint, MeshPoint mPoint) { 90 return (center + getNeighborSharingBoth(pPoint, mPoint).center) / 2; 91 } 92 93 //Returns the neighboring QuadCell that contains the two given points 94 public QuadCell getNeighborSharingBoth(MeshPoint a, MeshPoint b) { 95 foreach (QuadCell neighbor ; neighbors) { 96 if (neighbor !is null && neighbor.containsVertex(a) && neighbor.containsVertex(b) ) { 97 return neighbor; 98 } 99 } 100 return null; 101 } 102 public QuadCell getNeighborSharingANotB(MeshPoint a, MeshPoint b) { 103 foreach (QuadCell neighbor ; neighbors) { 104 if (neighbor !is null && neighbor.containsVertex(a) && !neighbor.containsVertex(b) ) { 105 return neighbor; 106 } 107 } 108 return null; 109 } 110 public bool containsVertex(MeshPoint point) { 111 foreach (MeshPoint cellVertex ; vertices) { 112 if (cellVertex == point) { 113 return true; 114 } 115 } 116 return false; 117 } 118 119 public void setNeighbor(int index, QuadCell cell) { 120 neighbors[index] = cell; 121 if (cell is null) { 122 surfacePatch.neighbors[index] = null; 123 } 124 else { 125 surfacePatch.neighbors[index] = cell.surfacePatch; 126 } 127 } 128 public QuadCell getNeighbor(int index) { 129 return neighbors[index]; 130 } 131 public int getNeighborIndex(QuadCell cell) { 132 for (int i = 0; i < neighbors.length; i++) { 133 if (neighbors[i] == cell) { 134 return i; 135 } 136 } 137 return -1; 138 } 139 public bool hasAllNeighbors() { 140 foreach (QuadCell neighbor ; neighbors) { 141 if (neighbor is null) { 142 return false; 143 } 144 } 145 return true; 146 } 147 public MeshPoint getCentroidPoint() { 148 for (int i = 0; i < vertices.length; i++) { 149 if (vertices[i].ptType == MeshPoint.P_TYPE_CENTROID) { 150 return vertices[i]; 151 } 152 } 153 //This can't happen, but it's better than returning null 154 throw new Exception("No centroid vertex"); 155 } 156 }