Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • adfc2e5
  • /
  • nurbs_plugin
  • /
  • LinABF.h
Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge Iframe embedding
swh:1:cnt:598fd664aa3f84ab0faf364aed53c703dcb43358
directory badge Iframe embedding
swh:1:dir:1527aafa4f478fbed854d026dc9a61c6f21f769d

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
LinABF.h
#pragma once
// Based on code by Rhaleb Zayer

#include "SurfaceMeshModel.h"
#include "SurfaceMeshHelper.h"

using namespace SurfaceMesh;

#include <Eigen/Core>
#include <Eigen/Sparse>
#include <Eigen/CholmodSupport>
using namespace Eigen;
typedef Eigen::Triplet<Scalar> T;

#define M_PI    3.14159265358979323846
#define M_PI_2  1.57079632679489661923
#define cot(x)	(tan(M_PI_2 - x))

struct LinABF{

	LinABF( SurfaceMeshModel * useMesh )
	{
		if(!useMesh->is_triangle_mesh()){
			qDebug() << "WARNING: mesh has been triangulated!";
			useMesh->triangulate();
		}

		this->mesh = useMesh;
		this->points = mesh->vertex_property<Vector3>(VPOINT);

		this->np = mesh->n_vertices();
		this->nt = mesh->n_faces();
		this->nA = 3 * nt;

		/// 1) Find boundary / internal vertices
		foreach(Vertex v, mesh->vertices()){
			if(mesh->is_boundary(v))
				b.insert( v.idx() );
			else
				internal.push_back( v );
		}

		fv.resize( nt );
		fv123.reserve( 3 * nt );
		ang123 = Eigen::VectorXd::Zero( 3 * nt );

		/// 2) Angle pre-processing
		foreach(Face f, mesh->faces())
		{
			// Collect vertices of face
			Surface_mesh::Vertex_around_face_circulator vit = mesh->vertices(f),vend=vit;
			do{ fv[f.idx()].push_back( Vertex(vit) ); } while(++vit != vend);
			std::vector<Vertex> & vface = fv[f.idx()];

			Vector3 r23 = (points[vface[2]] - points[vface[1]]).normalized();
			Vector3 r31 = (points[vface[0]] - points[vface[2]]).normalized();
			Vector3 r12 = (points[vface[1]] - points[vface[0]]).normalized();

			int i = f.idx() * 3;
			ang123[i + 0] = acos(-dot(r12, r31));
			ang123[i + 1] = acos(-dot(r12, r23));
			ang123[i + 2] = acos(-dot(r31, r23));

			fv123.push_back(vface[0]);	fv123.push_back(vface[1]);	fv123.push_back(vface[2]);
			fv231.push_back(vface[1]);	fv231.push_back(vface[2]);	fv231.push_back(vface[0]);
		}

		/// 3) Update with optimal angles:

		// Fill with corresponding ones
		std::vector< T > Mang_T;
		foreach(Face f, mesh->faces()){
			int i = f.idx() * 3;
			Mang_T.push_back( T(fv123[i+0].idx(), fv231[i+0].idx(), ang123[i+0]) );
			Mang_T.push_back( T(fv123[i+1].idx(), fv231[i+1].idx(), ang123[i+1]) );
			Mang_T.push_back( T(fv123[i+2].idx(), fv231[i+2].idx(), ang123[i+2]) );
		}

		// Ring angles
		Mang = Eigen::SparseMatrix<Scalar,Eigen::RowMajor>(np,np);
		Mang.setFromTriplets( Mang_T.begin(), Mang_T.end() );

		// Sum it up
		Eigen::VectorXd sumringangles(np);
		for(int i = 0; i < np; i++) sumringangles(i) = Mang.row(i).sum();

		// Optimal angles
		foreach(Face f, mesh->faces())
		{
			int i = f.idx() * 3;

			double ang1opt = 2 * M_PI * ang123[i+0] / sumringangles[ fv123[i+0].idx() ];
			double ang2opt = 2 * M_PI * ang123[i+1] / sumringangles[ fv123[i+1].idx() ];
			double ang3opt = 2 * M_PI * ang123[i+2] / sumringangles[ fv123[i+2].idx() ];

			ang123opt.push_back(ang1opt);	ang123opt.push_back(ang2opt);	ang123opt.push_back(ang3opt);
		}

		// Find angles with big deficit
		rhsk = Eigen::VectorXd::Constant(np, 2 * M_PI) - sumringangles;

		// Threshold 1.0
		QSet<int> icha;
		for(int i = 0; i < (int)rhsk.rows(); i++) if(abs(rhsk(i)) > 1.0) icha.insert(i);

		// Exclude boundary
		QSet<int> icha_internal = icha - b;
		//std::set_difference(icha.begin(), icha.end(), b.begin(), b.end(), std::inserter(icha_internal, icha_internal.end()));

		// Go over all ang123 and replace 'vi' values with optimal
		foreach(int vi, icha_internal){
			foreach(Face f, mesh->faces()){
				int fidx = f.idx();

				int v1 = 3*fidx + 0;
				int v2 = 3*fidx + 1;
				int v3 = 3*fidx + 2;

				if(vi == fv[fidx][0].idx()) ang123[v1] = ang123opt[v1];
				if(vi == fv[fidx][1].idx()) ang123[v2] = ang123opt[v2];
				if(vi == fv[fidx][2].idx()) ang123[v3] = ang123opt[v3];
			}
		}

		/// 4) Fill condition matrices
		std::vector< T > B1_T, B2_T, B3_T, tmp_T;

		// B1:
		rhs1 = Eigen::VectorXd(nt);
		for(int i = 0; i < nt; i++){
			B1_T.push_back(T(i,3*i+0, 1));
			B1_T.push_back(T(i,3*i+1, 1));
			B1_T.push_back(T(i,3*i+2, 1));

			// Sum angles
			Scalar sum = ang123[3*i+0] + ang123[3*i+1] + ang123[3*i+2];
			rhs1(i) = M_PI - sum;
		}
		B1 = Eigen::SparseMatrix<Scalar>(nt,nA);
		B1.setFromTriplets( B1_T.begin(), B1_T.end() );
		B1_T.clear();

		// B2:
		for(int idx = 0; idx < nt; idx++)
		{
			int a1 = fv[idx][0].idx();
			int a2 = fv[idx][1].idx();
			int a3 = fv[idx][2].idx();

			int it0 = 3*idx + 0;
			int it1 = 3*idx + 1;
			int it2 = 3*idx + 2;

			if(!b.contains(a1)) tmp_T.push_back( T(a1,it0, 1) );
			if(!b.contains(a2)) tmp_T.push_back( T(a2,it1, 1) );
			if(!b.contains(a3)) tmp_T.push_back( T(a3,it2, 1) );
		}

		// Only compute for internals
		{
			std::set<int> usedRow;
			std::map<int,int> rowMap;
			foreach(T t, tmp_T) usedRow.insert(t.row());
			foreach(int r, usedRow) rowMap[r] = rowMap.size();
			foreach(T t, tmp_T) B2_T.push_back( T(rowMap[t.row()],t.col(),t.value()) );
		}

		B2 = Eigen::SparseMatrix<Scalar>(np - b.size(), nA);
		B2.setFromTriplets( B2_T.begin(), B2_T.end() );
		B2_T.clear();

		Eigen::VectorXd b2_ang123 = B2 * ang123;
		Eigen::VectorXd two_pi = Eigen::VectorXd::Constant(b2_ang123.rows(), 2 * M_PI);
		rhs2 = two_pi - b2_ang123;

		// Side condition
		// B3:
		tmp_T.clear();
		for(int idx = 0; idx < nt; idx++)
		{
			int a1 = fv[idx][0].idx();
			int a2 = fv[idx][1].idx();
			int a3 = fv[idx][2].idx();

			int it0 = 3*idx + 0;
			int it1 = 3*idx + 1;
			int it2 = 3*idx + 2;

			if(!b.contains(a1)){
				tmp_T.push_back( T(a1,it2, cot( ang123(it2) )) );
				tmp_T.push_back( T(a1,it1, -cot( ang123(it1) )) );
			}

			if(!b.contains(a2)){
				tmp_T.push_back( T(a2,it0, cot( ang123(it0) )) );
				tmp_T.push_back( T(a2,it2, -cot( ang123(it2) )) );
			}

			if(!b.contains(a3)){
				tmp_T.push_back( T(a3,it1, cot( ang123(it1) )) );
				tmp_T.push_back( T(a3,it0, -cot( ang123(it0) )) );
			}
		}

		// Only compute for internals
		{
			std::set<int> usedRow;
			std::map<int,int> rowMap;
			foreach(T t, tmp_T) usedRow.insert(t.row());
			foreach(int r, usedRow) rowMap[r] = rowMap.size();
			foreach(T t, tmp_T) B3_T.push_back( T(rowMap[ t.row() ],t.col(),t.value()) );
		}

		B3 = Eigen::SparseMatrix<Scalar>(np - b.size(), nA);
		B3.setFromTriplets( B3_T.begin(), B3_T.end() );
		B3_T.clear();

		// CC:
		tmp_T.clear();
		for(int idx = 0; idx < nt; idx++)
		{
			int a1 = fv[idx][0].idx();
			int a2 = fv[idx][1].idx();
			int a3 = fv[idx][2].idx();

			int it0 = 3*idx + 0;
			int it1 = 3*idx + 1;
			int it2 = 3*idx + 2;

			double vs0 = log( sin(ang123(it0)) );
			double vs1 = log( sin(ang123(it1)) );
			double vs2 = log( sin(ang123(it2)) );

			tmp_T.push_back( T(a1,it2, -vs2 ) );
			tmp_T.push_back( T(a1,it1,  vs1 ) );

			tmp_T.push_back( T(a2,it0, -vs0) );
			tmp_T.push_back( T(a2,it2,  vs2) );

			tmp_T.push_back( T(a3,it1, -vs1) );
			tmp_T.push_back( T(a3,it0,  vs0) );
		}
		CC = Eigen::SparseMatrix<Scalar,Eigen::RowMajor>(np, nA);
		CC.setFromTriplets( tmp_T.begin(), tmp_T.end() );
		tmp_T.clear();

		Eigen::VectorXd sumCC(np);
		for(int i = 0; i < np; i++) sumCC(i) = CC.row(i).sum();

		// Fill 'rhs3' with only sum for internals
		rhs3 = Eigen::VectorXd::Zero(np - b.size());
		for(int i = 0, j = 0; i < np; i++) if(!b.contains(i)) rhs3(j++) = sumCC(i); 

		// Gather all
		int b1 = B1.rows(), b2 = B2.rows(), b3 = B3.rows();
		Eigen::SparseMatrix<Scalar,Eigen::RowMajor> M = Eigen::SparseMatrix<Scalar,Eigen::RowMajor>(b1 + b2 + b3, nA);
		M.middleRows(0		, b1) = B1;
		M.middleRows(b1		, b2) = B2;
		M.middleRows(b1 + b2, b3) = B3;

		// For change of variables
		tmp_T.clear(); for(int i = 0; i < nA; i++) tmp_T.push_back(T(i,i,ang123(i)));
		W = Eigen::SparseMatrix<Scalar>(nA,nA);
		W.setFromTriplets(tmp_T.begin(),tmp_T.end()); tmp_T.clear();

		// Matrix A:
		A = M * W;
		At = A.transpose();

		// Vector rhs:
		int j = 0;
		rhs = Eigen::VectorXd::Zero( rhs1.size() + rhs2.size() + rhs3.size() );
		for(int i = 0; i < rhs1.size(); i++) rhs(j++) = rhs1(i);
		for(int i = 0; i < rhs2.size(); i++) rhs(j++) = rhs2(i);
		for(int i = 0; i < rhs3.size(); i++) rhs(j++) = rhs3(i);

		// Solve:
		Eigen::CholmodSupernodalLLT< Eigen::SparseMatrix<Scalar> > solver(A * At);
		Eigen::VectorXd ee = At * solver.solve( rhs );
		ee = W * ee;
		angsol = ang123 + ee;

		solution.resize(nt,3);
		for(int i = 0; i < nt; i++)
		{
			solution(i,0) = angsol(3*i + 0);
			solution(i,1) = angsol(3*i + 1);
			solution(i,2) = angsol(3*i + 2);
		}

		/// 5) Get positions from angles
		Eigen::VectorXd coef(nt), a(nt), b(nt);

		for(int i = 0; i < nt; i++)
		{
			coef(i) = sin(solution(i,1)) / sin(solution(i,2));
			a(i) = coef(i) * cos( solution(i,0) );
			b(i) = coef(i) * sin( solution(i,0) );
		}

		std::vector< T > MV_T;
		for(int i = 0; i < nt; i++)
		{
			int a1 = fv[i][0].idx();
			int a2 = fv[i][1].idx();
			int a3 = fv[i][2].idx();

			MV_T.push_back( T(i, a1		, 1-a(i) ) );
			MV_T.push_back( T(i, a1+np	,	b(i) ) );
			MV_T.push_back( T(i, a2		,	a(i) ) );
			MV_T.push_back( T(i, a2+np	,  -b(i) ) );
			MV_T.push_back( T(i, a3		,	 -1  ) );

			int j = i + nt;

			MV_T.push_back( T(j, a1		,  -b(i) ) );
			MV_T.push_back( T(j, a1+np	, 1-a(i) ) );
			MV_T.push_back( T(j, a2		,	b(i) ) );
			MV_T.push_back( T(j, a2+np	,   a(i) ) );
			MV_T.push_back( T(j, a3+np	,	 -1  ) );
		}

		MV = Eigen::SparseMatrix<Scalar,Eigen::RowMajor>(2*nt,2*np);
		MV.setFromTriplets( MV_T.begin(), MV_T.end() );
		MVt = MV.transpose();
		MV = MVt * MV;

		// Pinned vertices conditions
		int a1 = fv[0][0].idx(), a2 = fv[0][1].idx();
		int pinned[] = { a1, a2, a1+np, a2+np };

		Eigen::SparseMatrix<Scalar,Eigen::RowMajor> MV_pinned(4,2*np);
		for(int i = 0; i < 4; i++) MV_pinned.insert(i,pinned[i]) = 1;
		for(int i = 0; i < 4; i++) MV.middleRows(pinned[i],1) = MV_pinned.row(i);

		uvc = Eigen::VectorXd::Zero( 2 * np );
		uvc( pinned[1] ) = 1.0;

		MVt = MV.transpose();
		Eigen::CholmodSupernodalLLT< Eigen::SparseMatrix<Scalar> > postion_solver( MVt * MV );
		Eigen::VectorXd uv = postion_solver.solve( MVt * uvc );

		// Save final UV coordinates
		this->tex_coord = mesh->vertex_property<Vec2d>("v:texture", Vec2d(0,0));
		foreach(Vertex v, mesh->vertices())
		{
			int i = v.idx();
			tex_coord[v] = Vec2d( uv(i), uv(i+np) );
		}
	}

	void applyUVToMesh()
	{
		foreach(Vertex v, mesh->vertices())
			points[v] = Vector3(tex_coord[v][0],tex_coord[v][1],0);
	}

	SurfaceMeshModel * mesh;
	Vector3VertexProperty points;

	int np, nt;								// Number of points / triangles
	int nA;									// Number of angles on triangular mesh
	QSet<int> b;							// Boundary vertices
	std::vector<Vertex> internal;			// Internal vertices

	std::vector< std::vector<Vertex> > fv;	// Vertex indices per face
	std::vector<Vertex> fv123;				// Flat version of fv
	std::vector<Vertex> fv231;				// Re-ordered version of fv123
	Eigen::VectorXd ang123, angsol;			// Angles of all faces f_ang1,f_ang2,f_ang3,..
	std::vector<Scalar> ang123opt;			// Optimal angles
	std::vector<Scalar> ang123sum;			// Sum of angles

	Eigen::SparseMatrix<Scalar,Eigen::RowMajor> Mang;
	Eigen::VectorXd rhsk;

	Eigen::SparseMatrix<Scalar> B1,B2,B3;	// Parts of system matrix 'A'
	Eigen::VectorXd rhs1, rhs2, rhs3;		// Right hand side
	Eigen::SparseMatrix<Scalar,Eigen::RowMajor> CC;
	Eigen::SparseMatrix<Scalar> W;

	// System
	Eigen::SparseMatrix<Scalar> A, At;
	Eigen::VectorXd rhs;
	Eigen::MatrixXd solution;

	Eigen::SparseMatrix<Scalar,Eigen::RowMajor> MV, MVt;
	Eigen::MatrixXd uvc;

	// Output
	SurfaceMeshModel::Vertex_property<Vec2d> tex_coord;
};

back to top

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API