Blender V5.0
uv_parametrizer.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <functional>
10#include <vector>
11
13
14#include "BLI_array.hh"
15#include "BLI_convexhull_2d.hh"
16#include "BLI_ghash.h"
17#include "BLI_math_geom.h"
18#include "BLI_math_matrix.h"
19#include "BLI_math_rotation.h"
20#include "BLI_math_vector.h"
21#include "BLI_polyfill_2d.h"
23#include "BLI_rand.h"
24
25#ifdef WITH_UV_SLIM
26# include "slim_matrix_transfer.h"
27#endif
28
29#include "GEO_uv_pack.hh"
30
31#include "eigen_capi.h"
32
33/* Utils */
34
35namespace blender::geometry {
36
37#define param_warning(message) \
38 {/* `printf("Warning %s:%d: %s\n", __FILE__, __LINE__, message);` */}(void)0
39
40/* Prevent unused function warnings when slim is disabled. */
41#ifdef WITH_UV_SLIM
42# define UNUSED_FUNCTION_NO_SLIM(x) x
43#else
44# define UNUSED_FUNCTION_NO_SLIM UNUSED_FUNCTION
45#endif
46
47/* Special Purpose Hash */
48
49using PHashKey = uintptr_t;
50
55
61
62/* Simplices */
63
64struct PVert {
66
67 union PVertUnion {
68 PHashKey key; /* Construct. */
69 int id; /* ABF/LSCM matrix index. */
70 HeapNode *heaplink; /* Edge collapsing. */
71 } u;
72
73 struct PEdge *edge;
74 float co[3];
75 float uv[2];
77
78 float weight;
81};
82
83struct PEdge {
85
86 union PEdgeUnion {
87 PHashKey key; /* Construct. */
88 int id; /* ABF matrix index. */
89 HeapNode *heaplink; /* Fill holes. */
90 PEdge *nextcollapse; /* Simplification. */
91 } u;
92
96 struct PFace *face;
97 float *orig_uv, old_uv[2];
99};
100
101struct PFace {
103
105 PHashKey key; /* Construct. */
106 int chart; /* Construct splitting. */
107 float area3d; /* Stretch. */
108 int id; /* ABF matrix index. */
109 } u;
110
113};
114
122
134
135/* for flipping faces */
136#define PEDGE_VERTEX_FLAGS (PEDGE_PIN)
137
144
145/* Chart */
146
172
173/* PHash
174 * - special purpose hash that keeps all its elements in a single linked list.
175 * - after construction, this hash is thrown away, and the list remains.
176 * - removing elements is not possible efficiently.
177 */
178
179static int PHashSizes[] = {
180 1, 3, 5, 11, 17, 37, 67, 131, 257, 521,
181 1031, 2053, 4099, 8209, 16411, 32771, 65537, 131101, 262147, 524309,
182 1048583, 2097169, 4194319, 8388617, 16777259, 33554467, 67108879, 134217757, 268435459,
183};
184
185#define PHASH_hash(ph, item) (uintptr_t(item) % uint((ph)->cursize))
186#define PHASH_edge(v1, v2) (((v1) < (v2)) ? ((v1) * 39) ^ ((v2) * 31) : ((v1) * 31) ^ ((v2) * 39))
187
188static PHash *phash_new(PHashLink **list, int sizehint)
189{
190 PHash *ph = MEM_callocN<PHash>("PHash");
191 ph->size = 0;
192 ph->cursize_id = 0;
193 ph->list = list;
194
195 while (PHashSizes[ph->cursize_id] < sizehint) {
196 ph->cursize_id++;
197 }
198
199 ph->cursize = PHashSizes[ph->cursize_id];
200 ph->buckets = MEM_calloc_arrayN<PHashLink *>(ph->cursize, "PHashBuckets");
201
202 return ph;
203}
204
205static void phash_safe_delete(PHash **pph)
206{
207 if (!*pph) {
208 return;
209 }
210 MEM_SAFE_FREE((*pph)->buckets);
211 MEM_freeN(*pph);
212 *pph = nullptr;
213}
214
215static int phash_size(PHash *ph)
216{
217 return ph->size;
218}
219
220static void phash_insert(PHash *ph, PHashLink *link)
221{
222 int size = ph->cursize;
223 uintptr_t hash = PHASH_hash(ph, link->key);
224 PHashLink *lookup = ph->buckets[hash];
225
226 if (lookup == nullptr) {
227 /* insert in front of the list */
228 ph->buckets[hash] = link;
229 link->next = *(ph->list);
230 *(ph->list) = link;
231 }
232 else {
233 /* insert after existing element */
234 link->next = lookup->next;
235 lookup->next = link;
236 }
237
238 ph->size++;
239
240 if (ph->size > (size * 3)) {
241 PHashLink *next = nullptr, *first = *(ph->list);
242
243 ph->cursize = PHashSizes[++ph->cursize_id];
244 MEM_freeN(ph->buckets);
245 ph->buckets = (PHashLink **)MEM_callocN(ph->cursize * sizeof(*ph->buckets), "PHashBuckets");
246 ph->size = 0;
247 *(ph->list) = nullptr;
248
249 for (link = first; link; link = next) {
250 next = link->next;
251 phash_insert(ph, link);
252 }
253 }
254}
255
257{
258 PHashLink *link;
259 uintptr_t hash = PHASH_hash(ph, key);
260
261 for (link = ph->buckets[hash]; link; link = link->next) {
262 if (link->key == key) {
263 return link;
264 }
265 if (PHASH_hash(ph, link->key) != hash) {
266 return nullptr;
267 }
268 }
269
270 return link;
271}
272
274{
275 uintptr_t hash = PHASH_hash(ph, key);
276
277 for (link = link->next; link; link = link->next) {
278 if (link->key == key) {
279 return link;
280 }
281 if (PHASH_hash(ph, link->key) != hash) {
282 return nullptr;
283 }
284 }
285
286 return link;
287}
288
289/* Angles close to 0 or 180 degrees cause rows filled with zeros in the linear_solver.
290 * The matrix will then be rank deficient and / or have poor conditioning.
291 * => Reduce the maximum angle to 179 degrees, and spread the remainder to the other angles.
292 */
293static void fix_large_angle(const float v_fix[3],
294 const float v1[3],
295 const float v2[3],
296 double *r_fix,
297 double *r_a1,
298 double *r_a2)
299{
300 const double max_angle = DEG2RAD(179.0);
301 const double fix_amount = *r_fix - max_angle;
302 if (fix_amount < 0.0f) {
303 return; /* angle is reasonable, i.e. less than 179 degrees. */
304 }
305
306 /* The triangle is probably degenerate, or close to it.
307 * Without loss of generality, transform the triangle such that
308 * v_fix == { 0, s}, *r_fix = 180 degrees
309 * v1 == {-x1, 0}, *r_a1 = 0
310 * v2 == { x2, 0}, *r_a2 = 0
311 *
312 * With `s = 0`, `x1 > 0`, `x2 > 0`
313 *
314 * Now make `s` a small number and do some math:
315 * tan(*r_a1) = s / x1
316 * tan(*r_a2) = s / x2
317 *
318 * Remember that `tan(angle) ~= angle`
319 *
320 * Rearrange to obtain:
321 * *r_a1 = fix_amount * x2 / (x1 + x2)
322 * *r_a2 = fix_amount * x1 / (x1 + x2)
323 */
324
325 const double dist_v1 = len_v3v3(v_fix, v1);
326 const double dist_v2 = len_v3v3(v_fix, v2);
327 const double sum = dist_v1 + dist_v2;
328 const double weight = (sum > 1e-20f) ? dist_v2 / sum : 0.5f;
329
330 /* Ensure sum of angles in triangle is unchanged. */
331 *r_fix -= fix_amount;
332 *r_a1 += fix_amount * weight;
333 *r_a2 += fix_amount * (1.0f - weight);
334}
335
336static void p_triangle_angles(const float v1[3],
337 const float v2[3],
338 const float v3[3],
339 double *r_a1,
340 double *r_a2,
341 double *r_a3)
342{
343 *r_a1 = angle_v3v3v3(v3, v1, v2);
344 *r_a2 = angle_v3v3v3(v1, v2, v3);
345 *r_a3 = angle_v3v3v3(v2, v3, v1);
346
347 /* Fix for degenerate geometry e.g. v1 = sum(v2 + v3). See #100874 */
348 fix_large_angle(v1, v2, v3, r_a1, r_a2, r_a3);
349 fix_large_angle(v2, v3, v1, r_a2, r_a3, r_a1);
350 fix_large_angle(v3, v1, v2, r_a3, r_a1, r_a2);
351
352 /* Workaround for degenerate geometry, e.g. v1 == v2 == v3. */
353 *r_a1 = max_dd(*r_a1, 0.001f);
354 *r_a2 = max_dd(*r_a2, 0.001f);
355 *r_a3 = max_dd(*r_a3, 0.001f);
356}
357
358static void p_face_angles(PFace *f, double *r_a1, double *r_a2, double *r_a3)
359{
360 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
361 PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
362
363 p_triangle_angles(v1->co, v2->co, v3->co, r_a1, r_a2, r_a3);
364}
365
366static float p_vec_cos(const float v1[3], const float v2[3], const float v3[3])
367{
368 return cos_v3v3v3(v1, v2, v3);
369}
370
371static void p_triangle_cos(const float v1[3],
372 const float v2[3],
373 const float v3[3],
374 float *r_cos1,
375 float *r_cos2,
376 float *r_cos3)
377{
378 *r_cos1 = p_vec_cos(v3, v1, v2);
379 *r_cos2 = p_vec_cos(v1, v2, v3);
380 *r_cos3 = p_vec_cos(v2, v3, v1);
381}
382
383static void UNUSED_FUNCTION(p_face_cos)(PFace *f, float *r_cos1, float *r_cos2, float *r_cos3)
384{
385 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
386 PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
387
388 p_triangle_cos(v1->co, v2->co, v3->co, r_cos1, r_cos2, r_cos3);
389}
390
391static float p_face_area(PFace *f)
392{
393 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
394 PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
395
396 return area_tri_v3(v1->co, v2->co, v3->co);
397}
398
399static float p_area_signed(const float v1[2], const float v2[2], const float v3[2])
400{
401 return 0.5f * (((v2[0] - v1[0]) * (v3[1] - v1[1])) - ((v3[0] - v1[0]) * (v2[1] - v1[1])));
402}
403
405{
406 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
407 PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
408
409 return 0.5f * (((v2->uv[0] - v1->uv[0]) * (v3->uv[1] - v1->uv[1])) -
410 ((v3->uv[0] - v1->uv[0]) * (v2->uv[1] - v1->uv[1])));
411}
412
413static float p_edge_length(PEdge *e)
414{
415 return len_v3v3(e->vert->co, e->next->vert->co);
416}
417
419{
420 return len_squared_v3v3(e->vert->co, e->next->vert->co);
421}
422
424{
425 return len_v2v2(e->vert->uv, e->next->vert->uv);
426}
427
428static void p_chart_uv_bbox(PChart *chart, float minv[2], float maxv[2])
429{
430 PVert *v;
431
432 INIT_MINMAX2(minv, maxv);
433
434 for (v = chart->verts; v; v = v->nextlink) {
435 minmax_v2v2_v2(minv, maxv, v->uv);
436 }
437}
438
439static float p_chart_uv_area(PChart *chart)
440{
441 float area = 0.0f;
442
443 for (PFace *f = chart->faces; f; f = f->nextlink) {
444 area += fabsf(p_face_uv_area_signed(f));
445 }
446
447 return area;
448}
449
450static void p_chart_uv_scale(PChart *chart, const float scale)
451{
452 if (scale == 1.0f) {
453 return; /* Identity transform. */
454 }
455
456 for (PVert *v = chart->verts; v; v = v->nextlink) {
457 v->uv[0] *= scale;
458 v->uv[1] *= scale;
459 }
460}
461
462static void uv_parametrizer_scale_x(ParamHandle *phandle, const float scale_x)
463{
464 if (scale_x == 1.0f) {
465 return; /* Identity transform. */
466 }
467
468 /* Scale every chart. */
469 for (int i = 0; i < phandle->ncharts; i++) {
470 PChart *chart = phandle->charts[i];
471 for (PVert *v = chart->verts; v; v = v->nextlink) {
472 v->uv[0] *= scale_x; /* Only scale x axis. */
473 }
474 }
475}
476
477static void p_chart_uv_translate(PChart *chart, const float trans[2])
478{
479 for (PVert *v = chart->verts; v; v = v->nextlink) {
480 v->uv[0] += trans[0];
481 v->uv[1] += trans[1];
482 }
483}
484
485static void p_chart_uv_transform(PChart *chart, const float mat[2][2])
486{
487 for (PVert *v = chart->verts; v; v = v->nextlink) {
488 mul_m2_v2(mat, v->uv);
489 }
490}
491
493{
494 PVert *v;
495 uint i = 0;
496
497 for (v = chart->verts; v; v = v->nextlink) {
498 copy_v2_v2(points[i++], v->uv);
499 }
500}
501
502static bool p_intersect_line_2d_dir(const float v1[2],
503 const float dir1[2],
504 const float v2[2],
505 const float dir2[2],
506 float r_isect[2])
507{
508 float lmbda, div;
509
510 div = dir2[0] * dir1[1] - dir2[1] * dir1[0];
511
512 if (div == 0.0f) {
513 return false;
514 }
515
516 lmbda = ((v1[1] - v2[1]) * dir1[0] - (v1[0] - v2[0]) * dir1[1]) / div;
517 r_isect[0] = v1[0] + lmbda * dir2[0];
518 r_isect[1] = v1[1] + lmbda * dir2[1];
519
520 return true;
521}
522
523/* Topological Utilities */
524
526{
527 return e->next->next->pair;
528}
529
530static const PEdge *p_wheel_edge_next(const PEdge *e)
531{
532 return e->next->next->pair;
533}
534
536{
537 return (e->pair) ? e->pair->next : nullptr;
538}
539
541{
542 return e->next->vert->edge;
543}
544
546{
547 PEdge *we = e, *last;
548
549 do {
550 last = we;
551 we = p_wheel_edge_next(we);
552 } while (we && (we != e));
553
554 return last->next->next;
555}
556
558{
559 return v->edge->pair;
560}
561
562static void p_face_flip(PFace *f)
563{
564 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
565 PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
566 int f1 = e1->flag, f2 = e2->flag, f3 = e3->flag;
567 float *orig_uv1 = e1->orig_uv, *orig_uv2 = e2->orig_uv, *orig_uv3 = e3->orig_uv;
568
569 e1->vert = v2;
570 e1->next = e3;
571 e1->orig_uv = orig_uv2;
572 e1->flag = (f1 & ~PEDGE_VERTEX_FLAGS) | (f2 & PEDGE_VERTEX_FLAGS);
573
574 e2->vert = v3;
575 e2->next = e1;
576 e2->orig_uv = orig_uv3;
577 e2->flag = (f2 & ~PEDGE_VERTEX_FLAGS) | (f3 & PEDGE_VERTEX_FLAGS);
578
579 e3->vert = v1;
580 e3->next = e2;
581 e3->orig_uv = orig_uv1;
582 e3->flag = (f3 & ~PEDGE_VERTEX_FLAGS) | (f1 & PEDGE_VERTEX_FLAGS);
583}
584
585#if 0
586static void p_chart_topological_sanity_check(PChart *chart)
587{
588 PVert *v;
589 PEdge *e;
590
591 for (v = chart->verts; v; v = v->nextlink) {
592 GEO_uv_parametrizer_test_equals_ptr("v->edge->vert", v, v->edge->vert);
593 }
594
595 for (e = chart->edges; e; e = e->nextlink) {
596 if (e->pair) {
597 GEO_uv_parametrizer_test_equals_ptr("e->pair->pair", e, e->pair->pair);
598 GEO_uv_parametrizer_test_equals_ptr("pair->vert", e->vert, e->pair->next->vert);
599 GEO_uv_parametrizer_test_equals_ptr("pair->next->vert", e->next->vert, e->pair->vert);
600 }
601 }
602}
603#endif
604
605/* Loading / Flushing */
606
608{
609 PEdge *e;
610 int nedges = 0, npins = 0;
611 float pinuv[2];
612
613 v->uv[0] = v->uv[1] = 0.0f;
614 pinuv[0] = pinuv[1] = 0.0f;
615 e = v->edge;
616 do {
617 if (e->orig_uv) {
618 if (e->flag & PEDGE_SELECT) {
619 v->flag |= PVERT_SELECT;
620 }
621
622 if (e->flag & PEDGE_PIN) {
623 pinuv[0] += e->orig_uv[0] * handle->aspect_y;
624 pinuv[1] += e->orig_uv[1];
625 npins++;
626 }
627 else {
628 v->uv[0] += e->orig_uv[0] * handle->aspect_y;
629 v->uv[1] += e->orig_uv[1];
630 }
631
632 nedges++;
633 }
634
636 } while (e && e != (v->edge));
637
638 if (npins > 0) {
639 v->uv[0] = pinuv[0] / npins;
640 v->uv[1] = pinuv[1] / npins;
641 v->flag |= PVERT_PIN;
642 }
643 else if (nedges > 0) {
644 v->uv[0] /= nedges;
645 v->uv[1] /= nedges;
646 }
647}
648
649static void p_chart_flush_collapsed_uvs(PChart *chart);
650
651static void p_flush_uvs(ParamHandle *handle, PChart *chart)
652{
653 const float blend = handle->blend;
654 const float invblend = 1.0f - blend;
655 const float invblend_x = invblend / handle->aspect_y;
656 for (PEdge *e = chart->edges; e; e = e->nextlink) {
657 if (e->orig_uv) {
658 e->orig_uv[0] = blend * e->old_uv[0] + invblend_x * e->vert->uv[0];
659 e->orig_uv[1] = blend * e->old_uv[1] + invblend * e->vert->uv[1];
660 }
661 }
662
663 if (chart->collapsed_edges) {
665
666 for (PEdge *e = chart->collapsed_edges; e; e = e->nextlink) {
667 if (e->orig_uv) {
668 e->orig_uv[0] = blend * e->old_uv[0] + invblend_x * e->vert->uv[0];
669 e->orig_uv[1] = blend * e->old_uv[1] + invblend * e->vert->uv[1];
670 }
671 }
672 }
673}
674
676{
677 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
678
679 if (e1->orig_uv) {
680 e1->old_uv[0] = e1->orig_uv[0];
681 e1->old_uv[1] = e1->orig_uv[1];
682 }
683 if (e2->orig_uv) {
684 e2->old_uv[0] = e2->orig_uv[0];
685 e2->old_uv[1] = e2->orig_uv[1];
686 }
687 if (e3->orig_uv) {
688 e3->old_uv[0] = e3->orig_uv[0];
689 e3->old_uv[1] = e3->orig_uv[1];
690 }
691}
692
694{
695 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
696
697 if (e1->orig_uv) {
698 e1->orig_uv[0] = e1->old_uv[0];
699 e1->orig_uv[1] = e1->old_uv[1];
700 }
701 if (e2->orig_uv) {
702 e2->orig_uv[0] = e2->old_uv[0];
703 e2->orig_uv[1] = e2->old_uv[1];
704 }
705 if (e3->orig_uv) {
706 e3->orig_uv[0] = e3->old_uv[0];
707 e3->orig_uv[1] = e3->old_uv[1];
708 }
709}
710
711/* Construction (use only during construction, relies on u.key being set */
712
714 ParamHandle *handle, PHashKey key, const float co[3], const float weight, PEdge *e)
715{
716 PVert *v = (PVert *)BLI_memarena_alloc(handle->arena, sizeof(*v));
717 copy_v3_v3(v->co, co);
718 v->weight = weight;
719
720 /* Sanity check, a single nan/inf point causes the entire result to be invalid.
721 * Note that values within the calculation may _become_ non-finite,
722 * so the rest of the code still needs to take this possibility into account. */
723 for (int i = 0; i < 3; i++) {
724 if (UNLIKELY(!isfinite(v->co[i]))) {
725 v->co[i] = 0.0f;
726 }
727 }
728
729 v->u.key = key;
730 v->edge = e;
731 v->flag = 0;
732
733 /* Unused, prevent uninitialized memory access on duplication. */
734 v->on_boundary_flag = false;
735 v->slim_id = 0;
736
737 phash_insert(handle->hash_verts, (PHashLink *)v);
738
739 return v;
740}
741
743 ParamHandle *handle, PHashKey key, const float co[3], const float weight, PEdge *e)
744{
745 PVert *v = (PVert *)phash_lookup(handle->hash_verts, key);
746
747 if (v) {
748 return v;
749 }
750 return p_vert_add(handle, key, co, weight, e);
751}
752
754{
755 PVert *nv = (PVert *)BLI_memarena_alloc(handle->arena, sizeof(*nv));
756
757 copy_v3_v3(nv->co, v->co);
758 nv->uv[0] = v->uv[0];
759 nv->uv[1] = v->uv[1];
760 nv->u.key = v->u.key;
761 nv->edge = v->edge;
762 nv->flag = v->flag;
763
764 nv->weight = v->weight;
765 nv->on_boundary_flag = v->on_boundary_flag;
766 nv->slim_id = v->slim_id;
767
768 return nv;
769}
770
771static PEdge *p_edge_lookup(ParamHandle *handle, const PHashKey *vkeys)
772{
773 PHashKey key = PHASH_edge(vkeys[0], vkeys[1]);
774 PEdge *e = (PEdge *)phash_lookup(handle->hash_edges, key);
775
776 while (e) {
777 if ((e->vert->u.key == vkeys[0]) && (e->next->vert->u.key == vkeys[1])) {
778 return e;
779 }
780 if ((e->vert->u.key == vkeys[1]) && (e->next->vert->u.key == vkeys[0])) {
781 return e;
782 }
783
784 e = (PEdge *)phash_next(handle->hash_edges, key, (PHashLink *)e);
785 }
786
787 return nullptr;
788}
789
790static int p_face_exists(ParamHandle *handle, const ParamKey *pvkeys, int i1, int i2, int i3)
791{
792 PHashKey *vkeys = (PHashKey *)pvkeys;
793 PHashKey key = PHASH_edge(vkeys[i1], vkeys[i2]);
794 PEdge *e = (PEdge *)phash_lookup(handle->hash_edges, key);
795
796 while (e) {
797 if ((e->vert->u.key == vkeys[i1]) && (e->next->vert->u.key == vkeys[i2])) {
798 if (e->next->next->vert->u.key == vkeys[i3]) {
799 return true;
800 }
801 }
802 else if ((e->vert->u.key == vkeys[i2]) && (e->next->vert->u.key == vkeys[i1])) {
803 if (e->next->next->vert->u.key == vkeys[i3]) {
804 return true;
805 }
806 }
807
808 e = (PEdge *)phash_next(handle->hash_edges, key, (PHashLink *)e);
809 }
810
811 return false;
812}
813
815{
816 const float *uv1, *uv2, *uvp1, *uvp2;
817 float limit[2];
818
819 limit[0] = 0.00001;
820 limit[1] = 0.00001;
821
822 uv1 = e->orig_uv;
823 uv2 = e->next->orig_uv;
824
825 if (e->vert->u.key == ep->vert->u.key) {
826 uvp1 = ep->orig_uv;
827 uvp2 = ep->next->orig_uv;
828 }
829 else {
830 uvp1 = ep->next->orig_uv;
831 uvp2 = ep->orig_uv;
832 }
833
834 if ((fabsf(uv1[0] - uvp1[0]) > limit[0]) || (fabsf(uv1[1] - uvp1[1]) > limit[1])) {
835 e->flag |= PEDGE_SEAM;
836 ep->flag |= PEDGE_SEAM;
837 return true;
838 }
839 if ((fabsf(uv2[0] - uvp2[0]) > limit[0]) || (fabsf(uv2[1] - uvp2[1]) > limit[1])) {
840 e->flag |= PEDGE_SEAM;
841 ep->flag |= PEDGE_SEAM;
842 return true;
843 }
844
845 return false;
846}
847
848static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uvs, PEdge **r_pair)
849{
850 PHashKey key;
851 PEdge *pe;
852 const PVert *v1, *v2;
853 PHashKey key1 = e->vert->u.key;
854 PHashKey key2 = e->next->vert->u.key;
855
856 if (e->flag & PEDGE_SEAM) {
857 return false;
858 }
859
860 key = PHASH_edge(key1, key2);
861 pe = (PEdge *)phash_lookup(handle->hash_edges, key);
862 *r_pair = nullptr;
863
864 while (pe) {
865 if (pe != e) {
866 v1 = pe->vert;
867 v2 = pe->next->vert;
868
869 if (((v1->u.key == key1) && (v2->u.key == key2)) ||
870 ((v1->u.key == key2) && (v2->u.key == key1)))
871 {
872
873 /* don't connect seams and t-junctions */
874 if ((pe->flag & PEDGE_SEAM) || *r_pair ||
875 (topology_from_uvs && p_edge_implicit_seam(e, pe)))
876 {
877 *r_pair = nullptr;
878 return false;
879 }
880
881 *r_pair = pe;
882 }
883 }
884
885 pe = (PEdge *)phash_next(handle->hash_edges, key, (PHashLink *)pe);
886 }
887
888 if (*r_pair && (e->vert == (*r_pair)->vert)) {
889 if ((*r_pair)->next->pair || (*r_pair)->next->next->pair) {
890 /* non unfoldable, maybe mobius ring or klein bottle */
891 *r_pair = nullptr;
892 return false;
893 }
894 }
895
896 return (*r_pair != nullptr);
897}
898
900 PEdge *e,
901 bool topology_from_uvs,
902 PEdge ***stack)
903{
904 PEdge *pair = nullptr;
905
906 if (!e->pair && p_edge_has_pair(handle, e, topology_from_uvs, &pair)) {
907 if (e->vert == pair->vert) {
908 p_face_flip(pair->face);
909 }
910
911 e->pair = pair;
912 pair->pair = e;
913
914 if (!(pair->face->flag & PFACE_CONNECTED)) {
915 **stack = pair;
916 (*stack)++;
917 }
918 }
919
920 return (e->pair != nullptr);
921}
922
923static int p_connect_pairs(ParamHandle *handle, bool topology_from_uvs)
924{
925 PEdge **stackbase = MEM_malloc_arrayN<PEdge *>(size_t(phash_size(handle->hash_faces)),
926 "Pstackbase");
927 PEdge **stack = stackbase;
928 PFace *f, *first;
929 PEdge *e, *e1, *e2;
930 PChart *chart = handle->construction_chart;
931 int ncharts = 0;
932
933 /* Connect pairs, count edges, set vertex-edge pointer to a pair-less edge. */
934 for (first = chart->faces; first; first = first->nextlink) {
935 if (first->flag & PFACE_CONNECTED) {
936 continue;
937 }
938
939 *stack = first->edge;
940 stack++;
941
942 while (stack != stackbase) {
943 stack--;
944 e = *stack;
945 e1 = e->next;
946 e2 = e1->next;
947
948 f = e->face;
949 f->flag |= PFACE_CONNECTED;
950
951 /* assign verts to charts so we can sort them later */
952 f->u.chart = ncharts;
953
954 if (!p_edge_connect_pair(handle, e, topology_from_uvs, &stack)) {
955 e->vert->edge = e;
956 }
957 if (!p_edge_connect_pair(handle, e1, topology_from_uvs, &stack)) {
958 e1->vert->edge = e1;
959 }
960 if (!p_edge_connect_pair(handle, e2, topology_from_uvs, &stack)) {
961 e2->vert->edge = e2;
962 }
963 }
964
965 ncharts++;
966 }
967
968 MEM_freeN(stackbase);
969
970 return ncharts;
971}
972
973static void p_split_vert(ParamHandle *handle, PChart *chart, PEdge *e)
974{
975 PEdge *we, *lastwe = nullptr;
976 PVert *v = e->vert;
977 bool copy = true;
978
979 if (e->flag & PEDGE_PIN) {
980 chart->has_pins = true;
981 }
982
983 if (e->flag & PEDGE_VERTEX_SPLIT) {
984 return;
985 }
986
987 /* rewind to start */
988 lastwe = e;
989 for (we = p_wheel_edge_prev(e); we && (we != e); we = p_wheel_edge_prev(we)) {
990 lastwe = we;
991 }
992
993 /* go over all edges in wheel */
994 for (we = lastwe; we; we = p_wheel_edge_next(we)) {
995 if (we->flag & PEDGE_VERTEX_SPLIT) {
996 break;
997 }
998
1000
1001 if (we == v->edge) {
1002 /* found it, no need to copy */
1003 copy = false;
1004 v->nextlink = chart->verts;
1005 chart->verts = v;
1006 chart->nverts++;
1007 }
1008 }
1009
1010 if (copy) {
1011 /* not found, copying */
1012 v->flag |= PVERT_SPLIT;
1013 v = p_vert_copy(handle, v);
1014 v->flag |= PVERT_SPLIT;
1015
1016 v->nextlink = chart->verts;
1017 chart->verts = v;
1018 chart->nverts++;
1019
1020 v->edge = lastwe;
1021
1022 we = lastwe;
1023 do {
1024 we->vert = v;
1025 we = p_wheel_edge_next(we);
1026 } while (we && (we != lastwe));
1027 }
1028}
1029
1030static PChart **p_split_charts(ParamHandle *handle, PChart *chart, int ncharts)
1031{
1032 PChart **charts = MEM_calloc_arrayN<PChart *>(ncharts, "PCharts");
1033
1034 for (int i = 0; i < ncharts; i++) {
1035 charts[i] = MEM_callocN<PChart>("PChart");
1036 }
1037
1038 PFace *f = chart->faces;
1039 while (f) {
1040 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
1041 PFace *nextf = f->nextlink;
1042
1043 PChart *nchart = charts[f->u.chart];
1044
1045 f->nextlink = nchart->faces;
1046 nchart->faces = f;
1047 e1->nextlink = nchart->edges;
1048 nchart->edges = e1;
1049 e2->nextlink = nchart->edges;
1050 nchart->edges = e2;
1051 e3->nextlink = nchart->edges;
1052 nchart->edges = e3;
1053
1054 nchart->nfaces++;
1055 nchart->nedges += 3;
1056
1057 p_split_vert(handle, nchart, e1);
1058 p_split_vert(handle, nchart, e2);
1059 p_split_vert(handle, nchart, e3);
1060
1061 f = nextf;
1062 }
1063
1064 return charts;
1065}
1066
1068{
1069 PFace *f;
1070
1071 /* allocate */
1072 f = (PFace *)BLI_memarena_alloc(handle->arena, sizeof(*f));
1073 f->flag = 0;
1074
1075 PEdge *e1 = (PEdge *)BLI_memarena_calloc(handle->arena, sizeof(*e1));
1076 PEdge *e2 = (PEdge *)BLI_memarena_calloc(handle->arena, sizeof(*e2));
1077 PEdge *e3 = (PEdge *)BLI_memarena_calloc(handle->arena, sizeof(*e3));
1078
1079 /* set up edges */
1080 f->edge = e1;
1081 e1->face = e2->face = e3->face = f;
1082
1083 e1->next = e2;
1084 e2->next = e3;
1085 e3->next = e1;
1086
1087 return f;
1088}
1089
1091 ParamKey key,
1092 const ParamKey *vkeys,
1093 const float **co,
1094 float **uv,
1095 const float *weight,
1096 int i1,
1097 int i2,
1098 int i3,
1099 const bool *pin,
1100 const bool *select)
1101{
1102 PFace *f = p_face_add(handle);
1103 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
1104
1105 float weight1, weight2, weight3;
1106 if (weight) {
1107 weight1 = weight[i1];
1108 weight2 = weight[i2];
1109 weight3 = weight[i3];
1110 }
1111 else {
1112 weight1 = 1.0f;
1113 weight2 = 1.0f;
1114 weight3 = 1.0f;
1115 }
1116
1117 e1->vert = p_vert_lookup(handle, vkeys[i1], co[i1], weight1, e1);
1118 e2->vert = p_vert_lookup(handle, vkeys[i2], co[i2], weight2, e2);
1119 e3->vert = p_vert_lookup(handle, vkeys[i3], co[i3], weight3, e3);
1120
1121 e1->orig_uv = uv[i1];
1122 e2->orig_uv = uv[i2];
1123 e3->orig_uv = uv[i3];
1124
1125 if (pin) {
1126 if (pin[i1]) {
1127 e1->flag |= PEDGE_PIN;
1128 }
1129 if (pin[i2]) {
1130 e2->flag |= PEDGE_PIN;
1131 }
1132 if (pin[i3]) {
1133 e3->flag |= PEDGE_PIN;
1134 }
1135 }
1136
1137 if (select) {
1138 if (select[i1]) {
1139 e1->flag |= PEDGE_SELECT;
1140 }
1141 if (select[i2]) {
1142 e2->flag |= PEDGE_SELECT;
1143 }
1144 if (select[i3]) {
1145 e3->flag |= PEDGE_SELECT;
1146 }
1147 }
1148
1149 f->u.key = key;
1150 phash_insert(handle->hash_faces, (PHashLink *)f);
1151
1152 e1->u.key = PHASH_edge(vkeys[i1], vkeys[i2]);
1153 e2->u.key = PHASH_edge(vkeys[i2], vkeys[i3]);
1154 e3->u.key = PHASH_edge(vkeys[i3], vkeys[i1]);
1155
1156 phash_insert(handle->hash_edges, (PHashLink *)e1);
1157 phash_insert(handle->hash_edges, (PHashLink *)e2);
1158 phash_insert(handle->hash_edges, (PHashLink *)e3);
1159
1160 return f;
1161}
1162
1163static PFace *p_face_add_fill(ParamHandle *handle, PChart *chart, PVert *v1, PVert *v2, PVert *v3)
1164{
1165 PFace *f = p_face_add(handle);
1166 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
1167
1168 e1->vert = v1;
1169 e2->vert = v2;
1170 e3->vert = v3;
1171
1172 e1->orig_uv = e2->orig_uv = e3->orig_uv = nullptr;
1173
1174 f->nextlink = chart->faces;
1175 chart->faces = f;
1176 e1->nextlink = chart->edges;
1177 chart->edges = e1;
1178 e2->nextlink = chart->edges;
1179 chart->edges = e2;
1180 e3->nextlink = chart->edges;
1181 chart->edges = e3;
1182
1183 chart->nfaces++;
1184 chart->nedges += 3;
1185
1186 return f;
1187}
1188
1189/* Construction: boundary filling */
1190
1191static void p_chart_boundaries(PChart *chart, PEdge **r_outer)
1192{
1193 PEdge *e, *be;
1194 float len, maxlen = -1.0;
1195
1196 chart->nboundaries = 0;
1197 if (r_outer) {
1198 *r_outer = nullptr;
1199 }
1200
1201 for (e = chart->edges; e; e = e->nextlink) {
1202 if (e->pair || (e->flag & PEDGE_DONE)) {
1203 continue;
1204 }
1205
1206 chart->nboundaries++;
1207
1208 len = 0.0f;
1209
1210 be = e;
1211 do {
1212 be->flag |= PEDGE_DONE;
1213 len += p_edge_length(be);
1214 be = be->next->vert->edge;
1215 } while (be != e);
1216
1217 if (r_outer && (len > maxlen)) {
1218 *r_outer = e;
1219 maxlen = len;
1220 }
1221 }
1222
1223 for (e = chart->edges; e; e = e->nextlink) {
1224 e->flag &= ~PEDGE_DONE;
1225 }
1226}
1227
1229{
1230 PEdge *we;
1231 PVert *v, *v1, *v2;
1232 float angle;
1233
1234 v = e->vert;
1235
1236 /* concave angle check -- could be better */
1237 angle = M_PI;
1238
1239 we = v->edge;
1240 do {
1241 v1 = we->next->vert;
1242 v2 = we->next->next->vert;
1243 angle -= angle_v3v3v3(v1->co, v->co, v2->co);
1244
1245 we = we->next->next->pair;
1246 } while (we && (we != v->edge));
1247
1248 return angle;
1249}
1250
1251static void p_chart_fill_boundary(ParamHandle *handle, PChart *chart, PEdge *be, int nedges)
1252{
1253 PEdge *e, *e1, *e2;
1254
1255 PFace *f;
1256 Heap *heap = BLI_heap_new();
1257 float angle;
1258
1259 e = be;
1260 do {
1262 e->u.heaplink = BLI_heap_insert(heap, angle, e);
1263
1265 } while (e != be);
1266
1267 if (nedges == 2) {
1268 /* no real boundary, but an isolated seam */
1269 e = be->next->vert->edge;
1270 e->pair = be;
1271 be->pair = e;
1272
1273 BLI_heap_remove(heap, e->u.heaplink);
1274 BLI_heap_remove(heap, be->u.heaplink);
1275 }
1276 else {
1277 while (nedges > 2) {
1278 PEdge *ne, *ne1, *ne2;
1279
1280 e = (PEdge *)BLI_heap_pop_min(heap);
1281
1282 e1 = p_boundary_edge_prev(e);
1283 e2 = p_boundary_edge_next(e);
1284
1285 BLI_heap_remove(heap, e1->u.heaplink);
1286 BLI_heap_remove(heap, e2->u.heaplink);
1287 e->u.heaplink = e1->u.heaplink = e2->u.heaplink = nullptr;
1288
1289 e->flag |= PEDGE_FILLED;
1290 e1->flag |= PEDGE_FILLED;
1291
1292 f = p_face_add_fill(handle, chart, e->vert, e1->vert, e2->vert);
1293 f->flag |= PFACE_FILLED;
1294
1295 ne = f->edge->next->next;
1296 ne1 = f->edge;
1297 ne2 = f->edge->next;
1298
1299 ne->flag = ne1->flag = ne2->flag = PEDGE_FILLED;
1300
1301 e->pair = ne;
1302 ne->pair = e;
1303 e1->pair = ne1;
1304 ne1->pair = e1;
1305
1306 ne->vert = e2->vert;
1307 ne1->vert = e->vert;
1308 ne2->vert = e1->vert;
1309
1310 if (nedges == 3) {
1311 e2->pair = ne2;
1312 ne2->pair = e2;
1313 }
1314 else {
1315 ne2->vert->edge = ne2;
1316
1317 ne2->u.heaplink = BLI_heap_insert(heap, p_edge_boundary_angle(ne2), ne2);
1318 e2->u.heaplink = BLI_heap_insert(heap, p_edge_boundary_angle(e2), e2);
1319 }
1320
1321 nedges--;
1322 }
1323 }
1324
1325 BLI_heap_free(heap, nullptr);
1326}
1327
1328static void p_chart_fill_boundaries(ParamHandle *handle, PChart *chart, const PEdge *outer)
1329{
1330 PEdge *e, *be; /* *enext - as yet unused */
1331 int nedges;
1332
1333 for (e = chart->edges; e; e = e->nextlink) {
1334 /* enext = e->nextlink; - as yet unused */
1335
1336 if (e->pair || (e->flag & PEDGE_FILLED)) {
1337 continue;
1338 }
1339
1340 nedges = 0;
1341 be = e;
1342 do {
1343 be->flag |= PEDGE_FILLED;
1344 be = be->next->vert->edge;
1345 nedges++;
1346 } while (be != e);
1347
1348 if (e != outer) {
1349 p_chart_fill_boundary(handle, chart, e, nedges);
1350 }
1351 }
1352}
1353
1354#if 0
1355/* Polygon kernel for inserting uv's non overlapping */
1356
1357static int p_polygon_point_in(const float cp1[2], const float cp2[2], const float p[2])
1358{
1359 if ((cp1[0] == p[0]) && (cp1[1] == p[1])) {
1360 return 2;
1361 }
1362 else if ((cp2[0] == p[0]) && (cp2[1] == p[1])) {
1363 return 3;
1364 }
1365 else {
1366 return (p_area_signed(cp1, cp2, p) >= 0.0f);
1367 }
1368}
1369
1370static void p_polygon_kernel_clip(float (*oldpoints)[2],
1371 int noldpoints,
1372 float (*newpoints)[2],
1373 int *r_nnewpoints,
1374 const float cp1[2],
1375 const float cp2[2])
1376{
1377 float *p2, *p1, isect[2];
1378 int i, p2in, p1in;
1379
1380 p1 = oldpoints[noldpoints - 1];
1381 p1in = p_polygon_point_in(cp1, cp2, p1);
1382 *r_nnewpoints = 0;
1383
1384 for (i = 0; i < noldpoints; i++) {
1385 p2 = oldpoints[i];
1386 p2in = p_polygon_point_in(cp1, cp2, p2);
1387
1388 if ((p2in >= 2) || (p1in && p2in)) {
1389 newpoints[*r_nnewpoints][0] = p2[0];
1390 newpoints[*r_nnewpoints][1] = p2[1];
1391 (*r_nnewpoints)++;
1392 }
1393 else if (p1in && !p2in) {
1394 if (p1in != 3) {
1395 p_intersect_line_2d(p1, p2, cp1, cp2, isect);
1396 newpoints[*r_nnewpoints][0] = isect[0];
1397 newpoints[*r_nnewpoints][1] = isect[1];
1398 (*r_nnewpoints)++;
1399 }
1400 }
1401 else if (!p1in && p2in) {
1402 p_intersect_line_2d(p1, p2, cp1, cp2, isect);
1403 newpoints[*r_nnewpoints][0] = isect[0];
1404 newpoints[*r_nnewpoints][1] = isect[1];
1405 (*r_nnewpoints)++;
1406
1407 newpoints[*r_nnewpoints][0] = p2[0];
1408 newpoints[*r_nnewpoints][1] = p2[1];
1409 (*r_nnewpoints)++;
1410 }
1411
1412 p1in = p2in;
1413 p1 = p2;
1414 }
1415}
1416
1417static void p_polygon_kernel_center(float (*points)[2], int npoints, float *center)
1418{
1419 int i, size, nnewpoints = npoints;
1420 float(*oldpoints)[2], (*newpoints)[2], *p1, *p2;
1421
1422 size = npoints * 3;
1423 oldpoints = MEM_malloc_arrayN<float[2]>(size_t(size), "PPolygonOldPoints");
1424 newpoints = MEM_malloc_arrayN<float[2]>(size_t(size), "PPolygonNewPoints");
1425
1426 memcpy(oldpoints, points, sizeof(float[2]) * npoints);
1427
1428 for (i = 0; i < npoints; i++) {
1429 p1 = points[i];
1430 p2 = points[(i + 1) % npoints];
1431 p_polygon_kernel_clip(oldpoints, nnewpoints, newpoints, &nnewpoints, p1, p2);
1432
1433 if (nnewpoints == 0) {
1434 /* degenerate case, use center of original face */
1435 memcpy(oldpoints, points, sizeof(float[2]) * npoints);
1436 nnewpoints = npoints;
1437 break;
1438 }
1439 else if (nnewpoints == 1) {
1440 /* degenerate case, use remaining point */
1441 center[0] = newpoints[0][0];
1442 center[1] = newpoints[0][1];
1443
1444 MEM_freeN(oldpoints);
1445 MEM_freeN(newpoints);
1446
1447 return;
1448 }
1449
1450 if (nnewpoints * 2 > size) {
1451 size *= 2;
1452 MEM_freeN(oldpoints);
1453 oldpoints = MEM_mallocN(sizeof(float[2]) * size, "oldpoints");
1454 memcpy(oldpoints, newpoints, sizeof(float[2]) * nnewpoints);
1455 MEM_freeN(newpoints);
1456 newpoints = MEM_mallocN(sizeof(float[2]) * size, "newpoints");
1457 }
1458 else {
1459 float(*sw_points)[2] = oldpoints;
1460 oldpoints = newpoints;
1461 newpoints = sw_points;
1462 }
1463 }
1464
1465 center[0] = center[1] = 0.0f;
1466
1467 for (i = 0; i < nnewpoints; i++) {
1468 center[0] += oldpoints[i][0];
1469 center[1] += oldpoints[i][1];
1470 }
1471
1472 center[0] /= nnewpoints;
1473 center[1] /= nnewpoints;
1474
1475 MEM_freeN(oldpoints);
1476 MEM_freeN(newpoints);
1477}
1478#endif
1479
1480/* Simplify/Complexity
1481 *
1482 * This is currently used for eliminating degenerate vertex coordinates.
1483 * In the future this can be used for efficient unwrapping of high resolution
1484 * charts at lower resolution. */
1485
1486#if 0
1487
1488static float p_vert_cotan(const float v1[3], const float v2[3], const float v3[3])
1489{
1490 float a[3], b[3], c[3], clen;
1491
1492 sub_v3_v3v3(a, v2, v1);
1493 sub_v3_v3v3(b, v3, v1);
1494 cross_v3_v3v3(c, a, b);
1495
1496 clen = len_v3(c);
1497
1498 if (clen == 0.0f) {
1499 return 0.0f;
1500 }
1501
1502 return dot_v3v3(a, b) / clen;
1503}
1504
1505static bool p_vert_flipped_wheel_triangle(PVert *v)
1506{
1507 PEdge *e = v->edge;
1508
1509 do {
1510 if (p_face_uv_area_signed(e->face) < 0.0f) {
1511 return true;
1512 }
1513
1515 } while (e && (e != v->edge));
1516
1517 return false;
1518}
1519
1520static bool p_vert_map_harmonic_weights(PVert *v)
1521{
1522 float weightsum, positionsum[2], olduv[2];
1523
1524 weightsum = 0.0f;
1525 positionsum[0] = positionsum[1] = 0.0f;
1526
1527 if (p_vert_interior(v)) {
1528 PEdge *e = v->edge;
1529
1530 do {
1531 float t1, t2, weight;
1532 PVert *v1, *v2;
1533
1534 v1 = e->next->vert;
1535 v2 = e->next->next->vert;
1536 t1 = p_vert_cotan(v2->co, e->vert->co, v1->co);
1537
1538 v1 = e->pair->next->vert;
1539 v2 = e->pair->next->next->vert;
1540 t2 = p_vert_cotan(v2->co, e->pair->vert->co, v1->co);
1541
1542 weight = 0.5f * (t1 + t2);
1543 weightsum += weight;
1544 positionsum[0] += weight * e->pair->vert->uv[0];
1545 positionsum[1] += weight * e->pair->vert->uv[1];
1546
1548 } while (e && (e != v->edge));
1549 }
1550 else {
1551 PEdge *e = v->edge;
1552
1553 do {
1554 float t1, t2;
1555 PVert *v1, *v2;
1556
1557 v2 = e->next->vert;
1558 v1 = e->next->next->vert;
1559
1560 t1 = p_vert_cotan(v1->co, v->co, v2->co);
1561 t2 = p_vert_cotan(v2->co, v->co, v1->co);
1562
1563 weightsum += t1 + t2;
1564 positionsum[0] += (v2->uv[1] - v1->uv[1]) + (t1 * v2->uv[0] + t2 * v1->uv[0]);
1565 positionsum[1] += (v1->uv[0] - v2->uv[0]) + (t1 * v2->uv[1] + t2 * v1->uv[1]);
1566
1568 } while (e && (e != v->edge));
1569 }
1570
1571 if (weightsum != 0.0f) {
1572 weightsum = 1.0f / weightsum;
1573 positionsum[0] *= weightsum;
1574 positionsum[1] *= weightsum;
1575 }
1576
1577 olduv[0] = v->uv[0];
1578 olduv[1] = v->uv[1];
1579 v->uv[0] = positionsum[0];
1580 v->uv[1] = positionsum[1];
1581
1582 if (p_vert_flipped_wheel_triangle(v)) {
1583 v->uv[0] = olduv[0];
1584 v->uv[1] = olduv[1];
1585
1586 return false;
1587 }
1588
1589 return true;
1590}
1591
1592static void p_vert_harmonic_insert(PVert *v)
1593{
1594 PEdge *e;
1595
1596 if (!p_vert_map_harmonic_weights(v)) {
1597 /* do face kernel center insertion: this is quite slow, but should
1598 * only be needed for 0.01 % of verts or so, when insert with harmonic
1599 * weights fails */
1600
1601 int npoints = 0, i;
1602 float(*points)[2];
1603
1604 e = v->edge;
1605 do {
1606 npoints++;
1608 } while (e && (e != v->edge));
1609
1610 if (e == nullptr) {
1611 npoints++;
1612 }
1613
1614 points = MEM_malloc_arrayN<float[2]>(size_t(npoints), "PHarmonicPoints");
1615
1616 e = v->edge;
1617 i = 0;
1618 do {
1619 PEdge *nexte = p_wheel_edge_next(e);
1620
1621 points[i][0] = e->next->vert->uv[0];
1622 points[i][1] = e->next->vert->uv[1];
1623
1624 if (nexte == nullptr) {
1625 i++;
1626 points[i][0] = e->next->next->vert->uv[0];
1627 points[i][1] = e->next->next->vert->uv[1];
1628 break;
1629 }
1630
1631 e = nexte;
1632 i++;
1633 } while (e != v->edge);
1634
1635 p_polygon_kernel_center(points, npoints, v->uv);
1636
1637 MEM_freeN(points);
1638 }
1639
1640 e = v->edge;
1641 do {
1642 if (!(e->next->vert->flag & PVERT_PIN)) {
1643 p_vert_map_harmonic_weights(e->next->vert);
1644 }
1646 } while (e && (e != v->edge));
1647
1648 p_vert_map_harmonic_weights(v);
1649}
1650#endif
1651
1653{
1654 PEdge *start = v->edge;
1655
1656 /* set v->edge pointer to the edge with no pair, if there is one */
1657 while (v->edge->pair) {
1658 v->edge = p_wheel_edge_prev(v->edge);
1659
1660 if (v->edge == start) {
1661 break;
1662 }
1663 }
1664}
1665
1666static void p_collapsing_verts(PEdge *edge, PEdge *pair, PVert **r_newv, PVert **r_keepv)
1667{
1668 /* the two vertices that are involved in the collapse */
1669 if (edge) {
1670 *r_newv = edge->vert;
1671 *r_keepv = edge->next->vert;
1672 }
1673 else {
1674 *r_newv = pair->next->vert;
1675 *r_keepv = pair->vert;
1676 }
1677}
1678
1679static void p_collapse_edge(PEdge *edge, PEdge *pair)
1680{
1681 PVert *oldv, *keepv;
1682 PEdge *e;
1683
1684 p_collapsing_verts(edge, pair, &oldv, &keepv);
1685
1686 /* change e->vert pointers from old vertex to the target vertex */
1687 e = oldv->edge;
1688 do {
1689 if ((e != edge) && !(pair && pair->next == e)) {
1690 e->vert = keepv;
1691 }
1692
1694 } while (e && (e != oldv->edge));
1695
1696 /* set keepv->edge pointer */
1697 if ((edge && (keepv->edge == edge->next)) || (keepv->edge == pair)) {
1698 if (edge && edge->next->pair) {
1699 keepv->edge = edge->next->pair->next;
1700 }
1701 else if (pair && pair->next->next->pair) {
1702 keepv->edge = pair->next->next->pair;
1703 }
1704 else if (edge && edge->next->next->pair) {
1705 keepv->edge = edge->next->next->pair;
1706 }
1707 else {
1708 keepv->edge = pair->next->pair->next;
1709 }
1710 }
1711
1712 /* update pairs and v->edge pointers */
1713 if (edge) {
1714 PEdge *e1 = edge->next, *e2 = e1->next;
1715
1716 if (e1->pair) {
1717 e1->pair->pair = e2->pair;
1718 }
1719
1720 if (e2->pair) {
1721 e2->pair->pair = e1->pair;
1722 e2->vert->edge = p_wheel_edge_prev(e2);
1723 }
1724 else {
1725 e2->vert->edge = p_wheel_edge_next(e2);
1726 }
1727
1728 p_vert_fix_edge_pointer(e2->vert);
1729 }
1730
1731 if (pair) {
1732 PEdge *e1 = pair->next, *e2 = e1->next;
1733
1734 if (e1->pair) {
1735 e1->pair->pair = e2->pair;
1736 }
1737
1738 if (e2->pair) {
1739 e2->pair->pair = e1->pair;
1740 e2->vert->edge = p_wheel_edge_prev(e2);
1741 }
1742 else {
1743 e2->vert->edge = p_wheel_edge_next(e2);
1744 }
1745
1746 p_vert_fix_edge_pointer(e2->vert);
1747 }
1748
1750
1751 /* mark for move to collapsed list later */
1752 oldv->flag |= PVERT_COLLAPSE;
1753
1754 if (edge) {
1755 PFace *f = edge->face;
1756 PEdge *e1 = edge->next, *e2 = e1->next;
1757
1758 f->flag |= PFACE_COLLAPSE;
1759 edge->flag |= PEDGE_COLLAPSE;
1760 e1->flag |= PEDGE_COLLAPSE;
1761 e2->flag |= PEDGE_COLLAPSE;
1762 }
1763
1764 if (pair) {
1765 PFace *f = pair->face;
1766 PEdge *e1 = pair->next, *e2 = e1->next;
1767
1768 f->flag |= PFACE_COLLAPSE;
1769 pair->flag |= PEDGE_COLLAPSE;
1770 e1->flag |= PEDGE_COLLAPSE;
1771 e2->flag |= PEDGE_COLLAPSE;
1772 }
1773}
1774
1775#if 0
1776static void p_split_vertex(PEdge *edge, PEdge *pair)
1777{
1778 PVert *newv, *keepv;
1779 PEdge *e;
1780
1781 p_collapsing_verts(edge, pair, &newv, &keepv);
1782
1783 /* update edge pairs */
1784 if (edge) {
1785 PEdge *e1 = edge->next, *e2 = e1->next;
1786
1787 if (e1->pair) {
1788 e1->pair->pair = e1;
1789 }
1790 if (e2->pair) {
1791 e2->pair->pair = e2;
1792 }
1793
1794 e2->vert->edge = e2;
1795 p_vert_fix_edge_pointer(e2->vert);
1796 keepv->edge = e1;
1797 }
1798
1799 if (pair) {
1800 PEdge *e1 = pair->next, *e2 = e1->next;
1801
1802 if (e1->pair) {
1803 e1->pair->pair = e1;
1804 }
1805 if (e2->pair) {
1806 e2->pair->pair = e2;
1807 }
1808
1809 e2->vert->edge = e2;
1810 p_vert_fix_edge_pointer(e2->vert);
1811 keepv->edge = pair;
1812 }
1813
1815
1816 /* set e->vert pointers to restored vertex */
1817 e = newv->edge;
1818 do {
1819 e->vert = newv;
1821 } while (e && (e != newv->edge));
1822}
1823#endif
1824
1826{
1827 PVert *oldv, *keepv;
1828
1829 p_collapsing_verts(edge, pair, &oldv, &keepv);
1830
1831 /* boundary edges */
1832 if (!edge || !pair) {
1833 /* avoid collapsing chart into an edge */
1834 if (edge && !edge->next->pair && !edge->next->next->pair) {
1835 return false;
1836 }
1837 if (pair && !pair->next->pair && !pair->next->next->pair) {
1838 return false;
1839 }
1840 }
1841 /* avoid merging two boundaries (oldv and keepv are on the 'other side' of
1842 * the chart) */
1843 else if (!p_vert_interior(oldv) && !p_vert_interior(keepv)) {
1844 return false;
1845 }
1846
1847 return true;
1848}
1849
1850#if 0
1851static bool p_collapse_normal_flipped(float *v1, float *v2, float *vold, float *vnew)
1852{
1853 float nold[3], nnew[3], sub1[3], sub2[3];
1854
1855 sub_v3_v3v3(sub1, vold, v1);
1856 sub_v3_v3v3(sub2, vold, v2);
1857 cross_v3_v3v3(nold, sub1, sub2);
1858
1859 sub_v3_v3v3(sub1, vnew, v1);
1860 sub_v3_v3v3(sub2, vnew, v2);
1861 cross_v3_v3v3(nnew, sub1, sub2);
1862
1863 return (dot_v3v3(nold, nnew) <= 0.0f);
1864}
1865
1866static bool p_collapse_allowed_geometric(PEdge *edge, PEdge *pair)
1867{
1868 PVert *oldv, *keepv;
1869 PEdge *e;
1870 float angulardefect, angle;
1871
1872 p_collapsing_verts(edge, pair, &oldv, &keepv);
1873
1874 angulardefect = 2 * M_PI;
1875
1876 e = oldv->edge;
1877 do {
1878 float a[3], b[3], minangle, maxangle;
1879 PEdge *e1 = e->next, *e2 = e1->next;
1880 PVert *v1 = e1->vert, *v2 = e2->vert;
1881 int i;
1882
1883 angle = p_vec_angle(v1->co, oldv->co, v2->co);
1884 angulardefect -= angle;
1885
1886 /* skip collapsing faces */
1887 if (v1 == keepv || v2 == keepv) {
1889 continue;
1890 }
1891
1892 if (p_collapse_normal_flipped(v1->co, v2->co, oldv->co, keepv->co)) {
1893 return false;
1894 }
1895
1896 a[0] = angle;
1897 a[1] = p_vec_angle(v2->co, v1->co, oldv->co);
1898 a[2] = M_PI - a[0] - a[1];
1899
1900 b[0] = p_vec_angle(v1->co, keepv->co, v2->co);
1901 b[1] = p_vec_angle(v2->co, v1->co, keepv->co);
1902 b[2] = M_PI - b[0] - b[1];
1903
1904 /* ABF criterion 1: avoid sharp and obtuse angles. */
1905 minangle = DEG2RADF(15.0);
1906 maxangle = M_PI - minangle;
1907
1908 for (i = 0; i < 3; i++) {
1909 if ((b[i] < a[i]) && (b[i] < minangle)) {
1910 return false;
1911 }
1912 else if ((b[i] > a[i]) && (b[i] > maxangle)) {
1913 return false;
1914 }
1915 }
1916
1918 } while (e && (e != oldv->edge));
1919
1920 if (p_vert_interior(oldv)) {
1921 /* HLSCM criterion: angular defect smaller than threshold. */
1922 if (fabsf(angulardefect) > DEG2RADF(30.0)) {
1923 return false;
1924 }
1925 }
1926 else {
1927 PVert *v1 = p_boundary_edge_next(oldv->edge)->vert;
1928 PVert *v2 = p_boundary_edge_prev(oldv->edge)->vert;
1929
1930 /* ABF++ criterion 2: avoid collapsing verts inwards. */
1931 if (p_vert_interior(keepv)) {
1932 return false;
1933 }
1934
1935 /* Don't collapse significant boundary changes. */
1936 angle = p_vec_angle(v1->co, oldv->co, v2->co);
1937 if (angle < DEG2RADF(160.0)) {
1938 return false;
1939 }
1940 }
1941
1942 return true;
1943}
1944
1945static bool p_collapse_allowed(PEdge *edge, PEdge *pair)
1946{
1947 PVert *oldv, *keepv;
1948
1949 p_collapsing_verts(edge, pair, &oldv, &keepv);
1950
1951 if (oldv->flag & PVERT_PIN) {
1952 return false;
1953 }
1954
1955 return (p_collapse_allowed_topologic(edge, pair) && p_collapse_allowed_geometric(edge, pair));
1956}
1957
1958static float p_collapse_cost(PEdge *edge, PEdge *pair)
1959{
1960 /* based on volume and boundary optimization from:
1961 * "Fast and Memory Efficient Polygonal Simplification" P. Lindstrom, G. Turk */
1962
1963 PVert *oldv, *keepv;
1964 PEdge *e;
1965 PFace *oldf1, *oldf2;
1966 float volumecost = 0.0f, areacost = 0.0f, edgevec[3], cost, weight, elen;
1967 float shapecost = 0.0f;
1968 float shapeold = 0.0f, shapenew = 0.0f;
1969 int nshapeold = 0, nshapenew = 0;
1970
1971 p_collapsing_verts(edge, pair, &oldv, &keepv);
1972 oldf1 = (edge) ? edge->face : nullptr;
1973 oldf2 = (pair) ? pair->face : nullptr;
1974
1975 sub_v3_v3v3(edgevec, keepv->co, oldv->co);
1976
1977 e = oldv->edge;
1978 do {
1979 float a1, a2, a3;
1980 float *co1 = e->next->vert->co;
1981 float *co2 = e->next->next->vert->co;
1982
1983 if (!ELEM(e->face, oldf1, oldf2)) {
1984 float tetrav2[3], tetrav3[3];
1985
1986 /* tetrahedron volume = (1/3!)*|a.(b x c)| */
1987 sub_v3_v3v3(tetrav2, co1, oldv->co);
1988 sub_v3_v3v3(tetrav3, co2, oldv->co);
1989 volumecost += fabsf(volume_tri_tetrahedron_signed_v3(tetrav2, tetrav3, edgevec));
1990
1991# if 0
1992 shapecost += dot_v3v3(co1, keepv->co);
1993
1994 if (p_wheel_edge_next(e) == nullptr) {
1995 shapecost += dot_v3v3(co2, keepv->co);
1996 }
1997# endif
1998
1999 p_triangle_angles(oldv->co, co1, co2, &a1, &a2, &a3);
2000 a1 = a1 - M_PI / 3.0;
2001 a2 = a2 - M_PI / 3.0;
2002 a3 = a3 - M_PI / 3.0;
2003 shapeold = (a1 * a1 + a2 * a2 + a3 * a3) / (M_PI_2 * M_PI_2);
2004
2005 nshapeold++;
2006 }
2007 else {
2008 p_triangle_angles(keepv->co, co1, co2, &a1, &a2, &a3);
2009 a1 = a1 - M_PI / 3.0;
2010 a2 = a2 - M_PI / 3.0;
2011 a3 = a3 - M_PI / 3.0;
2012 shapenew = (a1 * a1 + a2 * a2 + a3 * a3) / (M_PI_2 * M_PI_2);
2013
2014 nshapenew++;
2015 }
2016
2018 } while (e && (e != oldv->edge));
2019
2020 if (!p_vert_interior(oldv)) {
2021 PVert *v1 = p_boundary_edge_prev(oldv->edge)->vert;
2022 PVert *v2 = p_boundary_edge_next(oldv->edge)->vert;
2023
2024 areacost = area_tri_v3(oldv->co, v1->co, v2->co);
2025 }
2026
2027 elen = len_v3(edgevec);
2028 weight = 1.0f; /* 0.2f */
2029 cost = weight * volumecost * volumecost + elen * elen * areacost * areacost;
2030# if 0
2031 cost += shapecost;
2032# else
2033 shapeold /= nshapeold;
2034 shapenew /= nshapenew;
2035 shapecost = (shapeold + 0.00001) / (shapenew + 0.00001);
2036
2037 cost *= shapecost;
2038# endif
2039
2040 return cost;
2041}
2042#endif
2043
2045 PVert *vert,
2046 float *r_mincost,
2047 PEdge **r_mine,
2048 const std::function<float(PEdge *, PEdge *)> &collapse_cost_fn,
2049 const std::function<float(PEdge *, PEdge *)> &collapse_allowed_fn)
2050{
2051 PEdge *e, *enext, *pair;
2052
2053 *r_mine = nullptr;
2054 *r_mincost = 0.0f;
2055 e = vert->edge;
2056 do {
2057 if (collapse_allowed_fn(e, e->pair)) {
2058 float cost = collapse_cost_fn(e, e->pair);
2059
2060 if ((*r_mine == nullptr) || (cost < *r_mincost)) {
2061 *r_mincost = cost;
2062 *r_mine = e;
2063 }
2064 }
2065
2066 enext = p_wheel_edge_next(e);
2067
2068 if (enext == nullptr) {
2069 /* The other boundary edge, where we only have the pair half-edge. */
2070 pair = e->next->next;
2071
2072 if (collapse_allowed_fn(nullptr, pair)) {
2073 float cost = collapse_cost_fn(nullptr, pair);
2074
2075 if ((*r_mine == nullptr) || (cost < *r_mincost)) {
2076 *r_mincost = cost;
2077 *r_mine = pair;
2078 }
2079 }
2080
2081 break;
2082 }
2083
2084 e = enext;
2085 } while (e != vert->edge);
2086}
2087
2088static void p_chart_post_collapse_flush(PChart *chart, PEdge *collapsed)
2089{
2090 /* Move to `collapsed_*`. */
2091
2092 PVert *v, *nextv = nullptr, *verts = chart->verts;
2093 PEdge *e, *nexte = nullptr, *edges = chart->edges, *laste = nullptr;
2094 PFace *f, *nextf = nullptr, *faces = chart->faces;
2095
2096 chart->verts = chart->collapsed_verts = nullptr;
2097 chart->edges = chart->collapsed_edges = nullptr;
2098 chart->faces = chart->collapsed_faces = nullptr;
2099
2100 chart->nverts = chart->nedges = chart->nfaces = 0;
2101
2102 for (v = verts; v; v = nextv) {
2103 nextv = v->nextlink;
2104
2105 if (v->flag & PVERT_COLLAPSE) {
2106 v->nextlink = chart->collapsed_verts;
2107 chart->collapsed_verts = v;
2108 }
2109 else {
2110 v->nextlink = chart->verts;
2111 chart->verts = v;
2112 chart->nverts++;
2113 }
2114 }
2115
2116 for (e = edges; e; e = nexte) {
2117 nexte = e->nextlink;
2118
2119 if (!collapsed || !(e->flag & PEDGE_COLLAPSE_EDGE)) {
2120 if (e->flag & PEDGE_COLLAPSE) {
2121 e->nextlink = chart->collapsed_edges;
2122 chart->collapsed_edges = e;
2123 }
2124 else {
2125 e->nextlink = chart->edges;
2126 chart->edges = e;
2127 chart->nedges++;
2128 }
2129 }
2130 }
2131
2132 /* these are added last so they can be popped of in the right order
2133 * for splitting */
2134 for (e = collapsed; e; e = e->nextlink) {
2135 e->nextlink = e->u.nextcollapse;
2136 laste = e;
2137 }
2138 if (laste) {
2139 laste->nextlink = chart->collapsed_edges;
2140 chart->collapsed_edges = collapsed;
2141 }
2142
2143 for (f = faces; f; f = nextf) {
2144 nextf = f->nextlink;
2145
2146 if (f->flag & PFACE_COLLAPSE) {
2147 f->nextlink = chart->collapsed_faces;
2148 chart->collapsed_faces = f;
2149 }
2150 else {
2151 f->nextlink = chart->faces;
2152 chart->faces = f;
2153 chart->nfaces++;
2154 }
2155 }
2156}
2157
2159 std::function<float(PEdge *, PEdge *)> collapse_cost_fn,
2160 std::function<float(PEdge *, PEdge *)> collapse_allowed_fn)
2161{
2162 /* For debugging. */
2163 static const int MAX_SIMPLIFY = INT_MAX;
2164
2165 /* Computes a list of edge collapses / vertex splits. The collapsed
2166 * simplices go in the `chart->collapsed_*` lists, The original and
2167 * collapsed may then be view as stacks, where the next collapse/split
2168 * is at the top of the respective lists. */
2169
2170 Heap *heap = BLI_heap_new();
2171 PEdge *collapsededges = nullptr;
2172 int ncollapsed = 0;
2173 Vector<PVert *> wheelverts;
2174 wheelverts.reserve(16);
2175
2176 /* insert all potential collapses into heap */
2177 for (PVert *v = chart->verts; v; v = v->nextlink) {
2178 float cost;
2179 PEdge *e = nullptr;
2180
2181 p_collapse_cost_vertex(v, &cost, &e, collapse_cost_fn, collapse_allowed_fn);
2182
2183 if (e) {
2184 v->u.heaplink = BLI_heap_insert(heap, cost, e);
2185 }
2186 else {
2187 v->u.heaplink = nullptr;
2188 }
2189 }
2190
2191 for (PEdge *e = chart->edges; e; e = e->nextlink) {
2192 e->u.nextcollapse = nullptr;
2193 }
2194
2195 /* pop edge collapse out of heap one by one */
2196 while (!BLI_heap_is_empty(heap)) {
2197 if (ncollapsed == MAX_SIMPLIFY) {
2198 break;
2199 }
2200
2201 HeapNode *link = BLI_heap_top(heap);
2202 PEdge *edge = (PEdge *)BLI_heap_pop_min(heap), *pair = edge->pair;
2203 PVert *oldv, *keepv;
2204 PEdge *wheele, *nexte;
2205
2206 /* remember the edges we collapsed */
2207 edge->u.nextcollapse = collapsededges;
2208 collapsededges = edge;
2209
2210 if (edge->vert->u.heaplink != link) {
2212 edge->next->vert->u.heaplink = nullptr;
2213 std::swap(edge, pair);
2214 }
2215 else {
2216 edge->flag |= PEDGE_COLLAPSE_EDGE;
2217 edge->vert->u.heaplink = nullptr;
2218 }
2219
2220 p_collapsing_verts(edge, pair, &oldv, &keepv);
2221
2222 /* gather all wheel verts and remember them before collapse */
2223 wheelverts.clear();
2224 wheele = oldv->edge;
2225
2226 do {
2227 wheelverts.append(wheele->next->vert);
2228 nexte = p_wheel_edge_next(wheele);
2229
2230 if (nexte == nullptr) {
2231 wheelverts.append(wheele->next->next->vert);
2232 }
2233
2234 wheele = nexte;
2235 } while (wheele && (wheele != oldv->edge));
2236
2237 /* collapse */
2238 p_collapse_edge(edge, pair);
2239
2240 for (PVert *v : wheelverts) {
2241 float cost;
2242 PEdge *collapse = nullptr;
2243
2244 if (v->u.heaplink) {
2245 BLI_heap_remove(heap, v->u.heaplink);
2246 v->u.heaplink = nullptr;
2247 }
2248
2249 p_collapse_cost_vertex(v, &cost, &collapse, collapse_cost_fn, collapse_allowed_fn);
2250
2251 if (collapse) {
2252 v->u.heaplink = BLI_heap_insert(heap, cost, collapse);
2253 }
2254 }
2255
2256 ncollapsed++;
2257 }
2258
2259 BLI_heap_free(heap, nullptr);
2260
2261 p_chart_post_collapse_flush(chart, collapsededges);
2262}
2263
2264#if 0
2265static void p_chart_post_split_flush(PChart *chart)
2266{
2267 /* Move from `collapsed_*`. */
2268
2269 PVert *v, *nextv = nullptr;
2270 PEdge *e, *nexte = nullptr;
2271 PFace *f, *nextf = nullptr;
2272
2273 for (v = chart->collapsed_verts; v; v = nextv) {
2274 nextv = v->nextlink;
2275 v->nextlink = chart->verts;
2276 chart->verts = v;
2277 chart->nverts++;
2278 }
2279
2280 for (e = chart->collapsed_edges; e; e = nexte) {
2281 nexte = e->nextlink;
2282 e->nextlink = chart->edges;
2283 chart->edges = e;
2284 chart->nedges++;
2285 }
2286
2287 for (f = chart->collapsed_faces; f; f = nextf) {
2288 nextf = f->nextlink;
2289 f->nextlink = chart->faces;
2290 chart->faces = f;
2291 chart->nfaces++;
2292 }
2293
2294 chart->collapsed_verts = nullptr;
2295 chart->collapsed_edges = nullptr;
2296 chart->collapsed_faces = nullptr;
2297}
2298
2299static void p_chart_complexify(PChart *chart)
2300{
2301 /* For debugging. */
2302 static const int MAX_COMPLEXIFY = INT_MAX;
2303
2304 PEdge *e, *pair, *edge;
2305 PVert *newv, *keepv;
2306 int x = 0;
2307
2308 for (e = chart->collapsed_edges; e; e = e->nextlink) {
2309 if (!(e->flag & PEDGE_COLLAPSE_EDGE)) {
2310 break;
2311 }
2312
2313 edge = e;
2314 pair = e->pair;
2315
2316 if (edge->flag & PEDGE_COLLAPSE_PAIR) {
2317 std::swap(edge, pair);
2318 }
2319
2320 p_split_vertex(edge, pair);
2321 p_collapsing_verts(edge, pair, &newv, &keepv);
2322
2323 if (x >= MAX_COMPLEXIFY) {
2324 newv->uv[0] = keepv->uv[0];
2325 newv->uv[1] = keepv->uv[1];
2326 }
2327 else {
2328 p_vert_harmonic_insert(newv);
2329 x++;
2330 }
2331 }
2332
2333 p_chart_post_split_flush(chart);
2334}
2335
2336static void p_chart_simplify(PChart *chart)
2337{
2338 /* Not implemented, needs proper reordering in split_flush. */
2339}
2340#endif
2341
2342/* ABF */
2343
2344#define ABF_MAX_ITER 20
2345
2353
2355{
2356 int i;
2357
2358 sys->alpha = MEM_malloc_arrayN<float>(size_t(sys->nangles), "ABFalpha");
2359 sys->beta = MEM_malloc_arrayN<float>(size_t(sys->nangles), "ABFbeta");
2360 sys->sine = MEM_malloc_arrayN<float>(size_t(sys->nangles), "ABFsine");
2361 sys->cosine = MEM_malloc_arrayN<float>(size_t(sys->nangles), "ABFcosine");
2362 sys->weight = MEM_malloc_arrayN<float>(size_t(sys->nangles), "ABFweight");
2363
2364 sys->bAlpha = MEM_malloc_arrayN<float>(size_t(sys->nangles), "ABFbalpha");
2365 sys->bTriangle = MEM_malloc_arrayN<float>(size_t(sys->nfaces), "ABFbtriangle");
2366 sys->bInterior = MEM_malloc_arrayN<float>(2 * size_t(sys->ninterior), "ABFbinterior");
2367
2368 sys->lambdaTriangle = MEM_calloc_arrayN<float>(sys->nfaces, "ABFlambdatri");
2369 sys->lambdaPlanar = MEM_calloc_arrayN<float>(sys->ninterior, "ABFlamdaplane");
2370 sys->lambdaLength = MEM_malloc_arrayN<float>(sys->ninterior, "ABFlambdalen");
2371
2372 sys->J2dt = MEM_malloc_arrayN<float[3]>(size_t(sys->nangles), "ABFj2dt");
2373 sys->bstar = MEM_malloc_arrayN<float>(size_t(sys->nfaces), "ABFbstar");
2374 sys->dstar = MEM_malloc_arrayN<float>(size_t(sys->nfaces), "ABFdstar");
2375
2376 for (i = 0; i < sys->ninterior; i++) {
2377 sys->lambdaLength[i] = 1.0;
2378 }
2379}
2380
2382{
2383 MEM_freeN(sys->alpha);
2384 MEM_freeN(sys->beta);
2385 MEM_freeN(sys->sine);
2386 MEM_freeN(sys->cosine);
2387 MEM_freeN(sys->weight);
2388 MEM_freeN(sys->bAlpha);
2389 MEM_freeN(sys->bTriangle);
2390 MEM_freeN(sys->bInterior);
2392 MEM_freeN(sys->lambdaPlanar);
2393 MEM_freeN(sys->lambdaLength);
2394 MEM_freeN(sys->J2dt);
2395 MEM_freeN(sys->bstar);
2396 MEM_freeN(sys->dstar);
2397}
2398
2400{
2401 float *sine = sys->sine;
2402 float *cosine = sys->cosine;
2403 const float *alpha = sys->alpha;
2404
2405 for (int i = 0; i < sys->nangles; i++) {
2406 const double angle = alpha[i];
2407 sine[i] = sin(angle);
2408 cosine[i] = cos(angle);
2409 }
2410}
2411
2412static float p_abf_compute_sin_product(PAbfSystem *sys, PVert *v, int aid)
2413{
2414 PEdge *e, *e1, *e2;
2415 float sin1, sin2;
2416
2417 sin1 = sin2 = 1.0;
2418
2419 e = v->edge;
2420 do {
2421 e1 = e->next;
2422 e2 = e->next->next;
2423
2424 if (aid == e1->u.id) {
2425 /* we are computing a derivative for this angle,
2426 * so we use cos and drop the other part */
2427 sin1 *= sys->cosine[e1->u.id];
2428 sin2 = 0.0;
2429 }
2430 else {
2431 sin1 *= sys->sine[e1->u.id];
2432 }
2433
2434 if (aid == e2->u.id) {
2435 /* see above */
2436 sin1 = 0.0;
2437 sin2 *= sys->cosine[e2->u.id];
2438 }
2439 else {
2440 sin2 *= sys->sine[e2->u.id];
2441 }
2442
2443 e = e->next->next->pair;
2444 } while (e && (e != v->edge));
2445
2446 return (sin1 - sin2);
2447}
2448
2450{
2451 PVert *v = e->vert, *v1 = e->next->vert, *v2 = e->next->next->vert;
2452 float deriv;
2453
2454 deriv = (sys->alpha[e->u.id] - sys->beta[e->u.id]) * sys->weight[e->u.id];
2455 deriv += sys->lambdaTriangle[f->u.id];
2456
2457 if (v->flag & PVERT_INTERIOR) {
2458 deriv += sys->lambdaPlanar[v->u.id];
2459 }
2460
2461 if (v1->flag & PVERT_INTERIOR) {
2462 float product = p_abf_compute_sin_product(sys, v1, e->u.id);
2463 deriv += sys->lambdaLength[v1->u.id] * product;
2464 }
2465
2466 if (v2->flag & PVERT_INTERIOR) {
2467 float product = p_abf_compute_sin_product(sys, v2, e->u.id);
2468 deriv += sys->lambdaLength[v2->u.id] * product;
2469 }
2470
2471 return deriv;
2472}
2473
2474static float p_abf_compute_gradient(PAbfSystem *sys, PChart *chart)
2475{
2476 PFace *f;
2477 PEdge *e;
2478 PVert *v;
2479 float norm = 0.0;
2480
2481 for (f = chart->faces; f; f = f->nextlink) {
2482 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
2483 float gtriangle, galpha1, galpha2, galpha3;
2484
2485 galpha1 = p_abf_compute_grad_alpha(sys, f, e1);
2486 galpha2 = p_abf_compute_grad_alpha(sys, f, e2);
2487 galpha3 = p_abf_compute_grad_alpha(sys, f, e3);
2488
2489 sys->bAlpha[e1->u.id] = -galpha1;
2490 sys->bAlpha[e2->u.id] = -galpha2;
2491 sys->bAlpha[e3->u.id] = -galpha3;
2492
2493 norm += galpha1 * galpha1 + galpha2 * galpha2 + galpha3 * galpha3;
2494
2495 gtriangle = sys->alpha[e1->u.id] + sys->alpha[e2->u.id] + sys->alpha[e3->u.id] - float(M_PI);
2496 sys->bTriangle[f->u.id] = -gtriangle;
2497 norm += gtriangle * gtriangle;
2498 }
2499
2500 for (v = chart->verts; v; v = v->nextlink) {
2501 if (v->flag & PVERT_INTERIOR) {
2502 float gplanar = -2 * M_PI, glength;
2503
2504 e = v->edge;
2505 do {
2506 gplanar += sys->alpha[e->u.id];
2507 e = e->next->next->pair;
2508 } while (e && (e != v->edge));
2509
2510 sys->bInterior[v->u.id] = -gplanar;
2511 norm += gplanar * gplanar;
2512
2513 glength = p_abf_compute_sin_product(sys, v, -1);
2514 sys->bInterior[sys->ninterior + v->u.id] = -glength;
2515 norm += glength * glength;
2516 }
2517 }
2518
2519 return norm;
2520}
2521
2523 const int id,
2524 const float dlambda1,
2525 const float pre)
2526{
2527 float alpha = sys->alpha[id];
2528 const float dalpha = (sys->bAlpha[id] - dlambda1);
2529 alpha += dalpha / sys->weight[id] - pre;
2530 sys->alpha[id] = clamp_f(alpha, 0.0f, float(M_PI));
2531}
2532
2533static bool p_abf_matrix_invert(PAbfSystem *sys, PChart *chart)
2534{
2535 int ninterior = sys->ninterior;
2536 int nvar = 2 * ninterior;
2537 LinearSolver *context = EIG_linear_solver_new(0, nvar, 1);
2538
2539 for (int i = 0; i < nvar; i++) {
2541 }
2542
2543 for (PFace *f = chart->faces; f; f = f->nextlink) {
2544 float wi1, wi2, wi3, b, si, beta[3], j2[3][3], W[3][3];
2545 float row1[6], row2[6], row3[6];
2546 int vid[6];
2547 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
2548 PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
2549
2550 wi1 = 1.0f / sys->weight[e1->u.id];
2551 wi2 = 1.0f / sys->weight[e2->u.id];
2552 wi3 = 1.0f / sys->weight[e3->u.id];
2553
2554 /* bstar1 = (J1*dInv*bAlpha - bTriangle) */
2555 b = sys->bAlpha[e1->u.id] * wi1;
2556 b += sys->bAlpha[e2->u.id] * wi2;
2557 b += sys->bAlpha[e3->u.id] * wi3;
2558 b -= sys->bTriangle[f->u.id];
2559
2560 /* si = J1*d*J1t */
2561 si = 1.0f / (wi1 + wi2 + wi3);
2562
2563 /* J1t*si*bstar1 - bAlpha */
2564 beta[0] = b * si - sys->bAlpha[e1->u.id];
2565 beta[1] = b * si - sys->bAlpha[e2->u.id];
2566 beta[2] = b * si - sys->bAlpha[e3->u.id];
2567
2568 /* use this later for computing other lambda's */
2569 sys->bstar[f->u.id] = b;
2570 sys->dstar[f->u.id] = si;
2571
2572 /* set matrix */
2573 W[0][0] = si - sys->weight[e1->u.id];
2574 W[0][1] = si;
2575 W[0][2] = si;
2576 W[1][0] = si;
2577 W[1][1] = si - sys->weight[e2->u.id];
2578 W[1][2] = si;
2579 W[2][0] = si;
2580 W[2][1] = si;
2581 W[2][2] = si - sys->weight[e3->u.id];
2582
2583 vid[0] = vid[1] = vid[2] = vid[3] = vid[4] = vid[5] = -1;
2584
2585 if (v1->flag & PVERT_INTERIOR) {
2586 vid[0] = v1->u.id;
2587 vid[3] = ninterior + v1->u.id;
2588
2589 sys->J2dt[e1->u.id][0] = j2[0][0] = 1.0f * wi1;
2590 sys->J2dt[e2->u.id][0] = j2[1][0] = p_abf_compute_sin_product(sys, v1, e2->u.id) * wi2;
2591 sys->J2dt[e3->u.id][0] = j2[2][0] = p_abf_compute_sin_product(sys, v1, e3->u.id) * wi3;
2592
2593 EIG_linear_solver_right_hand_side_add(context, 0, v1->u.id, j2[0][0] * beta[0]);
2595 context, 0, ninterior + v1->u.id, j2[1][0] * beta[1] + j2[2][0] * beta[2]);
2596
2597 row1[0] = j2[0][0] * W[0][0];
2598 row2[0] = j2[0][0] * W[1][0];
2599 row3[0] = j2[0][0] * W[2][0];
2600
2601 row1[3] = j2[1][0] * W[0][1] + j2[2][0] * W[0][2];
2602 row2[3] = j2[1][0] * W[1][1] + j2[2][0] * W[1][2];
2603 row3[3] = j2[1][0] * W[2][1] + j2[2][0] * W[2][2];
2604 }
2605
2606 if (v2->flag & PVERT_INTERIOR) {
2607 vid[1] = v2->u.id;
2608 vid[4] = ninterior + v2->u.id;
2609
2610 sys->J2dt[e1->u.id][1] = j2[0][1] = p_abf_compute_sin_product(sys, v2, e1->u.id) * wi1;
2611 sys->J2dt[e2->u.id][1] = j2[1][1] = 1.0f * wi2;
2612 sys->J2dt[e3->u.id][1] = j2[2][1] = p_abf_compute_sin_product(sys, v2, e3->u.id) * wi3;
2613
2614 EIG_linear_solver_right_hand_side_add(context, 0, v2->u.id, j2[1][1] * beta[1]);
2616 context, 0, ninterior + v2->u.id, j2[0][1] * beta[0] + j2[2][1] * beta[2]);
2617
2618 row1[1] = j2[1][1] * W[0][1];
2619 row2[1] = j2[1][1] * W[1][1];
2620 row3[1] = j2[1][1] * W[2][1];
2621
2622 row1[4] = j2[0][1] * W[0][0] + j2[2][1] * W[0][2];
2623 row2[4] = j2[0][1] * W[1][0] + j2[2][1] * W[1][2];
2624 row3[4] = j2[0][1] * W[2][0] + j2[2][1] * W[2][2];
2625 }
2626
2627 if (v3->flag & PVERT_INTERIOR) {
2628 vid[2] = v3->u.id;
2629 vid[5] = ninterior + v3->u.id;
2630
2631 sys->J2dt[e1->u.id][2] = j2[0][2] = p_abf_compute_sin_product(sys, v3, e1->u.id) * wi1;
2632 sys->J2dt[e2->u.id][2] = j2[1][2] = p_abf_compute_sin_product(sys, v3, e2->u.id) * wi2;
2633 sys->J2dt[e3->u.id][2] = j2[2][2] = 1.0f * wi3;
2634
2635 EIG_linear_solver_right_hand_side_add(context, 0, v3->u.id, j2[2][2] * beta[2]);
2637 context, 0, ninterior + v3->u.id, j2[0][2] * beta[0] + j2[1][2] * beta[1]);
2638
2639 row1[2] = j2[2][2] * W[0][2];
2640 row2[2] = j2[2][2] * W[1][2];
2641 row3[2] = j2[2][2] * W[2][2];
2642
2643 row1[5] = j2[0][2] * W[0][0] + j2[1][2] * W[0][1];
2644 row2[5] = j2[0][2] * W[1][0] + j2[1][2] * W[1][1];
2645 row3[5] = j2[0][2] * W[2][0] + j2[1][2] * W[2][1];
2646 }
2647
2648 for (int i = 0; i < 3; i++) {
2649 int r = vid[i];
2650
2651 if (r == -1) {
2652 continue;
2653 }
2654
2655 for (int j = 0; j < 6; j++) {
2656 int c = vid[j];
2657
2658 if (c == -1) {
2659 continue;
2660 }
2661
2662 if (i == 0) {
2663 EIG_linear_solver_matrix_add(context, r, c, j2[0][i] * row1[j]);
2664 }
2665 else {
2666 EIG_linear_solver_matrix_add(context, r + ninterior, c, j2[0][i] * row1[j]);
2667 }
2668
2669 if (i == 1) {
2670 EIG_linear_solver_matrix_add(context, r, c, j2[1][i] * row2[j]);
2671 }
2672 else {
2673 EIG_linear_solver_matrix_add(context, r + ninterior, c, j2[1][i] * row2[j]);
2674 }
2675
2676 if (i == 2) {
2677 EIG_linear_solver_matrix_add(context, r, c, j2[2][i] * row3[j]);
2678 }
2679 else {
2680 EIG_linear_solver_matrix_add(context, r + ninterior, c, j2[2][i] * row3[j]);
2681 }
2682 }
2683 }
2684 }
2685
2686 bool success = EIG_linear_solver_solve(context);
2687
2688 if (success) {
2689 for (PFace *f = chart->faces; f; f = f->nextlink) {
2690 float pre[3];
2691 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
2692 PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
2693
2694 pre[0] = pre[1] = pre[2] = 0.0f;
2695
2696 if (v1->flag & PVERT_INTERIOR) {
2697 float x = EIG_linear_solver_variable_get(context, 0, v1->u.id);
2698 float x2 = EIG_linear_solver_variable_get(context, 0, ninterior + v1->u.id);
2699 pre[0] += sys->J2dt[e1->u.id][0] * x;
2700 pre[1] += sys->J2dt[e2->u.id][0] * x2;
2701 pre[2] += sys->J2dt[e3->u.id][0] * x2;
2702 }
2703
2704 if (v2->flag & PVERT_INTERIOR) {
2705 float x = EIG_linear_solver_variable_get(context, 0, v2->u.id);
2706 float x2 = EIG_linear_solver_variable_get(context, 0, ninterior + v2->u.id);
2707 pre[0] += sys->J2dt[e1->u.id][1] * x2;
2708 pre[1] += sys->J2dt[e2->u.id][1] * x;
2709 pre[2] += sys->J2dt[e3->u.id][1] * x2;
2710 }
2711
2712 if (v3->flag & PVERT_INTERIOR) {
2713 float x = EIG_linear_solver_variable_get(context, 0, v3->u.id);
2714 float x2 = EIG_linear_solver_variable_get(context, 0, ninterior + v3->u.id);
2715 pre[0] += sys->J2dt[e1->u.id][2] * x2;
2716 pre[1] += sys->J2dt[e2->u.id][2] * x2;
2717 pre[2] += sys->J2dt[e3->u.id][2] * x;
2718 }
2719
2720 float dlambda1 = pre[0] + pre[1] + pre[2];
2721 dlambda1 = sys->dstar[f->u.id] * (sys->bstar[f->u.id] - dlambda1);
2722
2723 sys->lambdaTriangle[f->u.id] += dlambda1;
2724
2725 p_abf_adjust_alpha(sys, e1->u.id, dlambda1, pre[0]);
2726 p_abf_adjust_alpha(sys, e2->u.id, dlambda1, pre[1]);
2727 p_abf_adjust_alpha(sys, e3->u.id, dlambda1, pre[2]);
2728 }
2729
2730 for (int i = 0; i < ninterior; i++) {
2731 sys->lambdaPlanar[i] += float(EIG_linear_solver_variable_get(context, 0, i));
2732 sys->lambdaLength[i] += float(EIG_linear_solver_variable_get(context, 0, ninterior + i));
2733 }
2734 }
2735
2736 EIG_linear_solver_delete(context);
2737
2738 return success;
2739}
2740
2741static bool p_chart_abf_solve(PChart *chart)
2742{
2743 PVert *v;
2744 PFace *f;
2745 PEdge *e, *e1, *e2, *e3;
2746 PAbfSystem sys;
2747 int i;
2748 float /* lastnorm, */ /* UNUSED */ limit = (chart->nfaces > 100) ? 1.0f : 0.001f;
2749
2750 /* setup id's */
2751 sys.ninterior = sys.nfaces = sys.nangles = 0;
2752
2753 for (v = chart->verts; v; v = v->nextlink) {
2754 if (p_vert_interior(v)) {
2755 v->flag |= PVERT_INTERIOR;
2756 v->u.id = sys.ninterior++;
2757 }
2758 else {
2759 v->flag &= ~PVERT_INTERIOR;
2760 }
2761 }
2762
2763 for (f = chart->faces; f; f = f->nextlink) {
2764 e1 = f->edge;
2765 e2 = e1->next;
2766 e3 = e2->next;
2767 f->u.id = sys.nfaces++;
2768
2769 /* angle id's are conveniently stored in half edges */
2770 e1->u.id = sys.nangles++;
2771 e2->u.id = sys.nangles++;
2772 e3->u.id = sys.nangles++;
2773 }
2774
2775 p_abf_setup_system(&sys);
2776
2777 /* compute initial angles */
2778 for (f = chart->faces; f; f = f->nextlink) {
2779 double a1, a2, a3;
2780
2781 e1 = f->edge;
2782 e2 = e1->next;
2783 e3 = e2->next;
2784 p_face_angles(f, &a1, &a2, &a3);
2785
2786 sys.alpha[e1->u.id] = sys.beta[e1->u.id] = a1;
2787 sys.alpha[e2->u.id] = sys.beta[e2->u.id] = a2;
2788 sys.alpha[e3->u.id] = sys.beta[e3->u.id] = a3;
2789
2790 sys.weight[e1->u.id] = 2.0f / (a1 * a1);
2791 sys.weight[e2->u.id] = 2.0f / (a2 * a2);
2792 sys.weight[e3->u.id] = 2.0f / (a3 * a3);
2793 }
2794
2795 for (v = chart->verts; v; v = v->nextlink) {
2796 if (v->flag & PVERT_INTERIOR) {
2797 float anglesum = 0.0, scale;
2798
2799 e = v->edge;
2800 do {
2801 anglesum += sys.beta[e->u.id];
2802 e = e->next->next->pair;
2803 } while (e && (e != v->edge));
2804
2805 scale = (anglesum == 0.0f) ? 0.0f : 2.0f * float(M_PI) / anglesum;
2806
2807 e = v->edge;
2808 do {
2809 sys.beta[e->u.id] = sys.alpha[e->u.id] = sys.beta[e->u.id] * scale;
2810 e = e->next->next->pair;
2811 } while (e && (e != v->edge));
2812 }
2813 }
2814
2815 if (sys.ninterior > 0) {
2816 p_abf_compute_sines(&sys);
2817
2818 /* iteration */
2819 // lastnorm = 1e10; /* UNUSED. */
2820
2821 for (i = 0; i < ABF_MAX_ITER; i++) {
2822 float norm = p_abf_compute_gradient(&sys, chart);
2823
2824 // lastnorm = norm; /* UNUSED. */
2825
2826 if (norm < limit) {
2827 break;
2828 }
2829
2830 if (!p_abf_matrix_invert(&sys, chart)) {
2831 param_warning("ABF failed to invert matrix");
2832 p_abf_free_system(&sys);
2833 return false;
2834 }
2835
2836 p_abf_compute_sines(&sys);
2837 }
2838
2839 if (i == ABF_MAX_ITER) {
2840 param_warning("ABF maximum iterations reached");
2841 p_abf_free_system(&sys);
2842 return false;
2843 }
2844 }
2845
2846 chart->abf_alpha = (float *)MEM_dupallocN(sys.alpha);
2847 p_abf_free_system(&sys);
2848
2849 return true;
2850}
2851
2852/* Least Squares Conformal Maps */
2853
2854static void p_chart_pin_positions(PChart *chart, PVert **pin1, PVert **pin2)
2855{
2856 if (!*pin1 || !*pin2 || *pin1 == *pin2) {
2857 /* degenerate case */
2858 PFace *f = chart->faces;
2859 *pin1 = f->edge->vert;
2860 *pin2 = f->edge->next->vert;
2861
2862 (*pin1)->uv[0] = 0.0f;
2863 (*pin1)->uv[1] = 0.5f;
2864 (*pin2)->uv[0] = 1.0f;
2865 (*pin2)->uv[1] = 0.5f;
2866 }
2867 else {
2868 int diru, dirv, dirx, diry;
2869 float sub[3];
2870
2871 sub_v3_v3v3(sub, (*pin1)->co, (*pin2)->co);
2872 sub[0] = fabsf(sub[0]);
2873 sub[1] = fabsf(sub[1]);
2874 sub[2] = fabsf(sub[2]);
2875
2876 if ((sub[0] > sub[1]) && (sub[0] > sub[2])) {
2877 dirx = 0;
2878 diry = (sub[1] > sub[2]) ? 1 : 2;
2879 }
2880 else if ((sub[1] > sub[0]) && (sub[1] > sub[2])) {
2881 dirx = 1;
2882 diry = (sub[0] > sub[2]) ? 0 : 2;
2883 }
2884 else {
2885 dirx = 2;
2886 diry = (sub[0] > sub[1]) ? 0 : 1;
2887 }
2888
2889 if (dirx == 2) {
2890 diru = 1;
2891 dirv = 0;
2892 }
2893 else {
2894 diru = 0;
2895 dirv = 1;
2896 }
2897
2898 (*pin1)->uv[diru] = (*pin1)->co[dirx];
2899 (*pin1)->uv[dirv] = (*pin1)->co[diry];
2900 (*pin2)->uv[diru] = (*pin2)->co[dirx];
2901 (*pin2)->uv[dirv] = (*pin2)->co[diry];
2902 }
2903}
2904
2905static bool p_chart_symmetry_pins(PChart *chart, PEdge *outer, PVert **pin1, PVert **pin2)
2906{
2907 PEdge *be, *lastbe = nullptr, *maxe1 = nullptr, *maxe2 = nullptr, *be1, *be2;
2908 PEdge *cure = nullptr, *firste1 = nullptr, *firste2 = nullptr, *nextbe;
2909 float maxlen = 0.0f, curlen = 0.0f, totlen = 0.0f, firstlen = 0.0f;
2910 float len1, len2;
2911
2912 /* find longest series of verts split in the chart itself, these are
2913 * marked during construction */
2914 be = outer;
2915 lastbe = p_boundary_edge_prev(be);
2916 do {
2917 float len = p_edge_length(be);
2918 totlen += len;
2919
2920 nextbe = p_boundary_edge_next(be);
2921
2922 if ((be->vert->flag & PVERT_SPLIT) || (lastbe->vert->flag & nextbe->vert->flag & PVERT_SPLIT))
2923 {
2924 if (!cure) {
2925 if (be == outer) {
2926 firste1 = be;
2927 }
2928 cure = be;
2929 }
2930 else {
2931 curlen += p_edge_length(lastbe);
2932 }
2933 }
2934 else if (cure) {
2935 if (curlen > maxlen) {
2936 maxlen = curlen;
2937 maxe1 = cure;
2938 maxe2 = lastbe;
2939 }
2940
2941 if (firste1 == cure) {
2942 firstlen = curlen;
2943 firste2 = lastbe;
2944 }
2945
2946 curlen = 0.0f;
2947 cure = nullptr;
2948 }
2949
2950 lastbe = be;
2951 be = nextbe;
2952 } while (be != outer);
2953
2954 /* make sure we also count a series of splits over the starting point */
2955 if (cure && (cure != outer)) {
2956 firstlen += curlen + p_edge_length(be);
2957
2958 if (firstlen > maxlen) {
2959 maxlen = firstlen;
2960 maxe1 = cure;
2961 maxe2 = firste2;
2962 }
2963 }
2964
2965 if (!maxe1 || !maxe2 || (maxlen < 0.5f * totlen)) {
2966 return false;
2967 }
2968
2969 /* find pin1 in the split vertices */
2970 be1 = maxe1;
2971 be2 = maxe2;
2972 len1 = 0.0f;
2973 len2 = 0.0f;
2974
2975 do {
2976 if (len1 < len2) {
2977 len1 += p_edge_length(be1);
2978 be1 = p_boundary_edge_next(be1);
2979 }
2980 else {
2981 be2 = p_boundary_edge_prev(be2);
2982 len2 += p_edge_length(be2);
2983 }
2984 } while (be1 != be2);
2985
2986 *pin1 = be1->vert;
2987
2988 /* find pin2 outside the split vertices */
2989 be1 = maxe1;
2990 be2 = maxe2;
2991 len1 = 0.0f;
2992 len2 = 0.0f;
2993
2994 do {
2995 if (len1 < len2) {
2996 be1 = p_boundary_edge_prev(be1);
2997 len1 += p_edge_length(be1);
2998 }
2999 else {
3000 len2 += p_edge_length(be2);
3001 be2 = p_boundary_edge_next(be2);
3002 }
3003 } while (be1 != be2);
3004
3005 *pin2 = be1->vert;
3006
3007 p_chart_pin_positions(chart, pin1, pin2);
3008
3009 return !equals_v3v3((*pin1)->co, (*pin2)->co);
3010}
3011
3012static void p_chart_extrema_verts(PChart *chart, PVert **pin1, PVert **pin2)
3013{
3014 float minv[3], maxv[3], dirlen;
3015 PVert *v, *minvert[3], *maxvert[3];
3016 int i, dir;
3017
3018 /* find minimum and maximum verts over x/y/z axes */
3019 minv[0] = minv[1] = minv[2] = 1e20;
3020 maxv[0] = maxv[1] = maxv[2] = -1e20;
3021
3022 minvert[0] = minvert[1] = minvert[2] = nullptr;
3023 maxvert[0] = maxvert[1] = maxvert[2] = nullptr;
3024
3025 for (v = chart->verts; v; v = v->nextlink) {
3026 for (i = 0; i < 3; i++) {
3027 if (v->co[i] < minv[i]) {
3028 minv[i] = v->co[i];
3029 minvert[i] = v;
3030 }
3031 if (v->co[i] > maxv[i]) {
3032 maxv[i] = v->co[i];
3033 maxvert[i] = v;
3034 }
3035 }
3036 }
3037
3038 /* find axes with longest distance */
3039 dir = 0;
3040 dirlen = -1.0;
3041
3042 for (i = 0; i < 3; i++) {
3043 if (maxv[i] - minv[i] > dirlen) {
3044 dir = i;
3045 dirlen = maxv[i] - minv[i];
3046 }
3047 }
3048
3049 *pin1 = minvert[dir];
3050 *pin2 = maxvert[dir];
3051
3052 p_chart_pin_positions(chart, pin1, pin2);
3053}
3054
3055static void p_chart_lscm_begin(PChart *chart, bool live, bool abf)
3056{
3057 BLI_assert(chart->context == nullptr);
3058
3059 bool select = false;
3060 bool deselect = false;
3061 int npins = 0;
3062
3063 /* Give vertices matrix indices, count pins and check selections. */
3064 for (PVert *v = chart->verts; v; v = v->nextlink) {
3065 if (v->flag & PVERT_PIN) {
3066 npins++;
3067 if (v->flag & PVERT_SELECT) {
3068 select = true;
3069 }
3070 }
3071
3072 if (!(v->flag & PVERT_SELECT)) {
3073 deselect = true;
3074 }
3075 }
3076
3077 if (live && (!select || !deselect)) {
3078 chart->skip_flush = true;
3079 return;
3080 }
3081#if 0
3082 p_chart_simplify_compute(chart, p_collapse_cost, p_collapse_allowed);
3083 p_chart_topological_sanity_check(chart);
3084#endif
3085
3086 if (npins == 1) {
3087 chart->area_uv = p_chart_uv_area(chart);
3088 for (PVert *v = chart->verts; v; v = v->nextlink) {
3089 if (v->flag & PVERT_PIN) {
3090 chart->single_pin = v;
3091 break;
3092 }
3093 }
3094 }
3095
3096 if (abf) {
3097 if (!p_chart_abf_solve(chart)) {
3098 param_warning("ABF solving failed: falling back to LSCM.\n");
3099 }
3100 }
3101
3102 /* ABF uses these indices for it's internal references.
3103 * Set the indices afterwards. */
3104 int id = 0;
3105 for (PVert *v = chart->verts; v; v = v->nextlink) {
3106 v->u.id = id++;
3107 }
3108
3109 if (npins <= 1) {
3110 /* No pins, let's find some ourself. */
3111 PEdge *outer;
3112 p_chart_boundaries(chart, &outer);
3113
3114 PVert *pin1, *pin2;
3115 /* Outer can be null with non-finite coordinates. */
3116 if (!(outer && p_chart_symmetry_pins(chart, outer, &pin1, &pin2))) {
3117 p_chart_extrema_verts(chart, &pin1, &pin2);
3118 }
3119
3120 chart->pin1 = pin1;
3121 chart->pin2 = pin2;
3122 }
3123
3124 chart->context = EIG_linear_least_squares_solver_new(2 * chart->nfaces, 2 * chart->nverts, 1);
3125}
3126
3127static bool p_chart_lscm_solve(ParamHandle *handle, PChart *chart)
3128{
3129 LinearSolver *context = chart->context;
3130
3131 for (PVert *v = chart->verts; v; v = v->nextlink) {
3132 if (v->flag & PVERT_PIN) {
3133 p_vert_load_pin_select_uvs(handle, v); /* Reload for Live Unwrap. */
3134 }
3135 }
3136
3137 if (chart->single_pin) {
3138 /* If only one pin, save location as origin. */
3139 copy_v2_v2(chart->origin, chart->single_pin->uv);
3140 }
3141
3142 if (chart->pin1) {
3143 PVert *pin1 = chart->pin1;
3144 PVert *pin2 = chart->pin2;
3145 EIG_linear_solver_variable_lock(context, 2 * pin1->u.id);
3146 EIG_linear_solver_variable_lock(context, 2 * pin1->u.id + 1);
3147 EIG_linear_solver_variable_lock(context, 2 * pin2->u.id);
3148 EIG_linear_solver_variable_lock(context, 2 * pin2->u.id + 1);
3149
3150 EIG_linear_solver_variable_set(context, 0, 2 * pin1->u.id, pin1->uv[0]);
3151 EIG_linear_solver_variable_set(context, 0, 2 * pin1->u.id + 1, pin1->uv[1]);
3152 EIG_linear_solver_variable_set(context, 0, 2 * pin2->u.id, pin2->uv[0]);
3153 EIG_linear_solver_variable_set(context, 0, 2 * pin2->u.id + 1, pin2->uv[1]);
3154 }
3155 else {
3156 /* Set and lock the pins. */
3157 for (PVert *v = chart->verts; v; v = v->nextlink) {
3158 if (v->flag & PVERT_PIN) {
3159 EIG_linear_solver_variable_lock(context, 2 * v->u.id);
3160 EIG_linear_solver_variable_lock(context, 2 * v->u.id + 1);
3161
3162 EIG_linear_solver_variable_set(context, 0, 2 * v->u.id, v->uv[0]);
3163 EIG_linear_solver_variable_set(context, 0, 2 * v->u.id + 1, v->uv[1]);
3164 }
3165 }
3166 }
3167
3168 /* Detect "up" direction based on pinned vertices. */
3169 float area_pinned_up = 0.0f;
3170 float area_pinned_down = 0.0f;
3171
3172 for (PFace *f = chart->faces; f; f = f->nextlink) {
3173 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
3174 PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
3175
3176 if ((v1->flag & PVERT_PIN) && (v2->flag & PVERT_PIN) && (v3->flag & PVERT_PIN)) {
3177 const float area = p_face_uv_area_signed(f);
3178
3179 if (area > 0.0f) {
3180 area_pinned_up += area;
3181 }
3182 else {
3183 area_pinned_down -= area;
3184 }
3185 }
3186 }
3187
3188 const bool flip_faces = (area_pinned_down > area_pinned_up);
3189
3190 /* Construct matrix. */
3191 const float *alpha = chart->abf_alpha;
3192 int row = 0;
3193 for (PFace *f = chart->faces; f; f = f->nextlink) {
3194 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
3195 PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
3196 double a1, a2, a3;
3197
3198 if (alpha) {
3199 /* Use abf angles if present. */
3200 a1 = *(alpha++);
3201 a2 = *(alpha++);
3202 a3 = *(alpha++);
3203 }
3204 else {
3205 p_face_angles(f, &a1, &a2, &a3);
3206 }
3207
3208 if (flip_faces) {
3209 std::swap(a2, a3);
3210 std::swap(e2, e3);
3211 std::swap(v2, v3);
3212 }
3213
3214 double sina1 = sin(a1);
3215 double sina2 = sin(a2);
3216 double sina3 = sin(a3);
3217
3218 const double sinmax = max_ddd(sina1, sina2, sina3);
3219
3220 /* Shift vertices to find most stable order. */
3221 if (sina3 != sinmax) {
3222 SHIFT3(PVert *, v1, v2, v3);
3223 SHIFT3(double, a1, a2, a3);
3224 SHIFT3(double, sina1, sina2, sina3);
3225
3226 if (sina2 == sinmax) {
3227 SHIFT3(PVert *, v1, v2, v3);
3228 SHIFT3(double, a1, a2, a3);
3229 SHIFT3(double, sina1, sina2, sina3);
3230 }
3231 }
3232
3233 /* Angle based lscm formulation. */
3234 const double ratio = (sina3 == 0.0f) ? 1.0f : sina2 / sina3;
3235 const double cosine = cos(a1) * ratio;
3236 const double sine = sina1 * ratio;
3237
3238 EIG_linear_solver_matrix_add(context, row, 2 * v1->u.id, cosine - 1.0f);
3239 EIG_linear_solver_matrix_add(context, row, 2 * v1->u.id + 1, -sine);
3240 EIG_linear_solver_matrix_add(context, row, 2 * v2->u.id, -cosine);
3241 EIG_linear_solver_matrix_add(context, row, 2 * v2->u.id + 1, sine);
3242 EIG_linear_solver_matrix_add(context, row, 2 * v3->u.id, 1.0);
3243 row++;
3244
3245 EIG_linear_solver_matrix_add(context, row, 2 * v1->u.id, sine);
3246 EIG_linear_solver_matrix_add(context, row, 2 * v1->u.id + 1, cosine - 1.0f);
3247 EIG_linear_solver_matrix_add(context, row, 2 * v2->u.id, -sine);
3248 EIG_linear_solver_matrix_add(context, row, 2 * v2->u.id + 1, -cosine);
3249 EIG_linear_solver_matrix_add(context, row, 2 * v3->u.id + 1, 1.0);
3250 row++;
3251 }
3252
3253 if (EIG_linear_solver_solve(context)) {
3254 for (PVert *v = chart->verts; v; v = v->nextlink) {
3255 v->uv[0] = EIG_linear_solver_variable_get(context, 0, 2 * v->u.id);
3256 v->uv[1] = EIG_linear_solver_variable_get(context, 0, 2 * v->u.id + 1);
3257 }
3258 return true;
3259 }
3260
3261 for (PVert *v = chart->verts; v; v = v->nextlink) {
3262 v->uv[0] = 0.0f;
3263 v->uv[1] = 0.0f;
3264 }
3265
3266 return false;
3267}
3268
3270{
3271 PVert *pin = chart->single_pin;
3272
3273 /* If only one pin, keep UV area the same. */
3274 const float new_area = p_chart_uv_area(chart);
3275 if (new_area > 0.0f) {
3276 const float scale = chart->area_uv / new_area;
3277 if (scale > 0.0f) {
3278 p_chart_uv_scale(chart, sqrtf(scale));
3279 }
3280 }
3281
3282 /* Translate to keep the pinned UV in place. */
3283 float offset[2];
3284 sub_v2_v2v2(offset, chart->origin, pin->uv);
3285 p_chart_uv_translate(chart, offset);
3286}
3287
3288static void p_chart_lscm_end(PChart *chart)
3289{
3291 chart->context = nullptr;
3292
3293 MEM_SAFE_FREE(chart->abf_alpha);
3294
3295 chart->pin1 = nullptr;
3296 chart->pin2 = nullptr;
3297 chart->single_pin = nullptr;
3298}
3299
3300/* Stretch */
3301
3302#define P_STRETCH_ITER 20
3303
3305{
3306 PVert *v;
3307
3308 for (v = chart->verts; v; v = v->nextlink) {
3309 if (v->edge->pair == nullptr) {
3310 v->flag |= PVERT_PIN;
3311 }
3312 else {
3313 v->flag &= ~PVERT_PIN;
3314 }
3315 }
3316}
3317
3318static float p_face_stretch(PFace *f)
3319{
3320 float T, w, tmp[3];
3321 float Ps[3], Pt[3];
3322 float a, c, area;
3323 PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
3324 PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert;
3325
3326 area = p_face_uv_area_signed(f);
3327
3328 if (area <= 0.0f) {
3329 /* When a face is flipped, provide a large penalty.
3330 * Add on a slight gradient to unflip the face, see also: #99781. */
3331 return 1e8f * (1.0f + p_edge_uv_length(e1) + p_edge_uv_length(e2) + p_edge_uv_length(e3));
3332 }
3333
3334 w = 1.0f / (2.0f * area);
3335
3336 /* compute derivatives */
3337 copy_v3_v3(Ps, v1->co);
3338 mul_v3_fl(Ps, (v2->uv[1] - v3->uv[1]));
3339
3340 copy_v3_v3(tmp, v2->co);
3341 mul_v3_fl(tmp, (v3->uv[1] - v1->uv[1]));
3342 add_v3_v3(Ps, tmp);
3343
3344 copy_v3_v3(tmp, v3->co);
3345 mul_v3_fl(tmp, (v1->uv[1] - v2->uv[1]));
3346 add_v3_v3(Ps, tmp);
3347
3348 mul_v3_fl(Ps, w);
3349
3350 copy_v3_v3(Pt, v1->co);
3351 mul_v3_fl(Pt, (v3->uv[0] - v2->uv[0]));
3352
3353 copy_v3_v3(tmp, v2->co);
3354 mul_v3_fl(tmp, (v1->uv[0] - v3->uv[0]));
3355 add_v3_v3(Pt, tmp);
3356
3357 copy_v3_v3(tmp, v3->co);
3358 mul_v3_fl(tmp, (v2->uv[0] - v1->uv[0]));
3359 add_v3_v3(Pt, tmp);
3360
3361 mul_v3_fl(Pt, w);
3362
3363 /* Sander Tensor */
3364 a = dot_v3v3(Ps, Ps);
3365 c = dot_v3v3(Pt, Pt);
3366
3367 T = sqrtf(0.5f * (a + c));
3368 if (f->flag & PFACE_FILLED) {
3369 T *= 0.2f;
3370 }
3371
3372 return T;
3373}
3374
3376{
3377 PEdge *e = v->edge;
3378 float sum = 0.0f;
3379
3380 do {
3381 sum += p_face_stretch(e->face);
3383 } while (e && e != (v->edge));
3384
3385 return sum;
3386}
3387
3388static void p_chart_stretch_minimize(PChart *chart, RNG *rng)
3389{
3390 PVert *v;
3391 PEdge *e;
3392 int j, nedges;
3393 float orig_stretch, low, stretch_low, high, stretch_high, mid, stretch;
3394 float orig_uv[2], dir[2], random_angle, trusted_radius;
3395
3396 for (v = chart->verts; v; v = v->nextlink) {
3397 if ((v->flag & PVERT_PIN) || !(v->flag & PVERT_SELECT)) {
3398 continue;
3399 }
3400
3401 orig_stretch = p_stretch_compute_vertex(v);
3402 orig_uv[0] = v->uv[0];
3403 orig_uv[1] = v->uv[1];
3404
3405 /* move vertex in a random direction */
3406 trusted_radius = 0.0f;
3407 nedges = 0;
3408 e = v->edge;
3409
3410 do {
3411 trusted_radius += p_edge_uv_length(e);
3412 nedges++;
3413
3415 } while (e && e != (v->edge));
3416
3417 trusted_radius /= 2 * nedges;
3418
3419 random_angle = BLI_rng_get_float(rng) * 2.0f * float(M_PI);
3420 dir[0] = trusted_radius * cosf(random_angle);
3421 dir[1] = trusted_radius * sinf(random_angle);
3422
3423 /* calculate old and new stretch */
3424 low = 0;
3425 stretch_low = orig_stretch;
3426
3427 add_v2_v2v2(v->uv, orig_uv, dir);
3428 high = 1;
3429 stretch = stretch_high = p_stretch_compute_vertex(v);
3430
3431 /* binary search for lowest stretch position */
3432 for (j = 0; j < P_STRETCH_ITER; j++) {
3433 mid = 0.5f * (low + high);
3434 v->uv[0] = orig_uv[0] + mid * dir[0];
3435 v->uv[1] = orig_uv[1] + mid * dir[1];
3436 stretch = p_stretch_compute_vertex(v);
3437
3438 if (stretch_low < stretch_high) {
3439 high = mid;
3440 stretch_high = stretch;
3441 }
3442 else {
3443 low = mid;
3444 stretch_low = stretch;
3445 }
3446 }
3447
3448 /* no luck, stretch has increased, reset to old values */
3449 if (stretch >= orig_stretch) {
3450 copy_v2_v2(v->uv, orig_uv);
3451 }
3452 }
3453}
3454
3455/* Minimum area enclosing rectangle for packing */
3456
3457static int p_compare_geometric_uv(const void *a, const void *b)
3458{
3459 const PVert *v1 = *(const PVert *const *)a;
3460 const PVert *v2 = *(const PVert *const *)b;
3461
3462 if (v1->uv[0] < v2->uv[0]) {
3463 return -1;
3464 }
3465 if (v1->uv[0] == v2->uv[0]) {
3466 if (v1->uv[1] < v2->uv[1]) {
3467 return -1;
3468 }
3469 if (v1->uv[1] == v2->uv[1]) {
3470 return 0;
3471 }
3472 return 1;
3473 }
3474 return 1;
3475}
3476
3477static bool p_chart_convex_hull(PChart *chart, PVert ***r_verts, int *r_nverts, int *r_right)
3478{
3479 /* Graham algorithm, taken from:
3480 * http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/117225 */
3481
3482 PEdge *be, *e;
3483 int npoints = 0, i, ulen, llen;
3484 PVert **U, **L, **points, **p;
3485
3486 p_chart_boundaries(chart, &be);
3487
3488 if (!be) {
3489 return false;
3490 }
3491
3492 e = be;
3493 do {
3494 npoints++;
3496 } while (e != be);
3497
3498 p = points = MEM_malloc_arrayN<PVert *>(2 * size_t(npoints), "PCHullpoints");
3499 U = MEM_malloc_arrayN<PVert *>(size_t(npoints), "PCHullU");
3500 L = MEM_malloc_arrayN<PVert *>(size_t(npoints), "PCHullL");
3501
3502 e = be;
3503 do {
3504 *p = e->vert;
3505 p++;
3507 } while (e != be);
3508
3509 qsort(points, npoints, sizeof(PVert *), p_compare_geometric_uv);
3510
3511 ulen = llen = 0;
3512 for (p = points, i = 0; i < npoints; i++, p++) {
3513 while ((ulen > 1) && (p_area_signed(U[ulen - 2]->uv, (*p)->uv, U[ulen - 1]->uv) <= 0)) {
3514 ulen--;
3515 }
3516 while ((llen > 1) && (p_area_signed(L[llen - 2]->uv, (*p)->uv, L[llen - 1]->uv) >= 0)) {
3517 llen--;
3518 }
3519
3520 U[ulen] = *p;
3521 ulen++;
3522 L[llen] = *p;
3523 llen++;
3524 }
3525
3526 npoints = 0;
3527 for (p = points, i = 0; i < ulen; i++, p++, npoints++) {
3528 *p = U[i];
3529 }
3530
3531 /* the first and last point in L are left out, since they are also in U */
3532 for (i = llen - 2; i > 0; i--, p++, npoints++) {
3533 *p = L[i];
3534 }
3535
3536 *r_verts = points;
3537 *r_nverts = npoints;
3538 *r_right = ulen - 1;
3539
3540 MEM_freeN(U);
3541 MEM_freeN(L);
3542
3543 return true;
3544}
3545
3546static float p_rectangle_area(float *p1, float *dir, float *p2, float *p3, float *p4)
3547{
3548 /* given 4 points on the rectangle edges and the direction of on edge,
3549 * compute the area of the rectangle */
3550
3551 float orthodir[2], corner1[2], corner2[2], corner3[2];
3552
3553 orthodir[0] = dir[1];
3554 orthodir[1] = -dir[0];
3555
3556 if (!p_intersect_line_2d_dir(p1, dir, p2, orthodir, corner1)) {
3557 return 1e10;
3558 }
3559
3560 if (!p_intersect_line_2d_dir(p1, dir, p4, orthodir, corner2)) {
3561 return 1e10;
3562 }
3563
3564 if (!p_intersect_line_2d_dir(p3, dir, p4, orthodir, corner3)) {
3565 return 1e10;
3566 }
3567
3568 return len_v2v2(corner1, corner2) * len_v2v2(corner2, corner3);
3569}
3570
3572{
3573 /* minimum area enclosing rectangle with rotating calipers, info:
3574 * http://cgm.cs.mcgill.ca/~orm/maer.html */
3575
3576 float rotated, minarea, minangle, area, len;
3577 float *angles, miny, maxy, v[2], a[4], mina;
3578 int npoints, right, i_min, i_max, i, idx[4], nextidx;
3579 PVert **points, *p1, *p2, *p3, *p4, *p1n;
3580
3581 /* compute convex hull */
3582 if (!p_chart_convex_hull(chart, &points, &npoints, &right)) {
3583 return 0.0;
3584 }
3585
3586 /* find left/top/right/bottom points, and compute angle for each point */
3587 angles = MEM_malloc_arrayN<float>(size_t(npoints), "PMinAreaAngles");
3588
3589 i_min = i_max = 0;
3590 miny = 1e10;
3591 maxy = -1e10;
3592
3593 for (i = 0; i < npoints; i++) {
3594 p1 = (i == 0) ? points[npoints - 1] : points[i - 1];
3595 p2 = points[i];
3596 p3 = (i == npoints - 1) ? points[0] : points[i + 1];
3597
3598 angles[i] = float(M_PI) - angle_v2v2v2(p1->uv, p2->uv, p3->uv);
3599
3600 if (points[i]->uv[1] < miny) {
3601 miny = points[i]->uv[1];
3602 i_min = i;
3603 }
3604 if (points[i]->uv[1] > maxy) {
3605 maxy = points[i]->uv[1];
3606 i_max = i;
3607 }
3608 }
3609
3610 /* left, top, right, bottom */
3611 idx[0] = 0;
3612 idx[1] = i_max;
3613 idx[2] = right;
3614 idx[3] = i_min;
3615
3616 v[0] = points[idx[0]]->uv[0];
3617 v[1] = points[idx[0]]->uv[1] + 1.0f;
3618 a[0] = angle_v2v2v2(points[(idx[0] + 1) % npoints]->uv, points[idx[0]]->uv, v);
3619
3620 v[0] = points[idx[1]]->uv[0] + 1.0f;
3621 v[1] = points[idx[1]]->uv[1];
3622 a[1] = angle_v2v2v2(points[(idx[1] + 1) % npoints]->uv, points[idx[1]]->uv, v);
3623
3624 v[0] = points[idx[2]]->uv[0];
3625 v[1] = points[idx[2]]->uv[1] - 1.0f;
3626 a[2] = angle_v2v2v2(points[(idx[2] + 1) % npoints]->uv, points[idx[2]]->uv, v);
3627
3628 v[0] = points[idx[3]]->uv[0] - 1.0f;
3629 v[1] = points[idx[3]]->uv[1];
3630 a[3] = angle_v2v2v2(points[(idx[3] + 1) % npoints]->uv, points[idx[3]]->uv, v);
3631
3632 /* 4 rotating calipers */
3633
3634 rotated = 0.0;
3635 minarea = 1e10;
3636 minangle = 0.0;
3637
3638 while (rotated <= float(M_PI_2)) { /* INVESTIGATE: how far to rotate? */
3639 /* rotate with the smallest angle */
3640 i_min = 0;
3641 mina = 1e10;
3642
3643 for (i = 0; i < 4; i++) {
3644 if (a[i] < mina) {
3645 mina = a[i];
3646 i_min = i;
3647 }
3648 }
3649
3650 rotated += mina;
3651 nextidx = (idx[i_min] + 1) % npoints;
3652
3653 a[i_min] = angles[nextidx];
3654 a[(i_min + 1) % 4] = a[(i_min + 1) % 4] - mina;
3655 a[(i_min + 2) % 4] = a[(i_min + 2) % 4] - mina;
3656 a[(i_min + 3) % 4] = a[(i_min + 3) % 4] - mina;
3657
3658 /* compute area */
3659 p1 = points[idx[i_min]];
3660 p1n = points[nextidx];
3661 p2 = points[idx[(i_min + 1) % 4]];
3662 p3 = points[idx[(i_min + 2) % 4]];
3663 p4 = points[idx[(i_min + 3) % 4]];
3664
3665 len = len_v2v2(p1->uv, p1n->uv);
3666
3667 if (len > 0.0f) {
3668 len = 1.0f / len;
3669 v[0] = (p1n->uv[0] - p1->uv[0]) * len;
3670 v[1] = (p1n->uv[1] - p1->uv[1]) * len;
3671
3672 area = p_rectangle_area(p1->uv, v, p2->uv, p3->uv, p4->uv);
3673
3674 /* remember smallest area */
3675 if (area < minarea) {
3676 minarea = area;
3677 minangle = rotated;
3678 }
3679 }
3680
3681 idx[i_min] = nextidx;
3682 }
3683
3684 /* try keeping rotation as small as possible */
3685 if (minangle > float(M_PI_4)) {
3686 minangle -= float(M_PI_2);
3687 }
3688
3689 MEM_freeN(angles);
3690 MEM_freeN(points);
3691
3692 return minangle;
3693}
3694
3696{
3697 float angle = p_chart_minimum_area_angle(chart);
3698 float sine = sinf(angle);
3699 float cosine = cosf(angle);
3700 PVert *v;
3701
3702 for (v = chart->verts; v; v = v->nextlink) {
3703 float oldu = v->uv[0], oldv = v->uv[1];
3704 v->uv[0] = cosine * oldu - sine * oldv;
3705 v->uv[1] = sine * oldu + cosine * oldv;
3706 }
3707}
3708
3710{
3711 Array<float2> points(chart->nverts);
3712
3713 p_chart_uv_to_array(chart, points);
3714
3716
3717 if (angle != 0.0f) {
3718 float mat[2][2];
3719 angle_to_mat2(mat, angle);
3720 p_chart_uv_transform(chart, mat);
3721 }
3722}
3723
3736
3738{
3740 arena = nullptr;
3742 polyfill_arena = nullptr;
3743 BLI_heap_free(polyfill_heap, nullptr);
3744 polyfill_heap = nullptr;
3745
3747
3751
3752 if (pin_hash) {
3753 BLI_ghash_free(pin_hash, nullptr, nullptr);
3754 pin_hash = nullptr;
3755 }
3756
3757 for (int i = 0; i < ncharts; i++) {
3759 }
3761
3762 if (rng) {
3764 rng = nullptr;
3765 }
3766}
3767
3768void uv_parametrizer_aspect_ratio(ParamHandle *phandle, const float aspect_y)
3769{
3770 BLI_assert(aspect_y > 0.0f);
3771 phandle->aspect_y = aspect_y;
3772}
3773
3779
3780ParamKey uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2])
3781{
3782 if (!handle->pin_hash) {
3783 return bmvertindex; /* No verts pinned. */
3784 }
3785
3786 const GeoUVPinIndex *pinuvlist = (const GeoUVPinIndex *)BLI_ghash_lookup(
3787 handle->pin_hash, POINTER_FROM_INT(bmvertindex));
3788 if (!pinuvlist) {
3789 return bmvertindex; /* Vert not pinned. */
3790 }
3791
3792 /* At least one of the UVs associated with bmvertindex is pinned. Find the best one. */
3793 float bestdistsquared = len_squared_v2v2(pinuvlist->uv, uv);
3794 ParamKey bestkey = pinuvlist->reindex;
3795 pinuvlist = pinuvlist->next;
3796 while (pinuvlist) {
3797 const float distsquared = len_squared_v2v2(pinuvlist->uv, uv);
3798 if (bestdistsquared > distsquared) {
3799 bestdistsquared = distsquared;
3800 bestkey = pinuvlist->reindex;
3801 }
3802 pinuvlist = pinuvlist->next;
3803 }
3804 return bestkey;
3805}
3806
3807static GeoUVPinIndex *new_geo_uv_pinindex(ParamHandle *handle, const float uv[2])
3808{
3809 GeoUVPinIndex *pinuv = (GeoUVPinIndex *)BLI_memarena_alloc(handle->arena, sizeof(*pinuv));
3810 pinuv->next = nullptr;
3811 copy_v2_v2(pinuv->uv, uv);
3812 pinuv->reindex = PARAM_KEY_MAX - (handle->unique_pin_count++);
3813 return pinuv;
3814}
3815
3816void uv_prepare_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2])
3817{
3818 if (!handle->pin_hash) {
3819 handle->pin_hash = BLI_ghash_int_new("uv pin reindex");
3820 }
3821
3822 GeoUVPinIndex *pinuvlist = (GeoUVPinIndex *)BLI_ghash_lookup(handle->pin_hash,
3823 POINTER_FROM_INT(bmvertindex));
3824 if (!pinuvlist) {
3826 handle->pin_hash, POINTER_FROM_INT(bmvertindex), new_geo_uv_pinindex(handle, uv));
3827 return;
3828 }
3829
3830 while (true) {
3831 if (equals_v2v2(pinuvlist->uv, uv)) {
3832 return;
3833 }
3834 if (!pinuvlist->next) {
3835 pinuvlist->next = new_geo_uv_pinindex(handle, uv);
3836 return;
3837 }
3838 pinuvlist = pinuvlist->next;
3839 }
3840}
3841
3842static void p_add_ngon(ParamHandle *handle,
3843 const ParamKey key,
3844 const int nverts,
3845 const ParamKey *vkeys,
3846 const float **co,
3847 float **uv, /* Output will eventually be written to `uv`. */
3848 const float *weight,
3849 const bool *pin,
3850 const bool *select)
3851{
3852 /* Allocate memory for polyfill. */
3853 MemArena *arena = handle->polyfill_arena;
3854 Heap *heap = handle->polyfill_heap;
3855 uint nfilltri = nverts - 2;
3856 uint(*tris)[3] = static_cast<uint(*)[3]>(
3857 BLI_memarena_alloc(arena, sizeof(*tris) * size_t(nfilltri)));
3858 float (*projverts)[2] = static_cast<float (*)[2]>(
3859 BLI_memarena_alloc(arena, sizeof(*projverts) * size_t(nverts)));
3860
3861 /* Calc normal, flipped: to get a positive 2d cross product. */
3862 float normal[3];
3863 zero_v3(normal);
3864
3865 const float *co_curr, *co_prev = co[nverts - 1];
3866 for (int j = 0; j < nverts; j++) {
3867 co_curr = co[j];
3868 add_newell_cross_v3_v3v3(normal, co_prev, co_curr);
3869 co_prev = co_curr;
3870 }
3871 if (UNLIKELY(normalize_v3(normal) == 0.0f)) {
3872 normal[2] = 1.0f;
3873 }
3874
3875 /* Project verts to 2d. */
3876 float axis_mat[3][3];
3877 axis_dominant_v3_to_m3_negate(axis_mat, normal);
3878 for (int j = 0; j < nverts; j++) {
3879 mul_v2_m3v3(projverts[j], axis_mat, co[j]);
3880 }
3881
3882 BLI_polyfill_calc_arena(projverts, nverts, 1, tris, arena);
3883
3884 /* Beautify helps avoid thin triangles that give numerical problems. */
3885 BLI_polyfill_beautify(projverts, nverts, tris, arena, heap);
3886
3887 /* Add triangles. */
3888 for (uint j = 0; j < nfilltri; j++) {
3889 uint *tri = tris[j];
3890 uint v0 = tri[0];
3891 uint v1 = tri[1];
3892 uint v2 = tri[2];
3893
3894 const ParamKey tri_vkeys[3] = {vkeys[v0], vkeys[v1], vkeys[v2]};
3895 const float *tri_co[3] = {co[v0], co[v1], co[v2]};
3896 float *tri_uv[3] = {uv[v0], uv[v1], uv[v2]};
3897
3898 const float *tri_weight = nullptr;
3899 float tri_weight_tmp[3];
3900
3901 if (weight) {
3902 tri_weight_tmp[0] = weight[v0];
3903 tri_weight_tmp[1] = weight[v1];
3904 tri_weight_tmp[2] = weight[v2];
3905 tri_weight = tri_weight_tmp;
3906 };
3907
3908 const bool tri_pin[3] = {pin[v0], pin[v1], pin[v2]};
3909 const bool tri_select[3] = {select[v0], select[v1], select[v2]};
3910
3912 handle, key, 3, tri_vkeys, tri_co, tri_uv, tri_weight, tri_pin, tri_select);
3913 }
3914
3915 BLI_memarena_clear(arena);
3916}
3917
3919 const ParamKey key,
3920 const int nverts,
3921 const ParamKey *vkeys,
3922 const float **co,
3923 float **uv,
3924 const float *weight,
3925 const bool *pin,
3926 const bool *select)
3927{
3928 BLI_assert(nverts >= 3);
3930
3931 if (nverts > 3) {
3932 /* Protect against (manifold) geometry which has a non-manifold triangulation.
3933 * See #102543. */
3934
3935 Vector<int, 32> permute;
3936 permute.reserve(nverts);
3937 for (int i = 0; i < nverts; i++) {
3938 permute.append_unchecked(i);
3939 }
3940
3941 for (int i = nverts - 1; i >= 0;) {
3942 /* Just check the "ears" of the n-gon.
3943 * For quads, this is sufficient.
3944 * For pentagons and higher, we might miss internal duplicate triangles, but note
3945 * that such cases are rare if the source geometry is manifold and non-intersecting. */
3946 const int pm = int(permute.size());
3947 BLI_assert(pm > 3);
3948 int i0 = permute[i];
3949 int i1 = permute[(i + 1) % pm];
3950 int i2 = permute[(i + 2) % pm];
3951 if (!p_face_exists(phandle, vkeys, i0, i1, i2)) {
3952 i--; /* All good. */
3953 continue;
3954 }
3955
3956 /* An existing triangle has already been inserted.
3957 * As a heuristic, attempt to add the *previous* triangle.
3958 * NOTE: Should probably call `uv_parametrizer_face_add`
3959 * instead of `p_face_add_construct`. */
3960 int iprev = permute[(i + pm - 1) % pm];
3961 p_face_add_construct(phandle, key, vkeys, co, uv, weight, iprev, i0, i1, pin, select);
3962
3963 permute.remove(i);
3964 if (permute.size() == 3) {
3965 break;
3966 }
3967 }
3968 if (permute.size() != nverts) {
3969 const int pm = int(permute.size());
3970 /* Add the remaining `pm-gon` data. */
3971 Array<ParamKey> vkeys_sub(pm);
3972 Array<const float *> co_sub(pm);
3973 Array<float *> uv_sub(pm);
3974 Array<float> weight_sub(weight ? pm : 0);
3975 Array<bool> pin_sub(pm);
3976 Array<bool> select_sub(pm);
3977 for (int i = 0; i < pm; i++) {
3978 int j = permute[i];
3979 vkeys_sub[i] = vkeys[j];
3980 co_sub[i] = co[j];
3981 uv_sub[i] = uv[j];
3982 if (weight) {
3983 weight_sub[i] = weight[j];
3984 }
3985 pin_sub[i] = pin && pin[j];
3986 select_sub[i] = select && select[j];
3987 }
3988 p_add_ngon(phandle,
3989 key,
3990 pm,
3991 &vkeys_sub.first(),
3992 &co_sub.first(),
3993 &uv_sub.first(),
3994 weight ? &weight_sub.first() : nullptr,
3995 &pin_sub.first(),
3996 &select_sub.first());
3997 return; /* Nothing more to do. */
3998 }
3999 /* No "ears" have previously been inserted. Continue as normal. */
4000 }
4001 if (nverts > 3) {
4002 /* ngon */
4003 p_add_ngon(phandle, key, nverts, vkeys, co, uv, weight, pin, select);
4004 }
4005 else if (!p_face_exists(phandle, vkeys, 0, 1, 2)) {
4006 /* triangle */
4007 p_face_add_construct(phandle, key, vkeys, co, uv, weight, 0, 1, 2, pin, select);
4008 }
4009}
4010
4012{
4014
4015 PEdge *e = p_edge_lookup(phandle, vkeys);
4016 if (e) {
4017 e->flag |= PEDGE_SEAM;
4018 }
4019}
4020
4022 bool fill_holes,
4023 bool topology_from_uvs,
4024 int *r_count_failed)
4025{
4026 int i, j;
4027
4029
4030 phandle->ncharts = p_connect_pairs(phandle, topology_from_uvs);
4031 phandle->charts = p_split_charts(phandle, phandle->construction_chart, phandle->ncharts);
4032
4033 MEM_freeN(phandle->construction_chart);
4034 phandle->construction_chart = nullptr;
4035
4036 phash_safe_delete(&phandle->hash_verts);
4037 phash_safe_delete(&phandle->hash_edges);
4038 phash_safe_delete(&phandle->hash_faces);
4039
4040 for (i = j = 0; i < phandle->ncharts; i++) {
4041 PChart *chart = phandle->charts[i];
4042
4043 PEdge *outer;
4044 p_chart_boundaries(chart, &outer);
4045
4046 if (!topology_from_uvs && chart->nboundaries == 0) {
4047 MEM_freeN(chart);
4048 if (r_count_failed) {
4049 *r_count_failed += 1;
4050 }
4051 continue;
4052 }
4053
4054 phandle->charts[j++] = chart;
4055
4056 if (fill_holes && chart->nboundaries > 1) {
4057 p_chart_fill_boundaries(phandle, chart, outer);
4058 }
4059
4060 for (PVert *v = chart->verts; v; v = v->nextlink) {
4061 p_vert_load_pin_select_uvs(phandle, v);
4062 }
4063 }
4064
4065 phandle->ncharts = j;
4066
4068}
4069
4070void uv_parametrizer_lscm_begin(ParamHandle *phandle, bool live, bool abf)
4071{
4073 phandle->state = PHANDLE_STATE_LSCM;
4074
4075 for (int i = 0; i < phandle->ncharts; i++) {
4076 for (PFace *f = phandle->charts[i]->faces; f; f = f->nextlink) {
4078 }
4079 p_chart_lscm_begin(phandle->charts[i], live, abf);
4080 }
4081}
4082
4083void uv_parametrizer_lscm_solve(ParamHandle *phandle, int *count_changed, int *count_failed)
4084{
4085 BLI_assert(phandle->state == PHANDLE_STATE_LSCM);
4086
4087 for (int i = 0; i < phandle->ncharts; i++) {
4088 PChart *chart = phandle->charts[i];
4089
4090 if (!chart->context) {
4091 continue;
4092 }
4093 const bool result = p_chart_lscm_solve(phandle, chart);
4094
4095 if (result && !chart->has_pins) {
4096 /* Every call to LSCM will eventually call uv_pack, so rotating here might be redundant. */
4098 }
4099 else if (result && chart->single_pin) {
4102 }
4103
4104 if (!result || !chart->has_pins) {
4105 p_chart_lscm_end(chart);
4106 }
4107
4108 if (result) {
4109 if (count_changed != nullptr) {
4110 *count_changed += 1;
4111 }
4112 }
4113 else {
4114 if (count_failed != nullptr) {
4115 *count_failed += 1;
4116 }
4117 }
4118 }
4119}
4120
4122{
4123 BLI_assert(phandle->state == PHANDLE_STATE_LSCM);
4124
4125 for (int i = 0; i < phandle->ncharts; i++) {
4126 p_chart_lscm_end(phandle->charts[i]);
4127#if 0
4128 p_chart_complexify(phandle->charts[i]);
4129#endif
4130 }
4131
4133}
4134
4136{
4138 phandle->state = PHANDLE_STATE_STRETCH;
4139
4140 phandle->rng = BLI_rng_new(31415926);
4141 phandle->blend = 0.0f;
4142
4143 for (int i = 0; i < phandle->ncharts; i++) {
4144 PChart *chart = phandle->charts[i];
4145
4146 for (PVert *v = chart->verts; v; v = v->nextlink) {
4147 v->flag &= ~PVERT_PIN; /* don't use user-defined pins */
4148 }
4149
4151
4152 for (PFace *f = chart->faces; f; f = f->nextlink) {
4154 f->u.area3d = p_face_area(f);
4155 }
4156 }
4157}
4158
4160{
4162 phandle->blend = blend;
4163}
4164
4166{
4168 for (int i = 0; i < phandle->ncharts; i++) {
4169 p_chart_stretch_minimize(phandle->charts[i], phandle->rng);
4170 }
4171}
4172
4178
4180{
4181 if (handle->ncharts == 0) {
4182 return;
4183 }
4184
4185 uv_parametrizer_scale_x(handle, 1.0f / handle->aspect_y);
4186
4187 Vector<PackIsland *> pack_island_vector;
4188
4189 for (int i = 0; i < handle->ncharts; i++) {
4190 PChart *chart = handle->charts[i];
4191 if (params.pin_method == ED_UVPACK_PIN_NONE && chart->has_pins) {
4192 continue;
4193 }
4194
4195 geometry::PackIsland *pack_island = new geometry::PackIsland();
4196 pack_island->caller_index = i;
4197 pack_island->aspect_y = handle->aspect_y;
4198 pack_island->pinned = chart->has_pins;
4199
4200 for (PFace *f = chart->faces; f; f = f->nextlink) {
4201 PVert *v0 = f->edge->vert;
4202 PVert *v1 = f->edge->next->vert;
4203 PVert *v2 = f->edge->next->next->vert;
4204 pack_island->add_triangle(v0->uv, v1->uv, v2->uv);
4205 }
4206
4207 pack_island_vector.append(pack_island);
4208 }
4209
4210 const float scale = pack_islands(pack_island_vector, params);
4211
4212 for (const int64_t i : pack_island_vector.index_range()) {
4213 PackIsland *pack_island = pack_island_vector[i];
4214 const float island_scale = pack_island->can_scale_(params) ? scale : 1.0f;
4215 PChart *chart = handle->charts[pack_island->caller_index];
4216
4217 float matrix[2][2];
4218 pack_island->build_transformation(island_scale, pack_island->angle, matrix);
4219 for (PVert *v = chart->verts; v; v = v->nextlink) {
4220 geometry::mul_v2_m2_add_v2v2(v->uv, matrix, v->uv, pack_island->pre_translate);
4221 }
4222
4223 pack_island_vector[i] = nullptr;
4224 delete pack_island;
4225 }
4226
4227 uv_parametrizer_scale_x(handle, handle->aspect_y);
4228}
4229
4230void uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned, bool scale_uv, bool shear)
4231{
4232 int i;
4233 float tot_area_3d = 0.0f;
4234 float tot_area_uv = 0.0f;
4235 float minv[2], maxv[2], trans[2];
4236
4237 if (phandle->ncharts == 0) {
4238 return;
4239 }
4240
4241 for (i = 0; i < phandle->ncharts; i++) {
4242 PChart *chart = phandle->charts[i];
4243
4244 if (ignore_pinned && chart->has_pins) {
4245 continue;
4246 }
4247
4248 /* Store original bounding box midpoint. */
4249 p_chart_uv_bbox(chart, minv, maxv);
4250 mid_v2_v2v2(chart->origin, minv, maxv);
4251
4252 if (scale_uv || shear) {
4253 /* It's possible that for some "bad" inputs, the following iteration will converge slowly or
4254 * perhaps even diverge. Rather than infinite loop, we only iterate a maximum of `max_iter`
4255 * times. (Also useful when making changes to the calculation.) */
4256 int max_iter = 10;
4257 for (int j = 0; j < max_iter; j++) {
4258 /* An island could contain millions of polygons. When summing many small values, we need to
4259 * use double precision in the accumulator to maintain accuracy. Note that the individual
4260 * calculations only need to be at single precision. */
4261 double scale_cou = 0;
4262 double scale_cov = 0;
4263 double scale_cross = 0;
4264 double weight_sum = 0;
4265 for (PFace *f = chart->faces; f; f = f->nextlink) {
4266 float m[2][2], s[2][2];
4267 PVert *va = f->edge->vert;
4268 PVert *vb = f->edge->next->vert;
4269 PVert *vc = f->edge->next->next->vert;
4270 s[0][0] = va->uv[0] - vc->uv[0];
4271 s[0][1] = va->uv[1] - vc->uv[1];
4272 s[1][0] = vb->uv[0] - vc->uv[0];
4273 s[1][1] = vb->uv[1] - vc->uv[1];
4274 /* Find the "U" axis and "V" axis in triangle coordinates. Normally this would require
4275 * SVD, but in 2D we can use a cheaper matrix inversion instead. */
4276 if (!invert_m2_m2(m, s)) {
4277 continue;
4278 }
4279 float cou[3], cov[3]; /* i.e. Texture "U" and texture "V" in 3D co-ordinates. */
4280 for (int k = 0; k < 3; k++) {
4281 cou[k] = m[0][0] * (va->co[k] - vc->co[k]) + m[0][1] * (vb->co[k] - vc->co[k]);
4282 cov[k] = m[1][0] * (va->co[k] - vc->co[k]) + m[1][1] * (vb->co[k] - vc->co[k]);
4283 }
4284 const float weight = p_face_area(f);
4285 scale_cou += len_v3(cou) * weight;
4286 scale_cov += len_v3(cov) * weight;
4287 if (shear) {
4288 normalize_v3(cov);
4289 normalize_v3(cou);
4290
4291 /* Why is scale_cross called `cross` when we call `dot`? The next line calculates:
4292 * `scale_cross += length(cross(cross(cou, face_normal), cov))`
4293 * By construction, both `cou` and `cov` are orthogonal to the face normal.
4294 * By definition, the normal vector has unit length. */
4295 scale_cross += dot_v3v3(cou, cov) * weight;
4296 }
4297 weight_sum += weight;
4298 }
4299 if (scale_cou * scale_cov < 1e-10f) {
4300 break;
4301 }
4302 const float scale_factor_u = scale_uv ? sqrtf(scale_cou / scale_cov) : 1.0f;
4303
4304 /* Compute correction transform. */
4305 float t[2][2];
4306 t[0][0] = scale_factor_u;
4307 t[1][0] = clamp_f(float(scale_cross / weight_sum), -0.5f, 0.5f);
4308 t[0][1] = 0;
4309 t[1][1] = 1.0f / scale_factor_u;
4310
4311 /* Apply the correction. */
4312 p_chart_uv_transform(chart, t);
4313
4314 /* How far from the identity transform are we? [[1,0],[0,1]] */
4315 const float err = fabsf(t[0][0] - 1.0f) + fabsf(t[1][0]) + fabsf(t[0][1]) +
4316 fabsf(t[1][1] - 1.0f);
4317
4318 const float tolerance = 1e-6f; /* Trade accuracy for performance. */
4319 if (err < tolerance) {
4320 /* Too slow? Use Richardson Extrapolation to accelerate the convergence. */
4321 break;
4322 }
4323 }
4324 }
4325
4326 chart->area_3d = 0.0f;
4327 chart->area_uv = 0.0f;
4328
4329 for (PFace *f = chart->faces; f; f = f->nextlink) {
4330 chart->area_3d += p_face_area(f);
4331 chart->area_uv += fabsf(p_face_uv_area_signed(f));
4332 }
4333
4334 tot_area_3d += chart->area_3d;
4335 tot_area_uv += chart->area_uv;
4336 }
4337
4338 if (tot_area_3d == 0.0f || tot_area_uv == 0.0f) {
4339 /* Prevent divide by zero. */
4340 return;
4341 }
4342
4343 const float tot_fac = tot_area_3d / tot_area_uv;
4344
4345 for (i = 0; i < phandle->ncharts; i++) {
4346 PChart *chart = phandle->charts[i];
4347
4348 if (ignore_pinned && chart->has_pins) {
4349 continue;
4350 }
4351
4352 if (chart->area_3d != 0.0f && chart->area_uv != 0.0f) {
4353 const float fac = chart->area_3d / chart->area_uv;
4354
4355 /* Average scale. */
4356 p_chart_uv_scale(chart, sqrtf(fac / tot_fac));
4357
4358 /* Get the current island bounding box. */
4359 p_chart_uv_bbox(chart, minv, maxv);
4360
4361 /* Move back to original midpoint. */
4362 mid_v2_v2v2(trans, minv, maxv);
4363 sub_v2_v2v2(trans, chart->origin, trans);
4364
4365 p_chart_uv_translate(chart, trans);
4366 }
4367 }
4368}
4369
4371{
4372 for (int i = 0; i < phandle->ncharts; i++) {
4373 PChart *chart = phandle->charts[i];
4374 if (chart->skip_flush) {
4375 continue;
4376 }
4377
4378 p_flush_uvs(phandle, chart);
4379 }
4380}
4381
4383{
4384 for (int i = 0; i < phandle->ncharts; i++) {
4385 PChart *chart = phandle->charts[i];
4386 for (PFace *f = chart->faces; f; f = f->nextlink) {
4388 }
4389 }
4390}
4391
4392/* -------------------------------------------------------------------- */
4395
4396static bool p_collapse_doubles_allowed(PEdge *edge, PEdge *pair, float threshold_squared)
4397{
4398 PVert *oldv, *keepv;
4399
4400 p_collapsing_verts(edge, pair, &oldv, &keepv);
4401
4402 /* Do not collapse a pinned vertex unless the target vertex
4403 * is also pinned. */
4404 if ((oldv->flag & PVERT_PIN) && !(keepv->flag & PVERT_PIN)) {
4405 return false;
4406 }
4407
4408 if (!p_collapse_allowed_topologic(edge, pair)) {
4409 return false;
4410 }
4411
4412 PEdge *collapse_e = edge ? edge : pair;
4413 return p_edge_length_squared(collapse_e) < threshold_squared;
4414}
4415
4416static float p_collapse_doubles_cost(PEdge *edge, PEdge *pair)
4417{
4418 PEdge *collapse_e = edge ? edge : pair;
4419 return p_edge_length_squared(collapse_e);
4420}
4421
4422static void UNUSED_FUNCTION(p_chart_collapse_doubles)(PChart *chart, const float threshold)
4423{
4424 const float threshold_squared = threshold * threshold;
4425
4427 return p_collapse_doubles_allowed(e, pair, threshold_squared);
4428 });
4429}
4430
4432{
4433 PEdge *e, *pair, *edge;
4434 PVert *newv, *keepv;
4435
4436 for (e = chart->collapsed_edges; e; e = e->nextlink) {
4437 if (!(e->flag & PEDGE_COLLAPSE_EDGE)) {
4438 break;
4439 }
4440 edge = e;
4441 pair = e->pair;
4442 if (edge->flag & PEDGE_COLLAPSE_PAIR) {
4443 std::swap(edge, pair);
4444 }
4445 p_collapsing_verts(edge, pair, &newv, &keepv);
4446
4447 if (!(newv->flag & PVERT_PIN)) {
4448 newv->uv[0] = keepv->uv[0];
4449 newv->uv[1] = keepv->uv[1];
4450 }
4451 }
4452}
4453
4455 const float corr_co1[3],
4456 const float corr_co2[3],
4457 const float min_area,
4458 const float min_angle_cos)
4459{
4460 /* Check whether the given corrected coordinates don't result in any other triangle with area
4461 * lower than min_area. */
4462
4463 const PVert *corr_v = corr_e->vert;
4464 const PEdge *e = corr_v->edge;
4465
4466 do {
4467 float area;
4468
4469 if (e == corr_e) {
4470 continue;
4471 }
4472
4473 if (!(e->face->flag & PFACE_DONE)) {
4474 continue;
4475 }
4476
4477 if (e->next->next == corr_e->pair) {
4478 PVert *other_v = e->next->next->vert;
4479 area = area_tri_v3(corr_co1, corr_co2, other_v->co);
4480 }
4481 else {
4482 const PVert *other_v1 = e->next->vert;
4483 const PVert *other_v2 = e->next->next->vert;
4484 area = area_tri_v3(corr_co1, other_v1->co, other_v2->co);
4485 }
4486
4487 if (area < min_area) {
4488 return false;
4489 }
4490
4491 float f_cos[3];
4492
4493 if (e->next->next == corr_e->pair) {
4494 PVert *other_v = e->next->next->vert;
4495 p_triangle_cos(corr_co1, corr_co2, other_v->co, f_cos, f_cos + 1, f_cos + 2);
4496 }
4497 else {
4498 const PVert *other_v1 = e->next->vert;
4499 const PVert *other_v2 = e->next->next->vert;
4500 p_triangle_cos(corr_co1, other_v1->co, other_v2->co, f_cos, f_cos + 1, f_cos + 2);
4501 }
4502
4503 for (int i = 0; i < 3; i++) {
4504 if (f_cos[i] > min_angle_cos) {
4505 return false;
4506 }
4507 }
4508
4509 } while ((e = p_wheel_edge_next(e)) && (e != corr_v->edge));
4510
4511 return true;
4512}
4513
4514static bool p_validate_corrected_coords(const PEdge *corr_e,
4515 const float corr_co[3],
4516 float min_area,
4517 float min_angle_cos)
4518{
4519 /* Check whether the given corrected coordinates don't result in any other triangle with area
4520 * lower than min_area. */
4521
4522 const PVert *corr_v = corr_e->vert;
4523 const PEdge *e = corr_v->edge;
4524
4525 do {
4526 if (!(e->face->flag & PFACE_DONE) && (e != corr_e)) {
4527 continue;
4528 }
4529
4530 const PVert *other_v1 = e->next->vert;
4531 const PVert *other_v2 = e->next->next->vert;
4532
4533 const float area = area_tri_v3(corr_co, other_v1->co, other_v2->co);
4534
4535 if (area < min_area) {
4536 return false;
4537 }
4538
4539 float f_cos[3];
4540 p_triangle_cos(corr_co, other_v1->co, other_v2->co, f_cos, f_cos + 1, f_cos + 2);
4541
4542 for (int i = 0; i < 3; i++) {
4543 if (f_cos[i] > min_angle_cos) {
4544 return false;
4545 }
4546 }
4547
4548 } while ((e = p_wheel_edge_next(e)) && (e != corr_v->edge));
4549
4550 return true;
4551}
4552
4553static bool p_edge_matrix(float R[3][3], const float edge_dir[3])
4554{
4555 static const constexpr float eps = 1.0e-5;
4556 static const constexpr float n1[3] = {0.0f, 0.0f, 1.0f};
4557 static const constexpr float n2[3] = {0.0f, 1.0f, 0.0f};
4558
4559 float edge_len = len_v3(edge_dir);
4560 if (edge_len < eps) {
4561 return false;
4562 }
4563
4564 float edge_dir_norm[3];
4565 copy_v3_v3(edge_dir_norm, edge_dir);
4566 mul_v3_fl(edge_dir_norm, 1.0f / edge_len);
4567
4568 float normal_dir[3];
4569 cross_v3_v3v3(normal_dir, edge_dir_norm, n1);
4570 float normal_len = len_v3(normal_dir);
4571
4572 if (normal_len < eps) {
4573 cross_v3_v3v3(normal_dir, edge_dir_norm, n2);
4574 normal_len = len_v3(normal_dir);
4575
4576 if (normal_len < eps) {
4577 return false;
4578 }
4579 }
4580
4581 mul_v3_fl(normal_dir, 1.0f / normal_len);
4582
4583 float tangent_dir[3];
4584 cross_v3_v3v3(tangent_dir, edge_dir_norm, normal_dir);
4585
4586 R[0][0] = edge_dir_norm[0];
4587 R[1][0] = edge_dir_norm[1];
4588 R[2][0] = edge_dir_norm[2];
4589
4590 R[0][1] = normal_dir[0];
4591 R[1][1] = normal_dir[1];
4592 R[2][1] = normal_dir[2];
4593
4594 R[0][2] = tangent_dir[0];
4595 R[1][2] = tangent_dir[1];
4596 R[2][2] = tangent_dir[2];
4597
4598 return true;
4599}
4600
4601static bool p_edge_matrix(float R[3][3], const PEdge *e)
4602{
4603 float edge_dir[3];
4604 copy_v3_v3(edge_dir, e->next->vert->co);
4605 sub_v3_v3(edge_dir, e->vert->co);
4606
4607 return p_edge_matrix(R, edge_dir);
4608}
4609
4610static const float CORR_ZERO_AREA_EPS = 1.0e-10f;
4611
4613 float min_area,
4614 float min_angle_cos)
4615{
4616 static const float3 ref_edges[] = {
4617 {1.0f, 0.0f, 0.0f},
4618 {0.0f, 1.0f, 0.0f},
4619 {0.0f, 0.0f, 1.0f},
4620 {0.0f, 1.0f, 1.0f},
4621 {1.0f, 0.0f, 1.0f},
4622 {1.0f, 1.0f, 0.0f},
4623
4624 {0.0f, 0.5f, 1.0f},
4625 {0.5f, 0.0f, 1.0f},
4626 {0.5f, 1.0f, 0.0f},
4627
4628 {0.0f, 1.0f, 0.5f},
4629 {1.0f, 0.0f, 0.5f},
4630 {1.0f, 0.5f, 0.0f},
4631 };
4632 static const int ref_edge_count = ARRAY_SIZE(ref_edges);
4633 static const int LEN_MULTIPLIER_COUNT = 3;
4634 bool corr_co_found = false;
4635
4636 float corr_len = 2.0f * std::sqrt((min_area + CORR_ZERO_AREA_EPS) / 3.0f / std::sqrt(3.0f));
4637
4638 for (int m = 0; m < LEN_MULTIPLIER_COUNT; m++) {
4639 for (int re = 0; re < ref_edge_count; re++) {
4640 float M[3][3];
4641
4642 if (!p_edge_matrix(M, ref_edges[re])) {
4643 continue;
4644 }
4645
4646 float corr_co[3][3];
4647 PEdge *e = f->edge;
4648
4649 for (int i = 0; i < 3; i++) {
4650 const float angle = (float(i) / 3.0f) * float(2.0 * M_PI);
4651 float corr_dir[3] = {0.0f, cos(angle), sin(angle)};
4652
4653 float corr_len_multiplied = corr_len * (m + 1);
4654
4655 mul_m3_v3(M, corr_dir);
4656 mul_v3_fl(corr_dir, corr_len_multiplied);
4657
4658 copy_v3_v3(corr_co[i], e->vert->co);
4659 add_v3_v3(corr_co[i], corr_dir);
4660 e = e->next;
4661 }
4662
4663 e = f->edge;
4664 for (int i = 0; i < 3; i++) {
4666 e, corr_co[i], corr_co[(i + 1) % 3], min_area, min_angle_cos))
4667 {
4668 return false;
4669 }
4670
4671 e = e->next;
4672 }
4673
4674 e = f->edge;
4675 for (int i = 0; i < 3; i++) {
4676 copy_v3_v3(e->vert->co, corr_co[i]);
4677 e = e->next;
4678 }
4679
4680 corr_co_found = true;
4681 break;
4682 }
4683
4684 if (corr_co_found) {
4685 break;
4686 }
4687 }
4688
4689 if (!corr_co_found) {
4690 return false;
4691 }
4692
4693 f->flag |= PFACE_DONE;
4694 return true;
4695}
4696
4697static bool p_chart_correct_degenerate_triangles2(PChart *chart, float min_area, float min_angle)
4698{
4699 static const float eps = 1.0e-6;
4700
4701 float min_angle_cos = std::cos(min_angle);
4702 float min_angle_sin = std::sin(min_angle + CORR_ZERO_AREA_EPS);
4703
4704 for (PFace *f = chart->faces; f; f = f->nextlink) {
4705 if (f->flag & PFACE_DONE) {
4706 continue;
4707 }
4708
4709 float face_area = p_face_area(f);
4710
4711 PEdge *max_edge = nullptr;
4712 float max_edge_len = -std::numeric_limits<float>::infinity();
4713
4714 PEdge *min_edge = nullptr;
4715 float min_edge_len = std::numeric_limits<float>::infinity();
4716
4717 PEdge *middle_edge = nullptr;
4718
4719 PEdge *e = f->edge;
4720 do {
4721 float len = p_edge_length(e);
4722
4723 if (len > max_edge_len) {
4724 max_edge = e;
4725 max_edge_len = len;
4726
4727 middle_edge = max_edge->next == min_edge ? min_edge->next : max_edge->next;
4728 }
4729
4730 if (len < min_edge_len) {
4731 min_edge = e;
4732 min_edge_len = len;
4733
4734 middle_edge = min_edge->next == max_edge ? max_edge->next : min_edge->next;
4735 }
4736
4737 e = e->next;
4738 } while (e != f->edge);
4739
4740 BLI_assert(max_edge);
4741 BLI_assert(min_edge);
4742 BLI_assert(middle_edge);
4743
4744 bool small_uniside_tri = (face_area <= min_area) && (min_edge == max_edge);
4745
4746 if ((max_edge_len < eps) || small_uniside_tri) {
4747 p_chart_correct_degenerate_triangle_point(f, min_area, min_angle_cos);
4748 continue;
4749 }
4750
4751 if (min_edge == max_edge) {
4752 BLI_assert(face_area > min_area);
4753 f->flag |= PFACE_DONE;
4754 continue;
4755 }
4756
4757 BLI_assert(middle_edge != max_edge);
4758 BLI_assert(middle_edge != min_edge);
4759
4760 float M[3][3];
4761 if (!p_edge_matrix(M, max_edge)) {
4762 continue;
4763 }
4764
4765 float max_face_cos =
4766 middle_edge->next == max_edge ?
4767 p_vec_cos(middle_edge->vert->co, max_edge->vert->co, min_edge->vert->co) :
4768 p_vec_cos(max_edge->vert->co, middle_edge->vert->co, min_edge->vert->co);
4769
4770 float angle_corr_len = max_face_cos > min_angle_cos ?
4771 p_edge_length(middle_edge) * min_angle_sin :
4772 0.0f;
4773
4774 if ((face_area > min_area) && (angle_corr_len == 0.0f)) {
4775 f->flag |= PFACE_DONE;
4776 continue;
4777 }
4778
4779 float corr_len = (min_area + CORR_ZERO_AREA_EPS) * 2.0f / max_edge_len;
4780 corr_len = std::max(corr_len, angle_corr_len);
4781
4782 PEdge *corr_e = max_edge->next->next;
4783 PVert *corr_v = corr_e->vert;
4784
4785 /* Check 4 distinct directions. */
4786 static const constexpr int DIR_COUNT = 16;
4787 static const constexpr int LEN_MULTIPLIER_COUNT = 2;
4788 float corr_co[3];
4789 bool corr_co_found = false;
4790
4791 for (int m = 0; m < LEN_MULTIPLIER_COUNT; m++) {
4792 for (int d = 0; d < DIR_COUNT; d++) {
4793 const float angle = (float(d) / DIR_COUNT) * float(2.0 * M_PI);
4794 float corr_dir[3] = {0.0f, cos(angle), sin(angle)};
4795
4796 const float corr_len_multiplied = corr_len * (m + 1);
4797
4798 mul_m3_v3(M, corr_dir);
4799 mul_v3_fl(corr_dir, corr_len_multiplied);
4800
4801 copy_v3_v3(corr_co, corr_v->co);
4802 add_v3_v3(corr_co, corr_dir);
4803
4804 if (p_validate_corrected_coords(corr_e, corr_co, min_area, min_angle_cos)) {
4805 corr_co_found = true;
4806 break;
4807 }
4808 }
4809
4810 if (corr_co_found) {
4811 break;
4812 }
4813 }
4814
4815 if (!corr_co_found) {
4816 continue;
4817 }
4818
4819 f->flag |= PFACE_DONE;
4820 copy_v3_v3(corr_v->co, corr_co);
4821 }
4822
4823 return true;
4824}
4825
4826#ifndef NDEBUG
4827
4828static bool p_validate_triangle_angles(const PVert *vert1,
4829 const PVert *vert2,
4830 const PVert *vert3,
4831 const float min_angle_cos)
4832{
4833 float t_cos[3];
4834 p_triangle_cos(vert1->co, vert2->co, vert3->co, t_cos, t_cos + 1, t_cos + 2);
4835
4836 for (int i = 0; i < 3; i++) {
4837 if (t_cos[i] > min_angle_cos) {
4838 return false;
4839 }
4840 }
4841
4842 return true;
4843}
4844
4845#endif
4846
4848 const float min_area,
4849 const float min_angle)
4850{
4851 /* Look for degenerate triangles: triangles with angles lower than `min_angle` or having area
4852 * lower than `min_area` and try to correct vertex coordinates so that the resulting triangle is
4853 * not degenerate.
4854 *
4855 * The return value indicates whether all triangles could be corrected.
4856 */
4857
4858 bool ret = p_chart_correct_degenerate_triangles2(chart, min_area, min_angle);
4859
4860#ifndef NDEBUG
4861 float min_angle_cos = std::cos(min_angle - CORR_ZERO_AREA_EPS);
4862#endif
4863
4864 for (PFace *f = chart->faces; f; f = f->nextlink) {
4865 if (!(f->flag & PFACE_DONE)) {
4866 ret = false;
4867 }
4868 else {
4869#ifndef NDEBUG
4870 float f_area = p_face_area(f);
4871 BLI_assert(f_area > (min_area - CORR_ZERO_AREA_EPS));
4872
4873 PVert *vert1 = f->edge->vert;
4874 PVert *vert2 = f->edge->next->vert;
4875 PVert *vert3 = f->edge->next->next->vert;
4876
4877 BLI_assert(p_validate_triangle_angles(vert1, vert2, vert3, min_angle_cos));
4878#endif
4879 }
4880
4881 f->flag &= ~PFACE_DONE;
4882 }
4883
4884 return ret;
4885}
4886
4888
4889/* -------------------------------------------------------------------- */
4892
4893#ifdef WITH_UV_SLIM
4894
4898static slim::MatrixTransfer *slim_matrix_transfer(const ParamSlimOptions *slim_options)
4899{
4901
4902 mt->use_weights = slim_options->weight_influence > 0.0f;
4903 mt->weight_influence = slim_options->weight_influence;
4904 mt->n_iterations = slim_options->iterations;
4905 mt->reflection_mode = slim_options->no_flip;
4906 mt->skip_initialization = slim_options->skip_init;
4907
4908 return mt;
4909}
4910
4915static void slim_allocate_matrices(const PChart *chart, slim::MatrixTransferChart *mt_chart)
4916{
4917 mt_chart->verts_num = chart->nverts;
4918 mt_chart->faces_num = chart->nfaces;
4919 mt_chart->edges_num = chart->nedges;
4920
4921 mt_chart->v_matrices.resize(mt_chart->verts_num * 3);
4922 mt_chart->uv_matrices.resize(mt_chart->verts_num * 2);
4923 mt_chart->pp_matrices.resize(mt_chart->verts_num * 2);
4924
4925 mt_chart->f_matrices.resize(mt_chart->faces_num * 3);
4926 mt_chart->p_matrices.resize(mt_chart->verts_num);
4927 mt_chart->b_vectors.resize(mt_chart->verts_num);
4928 /* Also clear memory for weight vectors, hence 'new' followed by `()`. */
4929 mt_chart->w_vectors.resize(mt_chart->verts_num, 0.0);
4930
4931 mt_chart->e_matrices.resize(mt_chart->edges_num * 2 * 2);
4932 mt_chart->el_vectors.resize(mt_chart->edges_num * 2);
4933}
4934
4938static void slim_transfer_edges(PChart *chart, slim::MatrixTransferChart *mt_chart)
4939{
4940 std::vector<int> &E = mt_chart->e_matrices;
4941 std::vector<double> &EL = mt_chart->el_vectors;
4942
4943 PEdge *outer;
4944 p_chart_boundaries(chart, &outer);
4945
4946 PEdge *be = outer;
4947 int eid = 0;
4948
4949 static const float DOUBLED_VERT_THRESHOLD = 1.0e-5;
4950
4951 do {
4952 E[eid] = be->vert->slim_id;
4953 const float edge_len = p_edge_length(be);
4954 EL[eid] = edge_len;
4955
4956 /* Temporary solution : SLIM doesn't support doubled vertices for now. */
4957 if (edge_len < DOUBLED_VERT_THRESHOLD) {
4958 mt_chart->succeeded = false;
4959 }
4960
4961 be = p_boundary_edge_next(be);
4962 E[eid + mt_chart->edges_num + mt_chart->boundary_vertices_num] = be->vert->slim_id;
4963 eid++;
4964
4965 } while (be != outer);
4966
4967 for (PEdge *e = chart->edges; e; e = e->nextlink) {
4968 PEdge *e1 = e->next;
4969
4970 E[eid] = e->vert->slim_id;
4971 const float edge_len = p_edge_length(e);
4972 EL[eid] = edge_len;
4973
4974 /* Temporary solution : SLIM doesn't support doubled vertices for now. */
4975 if (edge_len < DOUBLED_VERT_THRESHOLD) {
4976 mt_chart->succeeded = false;
4977 }
4978
4979 E[eid + mt_chart->edges_num + mt_chart->boundary_vertices_num] = e1->vert->slim_id;
4980 eid++;
4981 }
4982}
4983
4987static void slim_transfer_vertices(const PChart *chart,
4988 slim::MatrixTransferChart *mt_chart,
4989 slim::MatrixTransfer *mt)
4990{
4991 int r = mt_chart->verts_num;
4992 std::vector<double> &v_mat = mt_chart->v_matrices;
4993 std::vector<double> &uv_mat = mt_chart->uv_matrices;
4994 std::vector<int> &p_mat = mt_chart->p_matrices;
4995 std::vector<double> &pp_mat = mt_chart->pp_matrices;
4996 std::vector<float> &w_vec = mt_chart->w_vectors;
4997
4998 int p_vid = 0;
4999 int vid = mt_chart->boundary_vertices_num;
5000
5001 /* For every vertex, fill up V matrix and P matrix (pinned vertices) */
5002 for (PVert *v = chart->verts; v; v = v->nextlink) {
5003 if (!v->on_boundary_flag) {
5004 if (mt->use_weights) {
5005 w_vec[vid] = v->weight;
5006 }
5007
5008 v->slim_id = vid;
5009 vid++;
5010 }
5011
5012 v_mat[v->slim_id] = v->co[0];
5013 v_mat[r + v->slim_id] = v->co[1];
5014 v_mat[2 * r + v->slim_id] = v->co[2];
5015
5016 uv_mat[v->slim_id] = v->uv[0];
5017 uv_mat[r + v->slim_id] = v->uv[1];
5018
5019 if (v->flag & PVERT_PIN || (mt->is_minimize_stretch && !(v->flag & PVERT_SELECT))) {
5020 mt_chart->pinned_vertices_num += 1;
5021
5022 p_mat[p_vid] = v->slim_id;
5023 pp_mat[2 * p_vid] = double(v->uv[0]);
5024 pp_mat[2 * p_vid + 1] = double(v->uv[1]);
5025 p_vid += 1;
5026 }
5027 }
5028}
5029
5033static void slim_transfer_boundary_vertices(PChart *chart,
5034 slim::MatrixTransferChart *mt_chart,
5035 const slim::MatrixTransfer *mt)
5036{
5037 std::vector<int> &b_vec = mt_chart->b_vectors;
5038 std::vector<float> &w_vec = mt_chart->w_vectors;
5039
5040 /* For every vertex, set slim_flag to 0 */
5041 for (PVert *v = chart->verts; v; v = v->nextlink) {
5042 v->on_boundary_flag = false;
5043 }
5044
5045 /* Find vertices on boundary and save into vector B */
5046 PEdge *outer;
5047 p_chart_boundaries(chart, &outer);
5048
5049 PEdge *be = outer;
5050 int vid = 0;
5051
5052 do {
5053 if (mt->use_weights) {
5054 w_vec[vid] = be->vert->weight;
5055 }
5056
5057 mt_chart->boundary_vertices_num += 1;
5058 be->vert->slim_id = vid;
5059 be->vert->on_boundary_flag = true;
5060 b_vec[vid] = vid;
5061
5062 vid += 1;
5063 be = p_boundary_edge_next(be);
5064
5065 } while (be != outer);
5066}
5067
5071static void slim_transfer_faces(const PChart *chart, slim::MatrixTransferChart *mt_chart)
5072{
5073 int fid = 0;
5074
5075 for (PFace *f = chart->faces; f; f = f->nextlink) {
5076 PEdge *e = f->edge;
5077 PEdge *e1 = e->next;
5078 PEdge *e2 = e1->next;
5079
5080 int r = mt_chart->faces_num;
5081 std::vector<int> &F = mt_chart->f_matrices;
5082
5083 F[fid] = e->vert->slim_id;
5084 F[r + fid] = e1->vert->slim_id;
5085 F[2 * r + fid] = e2->vert->slim_id;
5086 fid++;
5087 }
5088}
5089
5093static void slim_convert_blender(ParamHandle *phandle, slim::MatrixTransfer *mt)
5094{
5095 static const float SLIM_CORR_MIN_AREA = 1.0e-8;
5096 static const float SLIM_CORR_MIN_ANGLE = DEG2RADF(1.0f);
5097
5098 mt->charts.resize(phandle->ncharts);
5099
5100 for (int i = 0; i < phandle->ncharts; i++) {
5101 PChart *chart = phandle->charts[i];
5102 slim::MatrixTransferChart *mt_chart = &mt->charts[i];
5103
5104 p_chart_correct_degenerate_triangles(chart, SLIM_CORR_MIN_AREA, SLIM_CORR_MIN_ANGLE);
5105
5106 mt_chart->succeeded = true;
5107 mt_chart->pinned_vertices_num = 0;
5108 mt_chart->boundary_vertices_num = 0;
5109
5110 /* Allocate memory for matrices of Vertices,Faces etc. for each chart. */
5111 slim_allocate_matrices(chart, mt_chart);
5112
5113 /* For each chart, fill up matrices. */
5114 slim_transfer_boundary_vertices(chart, mt_chart, mt);
5115 slim_transfer_vertices(chart, mt_chart, mt);
5116 slim_transfer_edges(chart, mt_chart);
5117 slim_transfer_faces(chart, mt_chart);
5118
5119 mt_chart->pp_matrices.resize(mt_chart->pinned_vertices_num * 2);
5120 mt_chart->pp_matrices.shrink_to_fit();
5121
5122 mt_chart->p_matrices.resize(mt_chart->pinned_vertices_num);
5123 mt_chart->p_matrices.shrink_to_fit();
5124
5125 mt_chart->b_vectors.resize(mt_chart->boundary_vertices_num);
5126 mt_chart->b_vectors.shrink_to_fit();
5127
5128 mt_chart->e_matrices.resize((mt_chart->edges_num + mt_chart->boundary_vertices_num) * 2);
5129 mt_chart->e_matrices.shrink_to_fit();
5130 }
5131}
5132
5133static void slim_transfer_data_to_slim(ParamHandle *phandle, const ParamSlimOptions *slim_options)
5134{
5135 slim::MatrixTransfer *mt = slim_matrix_transfer(slim_options);
5136
5137 slim_convert_blender(phandle, mt);
5138 phandle->slim_mt = mt;
5139}
5140
5144static void slim_flush_uvs(ParamHandle *phandle,
5145 slim::MatrixTransfer *mt,
5146 int *count_changed,
5147 int *count_failed,
5148 bool live = false)
5149{
5150 int vid;
5151 PVert *v;
5152
5153 for (int i = 0; i < phandle->ncharts; i++) {
5154 PChart *chart = phandle->charts[i];
5155 slim::MatrixTransferChart *mt_chart = &mt->charts[i];
5156
5157 if (mt_chart->succeeded) {
5158 if (count_changed) {
5159 (*count_changed)++;
5160 }
5161
5162 const std::vector<double> &UV = mt_chart->uv_matrices;
5163 for (v = chart->verts; v; v = v->nextlink) {
5164 if (v->flag & PVERT_PIN) {
5165 continue;
5166 }
5167
5168 vid = v->slim_id;
5169 v->uv[0] = UV[vid];
5170 v->uv[1] = UV[mt_chart->verts_num + vid];
5171 }
5172 }
5173 else {
5174 if (count_failed) {
5175 (*count_failed)++;
5176 }
5177
5178 if (!live) {
5179 for (v = chart->verts; v; v = v->nextlink) {
5180 v->uv[0] = 0.0f;
5181 v->uv[1] = 0.0f;
5182 }
5183 }
5184 }
5185 }
5186}
5187
5191static void slim_free_matrix_transfer(ParamHandle *phandle)
5192{
5193 delete phandle->slim_mt;
5194 phandle->slim_mt = nullptr;
5195}
5196
5197static void slim_get_pinned_vertex_data(ParamHandle *phandle,
5198 PChart *chart,
5199 slim::MatrixTransferChart &mt_chart,
5200 slim::PinnedVertexData &pinned_vertex_data)
5201{
5202 std::vector<int> &pinned_vertex_indices = pinned_vertex_data.pinned_vertex_indices;
5203 std::vector<double> &pinned_vertex_positions_2D = pinned_vertex_data.pinned_vertex_positions_2D;
5204 std::vector<int> &selected_pins = pinned_vertex_data.selected_pins;
5205
5206 pinned_vertex_indices.clear();
5207 pinned_vertex_positions_2D.clear();
5208 selected_pins.clear();
5209
5210 /* Boundary vertices have lower slim_ids, process them first. */
5211 PEdge *outer;
5212 p_chart_boundaries(chart, &outer);
5213 PEdge *be = outer;
5214 do {
5215 if (be->vert->flag & PVERT_PIN) {
5216 /* Reload vertex position. */
5217 p_vert_load_pin_select_uvs(phandle, be->vert);
5218
5219 if (be->vert->flag & PVERT_SELECT) {
5220 selected_pins.push_back(be->vert->slim_id);
5221 }
5222
5223 pinned_vertex_indices.push_back(be->vert->slim_id);
5224 pinned_vertex_positions_2D.push_back(be->vert->uv[0]);
5225 pinned_vertex_positions_2D.push_back(be->vert->uv[1]);
5226 }
5227 be = p_boundary_edge_next(be);
5228 } while (be != outer);
5229
5230 PVert *v;
5231 for (v = chart->verts; v; v = v->nextlink) {
5232 if (!v->on_boundary_flag && (v->flag & PVERT_PIN)) {
5233 /* Reload `v`. */
5234 p_vert_load_pin_select_uvs(phandle, v);
5235
5236 if (v->flag & PVERT_SELECT) {
5237 selected_pins.push_back(v->slim_id);
5238 }
5239 pinned_vertex_indices.push_back(v->slim_id);
5240 pinned_vertex_positions_2D.push_back(v->uv[0]);
5241 pinned_vertex_positions_2D.push_back(v->uv[1]);
5242 }
5243 }
5244
5245 mt_chart.pinned_vertices_num = pinned_vertex_indices.size();
5246}
5247
5248#endif /* WITH_UV_SLIM */
5249
5251
5252/* -------------------------------------------------------------------- */
5255
5257 const ParamSlimOptions *slim_options,
5258 int *count_changed,
5259 int *count_failed)
5260{
5261#ifdef WITH_UV_SLIM
5262 slim_transfer_data_to_slim(phandle, slim_options);
5263 slim::MatrixTransfer *mt = phandle->slim_mt;
5264
5265 mt->parametrize();
5266
5267 slim_flush_uvs(phandle, mt, count_changed, count_failed);
5268 slim_free_matrix_transfer(phandle);
5269#else
5270 *count_changed = 0;
5271 *count_failed = 0;
5272 UNUSED_VARS(phandle, slim_options, count_changed, count_failed);
5273#endif /* !WITH_UV_SLIM */
5274}
5275
5277{
5278#ifdef WITH_UV_SLIM
5279 slim_transfer_data_to_slim(phandle, slim_options);
5280 slim::MatrixTransfer *mt = phandle->slim_mt;
5281
5282 for (int i = 0; i < phandle->ncharts; i++) {
5283 for (PFace *f = phandle->charts[i]->faces; f; f = f->nextlink) {
5285 }
5286 }
5287
5288 for (int i = 0; i < phandle->ncharts; i++) {
5289 PChart *chart = phandle->charts[i];
5290 slim::MatrixTransferChart &mt_chart = mt->charts[i];
5291
5292 bool select = false, deselect = false;
5293
5294 /* Give vertices matrix indices and count pins. */
5295 for (PVert *v = chart->verts; v; v = v->nextlink) {
5296 if (v->flag & PVERT_PIN) {
5297 if (v->flag & PVERT_SELECT) {
5298 select = true;
5299 }
5300 }
5301
5302 if (!(v->flag & PVERT_SELECT)) {
5303 deselect = true;
5304 }
5305 }
5306
5307 if (!select || !deselect) {
5308 chart->skip_flush = true;
5309 mt_chart.succeeded = false;
5310 continue;
5311 }
5312
5313 mt->setup_slim_data(mt_chart);
5314 }
5315#else
5316 UNUSED_VARS(phandle, slim_options);
5317#endif /* !WITH_UV_SLIM */
5318}
5319
5321{
5322#ifdef WITH_UV_SLIM
5323 slim::MatrixTransfer *mt = phandle->slim_mt;
5324
5325 /* Do one iteration and transfer UVs. */
5326 for (int i = 0; i < phandle->ncharts; i++) {
5327 mt->charts[i].parametrize_single_iteration();
5328 mt->charts[i].transfer_uvs_blended(blend);
5329 }
5330
5331 /* Assign new UVs back to each vertex. */
5332 slim_flush_uvs(phandle, mt, nullptr, nullptr);
5333#else
5334 UNUSED_VARS(phandle, blend);
5335#endif /* !WITH_UV_SLIM */
5336}
5337
5339{
5340#ifdef WITH_UV_SLIM
5341 slim::MatrixTransfer *mt = phandle->slim_mt;
5342
5343 /* Do one iteration and transfer UVs */
5344 for (int i = 0; i < phandle->ncharts; i++) {
5345 PChart *chart = phandle->charts[i];
5346 slim::MatrixTransferChart &mt_chart = mt->charts[i];
5347
5348 if (!mt_chart.data) {
5349 continue;
5350 }
5351
5352 slim_get_pinned_vertex_data(phandle, chart, mt_chart, mt->pinned_vertex_data);
5353
5354 mt->parametrize_live(mt_chart, mt->pinned_vertex_data);
5355 }
5356
5357 /* Assign new UVs back to each vertex. */
5358 const bool live = true;
5359 slim_flush_uvs(phandle, mt, nullptr, nullptr, live);
5360#else
5361 UNUSED_VARS(phandle);
5362#endif /* !WITH_UV_SLIM */
5363}
5364
5366{
5367#ifdef WITH_UV_SLIM
5368 slim::MatrixTransfer *mt = phandle->slim_mt;
5369
5370 for (int i = 0; i < phandle->ncharts; i++) {
5371 slim::MatrixTransferChart *mt_chart = &mt->charts[i];
5372 mt_chart->free_slim_data();
5373 }
5374
5375 slim_free_matrix_transfer(phandle);
5376#else
5377 UNUSED_VARS(phandle);
5378#endif /* WITH_UV_SLIM */
5379}
5380
5382{
5383#ifdef WITH_UV_SLIM
5384 return phandle->slim_mt != nullptr;
5385#else
5386 UNUSED_VARS(phandle);
5387 return false;
5388#endif
5389}
5390
5392
5393} // namespace blender::geometry
#define BLI_assert(a)
Definition BLI_assert.h:46
float BLI_convexhull_aabb_fit_points_2d(blender::Span< blender::float2 > points)
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.cc:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
GHash * BLI_ghash_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
HeapNode * BLI_heap_top(const Heap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:279
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.cc:191
Heap * BLI_heap_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.cc:171
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:269
void * BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:291
HeapNode * BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1)
Definition BLI_heap.cc:234
void void BLI_heap_remove(Heap *heap, HeapNode *node) ATTR_NONNULL(1
Heap * BLI_heap_new(void) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.cc:186
MINLINE double max_ddd(double a, double b, double c)
MINLINE float clamp_f(float value, float min, float max)
MINLINE double max_dd(double a, double b)
#define DEG2RAD(_deg)
#define DEG2RADF(_deg)
#define M_PI_2
#define M_PI
#define M_PI_4
float volume_tri_tetrahedron_signed_v3(const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:271
void axis_dominant_v3_to_m3_negate(float r_mat[3][3], const float normal[3])
float area_tri_v3(const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:100
bool invert_m2_m2(float inverse[2][2], const float mat[2][2])
void mul_m3_v3(const float M[3][3], float r[3])
void mul_v2_m3v3(float r[2], const float M[3][3], const float a[3])
void mul_m2_v2(const float mat[2][2], float vec[2])
void angle_to_mat2(float R[2][2], float angle)
float angle_v2v2v2(const float a[2], const float b[2], const float c[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
float cos_v3v3v3(const float p1[3], const float p2[3], const float p3[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_newell_cross_v3_v3v3(float n[3], const float v_prev[3], const float v_curr[3])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
void minmax_v2v2_v2(float min[2], float max[2], const float vec[2])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
void mid_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void zero_v3(float r[3])
float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v3(float n[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
#define BLI_MEMARENA_STD_BUFSIZE
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void * BLI_memarena_calloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1)
void BLI_polyfill_calc_arena(const float(*coords)[2], unsigned int coords_num, int coords_sign, unsigned int(*r_tris)[3], struct MemArena *arena)
#define BLI_POLYFILL_ALLOC_NGON_RESERVE
void BLI_polyfill_beautify(const float(*coords)[2], unsigned int coords_num, unsigned int(*tris)[3], struct MemArena *arena, struct Heap *eheap)
Random number functions.
struct RNG * BLI_rng_new(unsigned int seed)
Definition rand.cc:39
void BLI_rng_free(struct RNG *rng) ATTR_NONNULL(1)
Definition rand.cc:53
float BLI_rng_get_float(struct RNG *rng) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition rand.cc:88
unsigned int uint
#define INIT_MINMAX2(min, max)
#define UNUSED_FUNCTION(x)
#define ARRAY_SIZE(arr)
#define UNUSED_VARS(...)
#define POINTER_FROM_INT(i)
#define SHIFT3(type, a, b, c)
#define UNLIKELY(x)
#define ELEM(...)
@ ED_UVPACK_PIN_NONE
#define PARAM_KEY_MAX
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
#define MEM_SAFE_FREE(v)
#define MEM_SIZE_OPTIMAL(size)
#define U
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
static T sum(const btAlignedObjectArray< T > &items)
SIMD_FORCE_INLINE btScalar norm() const
Return the norm (length) of the vector.
Definition btVector3.h:263
slim::MatrixTransfer * slim_mt
const T & first() const
Definition BLI_array.hh:281
int64_t size() const
void append(const T &value)
void remove(const int64_t index)
IndexRange index_range() const
void append_unchecked(const T &value)
void reserve(const int64_t min_capacity)
bool can_scale_(const UVPackIsland_Params &params) const
Definition uv_pack.cc:2409
void build_transformation(float scale, double angle, float r_matrix[2][2]) const
Definition uv_pack.cc:2332
void add_triangle(float2 uv0, float2 uv1, float2 uv2)
Definition uv_pack.cc:140
nullptr float
static float verts[][3]
#define sin
#define cos
#define select(A, B, C)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
LinearSolver * EIG_linear_solver_new(int num_rows, int num_columns, int num_right_hand_sides)
void EIG_linear_solver_variable_set(LinearSolver *solver, int rhs, int index, double value)
void EIG_linear_solver_right_hand_side_add(LinearSolver *solver, int rhs, int index, double value)
LinearSolver * EIG_linear_least_squares_solver_new(int num_rows, int num_columns, int num_right_hand_sides)
void EIG_linear_solver_delete(LinearSolver *solver)
double EIG_linear_solver_variable_get(LinearSolver *solver, int rhs, int index)
void EIG_linear_solver_matrix_add(LinearSolver *solver, int row, int col, double value)
bool EIG_linear_solver_solve(LinearSolver *solver)
void EIG_linear_solver_variable_lock(LinearSolver *solver, int index)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float beta(const float x, const float y)
Definition math_base.h:661
static ulong * next
#define M
static int corner1[12]
#define T
#define F
#define R
#define L
static char faces[256]
static int corner2[12]
static void p_chart_rotate_minimum_area(PChart *chart)
bool uv_parametrizer_is_slim(const ParamHandle *phandle)
static PVert * p_vert_add(ParamHandle *handle, PHashKey key, const float co[3], const float weight, PEdge *e)
static PFace * p_face_add_construct(ParamHandle *handle, ParamKey key, const ParamKey *vkeys, const float **co, float **uv, const float *weight, int i1, int i2, int i3, const bool *pin, const bool *select)
void uv_parametrizer_construct_end(ParamHandle *phandle, bool fill_holes, bool topology_from_uvs, int *r_count_failed=nullptr)
static bool p_validate_corrected_coords(const PEdge *corr_e, const float corr_co[3], float min_area, float min_angle_cos)
static void p_chart_fill_boundaries(ParamHandle *handle, PChart *chart, const PEdge *outer)
static PFace * p_face_add_fill(ParamHandle *handle, PChart *chart, PVert *v1, PVert *v2, PVert *v3)
void uv_parametrizer_edge_set_seam(ParamHandle *phandle, const ParamKey *vkeys)
static void p_vert_load_pin_select_uvs(ParamHandle *handle, PVert *v)
static bool p_chart_lscm_solve(ParamHandle *handle, PChart *chart)
static void p_face_angles(PFace *f, double *r_a1, double *r_a2, double *r_a3)
static bool p_intersect_line_2d_dir(const float v1[2], const float dir1[2], const float v2[2], const float dir2[2], float r_isect[2])
void uv_parametrizer_average(ParamHandle *handle, bool ignore_pinned, bool scale_uv, bool shear)
static float p_chart_uv_area(PChart *chart)
void uv_parametrizer_flush(ParamHandle *handle)
static PHash * phash_new(PHashLink **list, int sizehint)
static bool UNUSED_FUNCTION_NO_SLIM p_chart_correct_degenerate_triangles(PChart *chart, const float min_area, const float min_angle)
static void p_face_backup_uvs(PFace *f)
static void p_abf_adjust_alpha(PAbfSystem *sys, const int id, const float dlambda1, const float pre)
static void p_chart_rotate_fit_aabb(PChart *chart)
static void p_flush_uvs(ParamHandle *handle, PChart *chart)
void uv_parametrizer_slim_live_begin(ParamHandle *phandle, const ParamSlimOptions *slim_options)
static void p_collapse_cost_vertex(PVert *vert, float *r_mincost, PEdge **r_mine, const std::function< float(PEdge *, PEdge *)> &collapse_cost_fn, const std::function< float(PEdge *, PEdge *)> &collapse_allowed_fn)
static float p_rectangle_area(float *p1, float *dir, float *p2, float *p3, float *p4)
void uv_parametrizer_slim_stretch_iteration(ParamHandle *phandle, float blend)
static float p_collapse_doubles_cost(PEdge *edge, PEdge *pair)
static bool p_collapse_allowed_topologic(PEdge *edge, PEdge *pair)
static void phash_safe_delete(PHash **pph)
static bool p_chart_symmetry_pins(PChart *chart, PEdge *outer, PVert **pin1, PVert **pin2)
static bool p_chart_convex_hull(PChart *chart, PVert ***r_verts, int *r_nverts, int *r_right)
static float p_vec_cos(const float v1[3], const float v2[3], const float v3[3])
void uv_parametrizer_slim_live_solve_iteration(ParamHandle *phandle)
static void p_chart_stretch_minimize(PChart *chart, RNG *rng)
static PEdge * p_edge_lookup(ParamHandle *handle, const PHashKey *vkeys)
static float p_face_stretch(PFace *f)
ParamKey uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2])
static void p_split_vert(ParamHandle *handle, PChart *chart, PEdge *e)
static PEdge * p_wheel_edge_prev(PEdge *e)
static void p_collapsing_verts(PEdge *edge, PEdge *pair, PVert **r_newv, PVert **r_keepv)
static void p_stretch_pin_boundary(PChart *chart)
static float p_chart_minimum_area_angle(PChart *chart)
static bool p_abf_matrix_invert(PAbfSystem *sys, PChart *chart)
static int phash_size(PHash *ph)
static bool p_chart_correct_degenerate_triangle_point(PFace *f, float min_area, float min_angle_cos)
static float p_edge_length(PEdge *e)
void uv_prepare_pin_index(ParamHandle *handle, const int bmvertindex, const float uv[2])
static void p_face_restore_uvs(PFace *f)
static PEdge * p_wheel_edge_next(PEdge *e)
void uv_parametrizer_slim_live_end(ParamHandle *phandle)
static void p_chart_lscm_end(PChart *chart)
static void UNUSED_FUNCTION p_chart_collapse_doubles(PChart *chart, const float threshold)
static int PHashSizes[]
static void p_abf_setup_system(PAbfSystem *sys)
static const float CORR_ZERO_AREA_EPS
static void p_abf_compute_sines(PAbfSystem *sys)
static int p_compare_geometric_uv(const void *a, const void *b)
static void p_chart_pin_positions(PChart *chart, PVert **pin1, PVert **pin2)
static float p_edge_length_squared(PEdge *e)
void uv_parametrizer_stretch_blend(ParamHandle *handle, float blend)
static void p_chart_uv_bbox(PChart *chart, float minv[2], float maxv[2])
static bool p_edge_matrix(float R[3][3], const float edge_dir[3])
void uv_parametrizer_slim_solve(ParamHandle *phandle, const ParamSlimOptions *slim_options, int *count_changed, int *count_failed)
static bool p_edge_connect_pair(ParamHandle *handle, PEdge *e, bool topology_from_uvs, PEdge ***stack)
void mul_v2_m2_add_v2v2(float r[2], const float mat[2][2], const float a[2], const float b[2])
Definition uv_pack.cc:46
static void p_chart_post_collapse_flush(PChart *chart, PEdge *collapsed)
static void p_collapse_edge(PEdge *edge, PEdge *pair)
static bool p_vert_interior(PVert *v)
static GeoUVPinIndex * new_geo_uv_pinindex(ParamHandle *handle, const float uv[2])
static float p_abf_compute_grad_alpha(PAbfSystem *sys, PFace *f, PEdge *e)
static float p_face_uv_area_signed(PFace *f)
static bool p_collapse_doubles_allowed(PEdge *edge, PEdge *pair, float threshold_squared)
static void p_chart_uv_to_array(PChart *chart, MutableSpan< float2 > points)
static void UNUSED_FUNCTION p_face_cos(PFace *f, float *r_cos1, float *r_cos2, float *r_cos3)
static void p_vert_fix_edge_pointer(PVert *v)
static void p_chart_uv_translate(PChart *chart, const float trans[2])
static bool p_edge_implicit_seam(PEdge *e, PEdge *ep)
static void p_chart_uv_scale(PChart *chart, const float scale)
void uv_parametrizer_lscm_end(ParamHandle *handle)
static void p_chart_fill_boundary(ParamHandle *handle, PChart *chart, PEdge *be, int nedges)
static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uvs, PEdge **r_pair)
static float p_area_signed(const float v1[2], const float v2[2], const float v3[2])
void uv_parametrizer_aspect_ratio(ParamHandle *handle, float aspect_y)
static void p_chart_uv_transform(PChart *chart, const float mat[2][2])
static float p_abf_compute_gradient(PAbfSystem *sys, PChart *chart)
void uv_parametrizer_stretch_begin(ParamHandle *handle)
static PEdge * p_boundary_edge_next(PEdge *e)
static void p_chart_lscm_begin(PChart *chart, bool live, bool abf)
static PHashLink * phash_lookup(PHash *ph, PHashKey key)
static void p_abf_free_system(PAbfSystem *sys)
static void p_triangle_angles(const float v1[3], const float v2[3], const float v3[3], double *r_a1, double *r_a2, double *r_a3)
void uv_parametrizer_flush_restore(ParamHandle *handle)
static bool p_chart_abf_solve(PChart *chart)
static bool p_chart_correct_degenerate_triangles2(PChart *chart, float min_area, float min_angle)
static PVert * p_vert_copy(ParamHandle *handle, PVert *v)
void uv_parametrizer_pack(ParamHandle *handle, const UVPackIsland_Params &params)
static bool p_validate_corrected_coords_point(const PEdge *corr_e, const float corr_co1[3], const float corr_co2[3], const float min_area, const float min_angle_cos)
static PFace * p_face_add(ParamHandle *handle)
static void p_triangle_cos(const float v1[3], const float v2[3], const float v3[3], float *r_cos1, float *r_cos2, float *r_cos3)
static float p_stretch_compute_vertex(PVert *v)
static PChart ** p_split_charts(ParamHandle *handle, PChart *chart, int ncharts)
static void p_add_ngon(ParamHandle *handle, const ParamKey key, const int nverts, const ParamKey *vkeys, const float **co, float **uv, const float *weight, const bool *pin, const bool *select)
static float p_abf_compute_sin_product(PAbfSystem *sys, PVert *v, int aid)
void uv_parametrizer_lscm_begin(ParamHandle *handle, bool live, bool abf)
static void p_chart_extrema_verts(PChart *chart, PVert **pin1, PVert **pin2)
float pack_islands(Span< PackIsland * > islands, const UVPackIsland_Params &params)
static float p_edge_boundary_angle(PEdge *e)
void uv_parametrizer_lscm_solve(ParamHandle *handle, int *count_changed, int *count_failed)
static void fix_large_angle(const float v_fix[3], const float v1[3], const float v2[3], double *r_fix, double *r_a1, double *r_a2)
static void p_chart_simplify_compute(PChart *chart, std::function< float(PEdge *, PEdge *)> collapse_cost_fn, std::function< float(PEdge *, PEdge *)> collapse_allowed_fn)
void uv_parametrizer_stretch_iter(ParamHandle *handle)
static void uv_parametrizer_scale_x(ParamHandle *phandle, const float scale_x)
static int p_face_exists(ParamHandle *handle, const ParamKey *pvkeys, int i1, int i2, int i3)
static void p_face_flip(PFace *f)
static void phash_insert(PHash *ph, PHashLink *link)
static float p_face_area(PFace *f)
static PVert * p_vert_lookup(ParamHandle *handle, PHashKey key, const float co[3], const float weight, PEdge *e)
static void p_chart_flush_collapsed_uvs(PChart *chart)
void uv_parametrizer_stretch_end(ParamHandle *handle)
static PEdge * p_boundary_edge_prev(PEdge *e)
static PHashLink * phash_next(PHash *ph, PHashKey key, PHashLink *link)
static float p_edge_uv_length(PEdge *e)
static void p_chart_boundaries(PChart *chart, PEdge **r_outer)
static int p_connect_pairs(ParamHandle *handle, bool topology_from_uvs)
static void p_chart_lscm_transform_single_pin(PChart *chart)
static bool p_validate_triangle_angles(const PVert *vert1, const PVert *vert2, const PVert *vert3, const float min_angle_cos)
void uv_parametrizer_face_add(ParamHandle *handle, const ParamKey key, const int nverts, const ParamKey *vkeys, const float **co, float **uv, const float *weight, const bool *pin, const bool *select)
VecBase< float, 3 > float3
static void copy(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node)
#define hash
Definition noise_c.cc:154
const btScalar eps
Definition poly34.cpp:11
return ret
#define fabsf
#define sqrtf
#define sinf
#define cosf
float co[3]
Definition rand.cc:33
union blender::geometry::PEdge::PEdgeUnion u
union blender::geometry::PFace::PFaceUnion u
union blender::geometry::PVert::PVertUnion u
std::vector< float > w_vectors
std::vector< double > el_vectors
std::vector< double > v_matrices
std::vector< double > uv_matrices
std::vector< double > pp_matrices
void setup_slim_data(MatrixTransferChart &chart) const
std::vector< MatrixTransferChart > charts
PinnedVertexData pinned_vertex_data
void parametrize_live(MatrixTransferChart &chart, const PinnedVertexData &pinned_vertex_data)
std::vector< int > selected_pins
std::vector< int > pinned_vertex_indices
std::vector< double > pinned_vertex_positions_2D
i
Definition text_draw.cc:230
static int blend(const Tex *tex, const float texvec[3], TexResult *texres)
#define UNUSED_FUNCTION_NO_SLIM
#define PHASH_hash(ph, item)
#define PHASH_edge(v1, v2)
#define PEDGE_VERTEX_FLAGS
#define P_STRETCH_ITER
#define ABF_MAX_ITER
#define param_warning(message)
uint len