Raw File
nurbs_plugin.cpp
#include <QFileDialog>
#include <QStack>

#include "nurbs_plugin_global.h"

#include "nurbs_plugin.h"
#include "NanoKdTree.h"
#include "StructureGraph.h"

#include "BoundaryFitting.h"

#include "interfaces/ModePluginDockWidget.h"

QVector<QColor> randColors;

//#include "LSCM.h"
//#include "LinABF.h"

QString groupID = "";
SurfaceMesh::SurfaceMeshModel * m = NULL;
RichParameterSet * mcf_params = NULL;

PointSoup pps,pps2;
SphereSoup spheres;
VectorSoup vs;
LineSegments lseg;

#include "PCA.h"
#include "SimilarSampling.h"
#include "GenericGraph.h"
#include "StructureGraph.h"

#define RADIANS(deg)    ((deg)/180.0 * M_PI)

void nurbs_plugin::create()
{
	if(widget) return;

	if(mesh()->name == "empty"){
		document()->deleteModel(document()->getModel("empty"));
	}	

	drawArea()->setRenderer( mesh(), "Transparent");
	drawArea()->camera()->setType(qglviewer::Camera::PERSPECTIVE);

	entireMesh = (SurfaceMeshModel*)document()->selectedModel();
	entirePoints = entireMesh->vertex_property<Vector3>("v:point");

	for(int i = 0; i < 10; i++)
		randColors.push_back(qRandomColor());

	loadGroupsFromOBJ();

	m = mesh();
	points = m->vertex_property<Vector3>("v:point");

	graph = new Structure::Graph;

	ModePluginDockWidget * dockwidget = new ModePluginDockWidget("NURBS plugin", mainWindow());
	widget = new NURBSTools(this);
	dockwidget->setWidget(widget);
	dockwidget->setWindowTitle(widget->windowTitle());
	mainWindow()->addDockWidget(Qt::RightDockWidgetArea,dockwidget);
	widget->fillList();

	mainWindow()->showMaximized();

	//buildSamples();
}

void nurbs_plugin::decorate()
{
	// Draw OBB
	//mesh_obb.draw();

	for(int i = 0; i < (int)curves.size(); i++)
	{
		NURBS::CurveDraw::draw( &curves[i], randColors[i%randColors.size()], true );
	}

	for(int i = 0; i < (int)rects.size(); i++)
	{
		NURBS::SurfaceDraw::draw( &rects[i], randColors[i%randColors.size()], true );
	}

	if(graph) graph->draw( this->drawArea() );

	ps.draw();
	vs.draw();
	lseg.draw();
	pps.draw();
	pps2.draw();

	spheres.draw();
}

void nurbs_plugin::clearAll()
{
	curves.clear();
	rects.clear();
}

void nurbs_plugin::saveAll()
{
	foreach(Structure::Node * n, graph->nodes)
	{
		QString nodeID = n->id;
		SurfaceMeshModel * nodeMesh = extractMesh( nodeID );

		// Assign sub-mesh to node
		n->property["mesh_filename"] = entireMesh->name + "/" + nodeID + ".obj";
		n->property["mesh"].setValue( QSharedPointer<SurfaceMeshModel>(nodeMesh) );
	}

	QString folderPath = QFileDialog::getExistingDirectory();
	QDir::setCurrent( folderPath );
	
	QString filename = folderPath + "/" + entireMesh->name + ".xml";
	graph->saveToFile( filename );
}

bool nurbs_plugin::keyPressEvent( QKeyEvent* event )
{
	bool used = false;

	if(event->key() == Qt::Key_E)
	{
		NURBS::NURBSCurved & c = curves.back();

		NURBS::NURBSCurved newCurve = NURBS::NURBSCurved::createCurveFromPoints( c.simpleRefine(1) );

		if(curves.size() == 1)
			curves.push_back( newCurve );
		else
			c = newCurve;

		used = true;
	}

	if(event->key() == Qt::Key_R)
	{
		NURBS::NURBSCurved & c = curves.back();

		c = NURBS::NURBSCurved::createCurveFromPoints( c.removeKnots(2) ) ;

		used = true;
	}

	if(event->key() == Qt::Key_Q)
	{
		NURBS::NURBSRectangled & r = rects.back();

		r = NURBS::NURBSRectangled::createSheetFromPoints( r.midPointRefined() );

		used = true;
	}

	if(event->key() == Qt::Key_W)
	{
		//QElapsedTimer timer; timer.start();

		//LinABF linabf(m);
		//mainWindow()->setStatusBarMessage(QString("LinABF time = %1 ms").arg(timer.elapsed()),512);

		//linabf.applyUVTom;

		//used = true;
	}

	if(event->key() == Qt::Key_Z)
	{
		NURBS::NURBSRectangled & r = rects.back();

		r = NURBS::NURBSRectangled::createSheetFromPoints( r.simpleRefine(1,0) );

		used = true;
	}

	if(event->key() == Qt::Key_X)
	{
		NURBS::NURBSRectangled & r = rects.back();

		r = NURBS::NURBSRectangled::createSheetFromPoints( r.simpleRefine(1,1) );

		used = true;
	}

	if(event->key() == Qt::Key_C)
	{
		NURBS::NURBSRectangled & r = rects.back();

		r = NURBS::NURBSRectangled::createSheetFromPoints( r.simpleRemove(r.mNumUCtrlPoints * 0.5,0) );

		used = true;
	}

	if(event->key() == Qt::Key_V)
	{
		NURBS::NURBSRectangled & r = rects.back();

		r = NURBS::NURBSRectangled::createSheetFromPoints( r.simpleRemove(r.mNumVCtrlPoints * 0.5,1) );

		used = true;
	}

	if(event->key() == Qt::Key_Space)
	{
		buildSamples();
		used = true;
	}

	drawArea()->updateGL();

	return used;
}

void nurbs_plugin::buildSamples()
{
	// Curve example
	int degree = 3;
	std::vector<Vector3d> cps;
	int steps = 10;
	double theta = M_PI * 5 / steps;
	double r = M_PI;

	for(int i = 0; i <= steps; i++){
		double x = (double(i) / steps) * r;
		double y = sin(i * theta) * r * 0.25;

		cps.push_back(Vector3d(x - r * 0.5, y, cos(i*theta)));
	}

	std::vector<Scalar> weight(cps.size(), 1.0);

	curves.push_back(NURBS::NURBSCurved(cps, weight, degree, false, true));

	// Rectangular surface
	double w = 1;
	double l = 2;

	int width = w * 5;
	int length = l * 5;

	std::vector< std::vector<Vector3d> > cpts( width, std::vector<Vector3d>(length) );
	std::vector< std::vector<Scalar> > weights( width, std::vector<Scalar>(length) );

	double omega = M_PI * 3 / qMin(width, length);

	Vector3d surface_pos(0,1,0);

	for(int i = 0; i < width; i++)
		for(int j = 0; j < length; j++){
			double x = double(i) / width;
			double y = double(j) / length;

			double delta = sqrt(x*x + y*y) * 0.5;

			cpts[i][j] = Vector3d(surface_pos.x() + x * w, surface_pos.y() + y * l, delta + sin(j * omega) * qMin(width, length) * 0.02);
		}

	rects.push_back( NURBS::NURBSRectangled(cpts, weights, degree, degree, false, false, true, true) );
}

void nurbs_plugin::prepareSkeletonize()
{
	// Remove visualization
	ps.clear();

	m->update_face_normals();
	m->update_vertex_normals();
	m->updateBoundingBox();

	drawArea()->setRenderer( m, "Wireframe");
	document()->setSelectedModel( m );

	// Voxelize
	if( widget->isVoxelize() )
	{
		FilterPlugin * voxResamplePlugin = pluginManager()->getFilter("Voxel Resampler");
		RichParameterSet * resample_params = new RichParameterSet;
		voxResamplePlugin->initParameters( resample_params );
		resample_params->setValue("voxel_scale", float( widget->voxelParamter() ));
		voxResamplePlugin->applyFilter( resample_params );
	}

	// Remesh
	if( widget->isRemesh() )
	{
		FilterPlugin * remeshPlugin = pluginManager()->getFilter("Isotropic Remesher");
		RichParameterSet * remesh_params = new RichParameterSet;
		remeshPlugin->initParameters( remesh_params );
		float edge_threshold = float(widget->remeshParamter() * m->bbox().diagonal().norm());

		if(widget->isProtectSmallFeatures()){
			remesh_params->setValue("keep_shortedges", true);
			edge_threshold *= 0.5;
		}

		remesh_params->setValue("edgelength_TH", edge_threshold);

		remeshPlugin->applyFilter( remesh_params );
	}

	// Compute MAT
	FilterPlugin * matPlugin = pluginManager()->getFilter("Voronoi based MAT");
	RichParameterSet * mat_params = new RichParameterSet;
	matPlugin->initParameters( mat_params );
	matPlugin->applyFilter( mat_params );
}

void nurbs_plugin::stepSkeletonizeMesh()
{	
	FilterPlugin * mcfPlugin = pluginManager()->getFilter("MCF Skeletonization");

	if(!mcf_params){
		mcf_params = new RichParameterSet;
		mcfPlugin->initParameters( mcf_params );

		// Custom MCF parameters
		if( !widget->isUseMedial() ) 
			mcf_params->setValue("omega_P_0", 0.0f);
	}
	mcfPlugin->applyFilter( mcf_params );
	 
	//drawArea()->setRenderer(m,"Flat Wire");
	//drawArea()->updateGL(); // may cause crashing of OpenGL
}

void nurbs_plugin::drawWithNames()
{
	// Faces
	foreach( const Face f, entireMesh->faces() )
	{
		// Collect points
		QVector<Vector3> pnts; 
		Surface_mesh::Vertex_around_face_circulator vit = entireMesh->vertices(f),vend=vit;
		do{ pnts.push_back(entirePoints[vit]); } while(++vit != vend);

		glPushName(f.idx());
		glBegin(GL_TRIANGLES);
		foreach(Vector3 p, pnts) glVector3(p);
		glEnd();
		glPopName();
	}
}


SurfaceMeshModel * nurbs_plugin::extractMesh( QString gid )
{
	SurfaceMeshModel * subMesh = NULL;

	QVector<int> part = groupFaces[gid];

	// Create copy of sub-part
	subMesh = new SurfaceMeshModel(groupID + ".obj", groupID);
	QSet<int> vertSet;
	QMap<Vertex,Vertex> vmap;
	foreach(int fidx, part){
		Surface_mesh::Vertex_around_face_circulator vit = entireMesh->vertices(Face(fidx)),vend=vit;
		do{ vertSet.insert(Vertex(vit).idx()); } while(++vit != vend);
	}
	foreach(int vidx, vertSet){
		vmap[Vertex(vidx)] = Vertex(vmap.size());
		subMesh->add_vertex( entirePoints[Vertex(vidx)] );
	}
	foreach(int fidx, part){
		std::vector<Vertex> pnts; 
		Surface_mesh::Vertex_around_face_circulator vit = entireMesh->vertices(Face(fidx)),vend=vit;
		do{ pnts.push_back(vmap[vit]); } while(++vit != vend);
		subMesh->add_face(pnts);
	}
	subMesh->updateBoundingBox();
	subMesh->isVisible = true;

	return subMesh;
}

bool nurbs_plugin::postSelection( const QPoint& point )
{
	Q_UNUSED(point);
	int selectedID = drawArea()->selectedName();
	if (selectedID == -1){
		ps.clear();
		document()->setSelectedModel(entireMesh);
        return false;
	}
	qDebug() << "Selected ID is " << selectedID;

	Vertex selectedVertex = entireMesh->vertices( Surface_mesh::Face(selectedID) );

	QString gid = faceGroup[entireMesh->face(entireMesh->halfedge(selectedVertex)).idx()];
	selectGroup(gid);

    return true;
}

void nurbs_plugin::selectGroup(QString gid)
{
	groupID = gid;
	m = extractMesh( groupID );
	QVector<int> part = groupFaces[gid];

	// Draw selection
	ps.clear();
	foreach(int fidx, part)
	{
		// Collect points
		QVector<QVector3> pnts; 
		Surface_mesh::Vertex_around_face_circulator vit = entireMesh->vertices(Face(fidx)),vend=vit;
		do{ pnts.push_back(entirePoints[vit]); } while(++vit != vend);

		ps.addPoly(pnts, QColor(255,0,0,100));
	}
}

void nurbs_plugin::loadGroupsFromOBJ()
{
	// Read obj file
	QFile file(mesh()->path);
	if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return;
	QTextStream inF(&file);
	int fidx = 0;
	while( !inF.atEnd() ){
		QString line = inF.readLine();
		if(!line.size()) continue;
		if(line.at(0).toAscii() == 'g'){
			QStringList groupLine = line.split(" ");
			QString gid = QString::number(groupFaces.size());
			if(groupLine.size()) gid = groupLine.at(1);
			while(true){
				QString line = inF.readLine();
				if(!line.startsWith("f")) break;
				groupFaces[gid].push_back(fidx);
				faceGroup[fidx] = gid;
				fidx++;
			}
		}
	}
}

Vector3d nurbs_plugin::pole( Vector3d center, double radius, SurfaceMeshModel * part )
{
	Vector3VertexProperty partPoints = part->vertex_property<Vector3>("v:point");

	std::vector<Vector3d> samples;
	QVector<Face> ifaces;
	Vector3d p;

	// intersect sphere with part, collect faces
	foreach( Face f, part->faces() ){
		QVector<Vector3d> pnts; 
		Surface_mesh::Vertex_around_face_circulator vit = part->vertices(Face(f)),vend=vit;
		do{ pnts.push_back(partPoints[vit]); } while(++vit != vend);

		if(TestSphereTriangle(center, radius, pnts[0], pnts[1], pnts[2], p)){
			ifaces.push_back(f);
			foreach(Vector3d s, sampleEdgeTri(pnts[0], pnts[1], pnts[2]))
				samples.push_back(s);
		}
	}

	Vector3d a,b,c;
	return PCA::mainAxis(samples,a,b,c).normalized();
}

std::vector<Vector3d> nurbs_plugin::sampleEdgeTri( Vector3d a, Vector3d b, Vector3d c )
{
	std::vector<Vector3d> samples;
	samples.push_back(a);samples.push_back(b);samples.push_back(c);
	samples.push_back((a+b) * 0.5);
	samples.push_back((b+c) * 0.5);
	samples.push_back((c+a) * 0.5);
	return samples;
}

double nurbs_plugin::minAngle(Face f, SurfaceMeshModel * ofMesh)
{
	double minAngle(DBL_MAX);
	Vector3VertexProperty pts = ofMesh->vertex_property<Vector3>("v:point");

	SurfaceMesh::Model::Halfedge_around_face_circulator h(ofMesh, f), eend = h;
	do{ 
		Vector3 a = pts[ofMesh->to_vertex(h)];
		Vector3 b = pts[ofMesh->from_vertex(h)];
		Vector3 c = pts[ofMesh->to_vertex(ofMesh->next_halfedge(h))];

		double d = dot((b-a).normalized(), (c-a).normalized());
		double angle = acos(qRanged(-1.0, d, 1.0));

		minAngle = qMin(angle, minAngle);
	} while(++h != eend);

	return minAngle;
}

void nurbs_plugin::convertToCurve()
{
	if(!m) return;

	document()->addModel( m );

	prepareSkeletonize(); 

	double theta = 15.0; // degrees

	for(int i = 0; i < 30; i++){
		stepSkeletonizeMesh();
	
		// Decide weather or not to keep contracting based on angle of faces
		bool isDone = true;
		foreach( Face f, m->faces() ){
			if( minAngle(f, m) > RADIANS(theta) )
				isDone = false;
		}

		if(isDone) break;
	}

	// Clear parameters
	mcf_params = NULL;

	foreach(Vertex v, m->vertices()) if(m->is_isolated(v)) m->remove_vertex(v);
	m->garbage_collection();

	// Overwrite any existing extracted nodes for selected part
	if(graph->getNode(groupID)) graph->removeNode(graph->getNode(groupID)->id);

	graph->addNode( new Structure::Curve(curveFit( m ), groupID) );

	// Clean up
	ps.clear();
	document()->setSelectedModel(entireMesh);
	document()->deleteModel(m);
	drawArea()->clear();
	
	m = NULL;

	drawArea()->updateGL();
}

void nurbs_plugin::convertToSheet()
{
	if(!m) return;

	document()->addModel( m );

	prepareSkeletonize(); 

	for(int i = 0; i < widget->contractIterations(); i++)
	{
		stepSkeletonizeMesh();
	}

	// Clear parameters
	mcf_params = NULL;

	// Clean up
	foreach(Vertex v, m->vertices()) if(m->is_isolated(v)) m->remove_vertex(v);
	m->garbage_collection();

	// Overwrite any existing extracted nodes for selected part
	if(graph->getNode(groupID)) graph->removeNode(graph->getNode(groupID)->id);

	graph->addNode( new Structure::Sheet(surfaceFit( m ), groupID) );

	document()->setSelectedModel( entireMesh );
	document()->deleteModel(m);
	drawArea()->clear();

	m = NULL;

	drawArea()->updateGL();
}

NURBS::NURBSCurved nurbs_plugin::curveFit( SurfaceMeshModel * part )
{
	NURBS::NURBSCurved fittedCurve;

	Vector3VertexProperty partPoints = part->vertex_property<Vector3>("v:point");

	GenericGraphs::Graph<int,double> g;
	SurfaceMeshHelper h(part);
	ScalarEdgeProperty elen = h.computeEdgeLengths();

	foreach(Edge e, part->edges()){
		Vertex v0 = part->vertex(e, 0);
		Vertex v1 = part->vertex(e, 1);
		g.AddEdge(v0.idx(), v1.idx(), elen[e]);
	}

	// Find initial furthest point
	g.DijkstraComputePaths(0);
	double max_dist = -DBL_MAX;
	int idxA = 0;
	for(int i = 0; i < (int)part->n_vertices(); i++){
		if(g.min_distance[i] > max_dist){
			max_dist = qMax(max_dist, g.min_distance[i]);
			idxA = i;
		}
	}

	// Find two furthest points
	g.DijkstraComputePaths(idxA);
	max_dist = -DBL_MAX;
	int idxB = 0;
	for(int i = 0; i < (int)part->n_vertices(); i++){
		if(g.min_distance[i] > max_dist){
			max_dist = qMax(max_dist, g.min_distance[i]);
			idxB = i;
		}
	}

	std::list<int> path = g.DijkstraShortestPath(idxA,idxB);

	// Check for loop case
	double r = 0.025 * part->bbox().diagonal().norm();
	QVector<int> pathA, pathB;
	foreach(int vi, path) pathA.push_back(vi);
	Vertex centerPath ( pathA[pathA.size() / 2] );

	foreach( Face f, part->faces() ){
		QVector<Vertex> vidx; 
		Surface_mesh::Vertex_around_face_circulator vit = part->vertices(Face(f)),vend=vit;
		do{ vidx.push_back(vit); } while(++vit != vend);
		Vector3d cp(0,0,0);
		if( TestSphereTriangle(partPoints[centerPath], r, partPoints[vidx[0]], partPoints[vidx[1]], partPoints[vidx[2]], cp) ){
			int v0 = vidx[0].idx();
			int v1 = vidx[1].idx();
			int v2 = vidx[2].idx();
			g.SetEdgeWeight(v0,v1,DBL_MAX);
			g.SetEdgeWeight(v1,v2,DBL_MAX);
			g.SetEdgeWeight(v2,v0,DBL_MAX);
		}
	}

	foreach(int vi, g.DijkstraShortestPath(idxB,idxA)) pathB.push_back(vi);

	QVector<int> finalPath = pathA;

	// We have a loop
	if(pathB.size() > 0.1 * pathA.size()) 
		finalPath += pathB;

	std::vector<Vector3d> polyLine;
	for(int i = 0; i < (int)finalPath.size(); i++)
		polyLine.push_back( partPoints[Vertex(finalPath[i])] );

	polyLine = refineByResolution(polyLine, r);
	//polyLine = smoothPolyline(polyLine, 1);

	return NURBS::NURBSCurved::createCurveFromPoints(polyLine);
}

std::vector<Vertex> nurbs_plugin::collectRings(SurfaceMeshModel * part, Vertex v, size_t min_nb)
{
	std::vector<Vertex> all;
	std::vector<Vertex> current_ring, next_ring;
	SurfaceMeshModel::Vertex_property<int> visited_map = part->vertex_property<int>("v:visit_map",-1);

	//initialize
	visited_map[v] = 0;
	current_ring.push_back(v);
	all.push_back(v);

	int i = 1;

	while ( (all.size() < min_nb) &&  (current_ring.size() != 0) ){
		// collect i-th ring
		std::vector<Vertex>::iterator it = current_ring.begin(), ite = current_ring.end();

		for(;it != ite; it++){
			// push neighbors of 
			SurfaceMeshModel::Halfedge_around_vertex_circulator hedgeb = part->halfedges(*it), hedgee = hedgeb;
			do{
				Vertex vj = part->to_vertex(hedgeb);

				if (visited_map[vj] == -1){
					visited_map[vj] = i;
					next_ring.push_back(vj);
					all.push_back(vj);
				}

				++hedgeb;
			} while(hedgeb != hedgee);
		}

		//next round must be launched from p_next_ring...
		current_ring = next_ring;
		next_ring.clear();

		i++;
	}

	//clean up
	part->remove_vertex_property(visited_map);

	return all;
}

NURBS::NURBSRectangled nurbs_plugin::surfaceFit( SurfaceMeshModel * part )
{
	points = part->vertex_property<Vector3>("v:point");
	
	/// Pick a side by clustering normals

	// 1) Find edge with flat dihedral angle
	SurfaceMeshModel::Vertex_property<double> vals = part->vertex_property<double>("v:vals",0);
	foreach(Vertex v, part->vertices()){
		double sum = 0.0;
		foreach(Vertex v, collectRings(part,v,12)){
			foreach(Halfedge h, part->onering_hedges(v)){
				sum += abs(calc_dihedral_angle( part, h ));
			}
		}
		vals[v] = sum;
	}

	double minSum = DBL_MAX;
	Vertex minVert;
	foreach(Vertex v, part->vertices()){
		if(vals[v] < minSum){
			minSum = vals[v];
			minVert = v;
		}
	}
	Halfedge startEdge = part->halfedge(minVert);

	// 2) Grow region by comparing difference of adjacent dihedral angles
	double angleThreshold = deg_to_rad( 40.0 );

	SurfaceMesh::Model::Vertex_property<bool> vvisited = part->add_vertex_property<bool>("v:visited", false);

	QStack<SurfaceMesh::Model::Vertex> to_visit;
	to_visit.push( part->to_vertex(startEdge) );

	while(!to_visit.empty())
	{
		Vertex cur_v = to_visit.pop();
		if( vvisited[cur_v] ) continue;
		vvisited[cur_v] = true;

		// Sum of angles around
		double sumAngles = 0.0;
		foreach(Halfedge hj, part->onering_hedges(cur_v)){
			sumAngles += abs(calc_dihedral_angle( part, hj ));
		}

		foreach(Halfedge hj, part->onering_hedges(cur_v))
		{
			Vertex vj = part->to_vertex(hj);
			if(sumAngles < angleThreshold)
				to_visit.push(vj);
			else
				vvisited[vj];
		}
	}

	// Get filtered inner vertices of selected side
	int shrink_count = 2;
	std::set<Vertex> inner;
	std::set<Vertex> border;
	for(int i = 0; i < shrink_count; i++)
	{
		std::set<Vertex> all_points;
		foreach(Vertex v, part->vertices())
			if(vvisited[v]) all_points.insert(v);

		border.clear();
		foreach(Vertex v, part->vertices()){
			if(vvisited[v]){
				foreach(Halfedge hj, part->onering_hedges(v)){
					Vertex vj = part->to_vertex(hj);
					if(!vvisited[vj])
						border.insert(vj);
				}
			}
		}

		inner.clear();
		std::set_difference(all_points.begin(), all_points.end(), border.begin(), border.end(),
			std::inserter(inner, inner.end()));

		// Shrink one level
		foreach(Vertex vv, border){
			foreach(Halfedge hj, part->onering_hedges(vv))
				vvisited[ part->to_vertex(hj) ] = false;
		}
	}

	SurfaceMesh::Model * submesh = NULL;

	bool isOpen = false;
	foreach(Vertex v, part->vertices()){
		if(part->is_boundary(v)){
			isOpen = true;
			break;
		}
	}

	if(!isOpen)
	{
		// Collect inner faces
		std::set<Face> innerFaces;
		std::set<Vertex> inFacesVerts;
		foreach(Vertex v, inner)
		{
			foreach(Halfedge hj, part->onering_hedges(v)){
				Face f = part->face(hj);
				innerFaces.insert(f);
				Surface_mesh::Vertex_around_face_circulator vit = part->vertices(f),vend=vit;
				do{ inFacesVerts.insert( Vertex(vit) ); } while(++vit != vend);
			}
		}

		// Create sub-mesh
		submesh = new SurfaceMesh::Model("SideFlat.obj","SideFlat");

		// Add vertices
		std::map<Vertex,Vertex> vmap;
		foreach(Vertex v, inFacesVerts){
			vmap[ v ] = Vertex(vmap.size());
			submesh->add_vertex( points[v] );
		}

		// Add faces
		foreach(Face f, innerFaces){
			std::vector<Vertex> verts;
			Surface_mesh::Vertex_around_face_circulator vit = part->vertices(f),vend=vit;
			do{ verts.push_back( Vertex(vit) ); } while(++vit != vend);
			submesh->add_triangle( vmap[verts[0]], vmap[verts[1]], vmap[verts[2]] );
		}
	}
	else
	{
		submesh = part;
	}

	{
		//ModifiedButterfly subdiv;
		//subdiv.subdivide((*(Surface_mesh*)submesh),1);
	}

	
	submesh->isVisible = false;

	if(part != submesh) document()->addModel( submesh );
	Vector3VertexProperty sub_points = submesh->vertex_property<Vector3>("v:point");

	// Smoothing
	{
		int numIteration = 3;
		bool protectBorders = true;

		Surface_mesh::Vertex_property<Point> newPositions = submesh->vertex_property<Point>("v:new_point",Vector3(0,0,0));
		Surface_mesh::Vertex_around_vertex_circulator vvit, vvend;

		// This method uses the basic equal weights Laplacian operator
		for(int iteration = 0; iteration < numIteration; iteration++)
		{
			Surface_mesh::Vertex_iterator vit, vend = submesh->vertices_end();

			// Original positions, for boundary
			for(vit = submesh->vertices_begin(); vit != vend; ++vit)
				newPositions[vit] = sub_points[vit];

			// Compute Laplacian
			for(vit = submesh->vertices_begin(); vit != vend; ++vit)
			{
				if(!protectBorders || (protectBorders && !submesh->is_boundary(vit)))
				{
					newPositions[vit] = Point(0,0,0);

					// Sum up neighbors
					vvit = vvend = submesh->vertices(vit);
					do{ newPositions[vit] += sub_points[vvit]; } while(++vvit != vvend);

					// Average it
					newPositions[vit] /= submesh->valence(vit);
				}
			}

			// Set vertices to final position
			for(vit = submesh->vertices_begin(); vit != vend; ++vit)
				sub_points[vit] = newPositions[vit];
		}

		submesh->remove_vertex_property(newPositions);
	}

	/// ==================
	// Fit rectangle

	BoundaryFitting bf( (SurfaceMeshModel*)submesh, widget->uCount(), widget->vCount() );

	/// ==================
	// Debug fitting

	if( false )
	{
		PointSoup * ps = new PointSoup;
		foreach(Vertex v, submesh->vertices())
		{
			//if(submesh->is_boundary(v))
			//	ps->addPoint( sub_points[v], qtJetColorMap(bf.vals[v]) );
		}
		drawArea()->addRenderObject(ps);

        FaceBarycenterHelper helper(submesh);
        Vector3FaceProperty fcenter = helper.compute();
		foreach(Face f, submesh->faces()){
			//if(submesh->is_boundary(f))
			vs.addVector(fcenter[f],bf.fgradient[f]);
		}

		drawArea()->addRenderObject( new LineSegments(bf.ls) );

		foreach(Vertex v, submesh->vertices()){
			//if(submesh->is_boundary(v))
			//	vs.addVector(sub_points[v],bf.vdirection[v]);
			//vs.addVector(sub_points[v],bf.vgradient[v]);
		}

		for(int i = 0; i < (int)bf.debugPoints2.size(); i++)
		{
			double t = double(i) / (bf.debugPoints2.size()-1);
			pps.addPoint(bf.debugPoints2[i], QColor(0,255 * t,50));
		}

		for(int i = 0; i < (int)bf.debugPoints.size(); i++)
		{
			//double t = double(i) / (bf.debugPoints.size()-1);
			//pps.addPoint(bf.debugPoints[i], QColor(255 * t,0,0));
		}

		for(int i = 1; i < (int)bf.debugPoints.size(); i++)
		{
			lseg.addLine( bf.debugPoints[i-1], bf.debugPoints[i], QColor(255,255,255,100) );
		}

		if(bf.corners.size())
		{
			PointSoup * ppp = new PointSoup(20);
			ppp->addPoint(bf.corners[0], QColor(255,0,0));
			ppp->addPoint(bf.corners[1], QColor(0,255,0));
			ppp->addPoint(bf.corners[2], QColor(0,0,255));
			ppp->addPoint(bf.corners[3], QColor(0,255,255));
			drawArea()->addRenderObject(ppp);
		}

		if(bf.main_edges.size())
		{
			PointSoup * ppe = new PointSoup(10);
			foreach(Vector3d p, bf.main_edges[0]) ppe->addPoint(p, QColor(255,0,0));
			foreach(Vector3d p, bf.main_edges[1]) ppe->addPoint(p, QColor(0,255,0));
			foreach(Vector3d p, bf.main_edges[2]) ppe->addPoint(p, QColor(0,0,255));
			foreach(Vector3d p, bf.main_edges[3]) ppe->addPoint(p, QColor(0,255,255));
			drawArea()->addRenderObject(ppe);
		}
	}
	/// ==================

	Array2D_Vector3 cp = bf.lines;
	//cp.clear(); // debug

	if(!cp.size()) return NURBS::NURBSRectangled::createSheet(Vector3d(0,0,0),Vector3d(0.01));

	Array2D_Real cw(cp.size(), Array1D_Real(cp.front().size(), 1.0));
	int degree = 3;
	return NURBS::NURBSRectangled(cp,cw,degree,degree,false,false,true,true);

	document()->deleteModel(m);

	// debug
	//return NURBS::NURBSRectangled::createSheet(Vector3d(0,0,0),Vector3d(0.01));
}

void nurbs_plugin::experiment()
{
	NanoKdTree tree;
	Vector3VertexProperty mesh_points = mesh()->get_vertex_property<Vector3>(VPOINT);

	foreach(Vertex v, mesh()->vertices())
	{
		tree.addPoint( mesh_points[v] );
	}

	tree.build();

	KDResults matches;
	tree.ball_search(Vector3d(0,0,0), 0.1, matches);

	drawArea()->deleteAllRenderObjects();

	foreach(KDResultPair r, matches)
	{
		drawArea()->drawPoint(mesh_points[Vertex(r.first)]);
	}
}

void nurbs_plugin::updateDrawArea()
{
	drawArea()->update();
}

void nurbs_plugin::loadGraph()
{
	QStringList fileNames = QFileDialog::getOpenFileNames(0, "Open Model",
		mainWindow()->settings()->getString("lastUsedDirectory"), "Model Files (*.xml)");
	if(fileNames.isEmpty()) return;

	graph = new Structure::Graph(fileNames.front());
}

Q_EXPORT_PLUGIN (nurbs_plugin)
back to top