DGtalTools  1.2.0
surface_approx.cpp
1 #include "surface_approx.h"
2 #include <regex>
3 #include <DGtal/dec/DiscreteExteriorCalculusFactory.h>
4 
5 bool ends_with(const std::string& value, const std::string& ending)
6 {
7  if (ending.size() > value.size()) return false;
8  return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
9 }
10 
11 template <typename Value>
12 Value
13 format_to(const std::string& str)
14 {
15  std::stringstream ss;
16  ss << str;
17  Value foo;
18  ss >> foo;
19  return foo;
20 }
21 
22 std::tuple<Calculus, FlatVector>
23 initCalculusAndNormalsFromSurfelNormalsCSV(const std::string& filename)
24 {
25  using DGtal::trace;
26  using std::endl;
27  using DGtal::PRIMAL;
28 
29  trace.beginBlock("loading surfels and normals from csv file");
30 
31  trace.info() << "filename=" << filename << endl;
32 
33  typedef DGtal::Z3i::RealVector RealVector;
34  typedef std::map<SCell, RealVector> Normals;
35  typedef std::vector<SCell> SCells;
36 
37  const KSpace kspace;
38 
39  static const std::string float_re = "([-+]?([[:digit:]]*\\.?[[:digit:]]+|[[:digit:]]+\\.?[[:digit:]]*)([eE][-+]?[[:digit:]]+)?)";
40  static const std::regex line_re("^ *(-?[[:digit:]]+) +(-?[[:digit:]]+) +(-?[[:digit:]]+) +([0-1]) +"+float_re+" +"+float_re+" +"+float_re+" *$", std::regex::extended);
41 
42  Normals normals;
43  SCells cells;
44 
45  try
46  {
47  std::ifstream handle(filename);
48 
49  while (true)
50  {
51  std::string line;
52  std::getline(handle, line);
53  if (!handle.good()) break;
54  std::smatch what;
55  if (std::regex_match(line, what, line_re) <= 0)
56  throw std::ios_base::failure("bad csv format");
57 
58  ASSERT( what.size() == 14 );
59 
60  const std::string xx_string(what[1].first, what[1].second);
61  const std::string yy_string(what[2].first, what[2].second);
62  const std::string zz_string(what[3].first, what[3].second);
63  const std::string sign_string(what[4].first, what[4].second);
64  const std::string nx_string(what[5].first, what[5].second);
65  const std::string ny_string(what[8].first, what[8].second);
66  const std::string nz_string(what[11].first, what[11].second);
67 
68  Point point;
69  point[0] = format_to<Point::Coordinate>(xx_string);
70  point[1] = format_to<Point::Coordinate>(yy_string);
71  point[2] = format_to<Point::Coordinate>(zz_string);
72 
73  const bool positive = format_to<bool>(sign_string);
74 
75  const SCell cell = kspace.sCell(point, positive);
76 
77  cells.push_back(cell);
78 
79  RealPoint normal;
80  normal[0] = format_to<RealPoint::Coordinate>(nx_string);
81  normal[1] = format_to<RealPoint::Coordinate>(ny_string);
82  normal[2] = format_to<RealPoint::Coordinate>(nz_string);
83 
84  normals.insert(std::make_pair(cell, normal));
85  }
86  }
87  catch (std::ios_base::failure &fail)
88  {
89  trace.error() << fail.what() << endl;
90  trace.endBlock();
91  exit(42);
92  }
93 
94  trace.info() << "cells_size=" << cells.size() << endl;
95  trace.info() << "normals_size=" << normals.size() << endl;
96 
97  ASSERT( normals.size() == cells.size() );
98 
99  Calculus calculus = DGtal::DiscreteExteriorCalculusFactory<Backend, int32_t>::createFromNSCells<2>(cells.begin(), cells.end(), true);
100 
101  const auto buildFlatVector = [&kspace, &calculus](const Normals& real_vectors)
102  {
103  ASSERT( real_vectors.size() == calculus.kFormLength(2, PRIMAL) );
104  const int nsurfels = (const int)real_vectors.size();
105  FlatVector vectors(3*real_vectors.size());
106  for (const std::pair<SCell, RealVector>& cell_pair : real_vectors)
107  {
108  const int index = (const int)calculus.getCellIndex(kspace.unsigns(cell_pair.first));
109  for (int dim=0; dim<3; dim++)
110  vectors[index+nsurfels*dim] = cell_pair.second[dim];
111  }
112  return vectors;
113  };
114 
115  trace.endBlock();
116 
117  return std::make_tuple(calculus, buildFlatVector(normals));
118 }
119 
120 void
121 exportOBJ(const Calculus& calculus, const FlatVector& positions, const std::string& filename)
122 {
123  using DGtal::PRIMAL;
124  using std::endl;
125 
126  ASSERT( !filename.empty() );
127 
128  const KSpace& kspace = calculus.myKSpace;
129  const int nvertices = (const int)calculus.kFormLength(0, PRIMAL);
130 
131  ASSERT( positions.size() == 3*nvertices );
132  typedef Calculus::Index Index;
133 
134  std::ofstream handle(filename);
135 
136  for (int index=0; index<nvertices; index++)
137  {
138  handle << "v";
139  for (int dim=0; dim<3; dim++)
140  handle << " " << positions[index+dim*nvertices];
141  handle << endl;
142  }
143 
144  for (const Calculus::ConstIterator::value_type& pair : calculus)
145  {
146  const Cell& face = pair.first;
147  if (kspace.uDim(face) != 2) continue;
148 
149  std::vector<Cell> vertices;
150  {
151  std::set<Cell> vertices_set;
152  for (const Cell& edge : kspace.uLowerIncident(face))
153  for (const Cell& vertex : kspace.uLowerIncident(edge))
154  if (calculus.containsCell(vertex))
155  vertices_set.insert(vertex);
156  ASSERT( vertices_set.size() == 4 );
157  std::copy(vertices_set.begin(), vertices_set.end(), std::back_inserter(vertices));
158  std::swap(vertices[2], vertices[3]);
159  }
160 
161  if (pair.second.flipped)
162  {
163  std::swap(vertices[0], vertices[1]);
164  std::swap(vertices[2], vertices[3]);
165  }
166 
167  handle << "f";
168  for (const Cell& cell : vertices)
169  {
170  const Index index = calculus.getCellIndex(cell);
171  ASSERT( index >= 0 && index < nvertices );
172  handle << " " << (index+1);
173  }
174  handle << endl;
175  }
176 }
177 
178 FlatVector
179 vertexNormals(const Calculus& calculus, const FlatVector& face_normals)
180 {
181  using DGtal::PRIMAL;
182 
183  const KSpace& kspace = calculus.myKSpace;
184  const int nfaces = (const int)calculus.kFormLength(2, PRIMAL);
185  const int nvertices = (const int)calculus.kFormLength(0, PRIMAL);
186 
187  ASSERT( face_normals.size() == 3*nfaces );
188 
189  typedef Calculus::Index Index;
190 
191  FlatVector vertex_normals = FlatVector::Zero(3*nvertices);
192  {
193  Index face_index = 0;
194  for (const SCell& signed_face : calculus.getIndexedSCells<2, PRIMAL>())
195  {
196  const Cell& face = kspace.unsigns(signed_face);
197 
198  std::set<Cell> face_vertices;
199  for (const Cell& edge : kspace.uLowerIncident(face))
200  for (const Cell& vertex : kspace.uLowerIncident(edge))
201  if (calculus.containsCell(vertex))
202  face_vertices.insert(vertex);
203  ASSERT( face_vertices.size() == 4 );
204 
205  for (const Cell& vertex : face_vertices)
206  {
207  const Index vertex_index = calculus.getCellIndex(vertex);
208  for (int dim=0; dim<3; dim++)
209  vertex_normals[vertex_index+dim*nvertices] += face_normals[face_index+dim*nfaces];
210  }
211 
212  face_index++;
213  }
214  }
215 
216  for (Index vertex_index=0; vertex_index<nvertices; vertex_index++)
217  {
218  double norm = 0;
219  for (int dim=0; dim<3; dim++)
220  {
221  const double foo = vertex_normals[vertex_index+dim*nvertices];
222  norm += foo*foo;
223  }
224  norm = sqrt(norm);
225  if (norm == 0) continue;
226  for (int dim=0; dim<3; dim++)
227  vertex_normals[vertex_index+dim*nvertices] /= norm;
228  }
229 
230  return vertex_normals;
231 }
232 
233 bool
234 checkOperatorSymmetry(const OperatorMatrix& matrix, const double tol)
235 {
236  typedef std::vector<Triplet> Triplets;
237 
238  Triplets matrix_transpose_triplets;
239  for (int kk=0; kk<matrix.outerSize(); kk++)
240  for(OperatorMatrix::InnerIterator ii(matrix, kk); ii; ++ii)
241  matrix_transpose_triplets.push_back(Triplet(ii.col(), ii.row(), ii.value()));
242 
243  OperatorMatrix matrix_transpose(matrix.cols(), matrix.rows());
244  matrix_transpose.setFromTriplets(matrix_transpose_triplets.begin(), matrix_transpose_triplets.end());
245  const OperatorMatrix foo = matrix - matrix_transpose;
246 
247  for (int kk=0; kk<foo.outerSize(); kk++)
248  for(OperatorMatrix::InnerIterator ii(foo, kk); ii; ++ii)
249  if (std::abs(ii.value()) > tol)
250  return false;
251 
252  return true;
253 }
254 
255 std::tuple<double, double>
256 approximateSurfaceEnergies(const Calculus& calculus, const FlatVector& normals, const FlatVector& positions)
257 {
258  using DGtal::PRIMAL;
259 
260  typedef Calculus::Index Index;
261 
262  const KSpace& kspace = calculus.myKSpace;
263  const CellEmbedder embedder(kspace);
264  const int nfaces = (const int)calculus.kFormLength(2, PRIMAL);
265  const int nvertices = (const int)calculus.kFormLength(0, PRIMAL);
266 
267  ASSERT( normals.size() == 3*nfaces );
268  ASSERT( positions.size() == 3*nvertices );
269 
270  double position_energy = 0;
271  for (const Calculus::ConstIterator::value_type& pair : calculus)
272  {
273  const Cell& vertex = pair.first;
274  if (kspace.uDim(vertex) != 0) continue;
275  const Index vertex_index = calculus.getCellIndex(vertex);
276 
277  ASSERT( pair.second.primal_size == 1 );
278  const double area = pair.second.dual_size;
279 
280  const RealPoint original_position = embedder(vertex);
281  RealPoint position;
282  for (int dim=0; dim<3; dim++)
283  position[dim] = positions[vertex_index+dim*nvertices];
284 
285  for (int dim=0; dim<3; dim++)
286  position_energy += area*pow(position[dim]-original_position[dim], 2);
287  }
288 
289  double align_energy = 0;
290  for (const Calculus::ConstIterator::value_type& pair : calculus)
291  {
292  const Cell& face = pair.first;
293  if (kspace.uDim(face) != 2) continue;
294 
295  ASSERT( pair.second.dual_size == 1 );
296  const double area = pair.second.primal_size;
297  ASSERT( area == 1 ); // true on cubical complexes
298 
299  RealPoint face_normal;
300  for (int dim=0; dim<3; dim++)
301  face_normal[dim] = normals[pair.second.index+dim*nfaces];
302 
303  std::vector<Cell> vertices;
304  {
305  std::set<Cell> vertices_set;
306  for (const Cell& edge : kspace.uLowerIncident(face))
307  for (const Cell& vertex : kspace.uLowerIncident(edge))
308  if (calculus.containsCell(vertex))
309  vertices_set.insert(vertex);
310  ASSERT( vertices_set.size() == 4 );
311  std::copy(vertices_set.begin(), vertices_set.end(), std::back_inserter(vertices));
312  std::swap(vertices[2], vertices[3]);
313  }
314 
315  std::vector<RealPoint> vertex_positions;
316  for (const Cell& vertex : vertices)
317  {
318  const Index vertex_index = calculus.getCellIndex(vertex);
319  RealPoint position;
320  for (int dim=0; dim<3; dim++)
321  position[dim] = positions[vertex_index+dim*nvertices];
322  vertex_positions.push_back(position);
323  }
324 
325  for (int kk=0; kk<4; kk++)
326  {
327  const int kk_next = (kk+1)%4;
328  align_energy += area*pow(face_normal.dot(vertex_positions[kk]-vertex_positions[kk_next]), 2);
329  }
330  }
331 
332  return std::make_tuple(position_energy, align_energy);
333 }
334 
335 std::tuple<FlatVector, FlatVector, FlatVector, FlatVector>
336 approximateSurface(const Calculus& calculus, const FlatVector& normals, const ApproxParams& params)
337 {
338  using DGtal::trace;
339  using std::endl;
340  using DGtal::PRIMAL;
341 
342  trace.beginBlock("approximating surface");
343 
344  typedef Calculus::Index Index;
345  typedef std::vector<Triplet> Triplets;
346 
347  const KSpace& kspace = calculus.myKSpace;
348  auto nfaces = calculus.kFormLength(2, PRIMAL);
349  auto nedges = calculus.kFormLength(1, PRIMAL);
350  auto nvertices = calculus.kFormLength(0, PRIMAL);
351  trace.info() << "nfaces=" << nfaces << endl;
352  trace.info() << "nedges=" << nedges << endl;
353  trace.info() << "nvertices=" << nvertices << endl;
354 
355  trace.info() << "computing operator" << endl;
356 
357  FlatVector original_centers(3*nfaces);
358  OperatorMatrix big_primal_hodge_2(3*nfaces, 3*nfaces);
359  OperatorMatrix vertex_positions_to_face_centers(3*nfaces, 3*nvertices);
360  OperatorMatrix align_normals(3*nvertices, 3*nvertices);
361  OperatorMatrix fairness_operator(3*nvertices, 3*nvertices);
362  {
363  Triplets big_primal_hodge_2_triplets;
364  Triplets vertex_positions_to_face_centers_triplets;
365  Triplets align_normals_triplets;
366  Triplets fairness_operator_triplets;
367  for (const Calculus::ConstIterator::value_type& pair : calculus)
368  {
369  const Cell& face = pair.first;
370  if (kspace.uDim(face) != 2) continue;
371  const Index face_index = pair.second.index;
372  const double face_area = pair.second.dual_size/pair.second.primal_size;
373  ASSERT( face_area == 1 );
374 
375  { // primal hodge 2
376  const double hodge_coeff = pair.second.dual_size/pair.second.primal_size;
377  ASSERT( hodge_coeff == 1 );
378  for (int dim=0; dim<3; dim++)
379  big_primal_hodge_2_triplets.push_back(Triplet(face_index+dim*nfaces, face_index+dim*nfaces, hodge_coeff));
380  }
381 
382  std::set<Cell> face_vertices;
383  for (const Cell& edge : kspace.uLowerIncident(face))
384  for (const Cell& vertex : kspace.uLowerIncident(edge))
385  if (calculus.containsCell(vertex))
386  face_vertices.insert(vertex);
387  ASSERT( face_vertices.size() == 4 );
388 
389 #if !defined(NDEBUG)
390  { // cell ordering check
391  std::set<Cell>::const_iterator aa = face_vertices.begin();
392  std::set<Cell>::const_iterator bb = face_vertices.begin();
393  ASSERT( bb != face_vertices.end() );
394  ++bb;
395  ASSERT( bb != face_vertices.end() );
396  std::vector<double> lengths;
397  while (aa != face_vertices.end())
398  {
399  const double length = (kspace.uKCoords(*aa)-kspace.uKCoords(*bb)).norm();
400  lengths.push_back(length);
401  ++aa;
402  ++bb;
403  if (bb == face_vertices.end()) bb= face_vertices.begin();
404  }
405  ASSERT( lengths == std::vector<double>({2,sqrt(8),2,sqrt(8)}) );
406  }
407 #endif
408 
409  std::vector<Cell> ordered_face_vertices;
410  std::copy(face_vertices.begin(), face_vertices.end(), std::back_inserter(ordered_face_vertices));
411  std::swap(ordered_face_vertices[2], ordered_face_vertices[3]);
412 
413  { // align normal
414  for (int normal_dim_ii=0; normal_dim_ii<3; normal_dim_ii++)
415  for (int normal_dim_jj=0; normal_dim_jj<3; normal_dim_jj++)
416  {
417  const double normal_coeff = face_area*normals(face_index+normal_dim_ii*nfaces)*normals(face_index+normal_dim_jj*nfaces);
418  //if (std::abs(normal_coeff) < 1e-3) continue;
419  for (int kk_vertex=0; kk_vertex<4; kk_vertex++)
420  {
421  const int kk_vertex_next = (kk_vertex+1)%4;
422  const int kk_vertex_prev = (kk_vertex+3)%4;
423  ASSERT( (kspace.uKCoords(ordered_face_vertices[kk_vertex])-kspace.uKCoords(ordered_face_vertices[kk_vertex_next])).norm() == 2 );
424  ASSERT( (kspace.uKCoords(ordered_face_vertices[kk_vertex])-kspace.uKCoords(ordered_face_vertices[kk_vertex_prev])).norm() == 2 );
425  const Index index_vertex = calculus.getCellIndex(ordered_face_vertices[kk_vertex]);
426  const Index index_vertex_next = calculus.getCellIndex(ordered_face_vertices[kk_vertex_next]);
427  const Index index_vertex_prev = calculus.getCellIndex(ordered_face_vertices[kk_vertex_prev]);
428  align_normals_triplets.push_back(Triplet(normal_dim_ii*nvertices+index_vertex, normal_dim_jj*nvertices+index_vertex, 2*normal_coeff));
429  align_normals_triplets.push_back(Triplet(normal_dim_ii*nvertices+index_vertex, normal_dim_jj*nvertices+index_vertex_prev, -normal_coeff));
430  align_normals_triplets.push_back(Triplet(normal_dim_ii*nvertices+index_vertex, normal_dim_jj*nvertices+index_vertex_next, -normal_coeff));
431  }
432  }
433  }
434 
435  { // fairness operator
436  for (int kk_vertex=0; kk_vertex<4; kk_vertex++)
437  {
438  const Index kk_index = calculus.getCellIndex(ordered_face_vertices[kk_vertex]);
439  for (int ll_vertex=0; ll_vertex<4; ll_vertex++)
440  {
441  const Index ll_index = calculus.getCellIndex(ordered_face_vertices[ll_vertex]);
442  const double coeff = kk_vertex%2 == ll_vertex%2 ? face_area : -face_area;
443  for (int dim_kk=0; dim_kk<3; dim_kk++)
444  for (int dim_ll=0; dim_ll<3; dim_ll++)
445  fairness_operator_triplets.push_back(Triplet(kk_index+dim_kk*nvertices, ll_index+dim_kk*nvertices, coeff));
446  }
447  }
448  }
449 
450  { // original centers
451  const CellEmbedder embedder(kspace);
452  RealPoint original_center;
453  for (const Cell& face_vertex : face_vertices)
454  {
455  original_center += embedder(face_vertex);
456  const Index vertex_index = calculus.getCellIndex(face_vertex);
457  for (int dim=0; dim<3; dim++)
458  vertex_positions_to_face_centers_triplets.push_back(Triplet(face_index+dim*nfaces, vertex_index+dim*nvertices, 1./4));
459  }
460  original_center /= 4;
461 
462  for (int dim=0; dim<3; dim++) original_centers[face_index+dim*nfaces] = original_center[dim];
463  }
464  }
465  big_primal_hodge_2.setFromTriplets(big_primal_hodge_2_triplets.begin(), big_primal_hodge_2_triplets.end());
466  vertex_positions_to_face_centers.setFromTriplets(vertex_positions_to_face_centers_triplets.begin(), vertex_positions_to_face_centers_triplets.end());
467  align_normals.setFromTriplets(align_normals_triplets.begin(), align_normals_triplets.end());
468  fairness_operator.setFromTriplets(fairness_operator_triplets.begin(), fairness_operator_triplets.end());
469 
470  }
471 
472  FlatVector original_positions(3*nvertices);
473  OperatorMatrix big_primal_hodge_0(3*nvertices, 3*nvertices);
474  OperatorMatrix vertex_barycenter(3*nvertices, 3*nvertices);
475  {
476  const CellEmbedder embedder(kspace);
477  Triplets big_primal_hodge_0_triplets;
478  Triplets vertex_barycenter_triplets;
479  for (const Calculus::ConstIterator::value_type& pair : calculus)
480  {
481  const Cell& vertex = pair.first;
482  if (kspace.uDim(vertex) != 0) continue;
483  const Index vertex_index = calculus.getCellIndex(vertex);
484 
485  { // primal hodge 0
486  ASSERT( pair.second.primal_size == 1 );
487  const double hodge_coeff = pair.second.dual_size/pair.second.primal_size;
488  for (int dim=0; dim<3; dim++)
489  big_primal_hodge_0_triplets.push_back(Triplet(vertex_index+dim*nvertices, vertex_index+dim*nvertices, hodge_coeff));
490  }
491 
492  { // original positions
493  const RealPoint coords = embedder(vertex);
494  for (int dim=0; dim<3; dim++) original_positions[pair.second.index+dim*nvertices] = coords[dim];
495  }
496 
497  { // vertex barycenter
498  std::set<Cell> neighbor_vertices;
499  std::set<Cell> neighbor_edges;
500  std::set<Cell> neighbor_faces;
501  for (const Cell& edge : kspace.uUpperIncident(vertex))
502  if (calculus.containsCell(edge))
503  {
504  neighbor_edges.insert(edge);
505  for (const Cell& neighbor_vertex : kspace.uLowerIncident(edge))
506  if (neighbor_vertex != vertex && calculus.containsCell(neighbor_vertex))
507  neighbor_vertices.insert(neighbor_vertex);
508  for (const Cell& neighbor_face : kspace.uUpperIncident(edge))
509  if (calculus.containsCell(neighbor_face))
510  neighbor_faces.insert(neighbor_face);
511  }
512 
513  const bool on_border = (neighbor_edges.size()-1 == neighbor_faces.size()); // see dgtal_complex for enumeration of all cases
514 
515  if (!on_border)
516  {
517  ASSERT( neighbor_vertices.size() >= 3 ); // >= 3 if surface has no border
518  ASSERT( neighbor_vertices.size() <= 6 );
519  if (neighbor_vertices.size() != pair.second.dual_size*4) trace.warning() << "non 2-manifold!!!!" << endl; // only detect manifold problem when surface has no border
520 
521  const double coeff = 1./neighbor_vertices.size();
522  for (const Cell& neighbor_vertex : neighbor_vertices)
523  {
524  const Index neighbor_vertex_index = calculus.getCellIndex(neighbor_vertex);
525  for (int dim=0; dim<3; dim++)
526  vertex_barycenter_triplets.push_back(Triplet(vertex_index+dim*nvertices, neighbor_vertex_index+dim*nvertices, coeff));
527  }
528 
529  for (int dim=0; dim<3; dim++)
530  vertex_barycenter_triplets.push_back(Triplet(vertex_index+dim*nvertices, vertex_index+dim*nvertices, -1));
531  }
532  }
533  }
534  big_primal_hodge_0.setFromTriplets(big_primal_hodge_0_triplets.begin(), big_primal_hodge_0_triplets.end());
535  vertex_barycenter.setFromTriplets(vertex_barycenter_triplets.begin(), vertex_barycenter_triplets.end());
536  }
537 
538  ASSERT( (vertex_positions_to_face_centers*original_positions-original_centers).array().maxCoeff() < 1e-5 );
539 
540  //typedef Backend::SolverSparseLU LinearSolver;
541  typedef Backend::SolverSimplicialLDLT LinearSolver;
542  const double lambda = params.regularization_position;
543  const double alpha = params.regularization_center;
544  const double gamma = params.align;
545  const double beta = params.fairness;
546  const double rau = params.barycenter;
547  trace.info() << "lambda=" << lambda << " (vertex regularization)" << endl;
548  trace.info() << "alpha=" << alpha << " (face center regularization)" << endl;
549  trace.info() << "gamma=" << gamma << " (normal align)" << endl;
550  trace.info() << "beta=" << beta << " (fairness)" << endl;
551  trace.info() << "rau=" << rau << " (barycenter)" << endl;
552 
553  const OperatorMatrix ope =
554  lambda*2*big_primal_hodge_0 +
555  alpha*2*vertex_positions_to_face_centers.transpose()*big_primal_hodge_2*vertex_positions_to_face_centers +
556  gamma*align_normals +
557  beta*2*fairness_operator +
558  rau*2*vertex_barycenter.transpose()*big_primal_hodge_0*vertex_barycenter;
559  const FlatVector rht =
560  lambda*2*big_primal_hodge_0*original_positions +
561  alpha*2*vertex_positions_to_face_centers.transpose()*big_primal_hodge_2*original_centers;
562 
563  using namespace DGtal; // for solver status pretty print
564 
565  trace.info() << "factorizing " << (checkOperatorSymmetry(ope) ? "" : "non-") << "symmetric operator ";
566  LinearSolver linear_solver;
567  linear_solver.compute(ope);
568  std::cout << linear_solver.info() << endl;
569 
570  if (linear_solver.info() == Eigen::Success)
571  {
572  trace.info() << "solving problem ";
573  const FlatVector regularized_positions = linear_solver.solve(rht);
574  std::cout << linear_solver.info() << endl;
575 
576  if (linear_solver.info() == Eigen::Success)
577  {
578  trace.endBlock();
579  return std::make_tuple(original_positions, regularized_positions, original_centers, FlatVector(vertex_positions_to_face_centers*regularized_positions));
580  }
581  }
582 
583  trace.endBlock();
584  return std::make_tuple(FlatVector(), FlatVector(), FlatVector(), FlatVector());
585 }
586 
Definition: ATu0v1.h:57
double regularization_position
double regularization_center
double barycenter