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 }