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