Blob Blame Raw


// tcg includes
#include "tcg/tcg_misc.h"

// tlin includes
#include "tlin/tlin.h"

// STD includes
#include <assert.h>
#include <memory>

#include "ext/plasticdeformer.h"

#include <cstring>

/*! \file plasticdeformer.cpp

  This file contains the implementation of a Mesh Deformer as specified in the
  Igarashi-Moscovich-Hughes paper "As-Rigid-As-Possible Shape Manipulation",
  with some slight modifications.
  \n\n
  Notably, the choice of handle points is not limited to mesh vertices, but free
  along the mesh surface. This can be achieved by constraining the classical
  least-squares minimization problems on a linear hyperplane, in the form:

  <P> \f$

  \left{ \begin{array}{c}
    v^t H v + v^t f + c   \rightarrow   \mbox{min} \\
    A v + d = 0
  \end{array} \right.

  \f$ </P>

  which can be solved using the Lagrange Multipliers theorem:

  <P> \f$

  \Rightarrow \left{ \begin{array}{c}
    H v + f = A^t \lambda
    A v + d = 0
  \end{array} \right.

  \Rightarrow \left( \begin{array}{c} H \\ A \end{array} \begin{array}{c} -A^t \\ 0 \end{array} \right)
              \left( \begin{array}{c} v \\ \lambda \end{array} \right) =
              \left( \begin{array}{c} -f \\ -d \end{array} \right)

  \f$ </P>
*/

//#define GL_DEBUG                                    // Debug using OpenGL. Must deform while drawing.
#ifdef GL_DEBUG  // NOTE: You should force deformations,
#include "tgl.h" // see plasticdeformersstorage.cpp
#endif

//******************************************************************************************
//    Local structures
//******************************************************************************************

namespace
{

//! A linear constraint of the kind   k[0] * v0 + k[1] * v1 + k[2] * v2 = b;     (b is not included though)
struct LinearConstraint {
	int m_h;	   //!< Original handle index corresponding to this constraint
	int m_v[3];	//!< The mesh vertex indices v0, v1, v2
	double m_k[3]; //!< Constraint coefficients
};

//-------------------------------------------------------------------------------------------

struct SuperFactors_free {
  void operator()(tlin::SuperFactors *f) { tlin::freeF(f); }
};

//-------------------------------------------------------------------------------------------

using SuperFactorsPtr =std::unique_ptr<tlin::SuperFactors, SuperFactors_free>;
using DoublePtr =std::unique_ptr<double[]>;
using TPointDPtr =std::unique_ptr<TPointD[]>;

} // namespace

//******************************************************************************************
//    Local functions
//******************************************************************************************

namespace
{

inline void vertices(const TTextureMesh &mesh, int faceIdx,
					 const TPointD *&p0, const TPointD *&p1, const TPointD *&p2)
{
	int v0, v1, v2;
	mesh.faceVertices(faceIdx, v0, v1, v2);

	p0 = &mesh.vertex(v0).P(), p1 = &mesh.vertex(v1).P(), p2 = &mesh.vertex(v2).P();
}

//-------------------------------------------------------------------------------------------

inline void buildTriangularCoordinates(const TTextureMesh &mesh, int &faceIdx, const TPointD &p,
									   double &c0, double &c1, double &c2)
{
	TPointD coords;
	const TPointD *p0, *p1, *p2;

	// Follow the hint first
	if (faceIdx >= 0 && faceIdx < mesh.facesCount() &&
		mesh.faceContains(faceIdx, p)) {
		vertices(mesh, faceIdx, p0, p1, p2);
		coords = tcg::point_ops::triCoords(p, *p0, *p1, *p2);
		c1 = coords.x, c2 = coords.y, c0 = 1.0 - c1 - c2;
		return;
	}

	// Find out the face searching on the mesh
	faceIdx = mesh.faceContaining(p);
	if (faceIdx >= 0) {
		vertices(mesh, faceIdx, p0, p1, p2);
		coords = tcg::point_ops::triCoords(p, *p0, *p1, *p2);
		c1 = coords.x, c2 = coords.y, c0 = 1.0 - c1 - c2;
		return;
	}
}

//-------------------------------------------------------------------------------------------

inline void addConstraint1d(int j, const LinearConstraint &cnstr, tlin::spmat &mat)
{
	int i, var, nVars = 3;
	for (i = 0; i < nVars; ++i) {
		var = cnstr.m_v[i];
		mat.at(j, var) += cnstr.m_k[i];
		mat.at(var, j) = mat(j, var);
	}
}

//-------------------------------------------------------------------------------------------

inline void addConstraint2d(int j, const LinearConstraint &cnstr, tlin::spmat &mat)
{
	int i, varX, varY, k = j + 1, nVars = 3;
	for (i = 0; i < nVars; ++i) {
		varX = 2 * cnstr.m_v[i], varY = varX + 1;

		mat.at(j, varX) += cnstr.m_k[i];
		mat.at(k, varY) += cnstr.m_k[i];
		mat.at(varX, j) = mat(j, varX);
		mat.at(varY, k) = mat(k, varY);
	}
}

//-------------------------------------------------------------------------------------------

inline void addGValues(
	int v0x, int v0y, int v1x, int v1y, int v2x, int v2y,
	double px, double py, double w, tlin::spmat &G)
{
	double sqPy = py * py, one_px = 1.0 - px;
	double a = w * (one_px * one_px + sqPy),
		   b = w * (px * one_px - sqPy),
		   c = w * (py * one_px + px * py),
		   d = w * (px * px + sqPy);

	sqPy *= w;
	one_px *= w;
	px *= w;
	py *= w;

	G.at(v0x, v0x) += a;
	G.at(v0x, v1x) += b;
	G.at(v0x, v1y) += c;
	G.at(v0x, v2x) -= one_px;
	G.at(v0x, v2y) -= py;

	G.at(v0y, v0y) += a;
	G.at(v0y, v1x) -= py;
	G.at(v0y, v1y) += b;
	G.at(v0y, v2x) += py;
	G.at(v0y, v2y) -= one_px;

	G.at(v1x, v0x) += b;
	G.at(v1x, v0y) += -py;
	G.at(v1x, v1x) += d;
	G.at(v1x, v2x) += -px;
	G.at(v1x, v2y) += py;

	G.at(v1y, v0x) += c;
	G.at(v1y, v0y) += b;
	G.at(v1y, v1y) += d;
	G.at(v1y, v2x) -= py;
	G.at(v1y, v2y) -= px;

	G.at(v2x, v0x) -= one_px;
	G.at(v2x, v0y) += py;
	G.at(v2x, v1x) -= px;
	G.at(v2x, v1y) -= py;
	G.at(v2x, v2x) += w;

	G.at(v2y, v0x) -= py;
	G.at(v2y, v0y) -= one_px;
	G.at(v2y, v1x) += py;
	G.at(v2y, v1y) -= px;
	G.at(v2y, v2y) += w;
}

//-------------------------------------------------------------------------------------------

void buildF(double px, double py, tlin::spmat &F)
{
	double one_px = 1.0 - px, sqPy = py * py;

	F.at(0, 0) = 1.0 + one_px * one_px + sqPy;
	F.at(1, 1) = F(0, 0);

	F.at(0, 2) = px * one_px - sqPy;
	F.at(0, 3) = py * one_px + px * py;
	F.at(1, 2) = -F(0, 3);
	F.at(1, 3) = F(0, 2);

	F.at(2, 0) = F(0, 2);
	F.at(2, 1) = F(1, 2);
	F.at(3, 0) = F(0, 3);
	F.at(3, 1) = F(1, 3);

	F.at(2, 2) = 1.0 + px * px + sqPy;
	F.at(3, 3) = F(2, 2);
}

//-------------------------------------------------------------------------------------------

void build_c(double v0x, double v0y, double v1x, double v1y, double v2x, double v2y, double px, double py, double *c)
{
	c[0] = (v0x + v2x * (1.0 - px) + v2y * py);
	c[1] = (v0y - v2x * py + v2y * (1.0 - px));
	c[2] = (v1x + v2x * px - v2y * py);
	c[3] = (v1y + v2y * px + v2x * py);
}

//-------------------------------------------------------------------------------------------

inline void addHValues(int v0, int v1, double w, tlin::spmat &H)
{
	H.at(v0, v0) += w;
	H.at(v1, v0) -= w;
	H.at(v0, v1) -= w;
	H.at(v1, v1) += w;
}

//-------------------------------------------------------------------------------------------

inline void add_f_values(int v0, int v1, double fit0, double fit1, double w, double *f)
{
	double f0_f1 = w * (fit0 - fit1);
	f[v0] += f0_f1;
	f[v1] -= f0_f1;
}

} // namespace

//******************************************************************************************
//    PlasticDeformer::Imp  definition
//******************************************************************************************

class PlasticDeformer::Imp
{
public:
	TTextureMeshP m_mesh;										  //!< Deformed mesh (cannot be changed)
	std::vector<PlasticHandle> m_handles;						  //!< Compiled handles
	std::vector<LinearConstraint> m_constraints1, m_constraints3; //!< Compiled constraints (depends on the above)
	bool m_compiled;											  //!< Whether the deformer is ready to deform()

public:
	Imp();

	void initialize(const TTextureMeshP &mesh);
	void compile(const std::vector<PlasticHandle> &handles, int *faceHints);
	void deform(const TPointD *dstHandles, double *dstVerticesCoords);

	void copyOriginals(double *dstVerticesCoords);

public:
	tlin::spmat m_G;		//!< Pre-initialized entries for the 1st
							//!< linear system
	SuperFactorsPtr m_invC; //!< C's factors (C is G plus linear constraints)
	DoublePtr m_q;			//!< Step 1's known term
	DoublePtr m_out;		//!< Step 1's result

	// Step 1 members:
	//   The first step of a MeshDeformer instance is about building the desired vertices configuration.
	void initializeStep1();
	void compileStep1(const std::vector<PlasticHandle> &handles);
	void deformStep1(const TPointD *dstHandles, double *dstVerticesCoords);

	void releaseInitializedData();

public:
	std::vector<SuperFactorsPtr> m_invF; //!< Each of step 2's systems factorizations

	TPointDPtr m_relativeCoords; //!< Faces' p2 coordinates in (p0, p1)'s orthogonal reference
	double m_v[4], m_c[4];		 //!< Known term and output coordinates

	TPointDPtr m_fitTriangles; //!< Output face coordinates

	// Step 2 members:
	//   The second step of MeshDeformer rigidly maps neighbourhoods of the original mesh
	//   to fit as much as possible the neighbourhoods in the step 1 result.
	void initializeStep2();
	void compileStep2(const std::vector<PlasticHandle> &handles);
	void deformStep2(const TPointD *dstHandles, double *dstVerticesCoords);

public:
	// NOTE: This step accepts separation in the X and Y components

	tlin::spmat m_H;		//!< Step 3's system entries
	SuperFactorsPtr m_invK; //!< System inverse

	DoublePtr m_fx, m_fy; //!< Known terms
	DoublePtr m_x, m_y;   //!< Output values

	// Step 3 members:
	//   The third step of MeshDeformer glues together the mapped neighbourhoods from step2.
	void initializeStep3();
	void compileStep3(const std::vector<PlasticHandle> &handles);
	void deformStep3(const TPointD *dstHandles, double *dstVerticesCoords);
};

//=================================================================================

PlasticDeformer::Imp::Imp()
{
}

//-------------------------------------------------------------------------------------------

void PlasticDeformer::Imp::initialize(const TTextureMeshP &mesh)
{
	assert("Input mesh must be squeezed!" &&
		   mesh->verticesCount() == mesh->vertices().nodesCount() &&
		   mesh->edgesCount() == mesh->edges().nodesCount() &&
		   mesh->facesCount() == mesh->faces().nodesCount());

	m_mesh = mesh;

	initializeStep1();
	initializeStep2();
	initializeStep3();

	m_compiled = false; // Compilation is expected after a new initialization
}

//-------------------------------------------------------------------------------------------

void PlasticDeformer::Imp::compile(const std::vector<PlasticHandle> &handles,
								   int *faceHints)
{
	assert(m_mesh);

	m_handles.clear(), m_handles.reserve(handles.size());
	m_constraints1.clear(), m_constraints3.clear();

	LinearConstraint constr;

	// Build the linear constraints raising from the mesh-handles pairing
	int h, hCount = handles.size();
	for (h = 0; h < hCount; ++h) {
		int localFaceIdx = -1, &faceIdx = faceHints ? faceHints[h] : localFaceIdx;

		::buildTriangularCoordinates(*m_mesh, faceIdx, handles[h].m_pos, constr.m_k[0], constr.m_k[1], constr.m_k[2]);

		if (faceIdx >= 0) {
			constr.m_h = h;

			m_mesh->faceVertices(faceIdx, constr.m_v[0], constr.m_v[1], constr.m_v[2]);
			m_constraints1.push_back(constr);
			if (handles[h].m_interpolate)
				m_constraints3.push_back(constr);

			// Store the handles to retain precompiled data
			m_handles.push_back(handles[h]);
		}
	}

	//m_handles = handles;                              // NOT directly copied like this: some handles could be
	// (geometrically) OUTSIDE the mesh!

	// Now, invoke the actual compilation procedures
	m_compiled = true;

	if (m_handles.size() < 2)
		return;

	compileStep1(handles); // These may set m_compiled = false. Must still be
	compileStep2(handles); // called even when that happens - as they are
	compileStep3(handles); // responsible for resources reclamation.
}

//-------------------------------------------------------------------------------------------

void PlasticDeformer::Imp::deform(const TPointD *dstHandles, double *dstVerticesCoords)
{
	assert(m_mesh);
	assert(dstVerticesCoords);

	// Deal with trivial cases
	if (!m_compiled || m_handles.size() == 0) {
		// Cannot deform anything - just copy the source mesh vertices into dst ones
		copyOriginals(dstVerticesCoords);
		return;
	}

	assert(dstHandles);

	if (m_handles.size() == 1) {
		// 1 handle inside the mesh - pure translational case

		const PlasticHandle &srcHandle = m_handles.front();
		const TPointD &dstHandlePos = dstHandles[m_constraints1.front().m_h];

		TPointD shift(dstHandlePos - srcHandle.m_pos);

		int v, vCount = m_mesh->verticesCount();
		for (v = 0; v != vCount; ++v, dstVerticesCoords += 2) {
			dstVerticesCoords[0] = m_mesh->vertex(v).P().x + shift.x;
			dstVerticesCoords[1] = m_mesh->vertex(v).P().y + shift.y;
		}

		return;
	}

	deformStep1(dstHandles, dstVerticesCoords);
	deformStep2(dstHandles, dstVerticesCoords);
	deformStep3(dstHandles, dstVerticesCoords);
}

//-------------------------------------------------------------------------------------------

void PlasticDeformer::Imp::copyOriginals(double *dstVerticesCoords)
{
	int v, vCount = m_mesh->verticesCount();
	for (v = 0; v != vCount; ++v, dstVerticesCoords += 2) {
		dstVerticesCoords[0] = m_mesh->vertex(v).P().x;
		dstVerticesCoords[1] = m_mesh->vertex(v).P().y;
	}
}

//-------------------------------------------------------------------------------------------

void PlasticDeformer::Imp::releaseInitializedData()
{
	// Release m_G and m_H
	{
		tlin::spmat temp;
		swap(m_G, temp);
	} // Ensure resources release by swapping them
	{
		tlin::spmat temp;
		swap(m_H, temp);
	} // with newly initialized instances
}

//******************************************************************************************
//    Plastic Deformation Step 1
//******************************************************************************************

void PlasticDeformer::Imp::initializeStep1()
{
	const TTextureMesh &mesh = *m_mesh;
	int vCount = mesh.verticesCount(), vCount_2 = 2 * vCount;

	m_G = tlin::spmat(vCount_2, vCount_2);

	// Initialize the linear system indices for the stored mesh
	int f, fCount = mesh.facesCount();
	for (f = 0; f < fCount; ++f) {
		int v0, v1, v2;
		mesh.faceVertices(f, v0, v1, v2);

		int v0x = 2 * v0;  // We'll use 2 params per vertex - with
		int v0y = v0x + 1; // adjacent x and y parameters
		int v1x = 2 * v1;
		int v1y = v1x + 1;
		int v2x = 2 * v2;
		int v2y = v2x + 1;

		const TTextureMesh::vertex_type &vx0 = mesh.vertex(v0);
		const TTextureMesh::vertex_type &vx1 = mesh.vertex(v1);
		const TTextureMesh::vertex_type &vx2 = mesh.vertex(v2);

		TPointD c0, c1, c2;
		c2 = tcg::point_ops::ortCoords(vx2.P(), vx0.P(), vx1.P());
		c0 = tcg::point_ops::ortCoords(vx0.P(), vx1.P(), vx2.P());
		c1 = tcg::point_ops::ortCoords(vx1.P(), vx2.P(), vx0.P());

		addGValues(v0x, v0y, v1x, v1y, v2x, v2y, c2.x, c2.y, vx2.P().rigidity, m_G);
		addGValues(v1x, v1y, v2x, v2y, v0x, v0y, c0.x, c0.y, vx0.P().rigidity, m_G);
		addGValues(v2x, v2y, v0x, v0y, v1x, v1y, c1.x, c1.y, vx1.P().rigidity, m_G);
	}
}

//-------------------------------------------------------------------------------------------

void PlasticDeformer::Imp::compileStep1(const std::vector<PlasticHandle> &handles)
{
	// First, release resources
	m_invC.reset();
	m_q.reset();
	m_out.reset();

	// Now, start compiling
	const TTextureMesh &mesh = *m_mesh;
	int vCount = mesh.verticesCount(), hCount = m_handles.size();

	int cSize = 2 * (vCount + hCount); // Coefficients count

	// Initialize C with G
	tlin::SuperMatrix *trC = 0;
	{
		tlin::spmat C(cSize, cSize);
		C.entries() = m_G.entries();
		C.entries().hashFunctor().m_cols = C.cols();
		C.entries().rehash(C.entries().buckets().size()); // Rehash to entries according to C's size

		// Add the entries constraining handles
		int i;
		std::vector<LinearConstraint>::iterator ht, hEnd(m_constraints1.end());
		for (i = 0, ht = m_constraints1.begin(); ht != hEnd; ++i, ++ht)
			addConstraint2d(2 * (vCount + i), *ht, C);

		tlin::traduceS(C, trC);
	}

	// Build m_invC
	tlin::SuperFactors *invC = 0;
	tlin::factorize(trC, invC);

	tlin::freeS(trC);

	if (invC) {
		m_invC.reset(invC);

		// Reallocate arrays
		m_q.reset(new double[cSize]);
		m_out.reset(new double[cSize]);

		memset(m_q.get(), 0, 2 * vCount * sizeof(double)); // Initialize the system's known term with 0
	} else
		m_compiled = false;
}

//-------------------------------------------------------------------------------------------

void PlasticDeformer::Imp::deformStep1(const TPointD *dstHandles, double *dstVerticesCoords)
{
	int vCount2 = 2 * m_mesh->verticesCount();
	int cSize = vCount2 + 2 * m_handles.size();

	// Copy destination handles into the system's known term
	int i, h;
	for (i = vCount2, h = 0; i < cSize; i += 2, ++h) {
		const TPointD &dstHandlePos = dstHandles[m_constraints1[h].m_h];

		m_q[i] = dstHandlePos.x;	 // NOTE: We could use calloc on-the-fly, sparing
		m_q[i + 1] = dstHandlePos.y; // some memory. Have to benchmark the cost, though...
	}

	// Solve the linear system
	double *out = m_out.get();
	tlin::solve(m_invC.get(), m_q.get(), out);

#ifdef GL_DEBUG

	glColor3d(1.0, 0.0, 0.0); // Red
	glBegin(GL_LINES);

	// Draw deformed mesh edges
	int e, eCount = m_mesh->edgesCount();
	for (e = 0; e < eCount; ++e) {
		const TTextureMesh::edge_type &ed = m_mesh->edge(e);

		double *x0 = out + 2 * ed.vertex(0), *x1 = out + 2 * ed.vertex(1);

		glVertex2d(*x0, *(x0 + 1));
		glVertex2d(*x1, *(x1 + 1));
	}

	glEnd();

#endif
}

//******************************************************************************************
//    Plastic Deformation Step 2
//******************************************************************************************

void PlasticDeformer::Imp::initializeStep2()
{
	const TTextureMesh &mesh = *m_mesh;
	int f, fCount = mesh.facesCount();

	// Clear and re-initialize vars
	tlin::spmat F(4, 4);
	tlin::SuperMatrix *trF;

	std::vector<SuperFactorsPtr>(fCount).swap(m_invF);

	m_relativeCoords.reset(new TPointD[fCount]);
	m_fitTriangles.reset(new TPointD[3 * fCount]);

	// Build step 2's system factorizations (yep, can be done at this point)
	const TPointD *p0, *p1, *p2;

	for (f = 0; f < fCount; ++f) {
		::vertices(mesh, f, p0, p1, p2);

		TPointD c(tcg::point_ops::ortCoords(*p2, *p0, *p1));
		m_relativeCoords[f] = c;

		F.clear();
		buildF(c.x, c.y, F);

		trF = 0;
		tlin::traduceS(F, trF);

		tlin::SuperFactors *invF = 0;

		tlin::factorize(trF, invF);
		m_invF[f].reset(invF);

		tlin::freeS(trF);
	}
}

//-------------------------------------------------------------------------------------------

void PlasticDeformer::Imp::compileStep2(const std::vector<PlasticHandle> &handles)
{
	// Nothing to do :)
}

//-------------------------------------------------------------------------------------------

void PlasticDeformer::Imp::deformStep2(const TPointD *dstHandles, double *dstVerticesCoords)
{
	const TTextureMesh &mesh = *m_mesh;
	int vCount = mesh.verticesCount();

	memset(m_fx.get(), 0, vCount * sizeof(double)); // These should be part of step 3...
	memset(m_fy.get(), 0, vCount * sizeof(double)); // They are filled here just for convenience

	// Build fit triangles
	TPointD *fitTri = m_fitTriangles.get(), *relCoord = m_relativeCoords.get();
	double *out1 = m_out.get();

	int f, fCount = mesh.facesCount();
	for (f = 0; f < fCount; ++f, fitTri += 3, ++relCoord) {
		int v0, v1, v2;
		m_mesh->faceVertices(f, v0, v1, v2);

		const RigidPoint &p0 = mesh.vertex(v0).P(), &p1 = mesh.vertex(v1).P(), &p2 = mesh.vertex(v2).P();

		double *v0x = out1 + (v0 << 1), *v0y = v0x + 1,
			   *v1x = out1 + (v1 << 1), *v1y = v1x + 1,
			   *v2x = out1 + (v2 << 1), *v2y = v2x + 1;

		build_c(
			*v0x, *v0y,
			*v1x, *v1y,
			*v2x, *v2y,
			relCoord->x, relCoord->y, m_c);

		double *vPtr = (double *)m_v;
		tlin::solve(m_invF[f].get(), (double *)m_c, vPtr);

		fitTri[0].x = m_v[0], fitTri[0].y = m_v[1];
		fitTri[1].x = m_v[2], fitTri[1].y = m_v[3];

		fitTri[2].x = fitTri[0].x + relCoord->x * (fitTri[1].x - fitTri[0].x) + relCoord->y * (fitTri[1].y - fitTri[0].y);
		fitTri[2].y = fitTri[0].y + relCoord->x * (fitTri[1].y - fitTri[0].y) + relCoord->y * (fitTri[0].x - fitTri[1].x);

		// Scale with respect to baricenter. The baricenter is used since it makes distance from vertices equally
		// weighting - which is the same expected when minimizing positions (by collateral effect) in the next step.
		TPointD baricenter(
			(fitTri[0].x + fitTri[1].x + fitTri[2].x) / 3.0,
			(fitTri[0].y + fitTri[1].y + fitTri[2].y) / 3.0);

		double scale = sqrt(
			norm2(TPointD(p1.x - p0.x, p1.y - p0.y)) /
			norm2(TPointD(fitTri[1].x - fitTri[0].x, fitTri[1].y - fitTri[0].y)));

		fitTri[0] = scale * (fitTri[0] - baricenter) + baricenter;
		fitTri[1] = scale * (fitTri[1] - baricenter) + baricenter;
		fitTri[2] = scale * (fitTri[2] - baricenter) + baricenter;

		// Build f -- note: this should be part of step 3, we're just avoiding the same cycle twice :)
		add_f_values(v0, v1, fitTri[0].x, fitTri[1].x, std::min(p0.rigidity, p1.rigidity), m_fx.get());
		add_f_values(v0, v1, fitTri[0].y, fitTri[1].y, std::min(p0.rigidity, p1.rigidity), m_fy.get());

		add_f_values(v1, v2, fitTri[1].x, fitTri[2].x, std::min(p1.rigidity, p2.rigidity), m_fx.get());
		add_f_values(v1, v2, fitTri[1].y, fitTri[2].y, std::min(p1.rigidity, p2.rigidity), m_fy.get());

		add_f_values(v2, v0, fitTri[2].x, fitTri[0].x, std::min(p2.rigidity, p0.rigidity), m_fx.get());
		add_f_values(v2, v0, fitTri[2].y, fitTri[0].y, std::min(p2.rigidity, p0.rigidity), m_fy.get());
	}

#ifdef GL_DEBUG

	glColor3d(0.0, 0.0, 1.0); // Blue

	// Draw fit triangles
	fitTri = m_fitTriangles.get();

	for (f = 0; f < fCount; ++f, fitTri += 3) {
		glBegin(GL_LINE_LOOP);

		glVertex2d(fitTri[0].x, fitTri[0].y);
		glVertex2d(fitTri[1].x, fitTri[1].y);
		glVertex2d(fitTri[2].x, fitTri[2].y);

		glEnd();
	}

#endif
}

//******************************************************************************************
//    Plastic Deformation Step 3
//******************************************************************************************

void PlasticDeformer::Imp::initializeStep3()
{
	const TTextureMesh &mesh = *m_mesh;
	int vCount = mesh.verticesCount();

	m_H = tlin::spmat(vCount, vCount);

	int f, fCount = mesh.facesCount();
	for (f = 0; f < fCount; ++f) {
		int v0, v1, v2;
		mesh.faceVertices(f, v0, v1, v2);

		const RigidPoint &p0 = mesh.vertex(v0).P(),
						 &p1 = mesh.vertex(v1).P(),
						 &p2 = mesh.vertex(v2).P();

		addHValues(v0, v1, std::min(p0.rigidity, p1.rigidity), m_H);
		addHValues(v1, v2, std::min(p1.rigidity, p2.rigidity), m_H);
		addHValues(v2, v0, std::min(p2.rigidity, p0.rigidity), m_H);
	}
}

//-------------------------------------------------------------------------------------------

void PlasticDeformer::Imp::compileStep3(const std::vector<PlasticHandle> &handles)
{
	// First, release resources
	m_invK.reset();
	m_x.reset();
	m_y.reset();
	m_fx.reset();
	m_fy.reset();

	// If compilation already failed, skip
	if (!m_compiled)
		return;

	const TTextureMesh &mesh = *m_mesh;

	int vCount = mesh.verticesCount();
	int kSize = vCount + m_constraints3.size();

	// Initialize K with H
	tlin::SuperMatrix *trK = 0;
	{
		tlin::spmat K(kSize, kSize);
		K.entries() = m_H.entries();
		K.entries().hashFunctor().m_cols = K.cols();
		K.entries().rehash(K.entries().buckets().size()); //Rehash to entries according to K's size

		// Add the entries constraining handles
		int c, cnstrCount = m_constraints3.size();
		for (c = 0; c < cnstrCount; ++c) {
			// Add the linear constraint to the system
			addConstraint1d(vCount + c, m_constraints3[c], K);
		}

		tlin::traduceS(K, trK);
	}

	// Build m_invK
	tlin::SuperFactors *invK = 0;
	tlin::factorize(trK, invK);

	tlin::freeS(trK);

	if (invK) {
		m_invK.reset(invK);

		m_x.reset((double *)malloc(kSize * sizeof(double)));
		m_y.reset((double *)malloc(kSize * sizeof(double)));
		m_fx.reset((double *)malloc(kSize * sizeof(double)));
		m_fy.reset((double *)malloc(kSize * sizeof(double)));
	} else
		m_compiled = false;
}

//-------------------------------------------------------------------------------------------

void PlasticDeformer::Imp::deformStep3(const TPointD *dstHandles, double *dstVerticesCoords)
{
	int v, vCount = m_mesh->verticesCount();
	int c;
	int h, hCount = m_handles.size();

	for (c = 0, h = 0; h < hCount; ++h) {
		if (!m_handles[h].m_interpolate)
			continue;

		const TPointD &dstHandlePos = dstHandles[m_constraints1[h].m_h];

		m_fx[vCount + c] = dstHandlePos.x;
		m_fy[vCount + c] = dstHandlePos.y;

		++c;
	}

	double *x = m_x.get(), *y = m_y.get();
	tlin::solve(m_invK.get(), m_fx.get(), x);
	tlin::solve(m_invK.get(), m_fy.get(), y);

	int i;
	for (i = v = 0; v < vCount; ++v, i += 2) {
		dstVerticesCoords[i] = m_x[v];
		dstVerticesCoords[i + 1] = m_y[v];
	}
}

//**********************************************************************************************
//    Plastic Deformer  implementation
//**********************************************************************************************

PlasticDeformer::PlasticDeformer()
	: m_imp(new Imp)
{
}

//---------------------------------------------------------------------------------

PlasticDeformer::~PlasticDeformer()
{
}

//---------------------------------------------------------------------------------

bool PlasticDeformer::compiled() const
{
	return m_imp->m_compiled;
}

//---------------------------------------------------------------------------------

void PlasticDeformer::initialize(const TTextureMeshP &mesh)
{
	m_imp->initialize(mesh);
}

//---------------------------------------------------------------------------------

bool PlasticDeformer::compile(const std::vector<PlasticHandle> &handles, int *faceHints)
{
	m_imp->compile(handles, faceHints);
	return compiled();
}

//---------------------------------------------------------------------------------

void PlasticDeformer::deform(const TPointD *dstHandles, double *dstVerticesCoords) const
{
	m_imp->deform(dstHandles, dstVerticesCoords);
}

//---------------------------------------------------------------------------------

void PlasticDeformer::releaseInitializedData()
{
	m_imp->releaseInitializedData();
}