Blender V5.0
slim.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2016 Michael Rabinovich
2 * 2023 Blender Authors
3 *
4 * SPDX-License-Identifier: MPL-2.0 */
5
9
10#include "slim.h"
11#include "doublearea.h"
13
14#include "BLI_assert.h"
15#include "BLI_math_base.h" /* M_PI */
16
17#include <vector>
18
19#include <Eigen/Geometry>
20#include <Eigen/IterativeLinearSolvers>
21#include <Eigen/SVD>
22#include <Eigen/SparseCholesky>
23
24namespace slim {
25
26/* GRAD
27 * G = grad(V,F)
28 *
29 * Compute the numerical gradient operator
30 *
31 * Inputs:
32 * V #vertices by 3 list of mesh vertex positions
33 * F #faces by 3 list of mesh face indices
34 * uniform #boolean (default false) - Use a uniform mesh instead of the vertices V
35 * Outputs:
36 * G #faces*dim by #V Gradient operator
37 *
38 *
39 * Gradient of a scalar function defined on piecewise linear elements (mesh)
40 * is constant on each triangle i,j,k:
41 * grad(Xijk) = (Xj-Xi) * (Vi - Vk)^R90 / 2A + (Xk-Xi) * (Vj - Vi)^R90 / 2A
42 * where Xi is the scalar value at vertex i, Vi is the 3D position of vertex
43 * i, and A is the area of triangle (i,j,k). ^R90 represent a rotation of
44 * 90 degrees
45 */
46template<typename DerivedV, typename DerivedF>
47static inline void grad(const Eigen::PlainObjectBase<DerivedV> &V,
48 const Eigen::PlainObjectBase<DerivedF> &F,
49 Eigen::SparseMatrix<typename DerivedV::Scalar> &G,
50 bool uniform = false)
51{
52 Eigen::Matrix<typename DerivedV::Scalar, Eigen::Dynamic, 3> eperp21(F.rows(), 3),
53 eperp13(F.rows(), 3);
54
55 for (int i = 0; i < F.rows(); ++i) {
56 /* Renaming indices of vertices of triangles for convenience. */
57 int i1 = F(i, 0);
58 int i2 = F(i, 1);
59 int i3 = F(i, 2);
60
61 /* #F x 3 matrices of triangle edge vectors, named after opposite vertices. */
62 Eigen::Matrix<typename DerivedV::Scalar, 1, 3> v32 = V.row(i3) - V.row(i2);
63 Eigen::Matrix<typename DerivedV::Scalar, 1, 3> v13 = V.row(i1) - V.row(i3);
64 Eigen::Matrix<typename DerivedV::Scalar, 1, 3> v21 = V.row(i2) - V.row(i1);
65 Eigen::Matrix<typename DerivedV::Scalar, 1, 3> n = v32.cross(v13);
66 /* Area of parallelogram is twice area of triangle.
67 * Area of parallelogram is || v1 x v2 ||.
68 * This does correct l2 norm of rows, so that it contains #F list of twice.
69 * triangle areas. */
70 double dblA = std::sqrt(n.dot(n));
71 Eigen::Matrix<typename DerivedV::Scalar, 1, 3> u;
72 if (!uniform) {
73 /* Now normalize normals to get unit normals. */
74 u = n / dblA;
75 }
76 else {
77 /* Abstract equilateral triangle v1=(0,0), v2=(h,0), v3=(h/2, (sqrt(3)/2)*h) */
78
79 /* Get h (by the area of the triangle). */
80 double h = sqrt((dblA) /
81 sin(M_PI / 3.0)); /* (h^2*sin(60))/2. = Area => h = sqrt(2*Area/sin_60) */
82
83 Eigen::VectorXd v1, v2, v3;
84 v1 << 0, 0, 0;
85 v2 << h, 0, 0;
86 v3 << h / 2., (sqrt(3) / 2.) * h, 0;
87
88 /* Now fix v32,v13,v21 and the normal. */
89 v32 = v3 - v2;
90 v13 = v1 - v3;
91 v21 = v2 - v1;
92 n = v32.cross(v13);
93 }
94
95 /* Rotate each vector 90 degrees around normal. */
96 double norm21 = std::sqrt(v21.dot(v21));
97 double norm13 = std::sqrt(v13.dot(v13));
98 eperp21.row(i) = u.cross(v21);
99 eperp21.row(i) = eperp21.row(i) / std::sqrt(eperp21.row(i).dot(eperp21.row(i)));
100 eperp21.row(i) *= norm21 / dblA;
101 eperp13.row(i) = u.cross(v13);
102 eperp13.row(i) = eperp13.row(i) / std::sqrt(eperp13.row(i).dot(eperp13.row(i)));
103 eperp13.row(i) *= norm13 / dblA;
104 }
105
106 std::vector<int> rs;
107 rs.reserve(F.rows() * 4 * 3);
108 std::vector<int> cs;
109 cs.reserve(F.rows() * 4 * 3);
110 std::vector<double> vs;
111 vs.reserve(F.rows() * 4 * 3);
112
113 /* Row indices. */
114 for (int r = 0; r < 3; r++) {
115 for (int j = 0; j < 4; j++) {
116 for (int i = r * F.rows(); i < (r + 1) * F.rows(); i++) {
117 rs.push_back(i);
118 }
119 }
120 }
121
122 /* Column indices. */
123 for (int r = 0; r < 3; r++) {
124 for (int i = 0; i < F.rows(); i++) {
125 cs.push_back(F(i, 1));
126 }
127 for (int i = 0; i < F.rows(); i++) {
128 cs.push_back(F(i, 0));
129 }
130 for (int i = 0; i < F.rows(); i++) {
131 cs.push_back(F(i, 2));
132 }
133 for (int i = 0; i < F.rows(); i++) {
134 cs.push_back(F(i, 0));
135 }
136 }
137
138 /* Values. */
139 for (int i = 0; i < F.rows(); i++) {
140 vs.push_back(eperp13(i, 0));
141 }
142 for (int i = 0; i < F.rows(); i++) {
143 vs.push_back(-eperp13(i, 0));
144 }
145 for (int i = 0; i < F.rows(); i++) {
146 vs.push_back(eperp21(i, 0));
147 }
148 for (int i = 0; i < F.rows(); i++) {
149 vs.push_back(-eperp21(i, 0));
150 }
151 for (int i = 0; i < F.rows(); i++) {
152 vs.push_back(eperp13(i, 1));
153 }
154 for (int i = 0; i < F.rows(); i++) {
155 vs.push_back(-eperp13(i, 1));
156 }
157 for (int i = 0; i < F.rows(); i++) {
158 vs.push_back(eperp21(i, 1));
159 }
160 for (int i = 0; i < F.rows(); i++) {
161 vs.push_back(-eperp21(i, 1));
162 }
163 for (int i = 0; i < F.rows(); i++) {
164 vs.push_back(eperp13(i, 2));
165 }
166 for (int i = 0; i < F.rows(); i++) {
167 vs.push_back(-eperp13(i, 2));
168 }
169 for (int i = 0; i < F.rows(); i++) {
170 vs.push_back(eperp21(i, 2));
171 }
172 for (int i = 0; i < F.rows(); i++) {
173 vs.push_back(-eperp21(i, 2));
174 }
175
176 /* Create sparse gradient operator matrix.. */
177 G.resize(3 * F.rows(), V.rows());
178 std::vector<Eigen::Triplet<typename DerivedV::Scalar>> triplets;
179 for (int i = 0; i < (int)vs.size(); ++i) {
180 triplets.push_back(Eigen::Triplet<typename DerivedV::Scalar>(rs[i], cs[i], vs[i]));
181 }
182 G.setFromTriplets(triplets.begin(), triplets.end());
183}
184
185/* Computes the polar decomposition (R,T) of a matrix A using SVD singular
186 * value decomposition
187 *
188 * Inputs:
189 * A 3 by 3 matrix to be decomposed
190 * Outputs:
191 * R 3 by 3 rotation matrix part of decomposition (**always rotataion**)
192 * T 3 by 3 stretch matrix part of decomposition
193 * U 3 by 3 left-singular vectors
194 * S 3 by 1 singular values
195 * V 3 by 3 right-singular vectors
196 */
197template<typename DerivedA,
198 typename DerivedR,
199 typename DerivedT,
200 typename DerivedU,
201 typename DerivedS,
202 typename DerivedV>
203static inline void polar_svd(const Eigen::PlainObjectBase<DerivedA> &A,
204 Eigen::PlainObjectBase<DerivedR> &R,
205 Eigen::PlainObjectBase<DerivedT> &T,
206 Eigen::PlainObjectBase<DerivedU> &U,
207 Eigen::PlainObjectBase<DerivedS> &S,
208 Eigen::PlainObjectBase<DerivedV> &V)
209{
210 using namespace std;
211 Eigen::JacobiSVD<DerivedA> svd;
212 svd.compute(A, Eigen::ComputeFullU | Eigen::ComputeFullV);
213 U = svd.matrixU();
214 V = svd.matrixV();
215 S = svd.singularValues();
216 R = U * V.transpose();
217 const auto &SVT = S.asDiagonal() * V.adjoint();
218 /* Check for reflection. */
219 if (R.determinant() < 0) {
220 /* Annoyingly the .eval() is necessary. */
221 auto W = V.eval();
222 W.col(V.cols() - 1) *= -1.;
223 R = U * W.transpose();
224 T = W * SVT;
225 }
226 else {
227 T = V * SVT;
228 }
229}
230
231static inline void compute_surface_gradient_matrix(const Eigen::MatrixXd &V,
232 const Eigen::MatrixXi &F,
233 const Eigen::MatrixXd &F1,
234 const Eigen::MatrixXd &F2,
235 Eigen::SparseMatrix<double> &D1,
236 Eigen::SparseMatrix<double> &D2)
237{
238 Eigen::SparseMatrix<double> G;
239 grad(V, F, G);
240 Eigen::SparseMatrix<double> Dx = G.block(0, 0, F.rows(), V.rows());
241 Eigen::SparseMatrix<double> Dy = G.block(F.rows(), 0, F.rows(), V.rows());
242 Eigen::SparseMatrix<double> Dz = G.block(2 * F.rows(), 0, F.rows(), V.rows());
243
244 D1 = F1.col(0).asDiagonal() * Dx + F1.col(1).asDiagonal() * Dy + F1.col(2).asDiagonal() * Dz;
245 D2 = F2.col(0).asDiagonal() * Dx + F2.col(1).asDiagonal() * Dy + F2.col(2).asDiagonal() * Dz;
246}
247
248static inline void compute_weighted_jacobians(SLIMData &s, const Eigen::MatrixXd &uv)
249{
250 BLI_assert(s.valid);
251
252 /* Ji=[D1*u,D2*u,D1*v,D2*v] */
253 s.Ji.col(0) = s.Dx * uv.col(0);
254 s.Ji.col(1) = s.Dy * uv.col(0);
255 s.Ji.col(2) = s.Dx * uv.col(1);
256 s.Ji.col(3) = s.Dy * uv.col(1);
257
258 /* Add weights. */
259 Eigen::VectorXd weights = s.weightPerFaceMap.cast<double>();
260 s.Ji.col(0) = weights.cwiseProduct(s.Ji.col(0));
261 s.Ji.col(1) = weights.cwiseProduct(s.Ji.col(1));
262 s.Ji.col(2) = weights.cwiseProduct(s.Ji.col(2));
263 s.Ji.col(3) = weights.cwiseProduct(s.Ji.col(3));
264}
265
266static inline void compute_unweighted_jacobians(SLIMData &s, const Eigen::MatrixXd &uv)
267{
268 BLI_assert(s.valid);
269
270 /* Ji=[D1*u,D2*u,D1*v,D2*v] */
271 s.Ji.col(0) = s.Dx * uv.col(0);
272 s.Ji.col(1) = s.Dy * uv.col(0);
273 s.Ji.col(2) = s.Dx * uv.col(1);
274 s.Ji.col(3) = s.Dy * uv.col(1);
275}
276
277static inline void compute_jacobians(SLIMData &s, const Eigen::MatrixXd &uv)
278{
279 BLI_assert(s.valid);
280
283 }
284 else {
286 }
287}
288
289static inline void update_weights_and_closest_rotations(SLIMData &s, Eigen::MatrixXd &uv)
290{
291 BLI_assert(s.valid);
292 compute_jacobians(s, uv);
293
294 const double eps = 1e-8;
295 double exp_f = s.exp_factor;
296
297 for (int i = 0; i < s.Ji.rows(); ++i) {
298 using Mat2 = Eigen::Matrix<double, 2, 2>;
299 using Vec2 = Eigen::Matrix<double, 2, 1>;
300 Mat2 ji, ri, ti, ui, vi;
301 Vec2 sing;
302 Vec2 closest_sing_vec;
303 Mat2 mat_W;
304 Vec2 m_sing_new;
305 double s1, s2;
306
307 ji(0, 0) = s.Ji(i, 0);
308 ji(0, 1) = s.Ji(i, 1);
309 ji(1, 0) = s.Ji(i, 2);
310 ji(1, 1) = s.Ji(i, 3);
311
312 polar_svd(ji, ri, ti, ui, sing, vi);
313
314 s1 = sing(0);
315 s2 = sing(1);
316
317 /* Update Weights according to energy. */
318 switch (s.slim_energy) {
319 case SLIMData::ARAP: {
320 m_sing_new << 1, 1;
321 break;
322 }
324 double s1_g = 2 * (s1 - pow(s1, -3));
325 double s2_g = 2 * (s2 - pow(s2, -3));
326 m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1)));
327 break;
328 }
329 case SLIMData::LOG_ARAP: {
330 double s1_g = 2 * (log(s1) / s1);
331 double s2_g = 2 * (log(s2) / s2);
332 m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1)));
333 break;
334 }
335 case SLIMData::CONFORMAL: {
336 double s1_g = 1 / (2 * s2) - s2 / (2 * pow(s1, 2));
337 double s2_g = 1 / (2 * s1) - s1 / (2 * pow(s2, 2));
338
339 double geo_avg = sqrt(s1 * s2);
340 double s1_min = geo_avg;
341 double s2_min = geo_avg;
342
343 m_sing_new << sqrt(s1_g / (2 * (s1 - s1_min))), sqrt(s2_g / (2 * (s2 - s2_min)));
344
345 /* Change local step. */
346 closest_sing_vec << s1_min, s2_min;
347 ri = ui * closest_sing_vec.asDiagonal() * vi.transpose();
348 break;
349 }
351 double s1_g = 2 * (s1 - pow(s1, -3));
352 double s2_g = 2 * (s2 - pow(s2, -3));
353
354 double in_exp = exp_f * ((pow(s1, 2) + pow(s2, 2)) / (2 * s1 * s2));
355 double exp_thing = exp(in_exp);
356
357 s1_g *= exp_thing * exp_f;
358 s2_g *= exp_thing * exp_f;
359
360 m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1)));
361 break;
362 }
364 double s1_g = 2 * (s1 - pow(s1, -3));
365 double s2_g = 2 * (s2 - pow(s2, -3));
366
367 double in_exp = exp_f * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2));
368 double exp_thing = exp(in_exp);
369
370 s1_g *= exp_thing * exp_f;
371 s2_g *= exp_thing * exp_f;
372
373 m_sing_new << sqrt(s1_g / (2 * (s1 - 1))), sqrt(s2_g / (2 * (s2 - 1)));
374 break;
375 }
376 }
377
378 if (std::abs(s1 - 1) < eps) {
379 m_sing_new(0) = 1;
380 }
381 if (std::abs(s2 - 1) < eps) {
382 m_sing_new(1) = 1;
383 }
384 mat_W = ui * m_sing_new.asDiagonal() * ui.transpose();
385
386 s.W_11(i) = mat_W(0, 0);
387 s.W_12(i) = mat_W(0, 1);
388 s.W_21(i) = mat_W(1, 0);
389 s.W_22(i) = mat_W(1, 1);
390
391 /* 2) Update local step (doesn't have to be a rotation, for instance in case of conformal
392 * energy). */
393 s.Ri(i, 0) = ri(0, 0);
394 s.Ri(i, 1) = ri(1, 0);
395 s.Ri(i, 2) = ri(0, 1);
396 s.Ri(i, 3) = ri(1, 1);
397 }
398}
399
400template<typename DerivedV, typename DerivedF>
401static inline void local_basis(const Eigen::PlainObjectBase<DerivedV> &V,
402 const Eigen::PlainObjectBase<DerivedF> &F,
403 Eigen::PlainObjectBase<DerivedV> &B1,
404 Eigen::PlainObjectBase<DerivedV> &B2,
405 Eigen::PlainObjectBase<DerivedV> &B3)
406{
407 using namespace Eigen;
408 using namespace std;
409 B1.resize(F.rows(), 3);
410 B2.resize(F.rows(), 3);
411 B3.resize(F.rows(), 3);
412
413 for (unsigned i = 0; i < F.rows(); ++i) {
414 Eigen::Matrix<typename DerivedV::Scalar, 1, 3> v1 =
415 (V.row(F(i, 1)) - V.row(F(i, 0))).normalized();
416 Eigen::Matrix<typename DerivedV::Scalar, 1, 3> t = V.row(F(i, 2)) - V.row(F(i, 0));
417 Eigen::Matrix<typename DerivedV::Scalar, 1, 3> v3 = v1.cross(t).normalized();
418 Eigen::Matrix<typename DerivedV::Scalar, 1, 3> v2 = v1.cross(v3).normalized();
419
420 B1.row(i) = v1;
421 B2.row(i) = -v2;
422 B3.row(i) = v3;
423 }
424}
425
426static inline void pre_calc(SLIMData &s)
427{
428 BLI_assert(s.valid);
429 if (!s.has_pre_calc) {
430 s.v_n = s.v_num;
431 s.f_n = s.f_num;
432
433 s.dim = 2;
434 Eigen::MatrixXd F1, F2, F3;
435 local_basis(s.V, s.F, F1, F2, F3);
436 compute_surface_gradient_matrix(s.V, s.F, F1, F2, s.Dx, s.Dy);
437
438 s.W_11.resize(s.f_n);
439 s.W_12.resize(s.f_n);
440 s.W_21.resize(s.f_n);
441 s.W_22.resize(s.f_n);
442
443 s.Dx.makeCompressed();
444 s.Dy.makeCompressed();
445 s.Dz.makeCompressed();
446 s.Ri.resize(s.f_n, s.dim * s.dim);
447 s.Ji.resize(s.f_n, s.dim * s.dim);
448 s.rhs.resize(s.dim * s.v_num);
449
450 /* Flattened weight matrix. */
451 s.WGL_M.resize(s.dim * s.dim * s.f_n);
452 for (int i = 0; i < s.dim * s.dim; i++) {
453 for (int j = 0; j < s.f_n; j++) {
454 s.WGL_M(i * s.f_n + j) = s.M(j);
455 }
456 }
457
458 s.first_solve = true;
459 s.has_pre_calc = true;
460 }
461}
462
463static inline void buildA(SLIMData &s, Eigen::SparseMatrix<double> &A)
464{
465 BLI_assert(s.valid);
466 /* Formula (35) in paper. */
467 std::vector<Eigen::Triplet<double>> IJV;
468
469 IJV.reserve(4 * (s.Dx.outerSize() + s.Dy.outerSize()));
470
471 /* A = [W11*Dx, W12*Dx;
472 * W11*Dy, W12*Dy;
473 * W21*Dx, W22*Dx;
474 * W21*Dy, W22*Dy]; */
475 for (int k = 0; k < s.Dx.outerSize(); ++k) {
476 for (Eigen::SparseMatrix<double>::InnerIterator it(s.Dx, k); it; ++it) {
477 int dx_r = it.row();
478 int dx_c = it.col();
479 double val = it.value();
480 double weight = s.weightPerFaceMap(dx_r);
481
482 IJV.emplace_back(dx_r, dx_c, weight * val * s.W_11(dx_r));
483 IJV.emplace_back(dx_r, s.v_n + dx_c, weight * val * s.W_12(dx_r));
484
485 IJV.emplace_back(2 * s.f_n + dx_r, dx_c, weight * val * s.W_21(dx_r));
486 IJV.emplace_back(2 * s.f_n + dx_r, s.v_n + dx_c, weight * val * s.W_22(dx_r));
487 }
488 }
489
490 for (int k = 0; k < s.Dy.outerSize(); ++k) {
491 for (Eigen::SparseMatrix<double>::InnerIterator it(s.Dy, k); it; ++it) {
492 int dy_r = it.row();
493 int dy_c = it.col();
494 double val = it.value();
495 double weight = s.weightPerFaceMap(dy_r);
496
497 IJV.emplace_back(s.f_n + dy_r, dy_c, weight * val * s.W_11(dy_r));
498 IJV.emplace_back(s.f_n + dy_r, s.v_n + dy_c, weight * val * s.W_12(dy_r));
499
500 IJV.emplace_back(3 * s.f_n + dy_r, dy_c, weight * val * s.W_21(dy_r));
501 IJV.emplace_back(3 * s.f_n + dy_r, s.v_n + dy_c, weight * val * s.W_22(dy_r));
502 }
503 }
504
505 A.setFromTriplets(IJV.begin(), IJV.end());
506}
507
508static inline void buildRhs(SLIMData &s, const Eigen::SparseMatrix<double> &At)
509{
510 BLI_assert(s.valid);
511
512 Eigen::VectorXd f_rhs(s.dim * s.dim * s.f_n);
513 f_rhs.setZero();
514
515 /* b = [W11*R11 + W12*R21; (formula (36))
516 * W11*R12 + W12*R22;
517 * W21*R11 + W22*R21;
518 * W21*R12 + W22*R22]; */
519 for (int i = 0; i < s.f_n; i++) {
520 f_rhs(i + 0 * s.f_n) = s.W_11(i) * s.Ri(i, 0) + s.W_12(i) * s.Ri(i, 1);
521 f_rhs(i + 1 * s.f_n) = s.W_11(i) * s.Ri(i, 2) + s.W_12(i) * s.Ri(i, 3);
522 f_rhs(i + 2 * s.f_n) = s.W_21(i) * s.Ri(i, 0) + s.W_22(i) * s.Ri(i, 1);
523 f_rhs(i + 3 * s.f_n) = s.W_21(i) * s.Ri(i, 2) + s.W_22(i) * s.Ri(i, 3);
524 }
525
526 Eigen::VectorXd uv_flat(s.dim * s.v_n);
527 for (int i = 0; i < s.dim; i++) {
528 for (int j = 0; j < s.v_n; j++) {
529 uv_flat(s.v_n * i + j) = s.V_o(j, i);
530 }
531 }
532
533 s.rhs = (At * s.WGL_M.asDiagonal() * f_rhs + s.proximal_p * uv_flat);
534}
535
536static inline void add_soft_constraints(SLIMData &s, Eigen::SparseMatrix<double> &L)
537{
538 BLI_assert(s.valid);
539 int v_n = s.v_num;
540 for (int d = 0; d < s.dim; d++) {
541 for (int i = 0; i < s.b.rows(); i++) {
542 int v_idx = s.b(i);
543 s.rhs(d * v_n + v_idx) += s.soft_const_p * s.bc(i, d); /* Right hand side. */
544 L.coeffRef(d * v_n + v_idx, d * v_n + v_idx) += s.soft_const_p; /* Diagonal of matrix. */
545 }
546 }
547}
548
549static inline void build_linear_system(SLIMData &s, Eigen::SparseMatrix<double> &L)
550{
551 BLI_assert(s.valid);
552 /* Formula (35) in paper. */
553 Eigen::SparseMatrix<double> A(s.dim * s.dim * s.f_n, s.dim * s.v_n);
554 buildA(s, A);
555
556 Eigen::SparseMatrix<double> At = A.transpose();
557 At.makeCompressed();
558
559 Eigen::SparseMatrix<double> id_m(At.rows(), At.rows());
560 id_m.setIdentity();
561
562 /* Add proximal penalty. */
563 L = At * s.WGL_M.asDiagonal() * A + s.proximal_p * id_m; /* Add also a proximal term. */
564 L.makeCompressed();
565
566 buildRhs(s, At);
567 Eigen::SparseMatrix<double> OldL = L;
569 L.makeCompressed();
570}
571
573 const Eigen::MatrixXd &Ji,
574 Eigen::VectorXd &areas,
575 Eigen::VectorXd &singularValues,
576 bool gatherSingularValues)
577{
578 BLI_assert(s.valid);
579 double energy = 0;
580
581 Eigen::Matrix<double, 2, 2> ji;
582 for (int i = 0; i < s.f_n; i++) {
583 ji(0, 0) = Ji(i, 0);
584 ji(0, 1) = Ji(i, 1);
585 ji(1, 0) = Ji(i, 2);
586 ji(1, 1) = Ji(i, 3);
587
588 using Mat2 = Eigen::Matrix<double, 2, 2>;
589 using Vec2 = Eigen::Matrix<double, 2, 1>;
590 Mat2 ri, ti, ui, vi;
591 Vec2 sing;
592 polar_svd(ji, ri, ti, ui, sing, vi);
593 double s1 = sing(0);
594 double s2 = sing(1);
595
596 switch (s.slim_energy) {
597 case SLIMData::ARAP: {
598 energy += areas(i) * (pow(s1 - 1, 2) + pow(s2 - 1, 2));
599 break;
600 }
602 energy += areas(i) * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2));
603
604 if (gatherSingularValues) {
605 singularValues(i) = s1;
606 singularValues(i + s.F.rows()) = s2;
607 }
608 break;
609 }
611 energy += areas(i) *
612 exp(s.exp_factor * (pow(s1, 2) + pow(s1, -2) + pow(s2, 2) + pow(s2, -2)));
613 break;
614 }
615 case SLIMData::LOG_ARAP: {
616 energy += areas(i) * (pow(log(s1), 2) + pow(log(s2), 2));
617 break;
618 }
619 case SLIMData::CONFORMAL: {
620 energy += areas(i) * ((pow(s1, 2) + pow(s2, 2)) / (2 * s1 * s2));
621 break;
622 }
624 energy += areas(i) * exp(s.exp_factor * ((pow(s1, 2) + pow(s2, 2)) / (2 * s1 * s2)));
625 break;
626 }
627 }
628 }
629
630 return energy;
631}
632
633static inline double compute_soft_const_energy(SLIMData &s, Eigen::MatrixXd &V_o)
634{
635 BLI_assert(s.valid);
636 double e = 0;
637 for (int i = 0; i < s.b.rows(); i++) {
638 e += s.soft_const_p * (s.bc.row(i) - V_o.row(s.b(i))).squaredNorm();
639 }
640 return e;
641}
642
643static inline double compute_energy(SLIMData &s,
644 Eigen::MatrixXd &V_new,
645 Eigen::VectorXd &singularValues,
646 bool gatherSingularValues)
647{
648 BLI_assert(s.valid);
649 compute_jacobians(s, V_new);
650 return compute_energy_with_jacobians(s, s.Ji, s.M, singularValues, gatherSingularValues) +
652}
653
654static inline double compute_energy(SLIMData &s, Eigen::MatrixXd &V_new)
655{
656 BLI_assert(s.valid);
657 Eigen::VectorXd temp;
658 return compute_energy(s, V_new, temp, false);
659}
660
661static inline double compute_energy(SLIMData &s,
662 Eigen::MatrixXd &V_new,
663 Eigen::VectorXd &singularValues)
664{
665 BLI_assert(s.valid);
666 return compute_energy(s, V_new, singularValues, true);
667}
668
669void slim_precompute(Eigen::MatrixXd &V,
670 Eigen::MatrixXi &F,
671 Eigen::MatrixXd &V_init,
672 SLIMData &data,
673 SLIMData::SLIM_ENERGY slim_energy,
674 Eigen::VectorXi &b,
675 Eigen::MatrixXd &bc,
676 double soft_p)
677{
678 BLI_assert(data.valid);
679 data.V = V;
680 data.F = F;
681 data.V_o = V_init;
682
683 data.v_num = V.rows();
684 data.f_num = F.rows();
685
686 data.slim_energy = slim_energy;
687
688 data.b = b;
689 data.bc = bc;
690 data.soft_const_p = soft_p;
691
692 data.proximal_p = 0.0001;
693
694 doublearea(V, F, data.M);
695 data.M /= 2.;
696 data.mesh_area = data.M.sum();
697
698 data.mesh_improvement_3d = false; /* Whether to use a jacobian derived from a real mesh or an
699 * abstract regular mesh (used for mesh improvement). */
700 data.exp_factor =
701 1.0; /* Param used only for exponential energies (e.g exponential symmetric dirichlet). */
702
703 assert(F.cols() == 3);
704
705 pre_calc(data);
706
707 data.energy = compute_energy(data, data.V_o) / data.mesh_area;
708}
709
710inline double computeGlobalScaleInvarianceFactor(Eigen::VectorXd &singularValues,
711 Eigen::VectorXd &areas)
712{
713 int nFaces = singularValues.rows() / 2;
714
715 Eigen::VectorXd areasChained(2 * nFaces);
716 areasChained << areas, areas;
717
718 /* Per face energy for face i with singvals si1 and si2 and area ai when scaling geometry by x is
719 *
720 * ai*(si1*x)^2 + ai*(si2*x)^2 + ai/(si1*x)^2 + ai/(si2*x)^2)
721 *
722 * The combined Energy of all faces is therefore
723 * (s1 and s2 are the sums over all ai*(si1^2) and ai*(si2^2) respectively. t1 and t2
724 * are the sums over all ai/(si1^2) and ai/(si2^2) respectively)
725 *
726 * s1*(x^2) + s2*(x^2) + t1/(x^2) + t2/(x^2)
727 *
728 * with a = (s1 + s2) and b = (t1 + t2) we get
729 *
730 * ax^2 + b/x^2
731 *
732 * it's derivative is
733 *
734 * 2ax - 2b/(x^3)
735 *
736 * and when we set it zero we get
737 *
738 * x^4 = b/a => x = sqrt(sqrt(b/a))
739 */
740
741 Eigen::VectorXd squaredSingularValues = singularValues.cwiseProduct(singularValues);
742 Eigen::VectorXd inverseSquaredSingularValues =
743 singularValues.cwiseProduct(singularValues).cwiseInverse();
744
745 Eigen::VectorXd weightedSquaredSingularValues = squaredSingularValues.cwiseProduct(areasChained);
746 Eigen::VectorXd weightedInverseSquaredSingularValues = inverseSquaredSingularValues.cwiseProduct(
747 areasChained);
748
749 double s1 = weightedSquaredSingularValues.head(nFaces).sum();
750 double s2 = weightedSquaredSingularValues.tail(nFaces).sum();
751
752 double t1 = weightedInverseSquaredSingularValues.head(nFaces).sum();
753 double t2 = weightedInverseSquaredSingularValues.tail(nFaces).sum();
754
755 double a = s1 + s2;
756 double b = t1 + t2;
757
758 double x = sqrt(sqrt(b / a));
759
760 return 1 / x;
761}
762
763static inline void solve_weighted_arap(SLIMData &s, Eigen::MatrixXd &uv)
764{
765 BLI_assert(s.valid);
766 using namespace Eigen;
767
768 Eigen::SparseMatrix<double> L;
770
771 /* Solve. */
772 Eigen::VectorXd Uc;
773 SimplicialLDLT<Eigen::SparseMatrix<double>> solver;
774 Uc = solver.compute(L).solve(s.rhs);
775
776 for (int i = 0; i < Uc.size(); i++) {
777 if (!std::isfinite(Uc(i))) {
778 throw SlimFailedException();
779 }
780 }
781
782 for (int i = 0; i < s.dim; i++) {
783 uv.col(i) = Uc.block(i * s.v_n, 0, s.v_n, 1);
784 }
785}
786
787Eigen::MatrixXd slim_solve(SLIMData &data, int iter_num)
788{
789 BLI_assert(data.valid);
790 Eigen::VectorXd singularValues;
791 bool are_pins_present = data.b.rows() > 0;
792
793 if (are_pins_present) {
794 singularValues.resize(data.F.rows() * 2);
795 data.energy = compute_energy(data, data.V_o, singularValues) / data.mesh_area;
796 }
797
798 for (int i = 0; i < iter_num; i++) {
799 Eigen::MatrixXd dest_res;
800 dest_res = data.V_o;
801
802 /* Solve Weighted Proxy. */
804 solve_weighted_arap(data, dest_res);
805
806 std::function<double(Eigen::MatrixXd &)> compute_energy_func = [&](Eigen::MatrixXd &aaa) {
807 return are_pins_present ? compute_energy(data, aaa, singularValues) :
808 compute_energy(data, aaa);
809 };
810
812 data.V_o,
813 dest_res,
814 compute_energy_func,
815 data.energy * data.mesh_area) /
816 data.mesh_area;
817
818 if (are_pins_present) {
819 data.globalScaleInvarianceFactor = computeGlobalScaleInvarianceFactor(singularValues,
820 data.M);
821 data.Dx /= data.globalScaleInvarianceFactor;
822 data.Dy /= data.globalScaleInvarianceFactor;
823 data.energy = compute_energy(data, data.V_o, singularValues) / data.mesh_area;
824 }
825 }
826
827 return data.V_o;
828}
829
830} // namespace slim
#define BLI_assert(a)
Definition BLI_assert.h:46
#define M_PI
#define U
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
#define A
#define assert(assertion)
#define log
#define sin
#define pow
#define exp
#define sqrt
#define T
#define F
#define R
#define L
#define G(x, y, z)
static double compute_soft_const_energy(SLIMData &s, Eigen::MatrixXd &V_o)
Definition slim.cpp:633
void slim_precompute(Eigen::MatrixXd &V, Eigen::MatrixXi &F, Eigen::MatrixXd &V_init, SLIMData &data, SLIMData::SLIM_ENERGY slim_energy, Eigen::VectorXi &b, Eigen::MatrixXd &bc, double soft_p)
Definition slim.cpp:669
static void compute_jacobians(SLIMData &s, const Eigen::MatrixXd &uv)
Definition slim.cpp:277
static void update_weights_and_closest_rotations(SLIMData &s, Eigen::MatrixXd &uv)
Definition slim.cpp:289
static void polar_svd(const Eigen::PlainObjectBase< DerivedA > &A, Eigen::PlainObjectBase< DerivedR > &R, Eigen::PlainObjectBase< DerivedT > &T, Eigen::PlainObjectBase< DerivedU > &U, Eigen::PlainObjectBase< DerivedS > &S, Eigen::PlainObjectBase< DerivedV > &V)
Definition slim.cpp:203
void doublearea(const Eigen::PlainObjectBase< DerivedV > &V, const Eigen::PlainObjectBase< DerivedF > &F, Eigen::PlainObjectBase< DeriveddblA > &dblA)
static void buildRhs(SLIMData &s, const Eigen::SparseMatrix< double > &At)
Definition slim.cpp:508
static void compute_surface_gradient_matrix(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const Eigen::MatrixXd &F1, const Eigen::MatrixXd &F2, Eigen::SparseMatrix< double > &D1, Eigen::SparseMatrix< double > &D2)
Definition slim.cpp:231
static double compute_energy_with_jacobians(SLIMData &s, const Eigen::MatrixXd &Ji, Eigen::VectorXd &areas, Eigen::VectorXd &singularValues, bool gatherSingularValues)
Definition slim.cpp:572
static void build_linear_system(SLIMData &s, Eigen::SparseMatrix< double > &L)
Definition slim.cpp:549
static void buildA(SLIMData &s, Eigen::SparseMatrix< double > &A)
Definition slim.cpp:463
static void pre_calc(SLIMData &s)
Definition slim.cpp:426
double computeGlobalScaleInvarianceFactor(Eigen::VectorXd &singularValues, Eigen::VectorXd &areas)
Definition slim.cpp:710
static void solve_weighted_arap(SLIMData &s, Eigen::MatrixXd &uv)
Definition slim.cpp:763
static void local_basis(const Eigen::PlainObjectBase< DerivedV > &V, const Eigen::PlainObjectBase< DerivedF > &F, Eigen::PlainObjectBase< DerivedV > &B1, Eigen::PlainObjectBase< DerivedV > &B2, Eigen::PlainObjectBase< DerivedV > &B3)
Definition slim.cpp:401
static void compute_weighted_jacobians(SLIMData &s, const Eigen::MatrixXd &uv)
Definition slim.cpp:248
static void add_soft_constraints(SLIMData &s, Eigen::SparseMatrix< double > &L)
Definition slim.cpp:536
Eigen::MatrixXd slim_solve(SLIMData &data, int iter_num)
Definition slim.cpp:787
double flip_avoiding_line_search(const Eigen::MatrixXi F, Eigen::MatrixXd &cur_v, Eigen::MatrixXd &dst_v, std::function< double(Eigen::MatrixXd &)> energy, double cur_energy)
static double compute_energy(SLIMData &s, Eigen::MatrixXd &V_new, Eigen::VectorXd &singularValues, bool gatherSingularValues)
Definition slim.cpp:643
static void compute_unweighted_jacobians(SLIMData &s, const Eigen::MatrixXd &uv)
Definition slim.cpp:266
static void grad(const Eigen::PlainObjectBase< DerivedV > &V, const Eigen::PlainObjectBase< DerivedF > &F, Eigen::SparseMatrix< typename DerivedV::Scalar > &G, bool uniform=false)
Definition slim.cpp:47
const btScalar eps
Definition poly34.cpp:11
bool withWeightedParameterization
Definition slim.h:63
Eigen::MatrixXi F
Definition slim.h:30
Eigen::MatrixXd bc
Definition slim.h:44
int f_num
Definition slim.h:76
Eigen::VectorXf weightPerFaceMap
Definition slim.h:65
Eigen::MatrixXd Ri
Definition slim.h:81
Eigen::VectorXd M
Definition slim.h:72
Eigen::VectorXi b
Definition slim.h:43
SLIM_ENERGY slim_energy
Definition slim.h:39
bool first_solve
Definition slim.h:93
bool has_pre_calc
Definition slim.h:94
Eigen::SparseMatrix< double > Dy
Definition slim.h:91
double proximal_p
Definition slim.h:77
Eigen::MatrixXd V
Definition slim.h:29
int v_num
Definition slim.h:75
@ SYMMETRIC_DIRICHLET
Definition slim.h:34
@ EXP_CONFORMAL
Definition slim.h:36
@ EXP_SYMMETRIC_DIRICHLET
Definition slim.h:37
Eigen::VectorXd WGL_M
Definition slim.h:79
double exp_factor
Definition slim.h:47
Eigen::VectorXd W_11
Definition slim.h:82
Eigen::SparseMatrix< double > Dx
Definition slim.h:91
Eigen::VectorXd W_22
Definition slim.h:86
Eigen::SparseMatrix< double > Dz
Definition slim.h:91
bool valid
Definition slim.h:26
Eigen::VectorXd W_21
Definition slim.h:85
Eigen::MatrixXd Ji
Definition slim.h:81
Eigen::MatrixXd V_o
Definition slim.h:56
Eigen::VectorXd rhs
Definition slim.h:80
Eigen::VectorXd W_12
Definition slim.h:83
double soft_const_p
Definition slim.h:45
i
Definition text_draw.cc:230
double Vec2[2]
CCL_NAMESPACE_BEGIN struct Window V