1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
#pragma once

#include <string>
#include <vector>
#include <map>
#include <set>
#include <memory>

#include "common.h"
#include "FencedRegion.h"
#include "PatchSet.h"
#include "MotorcycleGraph.h"

#include "Statistics.h"

#include <nsessentials/math/BoundingBox.h>
#include <nsessentials/util/UnionFind.h>

const Eigen::Vector4f valenceColors[] =
{
	Eigen::Vector4f(0.843137255f,	0.188235294f,	0.152941176f, 1.0f),
	Eigen::Vector4f(0.988235294f,	0.552941176f,	0.349019608f, 1.0f),
	Eigen::Vector4f(0.996078431f,	0.878431373f,	0.564705882f, 1.0f),
	Eigen::Vector4f(1.0f,			1.0f,			1.0f,		  1.0f),
	Eigen::Vector4f(0.878431373f,	0.952941176f,	0.97254902f , 1.0f),
	Eigen::Vector4f(0.568627451f,	0.749019608f,	0.858823529f, 1.0f),
	Eigen::Vector4f(0.270588235f,	0.458823529f,	0.705882353f, 1.0f),
};

//Returns the color for a given valence defect. A valence defect of 0 will produce a 
//neutral color.
static const Eigen::Vector4f GetValenceColor(int valenceDefect)
{
	size_t colorIndex = std::max<size_t>(0, std::min<size_t>(6, valenceDefect + 3));
	return valenceColors[colorIndex];
}

const int segmentColorCount = 9;
const int segmentColors[segmentColorCount][3] =
{
	{ 166, 206, 227 },
	{ 31,120,180 },
	{ 251,154,153 },
	/*{ 227,26,28 },*/
	{ 253,191,111 },
	{ 255,127,0 },
	{ 202,178,214 },
	{ 106,61,154 },
	{ 255,255,153 },
	{ 177,89,40 }
};

//Returns an arbitrary color for a given segment index. Neighboring segment
//indices will produce different colors.
static const Eigen::Vector4f GetSegmentColor(int segment)
{
	return Eigen::Vector4f(segmentColors[segment % segmentColorCount][0] / 255.0f, segmentColors[segment % segmentColorCount][1] / 255.0f, segmentColors[segment % segmentColorCount][2] / 255.0f, 1.0f);
}

//Represents the result of singularity classification
enum ClassificationResult
{
	//A non-degenerate regular fenced region could be found.
	RectilinearPatch,

	//A non-degenerate irregular fenced region could be found.
	NonRectilinearPatch,

	//No non-degenerate fenced region could be found.
	NoPatch
};


//Deprecated
enum MultiplierStrategy
{
	AllInvalid,
	IterativeRounding,
};

//Represents the strategy to calculate arc lengths during parametrization
enum ArclengthStrategy
{
	//ArclengthStrategySimple
	Simple,

	//ArclengthStrategyGurobiSeparate
	GurobiSeparate,

	//ArclengthStrategyGlobalGurobi
	GurobiGlobal,
};

//Holds all relevant data for calculating a motorcycle graph and parametrizing the patches.
class Data
{
public:
	Data();	

	//Resets the data object
	void Clear();

	//Loads a mesh from a file and prepares it for processing.
	//filename - the file to load
	//mergeTriangulatedQuads - try to merge neighboring triangles in the mesh if their union becomes near rectangular
	//mergeAngleTreshold - angle threshold for triangle merging (deviation from 90°) in radians
	void LoadMesh(const std::string & filename, bool mergeTriangulatedQuads, float mergeAngleThreshold);

	//Loads a mesh for the sole purpose of visualization. No processing can be performed on this mesh.
	//filename - the file to load
	//scaleTexCoords - determines if the read texture coordinates should be scaled up to match pixel coordinates
	void LoadMeshForVisualization(const std::string& filename, bool scaleTexCoords);

	//Saves the geometry of the current mesh to a PLY file.
	void SaveMeshPLY(const std::string& file) const;

	//Saves the geometry and parametrization of the current mesh to an OBJ file.
	//file - target file name
	//unsubdivide - removes the Catmull Clark subdivision where possible (if the original mesh was not a pure quad mesh)
	void SaveMeshOBJ(const std::string& file, bool unsubdivide = true) const;

	//Finds irregular vertices in the mesh. Also calls FindMetaSingularities() at the end.
	void FindSingularities();

	//Finds meta singularities based on the current set of irregular vertices and fenced regions. A meta
	//singularity is either an irregular vertex with no fenced region or an arbitrary vertex within a 
	//fenced region.
	void FindMetaSingularities();

	//Tries to grow minimal fenced region around every singularity. 
	void ClassifyAllSingularities();

	//Tries to merge neighboring fenced region in order to decrease the number of spawned motorcycles.
	void TryToMergePatches();

	//Calculates the motorcycle graph for the current set of meta singularities.
	void CalculateMotorcycleGraph(bool deactivateUnnecessaryMotorcycles = true);

	//Extracts rectangular patches from the current motorcycle graph.
	MotorcycleGraph::ExtractionStatistics ExtractPatches(bool splitNonRectangularPatches = true);
	
	//Calculates a parametrization for the current set of rectangular patches.
	//targetLengthPerEdge - the average parametric target length of edges; used to determine a global scaling 
	//                      factor between geometric and parametric domain.
	//parametrizationErrorThreshold - determines the upper threshold of the parametrization energy above which 
	//                                parametrization is re-attempted after relaxing the system. Details depend 
	//                                on the chosen parametrization strategy
	//discreteOptimizationTimeLimit - the time limit for discrete optimization steps if there are any.
	//arclengthStrategy - the strategy to use to determine parametric arc lengths.
	void CalculateParametrization(float targetLengthPerEdge, float parametrizationErrorThreshold, const std::chrono::steady_clock::duration& discreteOptimizationTimeLimit, ArclengthStrategy arclengthStrategy);
	
	//Packs the current parametrization into a rectangular domain for a given MIP level factor (positive power of 2).
	//Texture coordinates will be scaled to the [0, 1] range and the members textureWidth and textureHeight will be set.
	void PackTexture(int mipFactor);
	
	//Tries to grow a minimal fenced region around a single singularity.
	//v - the singularity to examine
	//patch - the resulting fenced region
	ClassificationResult ClassifySingularity(const SingularityInfo& v, FencedRegion& patch, bool verbose = true);
	
	

	//Prints information about the number and sizes of singularities and fenced regions to std::cout.
	void PrintSingularityStatistics() const;

	//Performs quantitative evaluation on the current parametrization.
	//logAreaRatios - Output variable. Statistics about logarithmic area ratios over all quads.
	//mips - Output variable. Statistics about MIPS energy over all triangles.
	//printStatistics - set to true in order to immediately print the gathered statistics.
	void EvaluateParametrization(Statistics& logAreaRatios, Statistics& mips, bool printStatistics);

	//TODO: fix
	//Performs remeshing of the current mesh based on the current parametrization (must be unpacked).
	//Saves the result in "remeshed.obj".
	void Remesh();

	//TODO: fix
	//Renders surface information represented in the mesh colors framework to a texture laid out
	//with the current packed parametrization.
	void RenderMeshColorsToTexture(const std::string& meshColorsFile);

	//Performs tangential smoothing of the current mesh geometry using OpenMesh.
	void TangentialSmooth();	

	//Saves the current fenced regions to a file.
	void SaveFencedRegions() const;		
	
	//Establishes colors for all texture patches and tries to ensure that neighboring patches
	//have different colors.
	void FindPatchColors();

	//Returns the object that stores texture coordinates for a given patch. Creates the storage if it does not exist.
	TextureCoordinatesStorage& AccessTexCoords(size_t patchIdx);

	//Returns the object that stores texture coordinates for a given patch. Throws an exception if it does not exist.
	const TextureCoordinatesStorage& AccessTexCoords(size_t patchIdx) const;

	//Generates texture coordinates storage objects for all currently existing patches.
	void GenerateTexCoordAccess();


	// --------  Constant accessors to data  --------

	//Returns the current motorcycle graph. Only available after calling CalculateMotorcycleGraph()
	const MotorcycleGraph* Motorcycles() const { return motorcycleGraph.get(); }

	//Returns the indices of all broken halfarc constraints (i.e. visible seams). Only available after
	//calling CalculateParametrization().
	const std::vector<size_t>& BrokenHalfarcs() const { return brokenHalfarcs; }

	//Returns the list of faces of the current mesh.
	const FaceList& Faces() const { return F; }

	//Returns the list of vertices of the current mesh.
	const Matrix3Xf& Vertices() const { return V; }

	//Returns the halfedge data structure of the current mesh.
	const HEMesh& Mesh() const { return mesh; }

	//Returns the bounding box of the current mesh.
	const nse::math::BoundingBox<float, 3>& MeshBoundingBox() const { return meshBoundingBox; }

	//Returns the current set of fenced regions. Only available after calling ClassifyAllSingularities().
	const PatchSet& FencedRegions() const { return fencedRegions; }

	//Returns if a given edge is an original edge (i.e. not created through subdivision).
	bool IsEdgeOriginal(HEMesh::EdgeHandle e) const { return mesh.property(isOriginalEdgeProp, e); }

	//Returns the current set of singularities.
	const std::vector<SingularityInfo>& Singularities() const { return singularities; }

	//Returns the current set of meta singularities (i.e. spawning points of motorcycles).
	const std::vector<MetaSingularity>& MetaSingularities() const { return metaSingularities; }

	//Returns the average edge length of the current mesh
	float AverageEdgeLength() const { return averageEdgeLength; }

	//Returns a structure that maps irregular vertices to their singularity information.
	const ManifoldnessAwareVertexMap<size_t>& VertexToSingularity() const { return vertexToSingularity; }

	//Returns a structure that maps vertices to their meta singularity information (if they are meta singularities)
	const ManifoldnessAwareVertexMap<size_t>& VertexToMetaSingularity() const { return vertexToMetaSingularity; }

	//Returns the color for a given patch. Only available after calling FindPatchColors().
	Eigen::Vector4f GetPatchColor(size_t patchIndex) const;
private:	

	//Represents an edge in the fenced region graph used for merging.
	struct GraphEdge
	{
		//Index of the source GraphNode of this edge.
		size_t from;
		
		//Index of the target GraphNode of this edge.
		size_t to;

		//Index of the last BFS node originating at the from GraphNode
		size_t fromBFSNode;
		
		//Index of the last BFS node originating at the to GraphNode
		size_t toBFSNode;

		//Length of the edge in number of faces
		size_t distance;

		//Temporary variable that determines if a merge operation uses this edge.
		bool enabled;
	};

	//Represents a node in the fenced region graph used for merging.
	struct GraphNode
	{
		//The index of the corresponding fenced region.
		size_t patchIdx;

		//List of all incident edges.
		//key = target patch id, value = idx of LoopGraphEdge
		std::map<size_t, size_t> outgoingEdges; 

		//Temporary variables that determine if the node has been processed.
		bool handled, handledForEvaluatingMergeQuality;

		GraphNode(size_t patchIdx)
			: patchIdx(patchIdx), handled(false)
		{ }
	};	

	//Mesh edge property that determines if an edge is original (i.e. not introduced through subdivision)
	OpenMesh::EPropHandleT<bool> isOriginalEdgeProp;

	//If Catmull Clark subdivision is performed after loading the mesh, represents information about
	//how the subdivision has been performed in order to undo it at the end.
	struct SubdivisionInfo
	{
		//The face that was generated after subdivision
		HEMesh::FaceHandle subdividedFace;

		//The corresponding corner of the original mesh that produced this face. This vertex is
		//also present in the subdivided mesh.
		HEMesh::VertexHandle originalCornerVertex;

		SubdivisionInfo() { }
		SubdivisionInfo(HEMesh::FaceHandle subdividedFace, HEMesh::VertexHandle originalCornerVertex)
			: subdividedFace(subdividedFace), originalCornerVertex(originalCornerVertex)
		{ }
	};
	
	//Loads a mesh into the structure, generating all auxiliary data structures
	void LoadPolygons(const Matrix3Xf& V, const FaceList& F, const std::vector<std::vector<unsigned int>>& subdivisionInfo, std::vector<HEMesh::FaceHandle>& outFaceHandles);

	//For each fenced region, gathers the edges over which motorcycles can enter it and stores them in 
	//fencedRegions.enteringEdgeToPatch.
	void RecordPatchEnteringEdges();

	//Generates a new empty motorcycle graph and subscribes to the necessary events.
	void GenerateNewMotorcycleGraph();

	//Evaluates the cost for merging a subgraph of the fenced region graph.
	//Returns if merging is possible (i.e. if the union becomes regular).
	//nodeSubset - Indices into nodes that represent the nodes in the subgraph.
	//edges - edges of the fenced region graph
	//singularities - Output variable. Number of meta singularities resulting from this merge.
	//totalFacesInPatches - Output variable. Total number of faces covered by fenced region in the subgraph of this merge.
	bool EvaluateMergeQuality(const std::set<size_t>& nodeSubset, std::vector<GraphNode>& nodes, const std::vector<GraphEdge>& edges, size_t& singularities, size_t& totalFacesInPatches);

	//Maximum size of fenced region in number of faces.
	const int maxPatchSize = 100;// 400;

	//Determines if the texCoords storage objects need to be re-allocated.
	bool texCoordsDirty = false;

	//Objects for storing texture coordinates for every texture patch.
	std::vector<TextureCoordinatesStorage> texCoords;

	FaceList F;
	Matrix3Xf V;

	HEMesh mesh;
	nse::math::BoundingBox<float, 3> meshBoundingBox;

	//For each original face, a list of the subdivided faces.
	std::vector<std::vector<SubdivisionInfo>> subdivisionInfo;

	std::vector<SingularityInfo> singularities;
	ManifoldnessAwareVertexMap<size_t> vertexToSingularity;

	std::vector<MetaSingularity> metaSingularities;
	ManifoldnessAwareVertexMap<size_t> vertexToMetaSingularity;
	bool metaSingularitiesDirty = true;

	PatchSet fencedRegions;

	int textureWidth = 0, textureHeight = 0;

	std::unique_ptr<::MotorcycleGraph> motorcycleGraph;

	//Holds pairs (i -> (j, k)) that represent that fenced region i has been merged from
	//fenced region j and k.
	std::map<size_t, std::array<size_t, 2>> regionWasMergedFrom;

	float averageEdgeLength;
	std::vector<size_t> brokenHalfarcs;


	const LengthMeasure lengthMeasure = Geometrical;

	std::vector<int> patchColors;

	//Union find to extract the connected components of the mesh
	nse::util::UnionFind connectedComponentsUF;

	//vertex ids of the representatives	for every connected component
	std::vector<unsigned int> connectedComponents; 
};