Browse Source

lscm hessian and spectral (#2214) [ci skip]

Alec Jacobson 2 years ago
parent
commit
598b0b194a

+ 67 - 33
include/igl/lscm.cpp

@@ -6,48 +6,44 @@
 // v. 2.0. If a copy of the MPL was not distributed with this file, You can
 // v. 2.0. If a copy of the MPL was not distributed with this file, You can
 // obtain one at http://mozilla.org/MPL/2.0/.
 // obtain one at http://mozilla.org/MPL/2.0/.
 #include "lscm.h"
 #include "lscm.h"
-
-#include "vector_area_matrix.h"
-#include "cotmatrix.h"
+#include "lscm_hessian.h"
+#include "massmatrix.h"
 #include "repdiag.h"
 #include "repdiag.h"
+#include "eigs.h"
 #include "min_quad_with_fixed.h"
 #include "min_quad_with_fixed.h"
 #include <iostream>
 #include <iostream>
 
 
+
+template <
+  typename DerivedV, 
+  typename DerivedF, 
+  typename Derivedb, 
+  typename Derivedbc, 
+  typename DerivedV_uv, 
+  typename QScalar>
 IGL_INLINE bool igl::lscm(
 IGL_INLINE bool igl::lscm(
-  const Eigen::MatrixXd& V,
-  const Eigen::MatrixXi& F,
-  const Eigen::VectorXi& b,
-  const Eigen::MatrixXd& bc,
-  Eigen::MatrixXd & V_uv,
-  Eigen::SparseMatrix<double> & Q)
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<Derivedb> & b,
+  const Eigen::MatrixBase<Derivedbc> & bc,
+  Eigen::PlainObjectBase<DerivedV_uv> & V_uv,
+  Eigen::SparseMatrix<QScalar> & Q)
 {
 {
   using namespace Eigen;
   using namespace Eigen;
   using namespace std;
   using namespace std;
-  
-  // Assemble the area matrix (note that A is #Vx2 by #Vx2)
-  SparseMatrix<double> A;
-  igl::vector_area_matrix(F,A);
-
-  // Assemble the cotan laplacian matrix
-  SparseMatrix<double> L;
-  igl::cotmatrix(V,F,L);
-
-  SparseMatrix<double> L_flat;
-  repdiag(L,2,L_flat);
+  igl::lscm_hessian(V,F,Q);
 
 
-  VectorXi b_flat(b.size()*bc.cols(),1);
-  VectorXd bc_flat(bc.size(),1);
+  Eigen::Matrix<typename Derivedb::Scalar,Eigen::Dynamic,1> b_flat(b.size()*bc.cols(),1);
+  Eigen::Matrix<typename Derivedbc::Scalar,Eigen::Dynamic,1> bc_flat(bc.size(),1);
   for(int c = 0;c<bc.cols();c++)
   for(int c = 0;c<bc.cols();c++)
   {
   {
     b_flat.block(c*b.size(),0,b.rows(),1) = c*V.rows() + b.array();
     b_flat.block(c*b.size(),0,b.rows(),1) = c*V.rows() + b.array();
     bc_flat.block(c*bc.rows(),0,bc.rows(),1) = bc.col(c);
     bc_flat.block(c*bc.rows(),0,bc.rows(),1) = bc.col(c);
   }
   }
-  
-  // Minimize the LSCM energy
-  Q = -L_flat - 2.*A;
+
   const VectorXd B_flat = VectorXd::Zero(V.rows()*2);
   const VectorXd B_flat = VectorXd::Zero(V.rows()*2);
-  igl::min_quad_with_fixed_data<double> data;
-  if(!igl::min_quad_with_fixed_precompute(Q,b_flat,SparseMatrix<double>(),true,data))
+  igl::min_quad_with_fixed_data<QScalar> data;
+  if(!igl::min_quad_with_fixed_precompute(Q,b_flat,SparseMatrix<QScalar>(),true,data))
   {
   {
     return false;
     return false;
   }
   }
@@ -68,17 +64,55 @@ IGL_INLINE bool igl::lscm(
   return true;
   return true;
 }
 }
 
 
+template <
+  typename DerivedV, 
+  typename DerivedF, 
+  typename Derivedb, 
+  typename Derivedbc, 
+  typename DerivedV_uv>
 IGL_INLINE bool igl::lscm(
 IGL_INLINE bool igl::lscm(
-  const Eigen::MatrixXd& V,
-  const Eigen::MatrixXi& F,
-  const Eigen::VectorXi& b,
-  const Eigen::MatrixXd& bc,
-  Eigen::MatrixXd & V_uv)
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<Derivedb> & b,
+  const Eigen::MatrixBase<Derivedbc> & bc,
+  Eigen::PlainObjectBase<DerivedV_uv> & V_uv)
 {
 {
-  Eigen::SparseMatrix<double> Q;
+  Eigen::SparseMatrix<typename DerivedV_uv::Scalar> Q;
   return lscm(V, F, b, bc, V_uv, Q);
   return lscm(V, F, b, bc, V_uv, Q);
 }
 }
 
 
+template <
+  typename DerivedV, 
+  typename DerivedF, 
+  typename DerivedV_uv>
+IGL_INLINE bool igl::lscm(
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  Eigen::PlainObjectBase<DerivedV_uv> & V_uv)
+{
+  using Scalar = typename DerivedV_uv::Scalar;
+  Eigen::SparseMatrix<Scalar> Q;
+  igl::lscm_hessian(V,F,Q);
+  Eigen::SparseMatrix<Scalar> M;
+  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_DEFAULT,M);
+  Eigen::SparseMatrix<Scalar> M2;
+  igl::repdiag(M,2,M2);
+  Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic> U;
+  Eigen::Matrix<Scalar,Eigen::Dynamic,1> S;
+  bool success = igl::eigs(Q,M2,3,igl::EIGS_TYPE_SM,U,S);
+  if(!success)
+  {
+    return false;
+  }
+  V_uv.resize(V.rows(),2);
+  V_uv<< U.col(0).head(V.rows()),U.col(0).tail(V.rows());
+  return true;
+}
+
 #ifdef IGL_STATIC_LIBRARY
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
 // Explicit template instantiation
+// generated by autoexplicit.sh
+template bool igl::lscm<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
+// generated by autoexplicit.sh
+template bool igl::lscm<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
 #endif
 #endif

+ 118 - 0
include/igl/lscm.cpp~

@@ -0,0 +1,118 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2014 Daniele Panozzo <[email protected]>
+//
+// This Source Code Form is subject to the terms of the Mozilla Public License
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can
+// obtain one at http://mozilla.org/MPL/2.0/.
+#include "lscm.h"
+#include "lscm_hessian.h"
+#include "massmatrix.h"
+#include "repdiag.h"
+#include "eigs.h"
+#include "min_quad_with_fixed.h"
+#include <iostream>
+
+
+template <
+  typename DerivedV, 
+  typename DerivedF, 
+  typename Derivedb, 
+  typename Derivedbc, 
+  typename DerivedV_uv, 
+  typename QScalar>
+IGL_INLINE bool igl::lscm(
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<Derivedb> & b,
+  const Eigen::MatrixBase<Derivedbc> & bc,
+  Eigen::PlainObjectBase<DerivedV_uv> & V_uv,
+  Eigen::SparseMatrix<QScalar> & Q)
+{
+  using namespace Eigen;
+  using namespace std;
+  igl::lscm_hessian(V,F,Q);
+
+  Eigen::Matrix<typename Derivedb::Scalar,Eigen::Dynamic,1> b_flat(b.size()*bc.cols(),1);
+  Eigen::Matrix<typename Derivedbc::Scalar,Eigen::Dynamic,1> bc_flat(bc.size(),1);
+  for(int c = 0;c<bc.cols();c++)
+  {
+    b_flat.block(c*b.size(),0,b.rows(),1) = c*V.rows() + b.array();
+    bc_flat.block(c*bc.rows(),0,bc.rows(),1) = bc.col(c);
+  }
+
+  const VectorXd B_flat = VectorXd::Zero(V.rows()*2);
+  igl::min_quad_with_fixed_data<QScalar> data;
+  if(!igl::min_quad_with_fixed_precompute(Q,b_flat,SparseMatrix<QScalar>(),true,data))
+  {
+    return false;
+  }
+
+  MatrixXd W_flat;
+  if(!min_quad_with_fixed_solve(data,B_flat,bc_flat,VectorXd(),W_flat))
+  {
+    return false;
+  }
+
+
+  assert(W_flat.rows() == V.rows()*2);
+  V_uv.resize(V.rows(),2);
+  for (unsigned i=0;i<V_uv.cols();++i)
+  {
+    V_uv.col(i) = W_flat.block(V_uv.rows()*i,0,V_uv.rows(),1);
+  }
+  return true;
+}
+
+template <
+  typename DerivedV, 
+  typename DerivedF, 
+  typename Derivedb, 
+  typename Derivedbc, 
+  typename DerivedV_uv>
+IGL_INLINE bool igl::lscm(
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<Derivedb> & b,
+  const Eigen::MatrixBase<Derivedbc> & bc,
+  Eigen::PlainObjectBase<DerivedV_uv> & V_uv)
+{
+  Eigen::SparseMatrix<typename DerivedV_uv::Scalar> Q;
+  return lscm(V, F, b, bc, V_uv, Q);
+}
+
+template <
+  typename DerivedV, 
+  typename DerivedF, 
+  typename DerivedV_uv>
+IGL_INLINE bool igl::lscm(
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  Eigen::PlainObjectBase<DerivedV_uv> & V_uv)
+{
+  using Scalar = typename DerivedV_uv::Scalar;
+  Eigen::SparseMatrix<Scalar> Q;
+  igl::lscm_hessian(V,F,Q);
+  Eigen::SparseMatrix<Scalar> M;
+  igl::massmatrix(V,F,igl::MASSMATRIX_TYPE_DEFAULT,M);
+  Eigen::SparseMatrix<Scalar> M2;
+  igl::repdiag(M,2,M2);
+  Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic> U;
+  Eigen::Matrix<Scalar,Eigen::Dynamic,1> S;
+  bool success = igl::eigs(Q,M2,3,igl::EIGS_TYPE_SM,U,S);
+  if(!success)
+  {
+    return false;
+  }
+  V_uv.resize(V.rows(),2);
+  V_uv<< U.col(0).head(V.rows()),U.col(0).tail(V.rows());
+  return true;
+}
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template instantiation
+// generated by autoexplicit.sh
+template bool igl::lscm<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
+// generated by autoexplicit.sh
+template bool igl::lscm<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
+#endif

+ 38 - 14
include/igl/lscm.h

@@ -21,7 +21,7 @@ namespace igl
   // Generation" [Lévy et al. 2002]), though this implementation follows the
   // Generation" [Lévy et al. 2002]), though this implementation follows the
   // derivation in: "Spectral Conformal Parameterization" [Mullen et al. 2008]
   // derivation in: "Spectral Conformal Parameterization" [Mullen et al. 2008]
   // (note, this does **not** implement the Eigen-decomposition based method in
   // (note, this does **not** implement the Eigen-decomposition based method in
-  // [Mullen et al. 2008], which is not equivalent). Input should be a manifold
+  // [Mullen et al. 2008], see below). Input should be a manifold
   // mesh (also no unreferenced vertices) and "boundary" (fixed vertices) `b`
   // mesh (also no unreferenced vertices) and "boundary" (fixed vertices) `b`
   // should contain at least two vertices per connected component.
   // should contain at least two vertices per connected component.
   //
   //
@@ -35,20 +35,44 @@ namespace igl
   //   Q  #Vx2 by #Vx2 symmetric positive semi-definite matrix for computing LSCM energy
   //   Q  #Vx2 by #Vx2 symmetric positive semi-definite matrix for computing LSCM energy
   // Returns true only on solver success.
   // Returns true only on solver success.
   //
   //
-  IGL_INLINE bool lscm( 
-      const Eigen::MatrixXd& V, 
-      const Eigen::MatrixXi& F,
-      const Eigen::VectorXi& b, 
-      const Eigen::MatrixXd& bc, 
-      Eigen::MatrixXd& V_uv,
-      Eigen::SparseMatrix<double>& Q);
+  template <
+    typename DerivedV, 
+    typename DerivedF, 
+    typename Derivedb, 
+    typename Derivedbc, 
+    typename DerivedV_uv, 
+    typename QScalar>
+  IGL_INLINE bool lscm(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedF> & F,
+    const Eigen::MatrixBase<Derivedb> & b,
+    const Eigen::MatrixBase<Derivedbc> & bc,
+    Eigen::PlainObjectBase<DerivedV_uv> & V_uv,
+    Eigen::SparseMatrix<QScalar> & Q);
   // Wrapper where the output Q is discarded
   // Wrapper where the output Q is discarded
-  IGL_INLINE bool lscm( 
-      const Eigen::MatrixXd& V, 
-      const Eigen::MatrixXi& F,
-      const Eigen::VectorXi& b, 
-      const Eigen::MatrixXd& bc, 
-      Eigen::MatrixXd& V_uv);
+  template <
+    typename DerivedV, 
+    typename DerivedF, 
+    typename Derivedb, 
+    typename Derivedbc, 
+    typename DerivedV_uv>
+  IGL_INLINE bool lscm(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedF> & F,
+    const Eigen::MatrixBase<Derivedb> & b,
+    const Eigen::MatrixBase<Derivedbc> & bc,
+    Eigen::PlainObjectBase<DerivedV_uv> & V_uv);
+  // Free boundary version. "Spectral Conformal Parameterization" using Eigen
+  // decomposition. Assumes mesh is a single connected component topologically
+  // equivalent to a chunk of the plane.
+  template <
+    typename DerivedV, 
+    typename DerivedF, 
+    typename DerivedV_uv>
+  IGL_INLINE bool lscm(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedF> & F,
+    Eigen::PlainObjectBase<DerivedV_uv> & V_uv);
 }
 }
 
 
 #ifndef IGL_STATIC_LIBRARY
 #ifndef IGL_STATIC_LIBRARY

+ 82 - 0
include/igl/lscm.h~

@@ -0,0 +1,82 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2014 Daniele Panozzo <[email protected]>
+//               2015 Alec Jacobson
+//
+// This Source Code Form is subject to the terms of the Mozilla Public License
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can
+// obtain one at http://mozilla.org/MPL/2.0/.
+#ifndef IGL_LSCM_H
+#define IGL_LSCM_H
+#include "igl_inline.h"
+
+#include <Eigen/Dense>
+#include <Eigen/Sparse>
+
+namespace igl 
+{
+  // Compute a Least-squares conformal map parametrization (equivalently
+  // derived in "Intrinsic Parameterizations of Surface Meshes" [Desbrun et al.
+  // 2002] and "Least Squares Conformal Maps for Automatic Texture Atlas
+  // Generation" [Lévy et al. 2002]), though this implementation follows the
+  // derivation in: "Spectral Conformal Parameterization" [Mullen et al. 2008]
+  // (note, this does **not** implement the Eigen-decomposition based method in
+  // [Mullen et al. 2008], see below). Input should be a manifold
+  // mesh (also no unreferenced vertices) and "boundary" (fixed vertices) `b`
+  // should contain at least two vertices per connected component.
+  //
+  // Inputs:
+  //   V  #V by 3 list of mesh vertex positions
+  //   F  #F by 3 list of mesh faces (must be triangles)
+  //   b  #b boundary indices into V
+  //   bc #b by 2 list of boundary values
+  // Outputs:
+  //   UV #V by 2 list of 2D mesh vertex positions in UV space
+  //   Q  #Vx2 by #Vx2 symmetric positive semi-definite matrix for computing LSCM energy
+  // Returns true only on solver success.
+  //
+  template <
+    typename DerivedV, 
+    typename DerivedF, 
+    typename Derivedb, 
+    typename Derivedbc, 
+    typename DerivedV_uv, 
+    typename QScalar>
+  IGL_INLINE bool lscm(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedF> & F,
+    const Eigen::MatrixBase<Derivedb> & b,
+    const Eigen::MatrixBase<Derivedbc> & bc,
+    Eigen::PlainObjectBase<DerivedV_uv> & V_uv,
+    Eigen::SparseMatrix<double> & Q)
+  // Wrapper where the output Q is discarded
+  template <
+    typename DerivedV, 
+    typename DerivedF, 
+    typename Derivedb, 
+    typename Derivedbc, 
+    typename DerivedV_uv>
+  IGL_INLINE bool lscm(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedF> & F,
+    const Eigen::MatrixBase<Derivedb> & b,
+    const Eigen::MatrixBase<Derivedbc> & bc,
+    Eigen::PlainObjectBase<DerivedV_uv> & V_uv);
+  // Free boundary version. "Spectral Conformal Parameterization" using Eigen
+  // decomposition. Assumes mesh is a single connected component topologically
+  // equivalent to a chunk of the plane.
+  template <
+    typename DerivedV, 
+    typename DerivedF, 
+    typename DerivedV_uv>
+  IGL_INLINE bool lscm(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedF> & F,
+    Eigen::PlainObjectBase<DerivedV_uv> & V_uv);
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#  include "lscm.cpp"
+#endif
+
+#endif

+ 33 - 0
include/igl/lscm_hessian.cpp

@@ -0,0 +1,33 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2023 Alec Jacobson
+//
+// This Source Code Form is subject to the terms of the Mozilla Public License
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can
+// obtain one at http://mozilla.org/MPL/2.0/.
+#include "lscm_hessian.h"
+#include "vector_area_matrix.h"
+#include "cotmatrix.h"
+#include "repdiag.h"
+template <typename DerivedV, typename DerivedF, typename QScalar>
+void igl::lscm_hessian(
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  Eigen::SparseMatrix<QScalar> & Q)
+{
+  // Assemble the area matrix (note that A is #Vx2 by #Vx2)
+  Eigen::SparseMatrix<QScalar> A;
+  igl::vector_area_matrix(F,A);
+  // Assemble the cotan laplacian matrix
+  Eigen::SparseMatrix<QScalar> L;
+  igl::cotmatrix(V,F,L);
+  Eigen::SparseMatrix<QScalar> L_flat;
+  igl::repdiag(L,2,L_flat);
+  Q = -L_flat - 2.*A;
+}
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template instantiation
+// generated by autoexplicit.sh
+template void igl::lscm_hessian<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, double>(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::SparseMatrix<double, 0, int>&);
+#endif

+ 31 - 0
include/igl/lscm_hessian.cpp~

@@ -0,0 +1,31 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2023 Alec Jacobson
+//
+// This Source Code Form is subject to the terms of the Mozilla Public License
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can
+// obtain one at http://mozilla.org/MPL/2.0/.
+#include "lscm_hessian.h"
+#include "vector_area_matrix.h"
+#include "cotmatrix.h"
+#include "repdiag.h"
+template <typename DerivedV, typename DerivedF, typename QScalar>
+void igl::lscm_hessian(
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  Eigen::SparseMatrix<QScalar> & Q)
+{
+  // Assemble the area matrix (note that A is #Vx2 by #Vx2)
+  Eigen::SparseMatrix<QScalar> A;
+  igl::vector_area_matrix(F,A);
+  // Assemble the cotan laplacian matrix
+  Eigen::SparseMatrix<QScalar> L;
+  igl::cotmatrix(V,F,L);
+  Eigen::SparseMatrix<QScalar> L_flat;
+  igl::repdiag(L,2,L_flat);
+  Q = -L_flat - 2.*A;
+}
+
+#ifdef IGL_STATIC_LIBRARY
+// Explicit template instantiation
+#endif

+ 46 - 0
include/igl/lscm_hessian.h

@@ -0,0 +1,46 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2023 Alec Jacobson
+//
+// This Source Code Form is subject to the terms of the Mozilla Public License
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can
+// obtain one at http://mozilla.org/MPL/2.0/.
+#ifndef IGL_LSCM_HESSIAN_H
+#define IGL_LSCM_HESSIAN_H
+#include <Eigen/Dense>
+#include <Eigen/Sparse>
+
+namespace igl 
+{
+  // Compute a Least-squares conformal map parametrization (equivalently
+  // derived in "Intrinsic Parameterizations of Surface Meshes" [Desbrun et al.
+  // 2002] and "Least Squares Conformal Maps for Automatic Texture Atlas
+  // Generation" [Lévy et al. 2002]), though this implementation follows the
+  // derivation in: "Spectral Conformal Parameterization" [Mullen et al. 2008]
+  // (note, this does **not** implement the Eigen-decomposition based method in
+  // [Mullen et al. 2008], which is not equivalent). Input should be a manifold
+  // mesh (also no unreferenced vertices) and "boundary" (fixed vertices) `b`
+  // should contain at least two vertices per connected component.
+  //
+  // Inputs:
+  //   V  #V by 3 list of mesh vertex positions
+  //   F  #F by 3 list of mesh faces (must be triangles)
+  //   b  #b boundary indices into V
+  //   bc #b by 2 list of boundary values
+  // Outputs:
+  //   UV #V by 2 list of 2D mesh vertex positions in UV space
+  //   Q  #Vx2 by #Vx2 symmetric positive semi-definite matrix for computing LSCM energy
+  // Returns true only on solver success.
+  //
+  template <typename DerivedV, typename DerivedF, typename QScalar>
+  void lscm_hessian(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedF> & F,
+    Eigen::SparseMatrix<QScalar> & Q);
+}
+
+#ifndef IGL_STATIC_LIBRARY
+#include "lscm_hessian.cpp"
+#endif
+
+#endif 

+ 44 - 0
include/igl/lscm_hessian.h~

@@ -0,0 +1,44 @@
+// This file is part of libigl, a simple c++ geometry processing library.
+//
+// Copyright (C) 2023 Alec Jacobson
+//
+// This Source Code Form is subject to the terms of the Mozilla Public License
+// v. 2.0. If a copy of the MPL was not distributed with this file, You can
+// obtain one at http://mozilla.org/MPL/2.0/.
+#ifndef IGL_LSCM_HESSIAN_H
+#define IGL_LSCM_HESSIAN_H
+#include <Eigen/Dense>
+#include <Eigen/Sparse>
+
+namespace igl 
+{
+  // Compute a Least-squares conformal map parametrization (equivalently
+  // derived in "Intrinsic Parameterizations of Surface Meshes" [Desbrun et al.
+  // 2002] and "Least Squares Conformal Maps for Automatic Texture Atlas
+  // Generation" [Lévy et al. 2002]), though this implementation follows the
+  // derivation in: "Spectral Conformal Parameterization" [Mullen et al. 2008]
+  // (note, this does **not** implement the Eigen-decomposition based method in
+  // [Mullen et al. 2008], which is not equivalent). Input should be a manifold
+  // mesh (also no unreferenced vertices) and "boundary" (fixed vertices) `b`
+  // should contain at least two vertices per connected component.
+  //
+  // Inputs:
+  //   V  #V by 3 list of mesh vertex positions
+  //   F  #F by 3 list of mesh faces (must be triangles)
+  //   b  #b boundary indices into V
+  //   bc #b by 2 list of boundary values
+  // Outputs:
+  //   UV #V by 2 list of 2D mesh vertex positions in UV space
+  //   Q  #Vx2 by #Vx2 symmetric positive semi-definite matrix for computing LSCM energy
+  // Returns true only on solver success.
+  //
+  template <typename DerivedV, typename DerivedF, typename QScalar>
+  void lscm_hessian(
+    const Eigen::MatrixBase<DerivedV> & V,
+    const Eigen::MatrixBase<DerivedF> & F,
+    Eigen::SparseMatrix<QScalar> & Q)
+  {
+  }
+}
+
+#endif 

+ 1 - 14
tutorial/502_LSCMParam/main.cpp

@@ -1,7 +1,5 @@
-#include <igl/boundary_loop.h>
 #include <igl/readOFF.h>
 #include <igl/readOFF.h>
 #include <igl/opengl/glfw/Viewer.h>
 #include <igl/opengl/glfw/Viewer.h>
-
 #include <igl/lscm.h>
 #include <igl/lscm.h>
 
 
 
 
@@ -38,19 +36,8 @@ int main(int argc, char *argv[])
   // Load a mesh in OFF format
   // Load a mesh in OFF format
   igl::readOFF(TUTORIAL_SHARED_PATH "/camelhead.off", V, F);
   igl::readOFF(TUTORIAL_SHARED_PATH "/camelhead.off", V, F);
 
 
-  // Fix two points on the boundary
-  VectorXi bnd,b(2,1);
-  igl::boundary_loop(F,bnd);
-  b(0) = bnd(0);
-  b(1) = bnd(bnd.size()/2);
-  MatrixXd bc(2,2);
-  bc<<0,0,1,0;
-
   // LSCM parametrization
   // LSCM parametrization
-  igl::lscm(V,F,b,bc,V_uv);
-
-  // Scale the uv
-  V_uv *= 5;
+  igl::lscm(V,F,V_uv);
 
 
   // Plot the mesh
   // Plot the mesh
   igl::opengl::glfw::Viewer viewer;
   igl::opengl::glfw::Viewer viewer;