1 /*
2     field.cpp: Routines for averaging orientations and directions subject
3     to various symmetry conditions. Also contains the Optimizer class which
4     uses these routines to smooth fields hierarchically.
5 
6     This file is part of the implementation of
7 
8         Instant Field-Aligned Meshes
9         Wenzel Jakob, Daniele Panozzo, Marco Tarini, and Olga Sorkine-Hornung
10         In ACM Transactions on Graphics (Proc. SIGGRAPH Asia 2015)
11 
12     All rights reserved. Use of this source code is governed by a
13     BSD-style license that can be found in the LICENSE.txt file.
14 */
15 
16 #include "field.h"
17 #include "serializer.h"
18 
19 static const Float sqrt_3_over_4 = 0.866025403784439f;
20 static const uint32_t INVALID = (uint32_t) -1;
21 
rotate180(const Vector3f & q,const Vector3f &)22 Vector3f rotate180(const Vector3f &q, const Vector3f &/* unused */) {
23     return -q;
24 }
25 
rotate180_by(const Vector3f & q,const Vector3f &,int amount)26 Vector3f rotate180_by(const Vector3f &q, const Vector3f &/* unused */, int amount) {
27     return (amount & 1) ? Vector3f(-q) : q;
28 }
29 
rshift180(Vector2i shift,int amount)30 Vector2i rshift180(Vector2i shift, int amount) {
31     if (amount & 1)
32         shift = -shift;
33     return shift;
34 }
35 
rotate90(const Vector3f & q,const Vector3f & n)36 Vector3f rotate90(const Vector3f &q, const Vector3f &n) {
37     return n.cross(q);
38 }
39 
rotate90_by(const Vector3f & q,const Vector3f & n,int amount)40 Vector3f rotate90_by(const Vector3f &q, const Vector3f &n, int amount) {
41     return ((amount & 1) ? (n.cross(q)) : q) * (amount < 2 ? 1.0f : -1.0f);
42 }
43 
rshift90(Vector2i shift,int amount)44 Vector2i rshift90(Vector2i shift, int amount) {
45     if (amount & 1)
46         shift = Vector2i(-shift.y(), shift.x());
47     if (amount >= 2)
48         shift = -shift;
49     return shift;
50 }
51 
rotate60(const Vector3f & d,const Vector3f & n)52 Vector3f rotate60(const Vector3f &d, const Vector3f &n) {
53     return sqrt_3_over_4 * n.cross(d) + 0.5f*(d + n * n.dot(d));
54 }
55 
rshift60(Vector2i shift,int amount)56 Vector2i rshift60(Vector2i shift, int amount) {
57     for (int i=0; i<amount; ++i)
58         shift = Vector2i(-shift.y(), shift.x() + shift.y());
59     return shift;
60 }
61 
rotate60_by(const Vector3f & d,const Vector3f & n,int amount)62 Vector3f rotate60_by(const Vector3f &d, const Vector3f &n, int amount) {
63     switch (amount) {
64         case 0: return d;
65         case 1: return rotate60(d, n);
66         case 2: return -rotate60(d, -n);
67         case 3: return -d;
68         case 4: return -rotate60(d, n);
69         case 5: return rotate60(d, -n);
70     }
71     throw std::runtime_error("rotate60: invalid argument");
72 }
73 
rotate_vector_into_plane(Vector3f q,const Vector3f & source_normal,const Vector3f & target_normal)74 Vector3f rotate_vector_into_plane(Vector3f q, const Vector3f &source_normal, const Vector3f &target_normal) {
75     const Float cosTheta = source_normal.dot(target_normal);
76     if (cosTheta < 0.9999f) {
77         Vector3f axis = source_normal.cross(target_normal);
78         q = q * cosTheta + axis.cross(q) +
79              axis * (axis.dot(q) * (1.0f - cosTheta) / axis.dot(axis));
80     }
81     return q;
82 }
83 
middle_point(const Vector3f & p0,const Vector3f & n0,const Vector3f & p1,const Vector3f & n1)84 inline Vector3f middle_point(const Vector3f &p0, const Vector3f &n0, const Vector3f &p1, const Vector3f &n1) {
85     /* How was this derived?
86      *
87      * Minimize \|x-p0\|^2 + \|x-p1\|^2, where
88      * dot(n0, x) == dot(n0, p0)
89      * dot(n1, x) == dot(n1, p1)
90      *
91      * -> Lagrange multipliers, set derivative = 0
92      *  Use first 3 equalities to write x in terms of
93      *  lambda_1 and lambda_2. Substitute that into the last
94      *  two equations and solve for the lambdas. Finally,
95      *  add a small epsilon term to avoid issues when n1=n2.
96      */
97     Float n0p0 = n0.dot(p0), n0p1 = n0.dot(p1),
98           n1p0 = n1.dot(p0), n1p1 = n1.dot(p1),
99           n0n1 = n0.dot(n1),
100           denom = 1.0f / (1.0f - n0n1*n0n1 + 1e-4f),
101           lambda_0 = 2.0f*(n0p1 - n0p0 - n0n1*(n1p0 - n1p1))*denom,
102           lambda_1 = 2.0f*(n1p0 - n1p1 - n0n1*(n0p1 - n0p0))*denom;
103 
104     return 0.5f * (p0 + p1) - 0.25f * (n0 * lambda_0 + n1 * lambda_1);
105 }
106 
compat_orientation_intrinsic_2(const Vector3f & q0,const Vector3f & n0,const Vector3f & _q1,const Vector3f & n1)107 std::pair<Vector3f, Vector3f> compat_orientation_intrinsic_2(
108     const Vector3f &q0, const Vector3f &n0, const Vector3f &_q1, const Vector3f &n1) {
109     const Vector3f q1 = rotate_vector_into_plane(_q1, n1, n0);
110     return std::make_pair(q0, q1 * signum(q1.dot(q0)));
111 }
112 
compat_orientation_intrinsic_4(const Vector3f & q0,const Vector3f & n0,const Vector3f & _q1,const Vector3f & n1)113 std::pair<Vector3f, Vector3f> compat_orientation_intrinsic_4(
114     const Vector3f &q0, const Vector3f &n0, const Vector3f &_q1, const Vector3f &n1) {
115     const Vector3f q1 = rotate_vector_into_plane(_q1, n1, n0);
116     const Vector3f t1 = n0.cross(q1);
117     const Float dp0 = q1.dot(q0), dp1 = t1.dot(q0);
118 
119     if (std::abs(dp0) > std::abs(dp1))
120         return std::make_pair(q0, q1 * signum(dp0));
121     else
122         return std::make_pair(q0, t1 * signum(dp1));
123 }
124 
125 std::pair<Vector3f, Vector3f>
compat_orientation_intrinsic_6(const Vector3f & q0,const Vector3f & n0,const Vector3f & _q1,const Vector3f & n1)126 compat_orientation_intrinsic_6(const Vector3f &q0,  const Vector3f &n0,
127                                const Vector3f &_q1, const Vector3f &n1) {
128     const Vector3f q1 = rotate_vector_into_plane(_q1, n1, n0);
129     const Vector3f t1[3] = { rotate60(q1, -n0), q1, rotate60(q1, n0) };
130     const Float dp[3] = { t1[0].dot(q0), t1[1].dot(q0), t1[2].dot(q0) };
131     const Float abs_dp[3] = { std::abs(dp[0]), std::abs(dp[1]), std::abs(dp[2]) };
132 
133     if (abs_dp[0] >= abs_dp[1] && abs_dp[0] >= abs_dp[2])
134         return std::make_pair(q0, t1[0] * signum(dp[0]));
135     else if (abs_dp[1] >= abs_dp[0] && abs_dp[1] >= abs_dp[2])
136         return std::make_pair(q0, t1[1] * signum(dp[1]));
137     else
138         return std::make_pair(q0, t1[2] * signum(dp[2]));
139 }
140 
141 std::pair<int, int>
compat_orientation_intrinsic_index_2(const Vector3f & q0,const Vector3f & n0,const Vector3f & _q1,const Vector3f & n1)142 compat_orientation_intrinsic_index_2(const Vector3f &q0,  const Vector3f &n0,
143                                      const Vector3f &_q1, const Vector3f &n1) {
144     const Vector3f q1 = rotate_vector_into_plane(_q1, n1, n0);
145     return std::make_pair(0, q1.dot(q0) > 0 ? 0 : 1);
146 }
147 
148 std::pair<int, int>
compat_orientation_intrinsic_index_4(const Vector3f & q0,const Vector3f & n0,const Vector3f & _q1,const Vector3f & n1)149 compat_orientation_intrinsic_index_4(const Vector3f &q0,  const Vector3f &n0,
150                                 const Vector3f &_q1, const Vector3f &n1) {
151     const Vector3f q1 = rotate_vector_into_plane(_q1, n1, n0);
152     const Float dp0 = q1.dot(q0), dp1 = n0.cross(q1).dot(q0);
153 
154     if (std::abs(dp0) > std::abs(dp1))
155         return std::make_pair(0, dp0 > 0 ? 0 : 2);
156     else
157         return std::make_pair(0, dp1 > 0 ? 1 : 3);
158 }
159 
160 std::pair<int, int>
compat_orientation_intrinsic_index_6(const Vector3f & q0,const Vector3f & n0,const Vector3f & _q1,const Vector3f & n1)161 compat_orientation_intrinsic_index_6(const Vector3f &q0,  const Vector3f &n0,
162                                 const Vector3f &_q1, const Vector3f &n1) {
163     const Vector3f q1 = rotate_vector_into_plane(_q1, n1, n0);
164     const Vector3f t1[3] = { rotate60(q1, -n0), q1, rotate60(q1, n0) };
165     const Float dp[3] = { t1[0].dot(q0), t1[1].dot(q0), t1[2].dot(q0) };
166     const Float abs_dp[3] = { std::abs(dp[0]), std::abs(dp[1]), std::abs(dp[2]) };
167 
168     if (abs_dp[0] >= abs_dp[1] && abs_dp[0] >= abs_dp[2])
169         return std::make_pair(0, dp[0] > 0 ? 5 : 2);
170     else if (abs_dp[1] >= abs_dp[0] && abs_dp[1] >= abs_dp[2])
171         return std::make_pair(0, dp[1] > 0 ? 0 : 3);
172     else
173         return std::make_pair(0, dp[2] > 0 ? 1 : 4);
174 }
175 
176 std::pair<Vector3f, Vector3f>
compat_orientation_extrinsic_2(const Vector3f & q0,const Vector3f & n0,const Vector3f & q1,const Vector3f & n1)177 compat_orientation_extrinsic_2(const Vector3f &q0, const Vector3f &n0,
178                                const Vector3f &q1, const Vector3f &n1) {
179     return std::make_pair(q0, q1 * signum(q0.dot(q1)));
180 }
181 
182 std::pair<Vector3f, Vector3f>
compat_orientation_extrinsic_4(const Vector3f & q0,const Vector3f & n0,const Vector3f & q1,const Vector3f & n1)183 compat_orientation_extrinsic_4(const Vector3f &q0, const Vector3f &n0,
184                                const Vector3f &q1, const Vector3f &n1) {
185     const Vector3f A[2] = { q0, n0.cross(q0) };
186     const Vector3f B[2] = { q1, n1.cross(q1) };
187 
188     Float best_score = -std::numeric_limits<Float>::infinity();
189     int best_a = 0, best_b = 0;
190 
191     for (int i = 0; i < 2; ++i) {
192         for (int j = 0; j < 2; ++j) {
193             Float score = std::abs(A[i].dot(B[j]));
194             if (score > best_score) {
195                 best_a = i;
196                 best_b = j;
197                 best_score = score;
198             }
199         }
200     }
201 
202     const Float dp = A[best_a].dot(B[best_b]);
203     return std::make_pair(A[best_a], B[best_b] * signum(dp));
204 }
205 
206 std::pair<Vector3f, Vector3f>
compat_orientation_extrinsic_6(const Vector3f & q0,const Vector3f & n0,const Vector3f & q1,const Vector3f & n1)207 compat_orientation_extrinsic_6(const Vector3f &q0, const Vector3f &n0,
208                                const Vector3f &q1, const Vector3f &n1) {
209     const Vector3f A[3] = { rotate60(q0, -n0), q0, rotate60(q0, n0) };
210     const Vector3f B[3] = { rotate60(q1, -n1), q1, rotate60(q1, n1) };
211 
212     Float best_score = -std::numeric_limits<Float>::infinity();
213     int best_a = 0, best_b = 0;
214 
215     for (int i = 0; i < 3; ++i) {
216         for (int j = 0; j < 3; ++j) {
217             Float score = std::abs(A[i].dot(B[j]));
218             if (score > best_score) {
219                 best_a = i;
220                 best_b = j;
221                 best_score = score;
222             }
223         }
224     }
225 
226     const Float dp = A[best_a].dot(B[best_b]);
227     return std::make_pair(A[best_a], B[best_b] * signum(dp));
228 }
229 
230 std::pair<int, int>
compat_orientation_extrinsic_index_2(const Vector3f & q0,const Vector3f & n0,const Vector3f & q1,const Vector3f & n1)231 compat_orientation_extrinsic_index_2(const Vector3f &q0, const Vector3f &n0,
232                                      const Vector3f &q1, const Vector3f &n1) {
233     return std::make_pair(0, q0.dot(q1) < 0 ? 1 : 0);
234 }
235 
236 std::pair<int, int>
compat_orientation_extrinsic_index_4(const Vector3f & q0,const Vector3f & n0,const Vector3f & q1,const Vector3f & n1)237 compat_orientation_extrinsic_index_4(const Vector3f &q0, const Vector3f &n0,
238                                      const Vector3f &q1, const Vector3f &n1) {
239     const Vector3f A[2] = { q0, n0.cross(q0) };
240     const Vector3f B[2] = { q1, n1.cross(q1) };
241 
242     Float best_score = -std::numeric_limits<Float>::infinity();
243     int best_a = 0, best_b = 0;
244 
245     for (int i = 0; i < 2; ++i) {
246         for (int j = 0; j < 2; ++j) {
247             Float score = std::abs(A[i].dot(B[j]));
248             if (score > best_score) {
249                 best_a = i;
250                 best_b = j;
251                 best_score = score;
252             }
253         }
254     }
255 
256     if (A[best_a].dot(B[best_b]) < 0)
257         best_b += 2;
258 
259     return std::make_pair(best_a, best_b);
260 }
261 
262 std::pair<int, int>
compat_orientation_extrinsic_index_6(const Vector3f & q0,const Vector3f & n0,const Vector3f & q1,const Vector3f & n1)263 compat_orientation_extrinsic_index_6(const Vector3f &q0, const Vector3f &n0,
264                                      const Vector3f &q1, const Vector3f &n1) {
265     const Vector3f A[3] = { rotate60(q0, -n0), q0, rotate60(q0, n0) };
266     const Vector3f B[3] = { rotate60(q1, -n1), q1, rotate60(q1, n1) };
267 
268     Float best_score = -std::numeric_limits<Float>::infinity();
269     int best_a = 0, best_b = 0;
270 
271     for (int i = 0; i < 3; ++i) {
272         for (int j = 0; j < 3; ++j) {
273             Float score = std::abs(A[i].dot(B[j]));
274             if (score > best_score) {
275                 best_a = i;
276                 best_b = j;
277                 best_score = score;
278             }
279         }
280     }
281 
282     if (A[best_a].dot(B[best_b]) < 0)
283         best_b += 3;
284 
285     return std::make_pair(best_a, best_b);
286 }
287 
position_floor_4(const Vector3f & o,const Vector3f & q,const Vector3f & n,const Vector3f & p,Float scale,Float inv_scale)288 inline Vector3f position_floor_4(const Vector3f &o, const Vector3f &q,
289                                  const Vector3f &n, const Vector3f &p,
290                                  Float scale, Float inv_scale) {
291     Vector3f t = n.cross(q);
292     Vector3f d = p - o;
293     return o +
294         q * std::floor(q.dot(d) * inv_scale) * scale +
295         t * std::floor(t.dot(d) * inv_scale) * scale;
296 }
297 
position_floor_index_4(const Vector3f & o,const Vector3f & q,const Vector3f & n,const Vector3f & p,Float,Float inv_scale)298 inline Vector2i position_floor_index_4(const Vector3f &o, const Vector3f &q,
299                                        const Vector3f &n, const Vector3f &p,
300                                        Float /* unused */, Float inv_scale) {
301     Vector3f t = n.cross(q);
302     Vector3f d = p - o;
303     return Vector2i(
304         (int) std::floor(q.dot(d) * inv_scale),
305         (int) std::floor(t.dot(d) * inv_scale));
306 }
307 
position_round_4(const Vector3f & o,const Vector3f & q,const Vector3f & n,const Vector3f & p,Float scale,Float inv_scale)308 inline Vector3f position_round_4(const Vector3f &o, const Vector3f &q,
309                                  const Vector3f &n, const Vector3f &p,
310                                  Float scale, Float inv_scale) {
311     Vector3f t = n.cross(q);
312     Vector3f d = p - o;
313     return o +
314         q * std::round(q.dot(d) * inv_scale) * scale +
315         t * std::round(t.dot(d) * inv_scale) * scale;
316 }
317 
position_round_index_4(const Vector3f & o,const Vector3f & q,const Vector3f & n,const Vector3f & p,Float,Float inv_scale)318 inline Vector2i position_round_index_4(const Vector3f &o, const Vector3f &q,
319                                        const Vector3f &n, const Vector3f &p,
320                                        Float /* unused */, Float inv_scale) {
321     Vector3f t = n.cross(q);
322     Vector3f d = p - o;
323     return Vector2i(
324         (int) std::round(q.dot(d) * inv_scale),
325         (int) std::round(t.dot(d) * inv_scale));
326 }
327 
position_round_3(const Vector3f & o,const Vector3f & q,const Vector3f & n,const Vector3f & p,Float scale,Float inv_scale)328 inline Vector3f position_round_3(const Vector3f &o, const Vector3f &q,
329                                  const Vector3f &n, const Vector3f &p,
330                                  Float scale, Float inv_scale) {
331     Vector3f t = rotate60(q, n);
332     Vector3f d = p - o;
333 
334     Float dpq = q.dot(d), dpt = t.dot(d);
335     Float u = std::floor(( 4*dpq - 2*dpt) * (1.0f / 3.0f) * inv_scale);
336     Float v = std::floor((-2*dpq + 4*dpt) * (1.0f / 3.0f) * inv_scale);
337 
338     Float best_cost = std::numeric_limits<Float>::infinity();
339     int best_i = -1;
340 
341     for (int i=0; i<4; ++i) {
342         Vector3f ot = o + (q*(u+(i&1)) + t*(v+((i&2)>>1))) * scale;
343         Float cost = (ot-p).squaredNorm();
344         if (cost < best_cost) {
345             best_i = i;
346             best_cost = cost;
347         }
348     }
349 
350     return o + (q*(u+(best_i&1)) + t*(v+((best_i&2)>>1))) * scale;
351 }
352 
353 
position_round_index_3(const Vector3f & o,const Vector3f & q,const Vector3f & n,const Vector3f & p,Float scale,Float inv_scale)354 inline Vector2i position_round_index_3(const Vector3f &o, const Vector3f &q,
355                                        const Vector3f &n, const Vector3f &p,
356                                        Float scale, Float inv_scale) {
357     Vector3f t = rotate60(q, n);
358     Vector3f d = p - o;
359     Float dpq = q.dot(d), dpt = t.dot(d);
360     int u = (int) std::floor(( 4*dpq - 2*dpt) * (1.0f / 3.0f) * inv_scale);
361     int v = (int) std::floor((-2*dpq + 4*dpt) * (1.0f / 3.0f) * inv_scale);
362 
363     Float best_cost = std::numeric_limits<Float>::infinity();
364     int best_i = -1;
365 
366     for (int i=0; i<4; ++i) {
367         Vector3f ot = o + (q*(u+(i&1)) + t * (v + ((i&2)>>1))) * scale;
368         Float cost = (ot-p).squaredNorm();
369         if (cost < best_cost) {
370             best_i = i;
371             best_cost = cost;
372         }
373     }
374 
375     return Vector2i(
376         u+(best_i&1), v+((best_i&2)>>1)
377     );
378 }
379 
position_floor_3(const Vector3f & o,const Vector3f & q,const Vector3f & n,const Vector3f & p,Float scale,Float inv_scale)380 inline Vector3f position_floor_3(const Vector3f &o, const Vector3f &q,
381                                  const Vector3f &n, const Vector3f &p,
382                                  Float scale, Float inv_scale) {
383     Vector3f t = rotate60(q, n);
384     Vector3f d = p - o;
385     Float dpq = q.dot(d), dpt = t.dot(d);
386     Float u = std::floor(( 4*dpq - 2*dpt) * (1.0f / 3.0f) * inv_scale);
387     Float v = std::floor((-2*dpq + 4*dpt) * (1.0f / 3.0f) * inv_scale);
388 
389     return o + (q*u + t*v) * scale;
390 }
391 
position_floor_index_3(const Vector3f & o,const Vector3f & q,const Vector3f & n,const Vector3f & p,Float,Float inv_scale)392 inline Vector2i position_floor_index_3(const Vector3f &o, const Vector3f &q,
393                                        const Vector3f &n, const Vector3f &p,
394                                        Float /* scale */, Float inv_scale) {
395     Vector3f t = rotate60(q, n);
396     Vector3f d = p - o;
397     Float dpq = q.dot(d), dpt = t.dot(d);
398     int u = (int) std::floor(( 4*dpq - 2*dpt) * (1.0f / 3.0f) * inv_scale);
399     int v = (int) std::floor((-2*dpq + 4*dpt) * (1.0f / 3.0f) * inv_scale);
400 
401     return Vector2i(u, v);
402 }
403 
compat_position_intrinsic_4(const Vector3f & p0,const Vector3f & n0,const Vector3f & q0,const Vector3f & o0,const Vector3f & p1,const Vector3f & n1,const Vector3f & _q1,const Vector3f & _o1,Float scale,Float inv_scale)404 std::pair<Vector3f, Vector3f> compat_position_intrinsic_4(
405         const Vector3f &p0, const Vector3f &n0, const Vector3f &q0, const Vector3f &o0,
406         const Vector3f &p1, const Vector3f &n1, const Vector3f &_q1, const Vector3f &_o1,
407         Float scale, Float inv_scale) {
408     Float cosTheta = n1.dot(n0);
409     Vector3f q1 = _q1, o1 = _o1;
410 
411     if (cosTheta < 0.9999f) {
412         Vector3f axis = n1.cross(n0);
413         Float factor = (1.0f - cosTheta) / axis.dot(axis);
414         Vector3f middle = middle_point(p0, n0, p1, n1);
415         o1 -= middle;
416         q1 = q1 * cosTheta + axis.cross(q1) + axis * (axis.dot(q1) * factor);
417         o1 = o1 * cosTheta + axis.cross(o1) + axis * (axis.dot(o1) * factor) + middle;
418     }
419 
420     return std::make_pair(
421         o0, position_round_4(o1, q1, n0, o0, scale, inv_scale)
422     );
423 }
424 
compat_position_intrinsic_index_4(const Vector3f & p0,const Vector3f & n0,const Vector3f & q0,const Vector3f & o0,const Vector3f & p1,const Vector3f & n1,const Vector3f & _q1,const Vector3f & _o1,Float scale,Float inv_scale,Float * error)425 std::pair<Vector2i, Vector2i> compat_position_intrinsic_index_4(
426         const Vector3f &p0, const Vector3f &n0, const Vector3f &q0, const Vector3f &o0,
427         const Vector3f &p1, const Vector3f &n1, const Vector3f &_q1, const Vector3f &_o1,
428         Float scale, Float inv_scale, Float *error) {
429     Vector3f q1 = _q1, o1 = _o1;
430     Float cosTheta = n1.dot(n0);
431 
432     if (cosTheta < 0.9999f) {
433         Vector3f axis = n1.cross(n0);
434         Float factor = (1.0f - cosTheta) / axis.dot(axis);
435         Vector3f middle = middle_point(p0, n0, p1, n1);
436         o1 -= middle;
437         q1 = q1 * cosTheta + axis.cross(q1) + axis * (axis.dot(q1) * factor);
438         o1 = o1 * cosTheta + axis.cross(o1) + axis * (axis.dot(o1) * factor) + middle;
439     }
440 
441     if (error)
442         *error = (o0 - position_round_4(o1, q1, n0, o0, scale, inv_scale)).squaredNorm();
443 
444     return std::make_pair(
445         Vector2i::Zero(), position_round_index_4(o1, q1, n0, o0, scale, inv_scale)
446     );
447 }
448 
compat_position_intrinsic_3(const Vector3f & p0,const Vector3f & n0,const Vector3f & q0,const Vector3f & o0,const Vector3f & p1,const Vector3f & n1,const Vector3f & _q1,const Vector3f & _o1,Float scale,Float inv_scale)449 std::pair<Vector3f, Vector3f> compat_position_intrinsic_3(
450         const Vector3f &p0, const Vector3f &n0, const Vector3f &q0, const Vector3f &o0,
451         const Vector3f &p1, const Vector3f &n1, const Vector3f &_q1, const Vector3f &_o1,
452         Float scale, Float inv_scale) {
453     Float cosTheta = n1.dot(n0);
454     Vector3f q1 = _q1, o1 = _o1;
455 
456     if (cosTheta < 0.9999f) {
457         Vector3f axis = n1.cross(n0);
458         Float factor = (1.0f - cosTheta) / axis.dot(axis);
459         Vector3f middle = middle_point(p0, n0, p1, n1);
460         o1 -= middle;
461         q1 = q1 * cosTheta + axis.cross(q1) + axis * (axis.dot(q1) * factor);
462         o1 = o1 * cosTheta + axis.cross(o1) + axis * (axis.dot(o1) * factor) + middle;
463     }
464 
465     return std::make_pair(
466         o0, position_round_3(o1, q1, n0, o0, scale, inv_scale)
467     );
468 }
469 
compat_position_intrinsic_index_3(const Vector3f & p0,const Vector3f & n0,const Vector3f & q0,const Vector3f & o0,const Vector3f & p1,const Vector3f & n1,const Vector3f & _q1,const Vector3f & _o1,Float scale,Float inv_scale,Float * error)470 std::pair<Vector2i, Vector2i> compat_position_intrinsic_index_3(
471         const Vector3f &p0, const Vector3f &n0, const Vector3f &q0, const Vector3f &o0,
472         const Vector3f &p1, const Vector3f &n1, const Vector3f &_q1, const Vector3f &_o1,
473         Float scale, Float inv_scale, Float *error) {
474     Vector3f q1 = _q1, o1 = _o1;
475     Float cosTheta = n1.dot(n0);
476 
477     if (cosTheta < 0.9999f) {
478         Vector3f axis = n1.cross(n0);
479         Float factor = (1.0f - cosTheta) / axis.dot(axis);
480         Vector3f middle = middle_point(p0, n0, p1, n1);
481         o1 -= middle;
482         q1 = q1 * cosTheta + axis.cross(q1) + axis * (axis.dot(q1) * factor);
483         o1 = o1 * cosTheta + axis.cross(o1) + axis * (axis.dot(o1) * factor) + middle;
484     }
485 
486     if (error)
487         *error = (o0 - position_round_3(o1, q1, n0, o0, scale, inv_scale)).squaredNorm();
488 
489     return std::make_pair(
490         Vector2i::Zero(), position_round_index_3(o1, q1, n0, o0, scale, inv_scale)
491     );
492 }
493 
494 
compat_position_extrinsic_4(const Vector3f & p0,const Vector3f & n0,const Vector3f & q0,const Vector3f & o0,const Vector3f & p1,const Vector3f & n1,const Vector3f & q1,const Vector3f & o1,Float scale,Float inv_scale)495 inline std::pair<Vector3f, Vector3f> compat_position_extrinsic_4(
496         const Vector3f &p0, const Vector3f &n0, const Vector3f &q0, const Vector3f &o0,
497         const Vector3f &p1, const Vector3f &n1, const Vector3f &q1, const Vector3f &o1,
498         Float scale, Float inv_scale) {
499 
500     Vector3f t0 = n0.cross(q0), t1 = n1.cross(q1);
501     Vector3f middle = middle_point(p0, n0, p1, n1);
502     Vector3f o0p = position_floor_4(o0, q0, n0, middle, scale, inv_scale);
503     Vector3f o1p = position_floor_4(o1, q1, n1, middle, scale, inv_scale);
504 
505     Float best_cost = std::numeric_limits<Float>::infinity();
506     int best_i = -1, best_j = -1;
507 
508     for (int i=0; i<4; ++i) {
509         Vector3f o0t = o0p + (q0 * (i&1) + t0 * ((i&2) >> 1)) * scale;
510         for (int j=0; j<4; ++j) {
511             Vector3f o1t = o1p + (q1 * (j&1) + t1 * ((j&2) >> 1)) * scale;
512             Float cost = (o0t-o1t).squaredNorm();
513 
514             if (cost < best_cost) {
515                 best_i = i;
516                 best_j = j;
517                 best_cost = cost;
518             }
519         }
520     }
521 
522     return std::make_pair(
523         o0p + (q0 * (best_i & 1) + t0 * ((best_i & 2) >> 1)) * scale,
524         o1p + (q1 * (best_j & 1) + t1 * ((best_j & 2) >> 1)) * scale);
525 }
526 
compat_position_extrinsic_index_4(const Vector3f & p0,const Vector3f & n0,const Vector3f & q0,const Vector3f & o0,const Vector3f & p1,const Vector3f & n1,const Vector3f & q1,const Vector3f & o1,Float scale,Float inv_scale,Float * error)527 std::pair<Vector2i, Vector2i> compat_position_extrinsic_index_4(
528         const Vector3f &p0, const Vector3f &n0, const Vector3f &q0, const Vector3f &o0,
529         const Vector3f &p1, const Vector3f &n1, const Vector3f &q1, const Vector3f &o1,
530         Float scale, Float inv_scale, Float *error) {
531     Vector3f t0 = n0.cross(q0), t1 = n1.cross(q1);
532     Vector3f middle = middle_point(p0, n0, p1, n1);
533     Vector2i o0p = position_floor_index_4(o0, q0, n0, middle, scale, inv_scale);
534     Vector2i o1p = position_floor_index_4(o1, q1, n1, middle, scale, inv_scale);
535 
536     Float best_cost = std::numeric_limits<Float>::infinity();
537     int best_i = -1, best_j = -1;
538 
539     for (int i=0; i<4; ++i) {
540         Vector3f o0t = o0 + (q0 * ((i&1)+o0p[0]) + t0 * (((i&2) >> 1) + o0p[1])) * scale;
541         for (int j=0; j<4; ++j) {
542             Vector3f o1t = o1 + (q1 * ((j&1)+o1p[0]) + t1 * (((j&2) >> 1) + o1p[1])) * scale;
543             Float cost = (o0t-o1t).squaredNorm();
544 
545             if (cost < best_cost) {
546                 best_i = i;
547                 best_j = j;
548                 best_cost = cost;
549             }
550         }
551     }
552     if (error)
553         *error = best_cost;
554 
555     return std::make_pair(
556         Vector2i((best_i & 1) + o0p[0], ((best_i & 2) >> 1) + o0p[1]),
557         Vector2i((best_j & 1) + o1p[0], ((best_j & 2) >> 1) + o1p[1]));
558 }
559 
compat_position_extrinsic_3(const Vector3f & p0,const Vector3f & n0,const Vector3f & q0,const Vector3f & _o0,const Vector3f & p1,const Vector3f & n1,const Vector3f & q1,const Vector3f & _o1,Float scale,Float inv_scale)560 std::pair<Vector3f, Vector3f> compat_position_extrinsic_3(
561         const Vector3f &p0, const Vector3f &n0, const Vector3f &q0, const Vector3f &_o0,
562         const Vector3f &p1, const Vector3f &n1, const Vector3f &q1, const Vector3f &_o1,
563         Float scale, Float inv_scale) {
564     Vector3f middle = middle_point(p0, n0, p1, n1);
565     Vector3f o0 = position_floor_3(_o0, q0, n0, middle, scale, inv_scale);
566     Vector3f o1 = position_floor_3(_o1, q1, n1, middle, scale, inv_scale);
567 
568     Vector3f t0 = rotate60(q0, n0), t1 = rotate60(q1, n1);
569     Float best_cost = std::numeric_limits<Float>::infinity();
570     int best_i = -1, best_j = -1;
571     for (int i=0; i<4; ++i) {
572         Vector3f o0t = o0 + (q0*(i&1) + t0*((i&2)>>1)) * scale;
573         for (int j=0; j<4; ++j) {
574             Vector3f o1t = o1 + (q1*(j&1) + t1*((j&2)>>1)) * scale;
575             Float cost = (o0t-o1t).squaredNorm();
576 
577             if (cost < best_cost) {
578                 best_i = i;
579                 best_j = j;
580                 best_cost = cost;
581             }
582         }
583     }
584 
585     return std::make_pair(
586         o0 + (q0*(best_i&1) + t0*((best_i&2)>>1)) * scale,
587         o1 + (q1*(best_j&1) + t1*((best_j&2)>>1)) * scale
588     );
589 }
590 
compat_position_extrinsic_index_3(const Vector3f & p0,const Vector3f & n0,const Vector3f & q0,const Vector3f & o0,const Vector3f & p1,const Vector3f & n1,const Vector3f & q1,const Vector3f & o1,Float scale,Float inv_scale,Float * error)591 std::pair<Vector2i, Vector2i> compat_position_extrinsic_index_3(
592         const Vector3f &p0, const Vector3f &n0, const Vector3f &q0, const Vector3f &o0,
593         const Vector3f &p1, const Vector3f &n1, const Vector3f &q1, const Vector3f &o1,
594         Float scale, Float inv_scale, Float *error) {
595     Vector3f t0 = rotate60(q0, n0), t1 = rotate60(q1, n1);
596     Vector3f middle = middle_point(p0, n0, p1, n1);
597     Vector2i o0i = position_floor_index_3(o0, q0, n0, middle, scale, inv_scale);
598     Vector2i o1i = position_floor_index_3(o1, q1, n1, middle, scale, inv_scale);
599 
600     Float best_cost = std::numeric_limits<Float>::infinity();
601     int best_i = -1, best_j = -1;
602     for (int i=0; i<4; ++i) {
603         Vector3f o0t = o0 + (q0*(o0i.x() + (i&1)) + t0*(o0i.y() + ((i&2)>>1))) * scale;
604         for (int j=0; j<4; ++j) {
605             Vector3f o1t = o1 + (q1*(o1i.x() + (j&1)) + t1*(o1i.y() + ((j&2)>>1))) * scale;
606             Float cost = (o0t-o1t).squaredNorm();
607 
608             if (cost < best_cost) {
609                 best_i = i;
610                 best_j = j;
611                 best_cost = cost;
612             }
613         }
614     }
615     if (error)
616         *error = best_cost;
617 
618     return std::make_pair(
619         Vector2i(o0i.x()+(best_i&1), o0i.y()+((best_i&2)>>1)),
620         Vector2i(o1i.x()+(best_j&1), o1i.y()+((best_j&2)>>1)));
621 }
622 
623 template <typename Compat, typename Rotate>
624 static inline Float
optimize_orientations_impl(MultiResolutionHierarchy & mRes,int level,Compat compat,Rotate rotate,const std::function<void (uint32_t)> & progress)625 optimize_orientations_impl(MultiResolutionHierarchy &mRes, int level,
626                            Compat compat, Rotate rotate,
627                            const std::function<void(uint32_t)> &progress) {
628     const std::vector<std::vector<uint32_t>> &phases = mRes.phases(level);
629     const AdjacencyMatrix &adj = mRes.adj(level);
630     const MatrixXf &N = mRes.N(level);
631     const MatrixXf &CQ = mRes.CQ(level);
632     const VectorXf &CQw = mRes.CQw(level);
633     MatrixXf &Q = mRes.Q(level);
634     const std::vector<uint32_t> *phase = nullptr;
635 
636     auto solve_normal = [&](const tbb::blocked_range<uint32_t> &range) {
637         for (uint32_t phaseIdx = range.begin(); phaseIdx<range.end(); ++phaseIdx) {
638             const uint32_t i = (*phase)[phaseIdx];
639             const Vector3f n_i = N.col(i);
640             Float weight_sum = 0.0f;
641             Vector3f sum = Q.col(i);
642             for (Link *link = adj[i]; link != adj[i+1]; ++link) {
643                 const uint32_t j = link->id;
644                 const Float weight = link->weight;
645                 if (weight == 0)
646                     continue;
647                 const Vector3f n_j = N.col(j);
648                 Vector3f q_j = Q.col(j);
649                 std::pair<Vector3f, Vector3f> value = compat(sum, n_i, q_j, n_j);
650                 sum = value.first * weight_sum + value.second * weight;
651                 sum -= n_i*n_i.dot(sum);
652                 weight_sum += weight;
653 
654                 Float norm = sum.norm();
655                 if (norm > RCPOVERFLOW)
656                     sum /= norm;
657             }
658 
659             if (CQw.size() > 0) {
660                 Float cw = CQw[i];
661                 if (cw != 0) {
662                     std::pair<Vector3f, Vector3f> value = compat(sum, n_i, CQ.col(i), n_i);
663                     sum = value.first * (1 - cw) + value.second * cw;
664                     sum -= n_i*n_i.dot(sum);
665 
666                     Float norm = sum.norm();
667                     if (norm > RCPOVERFLOW)
668                         sum /= norm;
669                 }
670             }
671 
672             if (weight_sum > 0)
673                 Q.col(i) = sum;
674         }
675     };
676 
677     auto solve_frozen = [&](const tbb::blocked_range<uint32_t> &range) {
678         for (uint32_t phaseIdx = range.begin(); phaseIdx<range.end(); ++phaseIdx) {
679             const uint32_t i = (*phase)[phaseIdx];
680             const Vector3f n_i = N.col(i);
681             Float weight_sum = 0.0f;
682             Vector3f sum = Vector3f::Zero();
683 
684             for (Link *link = adj[i]; link != adj[i+1]; ++link) {
685                 const uint32_t j = link->id;
686                 const Float weight = link->weight;
687                 if (weight == 0)
688                     continue;
689                 const Vector3f n_j = N.col(j);
690                 Vector3f q_j = Q.col(j);
691 
692                 Vector3f temp = rotate(q_j, n_j, link->ivar[1].rot);
693                 sum += weight * rotate(temp, -n_i, link->ivar[0].rot);
694                 weight_sum += weight;
695             }
696 
697             sum -= n_i*n_i.dot(sum);
698             Float norm = sum.norm();
699             if (norm > RCPOVERFLOW)
700                 sum /= norm;
701 
702             if (weight_sum > 0)
703                 Q.col(i) = sum;
704         }
705     };
706 
707     Float error = 0.0f;
708     for (const std::vector<uint32_t> &phase_ : phases) {
709         tbb::blocked_range<uint32_t> range(0u, (uint32_t)phase_.size(), GRAIN_SIZE);
710         phase = &phase_;
711         if (mRes.frozenQ())
712             tbb::parallel_for(range, solve_frozen);
713         else
714             tbb::parallel_for(range, solve_normal);
715         progress(phase_.size());
716     }
717 
718     return error;
719 }
720 
721 template <typename Functor>
error_orientations_impl(const MultiResolutionHierarchy & mRes,int level,Functor functor)722 static inline Float error_orientations_impl(const MultiResolutionHierarchy &mRes,
723                                             int level, Functor functor) {
724     const AdjacencyMatrix &adj = mRes.adj(level);
725     const MatrixXf &N = mRes.N(level);
726     const MatrixXf &Q = mRes.Q(level);
727 
728     auto map = [&](const tbb::blocked_range<uint32_t> &range, Float error) -> Float {
729         for (uint32_t i = range.begin(); i<range.end(); ++i) {
730             Vector3f q_i = Q.col(i).normalized(), n_i = N.col(i);
731             for (Link *link = adj[i]; link != adj[i+1]; ++link) {
732                 const uint32_t j = link->id;
733                 Vector3f q_j = Q.col(j).normalized(), n_j = N.col(j);
734                 std::pair<Vector3f, Vector3f> value =
735                     functor(q_i.normalized(), n_i, q_j.normalized(), n_j);
736                 Float angle = fast_acos(std::min((Float) 1, value.first.dot(value.second))) * 180 / M_PI;
737                 error += angle*angle;
738             }
739         }
740         return error;
741     };
742 
743     auto reduce = [](Float error1, Float error2) -> Float {
744         return error1 + error2;
745     };
746 
747     return tbb::parallel_reduce(
748         tbb::blocked_range<uint32_t>(0, mRes.size(level), GRAIN_SIZE), 0.0,
749         map, reduce
750     ) / (Float) (adj[mRes.size(level)] - adj[0]);
751 }
752 
optimize_orientations(MultiResolutionHierarchy & mRes,int level,bool extrinsic,int rosy,const std::function<void (uint32_t)> & progress)753 Float optimize_orientations(MultiResolutionHierarchy &mRes, int level,
754                             bool extrinsic, int rosy,
755                             const std::function<void(uint32_t)> &progress) {
756     if (rosy == 2) {
757         if (extrinsic)
758             return optimize_orientations_impl(mRes, level, compat_orientation_extrinsic_2, rotate180_by, progress);
759         else
760             return optimize_orientations_impl(mRes, level, compat_orientation_intrinsic_2, rotate180_by, progress);
761     } else if (rosy == 4) {
762         if (extrinsic)
763             return optimize_orientations_impl(mRes, level, compat_orientation_extrinsic_4, rotate90_by, progress);
764         else
765             return optimize_orientations_impl(mRes, level, compat_orientation_intrinsic_4, rotate90_by, progress);
766     } else if (rosy == 6) {
767         if (extrinsic)
768             return optimize_orientations_impl(mRes, level, compat_orientation_extrinsic_6, rotate60_by, progress);
769         else
770             return optimize_orientations_impl(mRes, level, compat_orientation_intrinsic_6, rotate60_by, progress);
771     } else {
772         throw std::runtime_error("Invalid rotation symmetry type " + std::to_string(rosy) + "!");
773     }
774 }
775 
error_orientations(MultiResolutionHierarchy & mRes,int level,bool extrinsic,int rosy)776 Float error_orientations(MultiResolutionHierarchy &mRes, int level,
777                          bool extrinsic, int rosy) {
778     if (rosy == 2) {
779         if (extrinsic)
780             return error_orientations_impl(mRes, level, compat_orientation_extrinsic_2);
781         else
782             return error_orientations_impl(mRes, level, compat_orientation_intrinsic_2);
783     } else if (rosy == 4) {
784         if (extrinsic)
785             return error_orientations_impl(mRes, level, compat_orientation_extrinsic_4);
786         else
787             return error_orientations_impl(mRes, level, compat_orientation_intrinsic_4);
788     } else if (rosy == 6) {
789         if (extrinsic)
790             return error_orientations_impl(mRes, level, compat_orientation_extrinsic_6);
791         else
792             return error_orientations_impl(mRes, level, compat_orientation_intrinsic_6);
793     } else {
794         throw std::runtime_error("Invalid rotation symmetry type " + std::to_string(rosy) + "!");
795     }
796 }
797 
798 template <typename Functor>
799 static inline void
freeze_ivars_orientations_impl(MultiResolutionHierarchy & mRes,int level,Functor functor)800 freeze_ivars_orientations_impl(MultiResolutionHierarchy &mRes, int level,
801                               Functor functor) {
802     const AdjacencyMatrix &adj = mRes.adj(level);
803     const MatrixXf &N = mRes.N(level);
804     const MatrixXf &Q = mRes.Q(level);
805 
806     auto map = [&](const tbb::blocked_range<uint32_t> &range) {
807         for (uint32_t i = range.begin(); i<range.end(); ++i) {
808             const Vector3f &q_i = Q.col(i), &n_i = N.col(i);
809             for (Link *link = adj[i]; link != adj[i+1]; ++link) {
810                 const uint32_t j = link->id;
811                 const Vector3f &q_j = Q.col(j), &n_j = N.col(j);
812                 std::pair<int, int> value =
813                     functor(q_i.normalized(), n_i, q_j.normalized(), n_j);
814                 link->ivar[0].rot = value.first;
815                 link->ivar[1].rot = value.second;
816             }
817         }
818     };
819 
820     tbb::parallel_for(
821         tbb::blocked_range<uint32_t>(0, mRes.size(level), GRAIN_SIZE), map);
822     mRes.setFrozenQ(true);
823 }
824 
freeze_ivars_orientations(MultiResolutionHierarchy & mRes,int level,bool extrinsic,int rosy)825 void freeze_ivars_orientations(MultiResolutionHierarchy &mRes, int level,
826                                bool extrinsic, int rosy) {
827     if (rosy != 4) /// only rosy=4 for now.
828         return;
829     if (rosy == 2) {
830         if (extrinsic)
831             freeze_ivars_orientations_impl(mRes, level, compat_orientation_extrinsic_index_2);
832         else
833             freeze_ivars_orientations_impl(mRes, level, compat_orientation_intrinsic_index_2);
834     } else if (rosy == 4) {
835         if (extrinsic)
836             freeze_ivars_orientations_impl(mRes, level, compat_orientation_extrinsic_index_4);
837         else
838             freeze_ivars_orientations_impl(mRes, level, compat_orientation_intrinsic_index_4);
839     } else if (rosy == 6) {
840         if (extrinsic)
841             freeze_ivars_orientations_impl(mRes, level, compat_orientation_extrinsic_index_6);
842         else
843             freeze_ivars_orientations_impl(mRes, level, compat_orientation_intrinsic_index_6);
844     } else {
845         throw std::runtime_error("Invalid rotation symmetry type " + std::to_string(rosy) + "!");
846     }
847 }
848 
compute_orientation_singularities_impl(const MultiResolutionHierarchy & mRes,std::map<uint32_t,uint32_t> & sing,Functor functor)849 template <int rosy, typename Functor> inline static void compute_orientation_singularities_impl(
850         const MultiResolutionHierarchy &mRes, std::map<uint32_t, uint32_t> &sing, Functor functor) {
851     const MatrixXf &N = mRes.N(), &Q = mRes.Q();
852     const MatrixXu &F = mRes.F();
853     tbb::spin_mutex mutex;
854     sing.clear();
855 
856     tbb::parallel_for(
857         tbb::blocked_range<uint32_t>(0u, (uint32_t) F.cols(), GRAIN_SIZE),
858         [&](const tbb::blocked_range<uint32_t> &range) {
859             for (uint32_t f = range.begin(); f < range.end(); ++f) {
860                 int index = 0;
861                 for (int k = 0; k < 3; ++k) {
862                     int i = F(k, f), j = F(k == 2 ? 0 : (k + 1), f);
863                     std::pair<int, int> value = functor(Q.col(i), N.col(i), Q.col(j), N.col(j));
864                     index += value.second - value.first;
865                 }
866                 index = modulo(index, rosy);
867                 if (index == 1 || index == rosy-1) {
868                     tbb::spin_mutex::scoped_lock lock(mutex);
869                     sing[f] = (uint32_t) index;
870                 }
871             }
872         }
873     );
874 }
875 
compute_orientation_singularities(const MultiResolutionHierarchy & mRes,std::map<uint32_t,uint32_t> & sing,bool extrinsic,int rosy)876 void compute_orientation_singularities(const MultiResolutionHierarchy &mRes, std::map<uint32_t, uint32_t> &sing, bool extrinsic, int rosy) {
877     if (rosy == 2) {
878         if (extrinsic)
879             compute_orientation_singularities_impl<2>(mRes, sing, compat_orientation_extrinsic_index_2);
880         else
881             compute_orientation_singularities_impl<2>(mRes, sing, compat_orientation_intrinsic_index_2);
882     } else if (rosy == 4) {
883         if (extrinsic)
884             compute_orientation_singularities_impl<4>(mRes, sing, compat_orientation_extrinsic_index_4);
885         else
886             compute_orientation_singularities_impl<4>(mRes, sing, compat_orientation_intrinsic_index_4);
887     } else if (rosy == 6) {
888         if (extrinsic)
889             compute_orientation_singularities_impl<6>(mRes, sing, compat_orientation_extrinsic_index_6);
890         else
891             compute_orientation_singularities_impl<6>(mRes, sing, compat_orientation_intrinsic_index_6);
892     } else {
893         throw std::runtime_error("Unknown rotational symmetry!");
894     }
895 }
896 
optimize_positions_impl(MultiResolutionHierarchy & mRes,int level,CompatFunctor compat_functor,RoundFunctor round_functor,const std::function<void (uint32_t)> & progress)897 template <typename CompatFunctor, typename RoundFunctor> static inline Float optimize_positions_impl(
898         MultiResolutionHierarchy &mRes, int level, CompatFunctor compat_functor, RoundFunctor round_functor,
899         const std::function<void(uint32_t)> &progress) {
900     const std::vector<std::vector<uint32_t>> &phases = mRes.phases(level);
901     const AdjacencyMatrix &adj = mRes.adj(level);
902     const MatrixXf &N = mRes.N(level), &Q = mRes.Q(level), &V = mRes.V(level);
903     const Float scale = mRes.scale(), inv_scale = 1.0f / scale;
904     const std::vector<uint32_t> *phase = nullptr;
905     const MatrixXf &CQ = mRes.CQ(level);
906     const MatrixXf &CO = mRes.CO(level);
907     const VectorXf &COw = mRes.COw(level);
908     MatrixXf &O = mRes.O(level);
909 
910     auto solve_normal = [&](const tbb::blocked_range<uint32_t> &range) {
911         for (uint32_t phaseIdx = range.begin(); phaseIdx<range.end(); ++phaseIdx) {
912             const uint32_t i = (*phase)[phaseIdx];
913             const Vector3f n_i = N.col(i), v_i = V.col(i);
914             Vector3f q_i = Q.col(i);
915 
916             Vector3f sum = O.col(i);
917             Float weight_sum = 0.0f;
918 
919             #if 1
920                 q_i.normalize();
921             #endif
922 
923             for (Link *link = adj[i]; link != adj[i+1]; ++link) {
924                 const uint32_t j = link->id;
925                 const Float weight = link->weight;
926                 if (weight == 0)
927                     continue;
928 
929                 const Vector3f n_j = N.col(j), v_j = V.col(j);
930                 Vector3f q_j = Q.col(j), o_j = O.col(j);
931 
932                 #if 1
933                     q_j.normalize();
934                 #endif
935 
936                 std::pair<Vector3f, Vector3f> value = compat_functor(
937                     v_i, n_i, q_i, sum, v_j, n_j, q_j, o_j, scale, inv_scale);
938 
939                 sum = value.first*weight_sum + value.second*weight;
940                 weight_sum += weight;
941                 if (weight_sum > RCPOVERFLOW)
942                     sum /= weight_sum;
943                 sum -= n_i.dot(sum - v_i)*n_i;
944             }
945 
946             if (COw.size() > 0) {
947                 Float cw = COw[i];
948                 if (cw != 0) {
949                     Vector3f co = CO.col(i), cq = CQ.col(i);
950                     Vector3f d = co - sum;
951                     d -= cq.dot(d)*cq;
952                     sum += cw * d;
953                     sum -= n_i.dot(sum - v_i)*n_i;
954                 }
955             }
956 
957             if (weight_sum > 0)
958                 O.col(i) = round_functor(sum, q_i, n_i, v_i, scale, inv_scale);
959         }
960     };
961 
962     auto solve_frozen = [&](const tbb::blocked_range<uint32_t> &range) {
963         for (uint32_t phaseIdx = range.begin(); phaseIdx<range.end(); ++phaseIdx) {
964             const uint32_t i = (*phase)[phaseIdx];
965             const Vector3f n_i = N.col(i), v_i = V.col(i);
966             Vector3f q_i = Q.col(i);
967 
968             Vector3f sum = Vector3f::Zero();
969             Float weight_sum = 0.0f;
970             #if 1
971                 q_i.normalize();
972             #endif
973             const Vector3f t_i = n_i.cross(q_i);
974 
975             for (Link *link = adj[i]; link != adj[i+1]; ++link) {
976                 const uint32_t j = link->id;
977                 const Float weight = link->weight;
978                 if (weight == 0)
979                     continue;
980 
981                 const Vector3f n_j = N.col(j);
982                 Vector3f q_j = Q.col(j), o_j = O.col(j);
983 
984                 #if 1
985                     q_j.normalize();
986                 #endif
987                 const Vector3f t_j = n_j.cross(q_j);
988 
989                 sum += o_j + scale * (
990                       q_j * link->ivar[1].translate_u
991                     + t_j * link->ivar[1].translate_v
992                     - q_i * link->ivar[0].translate_u
993                     - t_i * link->ivar[0].translate_v);
994 
995                 weight_sum += weight;
996             }
997             sum /= weight_sum;
998             sum -= n_i.dot(sum - v_i)*n_i;
999 
1000             if (weight_sum > 0)
1001                 O.col(i) = sum;
1002         }
1003     };
1004 
1005     Float error = 0.0f;
1006     for (const std::vector<uint32_t> &phase_ : phases) {
1007         tbb::blocked_range<uint32_t> range(0u, (uint32_t)phase_.size(), GRAIN_SIZE);
1008         phase = &phase_;
1009         if (mRes.frozenO())
1010             tbb::parallel_for(range, solve_frozen);
1011         else
1012             tbb::parallel_for(range, solve_normal);
1013         progress(phase_.size());
1014     }
1015 
1016     return error;
1017 }
1018 
1019 template <typename Functor>
error_positions_impl(const MultiResolutionHierarchy & mRes,int level,Functor functor)1020 static inline Float error_positions_impl(const MultiResolutionHierarchy &mRes,
1021                                          int level, Functor functor) {
1022     const AdjacencyMatrix &adj = mRes.adj(level);
1023     const MatrixXf &N = mRes.N(level), &Q = mRes.Q(level);
1024     const MatrixXf &O = mRes.O(level), &V = mRes.V(level);
1025     const Float scale = mRes.scale(), inv_scale = 1.0f / scale;
1026 
1027     auto map = [&](const tbb::blocked_range<uint32_t> &range, Float error) -> Float {
1028         for (uint32_t i = range.begin(); i<range.end(); ++i) {
1029             const Vector3f &n_i = N.col(i), &v_i = V.col(i), &o_i = O.col(i);
1030             Vector3f q_i = Q.col(i);
1031             #if 1
1032                 q_i.normalize();
1033             #endif
1034             for (Link *link = adj[i]; link != adj[i+1]; ++link) {
1035                 const uint32_t j = link->id;
1036                 const Vector3f &n_j = N.col(j), &v_j = V.col(j), &o_j = O.col(j);
1037                 Vector3f q_j = Q.col(j);
1038 
1039                 #if 1
1040                     q_j.normalize();
1041                 #endif
1042 
1043                 std::pair<Vector3f, Vector3f> value = functor(
1044                     v_i, n_i, q_i, o_i, v_j, n_j, q_j, o_j, scale, inv_scale);
1045 
1046                 error += (value.first-value.second).cast<double>().squaredNorm();
1047             }
1048         }
1049         return error;
1050     };
1051 
1052     auto reduce = [&](double error1, double error2) -> double {
1053         return error1 + error2;
1054     };
1055 
1056     double total = tbb::parallel_reduce(
1057         tbb::blocked_range<uint32_t>(0, mRes.size(level), GRAIN_SIZE), 0.0,
1058         map, reduce
1059     );
1060     return total / (double) (adj[mRes.size(level)] - adj[0]);
1061 }
1062 
optimize_positions(MultiResolutionHierarchy & mRes,int level,bool extrinsic,int posy,const std::function<void (uint32_t)> & progress)1063 Float optimize_positions(MultiResolutionHierarchy &mRes, int level,
1064                          bool extrinsic, int posy,
1065                          const std::function<void(uint32_t)> &progress) {
1066     if (posy == 3) {
1067         if (extrinsic)
1068             return optimize_positions_impl(mRes, level, compat_position_extrinsic_3, position_round_3, progress);
1069         else
1070             return optimize_positions_impl(mRes, level, compat_position_intrinsic_3, position_round_3, progress);
1071     } else if (posy == 4) {
1072         if (extrinsic)
1073             return optimize_positions_impl(mRes, level, compat_position_extrinsic_4, position_round_4, progress);
1074         else
1075             return optimize_positions_impl(mRes, level, compat_position_intrinsic_4, position_round_4, progress);
1076     } else {
1077         throw std::runtime_error("Invalid position symmetry type " + std::to_string(posy) + "!");
1078     }
1079 }
1080 
error_positions(MultiResolutionHierarchy & mRes,int level,bool extrinsic,int posy)1081 Float error_positions(MultiResolutionHierarchy &mRes, int level, bool extrinsic,
1082                       int posy) {
1083     if (posy == 3) {
1084         if (extrinsic)
1085             return error_positions_impl(mRes, level, compat_position_extrinsic_3);
1086         else
1087             return error_positions_impl(mRes, level, compat_position_intrinsic_3);
1088     } else if (posy == 4) {
1089         if (extrinsic)
1090             return error_positions_impl(mRes, level, compat_position_extrinsic_4);
1091         else
1092             return error_positions_impl(mRes, level, compat_position_intrinsic_4);
1093     } else {
1094         throw std::runtime_error("Invalid position symmetry type " + std::to_string(posy) + "!");
1095     }
1096 }
1097 
1098 template <int rosy, bool extrinsic, typename RotateFunctorRoSy,
1099           typename RotateShiftFunctor,
1100           typename CompatPositionIndex>
compute_position_singularities(const MultiResolutionHierarchy & mRes,const std::map<uint32_t,uint32_t> & orient_sing,std::map<uint32_t,Vector2i> & pos_sing,RotateFunctorRoSy rotateFunctor_rosy,RotateShiftFunctor rshift,CompatPositionIndex compatPositionIndex)1101 void compute_position_singularities(
1102     const MultiResolutionHierarchy &mRes,
1103     const std::map<uint32_t, uint32_t> &orient_sing,
1104     std::map<uint32_t, Vector2i> &pos_sing,
1105     RotateFunctorRoSy rotateFunctor_rosy,
1106     RotateShiftFunctor rshift, CompatPositionIndex compatPositionIndex) {
1107     const MatrixXf &V = mRes.V(), &N = mRes.N(), &Q = mRes.Q(), &O = mRes.O();
1108     const MatrixXu &F = mRes.F();
1109     tbb::spin_mutex mutex;
1110     pos_sing.clear();
1111 
1112     const Float scale = mRes.scale(), inv_scale = 1.0f / scale;
1113 
1114     tbb::parallel_for(
1115         tbb::blocked_range<uint32_t>(0u, (uint32_t) F.cols(), GRAIN_SIZE),
1116         [&](const tbb::blocked_range<uint32_t> &range) {
1117             for (uint32_t f = range.begin(); f<range.end(); ++f) {
1118                 if (orient_sing.find(f) != orient_sing.end())
1119                     continue;
1120                 Vector2i index = Vector2i::Zero();
1121                 uint32_t i0 = F(0, f), i1 = F(1, f), i2 = F(2, f);
1122 
1123                 Vector3f q[3] = { Q.col(i0).normalized(), Q.col(i1).normalized(), Q.col(i2).normalized() };
1124                 Vector3f n[3] = { N.col(i0), N.col(i1), N.col(i2) };
1125                 Vector3f o[3] = { O.col(i0), O.col(i1), O.col(i2) };
1126                 Vector3f v[3] = { V.col(i0), V.col(i1), V.col(i2) };
1127 
1128                 int best[3];
1129                 Float best_dp = -std::numeric_limits<double>::infinity();
1130                 for (int i=0; i<rosy; ++i) {
1131                     Vector3f v0 = rotateFunctor_rosy(q[0], n[0], i);
1132                     for (int j=0; j<rosy; ++j) {
1133                         Vector3f v1 = rotateFunctor_rosy(q[1], n[1], j);
1134                         for (int k=0; k<rosy; ++k) {
1135                             Vector3f v2 = rotateFunctor_rosy(q[2], n[2], k);
1136                             Float dp = std::min(std::min(v0.dot(v1), v1.dot(v2)), v2.dot(v0));
1137                             if (dp > best_dp) {
1138                                 best_dp = dp;
1139                                 best[0] = i; best[1] = j; best[2] = k;
1140                             }
1141                         }
1142                     }
1143                 }
1144                 for (int k=0; k<3; ++k)
1145                     q[k] = rotateFunctor_rosy(q[k], n[k], best[k]);
1146 
1147                 for (int k=0; k<3; ++k) {
1148                     int kn = k == 2 ? 0 : (k+1);
1149 
1150                     std::pair<Vector2i, Vector2i> value =
1151                         compatPositionIndex(
1152                             v[k],  n[k],  q[k],  o[k],
1153                             v[kn], n[kn], q[kn], o[kn],
1154                             scale, inv_scale, nullptr);
1155 
1156                     index += value.first - value.second;
1157                 }
1158 
1159                 if (index != Vector2i::Zero()) {
1160                     tbb::spin_mutex::scoped_lock lock(mutex);
1161                     pos_sing[f] = rshift(index, best[0]);
1162                 }
1163             }
1164         }
1165     );
1166 }
1167 
1168 template <typename Functor>
freeze_ivars_positions_impl(MultiResolutionHierarchy & mRes,int level,Functor functor)1169 static inline void freeze_ivars_positions_impl(MultiResolutionHierarchy &mRes,
1170                                                int level, Functor functor) {
1171     const AdjacencyMatrix &adj = mRes.adj(level);
1172     const MatrixXf &N = mRes.N(level), &Q = mRes.Q(level), &V = mRes.V(level), &O = mRes.O(level);
1173     const Float scale = mRes.scale(), inv_scale = 1.0f / scale;
1174 
1175     auto map = [&](const tbb::blocked_range<uint32_t> &range) {
1176         for (uint32_t i = range.begin(); i<range.end(); ++i) {
1177             const Vector3f n_i = N.col(i), v_i = V.col(i), o_i = O.col(i);
1178             Vector3f q_i = Q.col(i);
1179             #if 1
1180                 q_i.normalize();
1181             #endif
1182 
1183             for (Link *link = adj[i]; link != adj[i+1]; ++link) {
1184                 const uint32_t j = link->id;
1185                 const Vector3f n_j = N.col(j), v_j = V.col(j);
1186                 Vector3f q_j = Q.col(j), o_j = O.col(j);
1187 
1188                 #if 1
1189                     q_j.normalize();
1190                 #endif
1191 
1192                 std::pair<Vector2i, Vector2i> value = functor(
1193                     v_i, n_i, q_i, o_i,
1194                     v_j, n_j, q_j, o_j,
1195                     scale, inv_scale, nullptr);
1196 
1197                 link->ivar[0].translate_u = value.first.x();
1198                 link->ivar[0].translate_v = value.first.y();
1199                 link->ivar[1].translate_u = value.second.x();
1200                 link->ivar[1].translate_v = value.second.y();
1201             }
1202         }
1203     };
1204 
1205     tbb::parallel_for(
1206         tbb::blocked_range<uint32_t>(0, mRes.size(level), GRAIN_SIZE), map);
1207 
1208     mRes.setFrozenO(true);
1209 }
1210 
freeze_ivars_positions(MultiResolutionHierarchy & mRes,int level,bool extrinsic,int posy)1211 void freeze_ivars_positions(MultiResolutionHierarchy &mRes, int level,
1212                          bool extrinsic, int posy) {
1213     if (posy != 4) /// only rosy=4 for now.
1214         return;
1215     if (posy == 3) {
1216         if (extrinsic)
1217             freeze_ivars_positions_impl(mRes, level, compat_position_extrinsic_index_3);
1218         else
1219             freeze_ivars_positions_impl(mRes, level, compat_position_intrinsic_index_3);
1220     } else if (posy == 4) {
1221         if (extrinsic)
1222             freeze_ivars_positions_impl(mRes, level, compat_position_extrinsic_index_4);
1223         else
1224             freeze_ivars_positions_impl(mRes, level, compat_position_intrinsic_index_4);
1225     } else {
1226         throw std::runtime_error("Invalid position symmetry type " + std::to_string(posy) + "!");
1227     }
1228 }
1229 
1230 void
compute_position_singularities(const MultiResolutionHierarchy & mRes,const std::map<uint32_t,uint32_t> & orient_sing,std::map<uint32_t,Vector2i> & pos_sing,bool extrinsic,int rosy,int posy)1231 compute_position_singularities(const MultiResolutionHierarchy &mRes,
1232                                const std::map<uint32_t, uint32_t> &orient_sing,
1233                                std::map<uint32_t, Vector2i> &pos_sing,
1234                                bool extrinsic, int rosy, int posy) {
1235     /* Some combinations don't make much sense, but let's support them anyways .. */
1236     if (rosy == 2) {
1237         if (posy == 3) {
1238             if (extrinsic)
1239                 compute_position_singularities<2, true>(
1240                     mRes, orient_sing, pos_sing, rotate180_by, rshift180,
1241                     compat_position_extrinsic_index_3);
1242             else
1243                 compute_position_singularities<2, false>(
1244                     mRes, orient_sing, pos_sing, rotate180_by, rshift180,
1245                     compat_position_intrinsic_index_3);
1246         } else if (posy == 4) {
1247             if (extrinsic)
1248                 compute_position_singularities<2, true>(
1249                     mRes, orient_sing, pos_sing, rotate180_by, rshift180,
1250                     compat_position_extrinsic_index_4);
1251             else
1252                 compute_position_singularities<2, false>(
1253                     mRes, orient_sing, pos_sing, rotate180_by, rshift180,
1254                     compat_position_intrinsic_index_4);
1255         } else {
1256             throw std::runtime_error(
1257                 "compute_position_singularities: unsupported!");
1258         }
1259     } else if (rosy == 4) {
1260         if (posy == 3) {
1261             if (extrinsic)
1262                 compute_position_singularities<4, true>(
1263                     mRes, orient_sing, pos_sing, rotate90_by, rshift90,
1264                     compat_position_extrinsic_index_3);
1265             else
1266                 compute_position_singularities<4, false>(
1267                     mRes, orient_sing, pos_sing, rotate90_by, rshift90,
1268                     compat_position_intrinsic_index_3);
1269         } else if (posy == 4) {
1270             if (extrinsic)
1271                 compute_position_singularities<4, true>(
1272                     mRes, orient_sing, pos_sing, rotate90_by, rshift90,
1273                     compat_position_extrinsic_index_4);
1274             else
1275                 compute_position_singularities<4, false>(
1276                     mRes, orient_sing, pos_sing, rotate90_by, rshift90,
1277                     compat_position_intrinsic_index_4);
1278         } else {
1279             throw std::runtime_error(
1280                 "compute_position_singularities: unsupported!");
1281         }
1282     } else if (rosy == 6) {
1283         if (posy == 3) {
1284             if (extrinsic)
1285                 compute_position_singularities<6, true>(
1286                     mRes, orient_sing, pos_sing, rotate60_by, rshift60,
1287                     compat_position_extrinsic_index_3);
1288             else
1289                 compute_position_singularities<6, false>(
1290                     mRes, orient_sing, pos_sing, rotate60_by, rshift60,
1291                     compat_position_intrinsic_index_3);
1292         } else if (posy == 4) {
1293             if (extrinsic)
1294                 compute_position_singularities<6, true>(
1295                     mRes, orient_sing, pos_sing, rotate60_by, rshift60,
1296                     compat_position_extrinsic_index_4);
1297             else
1298                 compute_position_singularities<6, false>(
1299                     mRes, orient_sing, pos_sing, rotate60_by, rshift60,
1300                     compat_position_intrinsic_index_4);
1301         } else {
1302             throw std::runtime_error("compute_position_singularities: unsupported!");
1303         }
1304     } else {
1305         throw std::runtime_error("compute_position_singularities: unsupported!");
1306     }
1307 }
1308 
move_orientation_singularity(MultiResolutionHierarchy & mRes,uint32_t f_src,uint32_t f_target)1309 bool move_orientation_singularity(MultiResolutionHierarchy &mRes, uint32_t f_src, uint32_t f_target) {
1310     int edge_idx[2], found = 0;
1311     cout << "Moving orientation singularity from face " << f_src << " to " << f_target << endl;
1312     const MatrixXu &F = mRes.F();
1313     const MatrixXf &N = mRes.N(), &Q = mRes.Q();
1314     AdjacencyMatrix &adj = mRes.adj();
1315 
1316     for (int i=0; i<3; ++i)
1317         for (int j=0; j<3; ++j)
1318             if (F(i, f_src) == F(j, f_target))
1319                 edge_idx[found++] = F(i, f_src);
1320 
1321     if (found != 2)
1322         throw std::runtime_error("move_orientation_singularity: invalid argument");
1323 
1324     int index = 0;
1325     for (int i=0; i<3; ++i) {
1326         uint32_t idx_cur = F(i, f_src), idx_next = F(i == 2 ? 0 : (i+1), f_src);
1327         const Link &l = search_adjacency(adj, idx_cur, idx_next);
1328         index += l.ivar[1].rot - l.ivar[0].rot;
1329     }
1330 
1331     index = modulo(index, 4);
1332     if (index == 0) {
1333         cout << "Warning: Starting point was not a singularity!" << endl;
1334         return false;
1335     } else {
1336         cout << "Singularity index is " << index << endl;
1337     }
1338 
1339     Link &l0 = search_adjacency(adj, edge_idx[0], edge_idx[1]);
1340     Link &l1 = search_adjacency(adj, edge_idx[1], edge_idx[0]);
1341     l1.ivar[0].rot = l0.ivar[1].rot;
1342     l1.ivar[1].rot = l0.ivar[0].rot;
1343     auto rotate = rotate90_by;
1344 
1345     Vector3f n0 = N.col(edge_idx[0]);
1346     Vector3f n1 = N.col(edge_idx[1]);
1347     Vector3f q0 = rotate(Q.col(edge_idx[0]).normalized(), n0, l0.ivar[0].rot);
1348     Vector3f q1 = rotate(Q.col(edge_idx[1]).normalized(), n1, l0.ivar[1].rot);
1349 
1350     Vector3f q0p = n0.cross(q0), q1p = n1.cross(q1);
1351 
1352     if (std::abs(q0p.dot(q1)) > std::abs(q1p.dot(q0)))
1353         l0.ivar[0].rot = l1.ivar[1].rot = modulo(l0.ivar[0].rot + (q0p.dot(q1) > 0 ? 1 : 3), 4);
1354     else
1355         l0.ivar[1].rot = l1.ivar[0].rot = modulo(l0.ivar[1].rot + (q1p.dot(q0) > 0 ? 1 : 3), 4);
1356 
1357     return true;
1358 }
1359 
move_position_singularity(MultiResolutionHierarchy & mRes,uint32_t f_src,uint32_t f_target)1360 bool move_position_singularity(MultiResolutionHierarchy &mRes, uint32_t f_src, uint32_t f_target) {
1361     cout << "Moving position singularity from face " << f_src << " to " << f_target << endl;
1362     const MatrixXu &F = mRes.F();
1363     const MatrixXf &N = mRes.N(), &Q = mRes.Q();
1364     AdjacencyMatrix &adj = mRes.adj();
1365 
1366     auto rotate = rotate90_by;
1367     auto rshift = rshift90;
1368     int rosy = 4;
1369 
1370     Vector3f q[3] = { Q.col(F(0, f_src)).normalized(), Q.col(F(1, f_src)).normalized(), Q.col(F(2, f_src)).normalized() };
1371     Vector3f n[3] = { N.col(F(0, f_src)), N.col(F(1, f_src)), N.col(F(2, f_src)) };
1372 
1373     int best[3];
1374     Float best_dp = 0;
1375     for (int i=0; i<rosy; ++i) {
1376         Vector3f v0 = rotate(q[0], n[0], i);
1377         for (int j=0; j<rosy; ++j) {
1378             Vector3f v1 = rotate(q[1], n[1], j);
1379             for (int k=0; k<rosy; ++k) {
1380                 Vector3f v2 = rotate(q[2], n[2], k);
1381                 Float dp = std::min(std::min(v0.dot(v1), v1.dot(v2)), v2.dot(v0));
1382                 if (dp > best_dp) {
1383                     best_dp = dp;
1384                     best[0] = i; best[1] = j; best[2] = k;
1385                 }
1386             }
1387         }
1388     }
1389 
1390     for (int i=0; i<3; ++i)
1391         q[i] = rotate(q[i], n[i], best[i]);
1392 
1393     Vector2i index = Vector2i::Zero();
1394     for (int i=0; i<3; ++i) {
1395         int j = (i+1) % 3;
1396         Link &l0 = search_adjacency(adj, F(i, f_src), F(j, f_src));
1397         index += rshift(l0.ivar[1].shift(),  modulo(-best[j], 4)) -
1398                  rshift(l0.ivar[0].shift(),  modulo(-best[i], 4));
1399     }
1400 
1401     if (index == Vector2i::Zero()) {
1402         cout << "Warning: Starting point was not a singularity!" << endl;
1403         return false;
1404     } else if (index.array().abs().sum() != 1) {
1405         cout << "Warning: Starting point is a high-degree singularity " << index.transpose() << endl;
1406         return false;
1407     } else {
1408         cout << "Singularity index is " << index.transpose() << endl;
1409     }
1410 
1411     int index_f[2], found = 0;
1412     for (int i = 0; i < 3; ++i)
1413         for (int j = 0; j < 3; ++j)
1414             if (F(i, f_src) == F(j, f_target))
1415                 index_f[found++] = i;
1416 
1417     if (found != 2)
1418         throw std::runtime_error("Internal error!");
1419 
1420     if (index_f[0] == 0 && index_f[1] == 2)
1421         std::swap(index_f[0], index_f[1]);
1422 
1423     Link &l0 = search_adjacency(adj, F(index_f[0], f_src), F(index_f[1], f_src));
1424     Link &l1 = search_adjacency(adj, F(index_f[1], f_src), F(index_f[0], f_src));
1425 
1426     if (l0.ivar[1].shift()  != l1.ivar[0].shift() ||
1427         l0.ivar[0].shift()  != l1.ivar[1].shift())
1428         throw std::runtime_error("Non-symmetry detected!");
1429 
1430     Vector2i delta_0 = rshift( index, best[index_f[0]]);
1431     Vector2i delta_1 = rshift(-index, best[index_f[1]]);
1432 
1433     int magnitude_0 = (l0.ivar[0].shift() + delta_0).cwiseAbs().maxCoeff();
1434     int magnitude_1 = (l0.ivar[1].shift() + delta_1).cwiseAbs().maxCoeff();
1435 
1436     if (magnitude_0 < magnitude_1) {
1437         Vector2i tmp = l0.ivar[0].shift() + delta_0;
1438         l0.ivar[0].setShift(tmp);
1439         l1.ivar[1].setShift(tmp);
1440     } else {
1441         Vector2i tmp = l0.ivar[1].shift() + delta_1;
1442         l0.ivar[1].setShift(tmp);
1443         l1.ivar[0].setShift(tmp);
1444     }
1445 
1446     index = Vector2i::Zero();
1447     for (int i=0; i<3; ++i) {
1448         int j = (i+1) % 3;
1449         Link &l = search_adjacency(adj, F(i, f_src), F(j, f_src));
1450         index += rshift(l.ivar[1].shift(), modulo(-best[j], 4)) -
1451                  rshift(l.ivar[0].shift(), modulo(-best[i], 4));
1452     }
1453     cout << "Afterwards = " << index.transpose() << endl;
1454 
1455     return true;
1456 }
1457 
Optimizer(MultiResolutionHierarchy & mRes,bool interactive)1458 Optimizer::Optimizer(MultiResolutionHierarchy &mRes, bool interactive)
1459     : mRes(mRes), mRunning(true), mOptimizeOrientations(false),
1460       mOptimizePositions(false), mLevel(-1), mLevelIterations(0),
1461       mHierarchical(false), mRoSy(-1), mPoSy(-1), mExtrinsic(true),
1462       mInteractive(interactive), mLastUpdate(0.0f), mProgress(1.f) {
1463     mThread = std::thread(&Optimizer::run, this);
1464 }
1465 
save(Serializer & state)1466 void Optimizer::save(Serializer &state) {
1467     state.set("running", mRunning);
1468     state.set("optimizeOrientations", mOptimizeOrientations);
1469     state.set("optimizePositions", mOptimizePositions);
1470     state.set("hierarchical", mHierarchical);
1471     state.set("progress", mProgress);
1472     state.set("extrinsic", mExtrinsic);
1473     state.set("levelIterations", mLevelIterations);
1474     state.set("rosy", mRoSy);
1475     state.set("posy", mPoSy);
1476     state.set("lastUpdate", mLastUpdate);
1477     state.set("level", mLevel);
1478 }
1479 
load(const Serializer & state)1480 void Optimizer::load(const Serializer &state) {
1481     state.get("running", mRunning);
1482     state.get("optimizeOrientations", mOptimizeOrientations);
1483     state.get("optimizePositions", mOptimizePositions);
1484     state.get("hierarchical", mHierarchical);
1485     state.get("progress", mProgress);
1486     state.get("extrinsic", mExtrinsic);
1487     state.get("levelIterations", mLevelIterations);
1488     state.get("rosy", mRoSy);
1489     state.get("posy", mPoSy);
1490     state.get("lastUpdate", mLastUpdate);
1491     state.get("level", mLevel);
1492 }
1493 
optimizeOrientations(int level)1494 void Optimizer::optimizeOrientations(int level) {
1495     if (level >= 0) {
1496         mLevel = level;
1497         mHierarchical = false;
1498     } else {
1499         mLevel = mRes.levels() - 1;
1500         mHierarchical = true;
1501     }
1502     if (level != 0)
1503         mRes.setFrozenQ(false);
1504     mLevelIterations = 0;
1505     mOptimizePositions = false;
1506     mOptimizeOrientations = true;
1507 #ifdef VISUALIZE_ERROR
1508     mError.resize(0);
1509 #endif
1510     mTimer.reset();
1511 }
1512 
optimizePositions(int level)1513 void Optimizer::optimizePositions(int level) {
1514     if (level >= 0) {
1515         mLevel = level;
1516         mHierarchical = false;
1517     } else {
1518         mLevel = mRes.levels() - 1;
1519         mHierarchical = true;
1520     }
1521     if (level != 0)
1522         mRes.setFrozenO(false);
1523     mLevelIterations = 0;
1524     mOptimizePositions = true;
1525     mOptimizeOrientations = false;
1526 #ifdef VISUALIZE_ERROR
1527     mError.resize(0);
1528 #endif
1529     mTimer.reset();
1530 }
1531 
wait()1532 void Optimizer::wait() {
1533     std::lock_guard<ordered_lock> lock(mRes.mutex());
1534     while (mRunning && (mOptimizePositions || mOptimizeOrientations))
1535         mCond.wait(mRes.mutex());
1536 }
1537 extern int nprocs;
1538 
run()1539 void Optimizer::run() {
1540     const int levelIterations = 6;
1541     uint32_t operations = 0;
1542     tbb::task_scheduler_init init(nprocs);
1543 
1544     auto progress = [&](uint32_t ops) {
1545         operations += ops;
1546         if (mHierarchical)
1547             mProgress = operations / (Float) (mRes.totalSize() * levelIterations);
1548         else
1549             mProgress = 1.f;
1550     };
1551 
1552     while (true) {
1553         std::lock_guard<ordered_lock> lock(mRes.mutex());
1554         while (mRunning && (mRes.levels() == 0 || (!mOptimizePositions && !mOptimizeOrientations)))
1555             mCond.wait(mRes.mutex());
1556 
1557         if (!mRunning)
1558             break;
1559         int level = mLevel;
1560         if (mLevelIterations++ == 0 && mHierarchical && level == mRes.levels() - 1)
1561             operations = 0;
1562 
1563         bool lastIterationAtLevel = mHierarchical &&
1564                                     mLevelIterations >= levelIterations;
1565 
1566         bool updateView = (mInteractive && mTimer.value() > 500) || !mHierarchical;
1567 #ifdef VISUALIZE_ERROR
1568         updateView = true;
1569 #endif
1570 
1571         Timer<> timer;
1572 
1573         if (mOptimizeOrientations) {
1574             optimize_orientations(mRes, level, mExtrinsic, mRoSy, progress);
1575 
1576             if (level > 0 && (lastIterationAtLevel || updateView)) {
1577                 int targetLevel = updateView ? 0 : (level - 1);
1578 
1579                 for (int i=level-1; i>=targetLevel; --i) {
1580                     const MatrixXf &srcField = mRes.Q(i + 1);
1581                     const MatrixXu &toUpper = mRes.toUpper(i);
1582                     MatrixXf &destField = mRes.Q(i);
1583                     const MatrixXf &N = mRes.N(i);
1584                     tbb::parallel_for(0u, (uint32_t) srcField.cols(), [&](uint32_t j) {
1585                         for (int k = 0; k<2; ++k) {
1586                             uint32_t dest = toUpper(k, j);
1587                             if (dest == INVALID)
1588                                 continue;
1589                             Vector3f q = srcField.col(j), n = N.col(dest);
1590                             destField.col(dest) = q - n * n.dot(q);
1591                         }
1592                     });
1593                 }
1594             }
1595             if (updateView || (level == 0 && lastIterationAtLevel)) {
1596                 mRes.setIterationsQ(mRes.iterationsQ() + 1);
1597 #ifdef VISUALIZE_ERROR
1598                 const int max = 1000;
1599                 if (mError.size() < max)
1600                     mError.conservativeResize(mError.size() + 1);
1601                 else
1602                     mError.head(max-1) = mError.tail(max-1).eval();
1603                 Float value = error_orientations(mRes, 0, mExtrinsic, mRoSy);
1604                 mError[mError.size()-1] = value;
1605 #endif
1606             }
1607         }
1608         if (mOptimizePositions) {
1609             optimize_positions(mRes, level, mExtrinsic, mPoSy, progress);
1610 
1611             if (level > 0 && (lastIterationAtLevel || updateView)) {
1612                 int targetLevel = updateView ? 0 : (level - 1);
1613 
1614                 for (int i=level-1; i>=targetLevel; --i) {
1615                     const MatrixXf &srcField = mRes.O(i + 1);
1616                     MatrixXf &destField = mRes.O(i);
1617                     const MatrixXf &N = mRes.N(i);
1618                     const MatrixXf &V = mRes.V(i);
1619                     const MatrixXu &toUpper = mRes.toUpper(i);
1620                     tbb::parallel_for(0u, (uint32_t) srcField.cols(), [&](uint32_t j) {
1621                         for (int k=0; k<2; ++k) {
1622                             uint32_t dest = toUpper(k, j);
1623                             if (dest == INVALID)
1624                                 continue;
1625                             Vector3f o = srcField.col(j), n = N.col(dest), v = V.col(dest);
1626                             o -= n * n.dot(o-v);
1627                             destField.col(dest) = o;
1628                         }
1629                     });
1630                 }
1631                 if (targetLevel == 0)
1632                     mRes.setIterationsO(mRes.iterationsO() + 1);
1633             }
1634             if (updateView || (level == 0 && lastIterationAtLevel)) {
1635                 mRes.setIterationsO(mRes.iterationsO() + 1);
1636 #ifdef VISUALIZE_ERROR
1637                 const int max = 1000;
1638                 if (mError.size() < max)
1639                     mError.conservativeResize(mError.size() + 1);
1640                 else
1641                     mError.head(max-1) = mError.tail(max-1).eval();
1642                 Float value = error_positions(mRes, 0, mExtrinsic, mPoSy);
1643                 mError[mError.size()-1] = value;
1644 #endif
1645             }
1646         }
1647 
1648         if (mHierarchical && mLevelIterations >= levelIterations) {
1649             if (--mLevel < 0) {
1650                 if (mOptimizeOrientations) {
1651                     if (!mRes.frozenQ())
1652                         freeze_ivars_orientations(mRes, 0, mExtrinsic, mRoSy);
1653                 }
1654                 if (mOptimizePositions) {
1655                     if (!mRes.frozenO())
1656                         freeze_ivars_positions(mRes, 0, mExtrinsic, mPoSy);
1657                 }
1658                 stop();
1659             }
1660             mLevelIterations = 0;
1661         }
1662 
1663         if (mAttractorStrokes.size() > 0 && mLevel == 0) {
1664             auto &value = mAttractorStrokes[mAttractorStrokes.size()-1];
1665             bool orientation = value.first;
1666             auto &stroke = value.second;
1667             if (stroke.size() < 2) {
1668                 mAttractorStrokes.pop_back();
1669             } else {
1670                 if (orientation) {
1671                     if (move_orientation_singularity(mRes, stroke[stroke.size()-1], stroke[stroke.size()-2])) {
1672                         stroke.pop_back();
1673                     } else {
1674                         mAttractorStrokes.pop_back();
1675                     }
1676                 } else {
1677                     if (move_position_singularity(mRes, stroke[stroke.size()-1], stroke[stroke.size()-2])) {
1678                         stroke.pop_back();
1679                     } else {
1680                         mAttractorStrokes.pop_back();
1681                     }
1682                 }
1683             }
1684             if (mAttractorStrokes.empty()) {
1685                 mHierarchical = true;
1686                 mLevelIterations = 0;
1687             }
1688         }
1689         if (updateView)
1690             mTimer.reset();
1691     }
1692 }
1693 
1694