Blender V4.5
uv_pack.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 "GEO_uv_pack.hh"
10
11#include "BKE_global.hh"
12
13#include "BLI_array.hh"
14#include "BLI_bounds.hh"
15#include "BLI_boxpack_2d.h"
16#include "BLI_convexhull_2d.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_rect.h"
24#include "BLI_vector.hh"
25
26#include "MEM_guardedalloc.h"
27
28namespace blender::geometry {
29
31class UVPhi {
32 public:
33 UVPhi() = default;
34 bool is_valid() const;
35
36 float2 translation = float2(-1.0f, -1.0f);
37 float rotation = 0.0f;
38 // bool reflect = false;
39};
40
41bool UVPhi::is_valid() const
42{
43 return translation.x != -1.0f;
44}
45
46void mul_v2_m2_add_v2v2(float r[2], const float mat[2][2], const float a[2], const float b[2])
47{
48 /* Compute `r = mat * (a + b)` with high precision.
49 *
50 * Often, linear transforms are written as:
51 * `A.x + b`
52 *
53 * When transforming UVs, the familiar expression can damage UVs due to round-off error,
54 * especially when using UDIM and if there are large numbers of islands.
55 *
56 * Instead, we provide a helper which evaluates:
57 * `A. (x + b)`
58 *
59 * To further reduce damage, all internal calculations are
60 * performed using double precision. */
61
62 const double x = double(a[0]) + double(b[0]);
63 const double y = double(a[1]) + double(b[1]);
64
65 r[0] = float(mat[0][0] * x + mat[1][0] * y);
66 r[1] = float(mat[0][1] * x + mat[1][1] * y);
67}
68
72static float dist_signed_squared_to_edge(const float2 probe, const float2 uva, const float2 uvb)
73{
74 const float2 edge = uvb - uva;
75 const float2 side = probe - uva;
76
77 const float edge_length_squared = math::length_squared(edge);
78 /* Tolerance here is to avoid division by zero later. */
79 if (edge_length_squared < 1e-40f) {
80 return math::length_squared(side);
81 }
82
83 const float numerator = edge.x * side.y - edge.y * side.x; /* c.f. cross product. */
84 const float numerator_ssq = numerator >= 0.0f ? numerator * numerator : -numerator * numerator;
85
86 return numerator_ssq / edge_length_squared;
87}
88
92static float get_aspect_scaled_extent(const rctf &extent, const UVPackIsland_Params &params)
93{
94 const float width = BLI_rctf_size_x(&extent);
95 const float height = BLI_rctf_size_y(&extent);
96 return std::max(width / params.target_aspect_y, height);
97}
98
102static float get_aspect_scaled_area(const rctf &extent, const UVPackIsland_Params &params)
103{
104 const float width = BLI_rctf_size_x(&extent);
105 const float height = BLI_rctf_size_y(&extent);
106 return (width / params.target_aspect_y) * height;
107}
108
112static bool is_larger(const rctf &a, const rctf &b, const UVPackIsland_Params &params)
113{
114 const float extent_a = get_aspect_scaled_extent(a, params);
115 const float extent_b = get_aspect_scaled_extent(b, params);
116
117 /* Equal extent, use smaller area. */
118 if (compare_ff_relative(extent_a, extent_b, FLT_EPSILON, 64)) {
119 const float area_a = get_aspect_scaled_area(a, params);
120 const float area_b = get_aspect_scaled_area(b, params);
121 return area_b < area_a;
122 }
123
124 return extent_b < extent_a;
125}
126
128{
129 /* Initialize to the identity transform. */
130 aspect_y = 1.0f;
131 pinned = false;
132 pre_translate = float2(0.0f);
133 angle = 0.0f;
134 caller_index = -31415927; /* Accidentally -pi */
135 pivot_ = float2(0.0f);
136 half_diagonal_ = float2(0.0f);
137 pre_rotate_ = 0.0f;
138}
139
140void PackIsland::add_triangle(const float2 uv0, const float2 uv1, const float2 uv2)
141{
142 /* Be careful with winding. */
143 if (dist_signed_squared_to_edge(uv0, uv1, uv2) < 0.0f) {
144 triangle_vertices_.append(uv0);
145 triangle_vertices_.append(uv1);
146 triangle_vertices_.append(uv2);
147 }
148 else {
149 triangle_vertices_.append(uv0);
150 triangle_vertices_.append(uv2);
151 triangle_vertices_.append(uv1);
152 }
153}
154
155void PackIsland::add_polygon(const Span<float2> uvs, MemArena *arena, Heap *heap)
156{
157 /* Internally, PackIsland uses triangles as the primitive, so we have to triangulate. */
158
159 int vert_count = int(uvs.size());
160 BLI_assert(vert_count >= 3);
161 int nfilltri = vert_count - 2;
162 if (nfilltri == 1) {
163 /* Trivial case, just one triangle. */
164 add_triangle(uvs[0], uvs[1], uvs[2]);
165 return;
166 }
167
168 /* Storage. */
169 uint(*tris)[3] = static_cast<uint(*)[3]>(
170 BLI_memarena_alloc(arena, sizeof(*tris) * size_t(nfilltri)));
171 const float(*source)[2] = reinterpret_cast<const float(*)[2]>(uvs.data());
172
173 /* Triangulate. */
174 BLI_polyfill_calc_arena(source, vert_count, 0, tris, arena);
175
176 /* Beautify improves performance of packer. (Optional)
177 * Long thin triangles, especially at 45 degree angles,
178 * can trigger worst-case performance in #trace_triangle.
179 * Using `Beautify` brings more inputs into average-case. */
180 BLI_polyfill_beautify(source, vert_count, tris, arena, heap);
181
182 /* Add as triangles. */
183 for (int j = 0; j < nfilltri; j++) {
184 uint *tri = tris[j];
185 add_triangle(source[tri[0]], source[tri[1]], source[tri[2]]);
186 }
187
188 BLI_heap_clear(heap, nullptr);
189}
190
191static bool can_rotate(const Span<PackIsland *> islands, const UVPackIsland_Params &params)
192{
193 for (const PackIsland *island : islands) {
194 if (!island->can_rotate_(params)) {
195 return false;
196 }
197 }
198 return true;
199}
200
202static float angle_match(float angle_radians, float target_radians)
203{
204 if (fabsf(angle_radians - target_radians) < DEG2RADF(0.1f)) {
205 return target_radians;
206 }
207 return angle_radians;
208}
209
210static float angle_wrap(float angle_radians)
211{
212 angle_radians = angle_radians - floorf((angle_radians + M_PI_2) / M_PI) * M_PI;
213 BLI_assert(DEG2RADF(-90.0f) <= angle_radians);
214 BLI_assert(angle_radians <= DEG2RADF(90.0f));
215 return angle_radians;
216}
217
219static float plusminus_90_angle(float angle_radians)
220{
221 angle_radians = angle_wrap(angle_radians);
222 angle_radians = angle_match(angle_radians, DEG2RADF(-90.0f));
223 angle_radians = angle_match(angle_radians, DEG2RADF(0.0f));
224 angle_radians = angle_match(angle_radians, DEG2RADF(90.0f));
225 BLI_assert(DEG2RADF(-90.0f) <= angle_radians);
226 BLI_assert(angle_radians <= DEG2RADF(90.0f));
227 return angle_radians;
228}
229
230void PackIsland::calculate_pre_rotation_(const UVPackIsland_Params &params)
231{
232 pre_rotate_ = 0.0f;
233 if (params.rotate_method == ED_UVPACK_ROTATION_CARDINAL) {
234 /* Arbitrary rotations are not allowed. */
235 return;
236 }
238 return; /* Nothing to do. */
239 }
240
241 BLI_assert(ELEM(params.rotate_method,
246
247 /* As a heuristic to improve layout efficiency, #PackIsland's are first rotated by an
248 * angle which minimizes the area of the enclosing AABB. This angle is stored in the
249 * `pre_rotate_` member. The different packing strategies will later rotate the island further,
250 * stored in the `angle_` member.
251 *
252 * As AABBs have 180 degree rotational symmetry, we only consider `-90 <= pre_rotate_ <= 90`.
253 *
254 * As a further heuristic, we "stand up" the AABBs so they are "tall" rather than "wide". */
255
256 /* TODO: Use "Rotating Calipers" directly. */
257 {
258 Array<float2> coords(triangle_vertices_.size());
259 for (const int64_t i : triangle_vertices_.index_range()) {
260 coords[i].x = triangle_vertices_[i].x * aspect_y;
261 coords[i].y = triangle_vertices_[i].y;
262 }
263
264 const float(*source)[2] = reinterpret_cast<const float(*)[2]>(coords.data());
265 float angle = -BLI_convexhull_aabb_fit_points_2d(source, int(coords.size()));
266
267 if (true) {
268 /* "Stand-up" islands. */
269
270 float matrix[2][2];
271 angle_to_mat2(matrix, -angle);
272 for (const int64_t i : coords.index_range()) {
273 mul_m2_v2(matrix, coords[i]);
274 }
275
276 Bounds<float2> island_bounds = *bounds::min_max(coords.as_span());
277 float2 diagonal = island_bounds.max - island_bounds.min;
278 switch (params.rotate_method) {
280 if (diagonal.x < diagonal.y) {
281 angle += DEG2RADF(90.0f);
282 }
284 break;
285 }
287 if (diagonal.x > diagonal.y) {
288 angle += DEG2RADF(90.0f);
289 }
291 break;
292 }
293 default: {
294 if (diagonal.y < diagonal.x) {
295 angle += DEG2RADF(90.0f);
296 }
298 break;
299 }
300 }
301 }
302 }
303 if (!pre_rotate_) {
304 return;
305 }
306
307 /* Pre-Rotate `triangle_vertices_`. */
308 float matrix[2][2];
309 build_transformation(1.0f, pre_rotate_, matrix);
310 for (const int64_t i : triangle_vertices_.index_range()) {
312 }
313}
314
316{
317 BLI_assert(BLI_heap_len(heap) == 0);
318
319 /* After all the triangles and polygons have been added to a #PackIsland, but before we can start
320 * running packing algorithms, there is a one-time finalization process where we can
321 * pre-calculate a few quantities about the island, including pre-rotation, bounding box, or
322 * computing convex hull.
323 * In the future, we might also detect special-cases for speed or efficiency, such as
324 * rectangle approximation, circle approximation, detecting if the shape has any holes,
325 * analyzing the shape for rotational symmetry or removing overlaps. */
326 BLI_assert(triangle_vertices_.size() >= 3);
327
328 calculate_pre_rotation_(params);
329
330 const eUVPackIsland_ShapeMethod shape_method = params.shape_method;
331 if (shape_method == ED_UVPACK_SHAPE_CONVEX) {
332 /* Compute convex hull of existing triangles. */
333 if (triangle_vertices_.size() <= 3) {
334 calculate_pivot_();
335 return; /* Trivial case, calculate pivot only. */
336 }
337
338 int vert_count = int(triangle_vertices_.size());
339
340 /* Allocate storage. */
341 int *index_map = static_cast<int *>(
342 BLI_memarena_alloc(arena, sizeof(*index_map) * vert_count));
343
344 /* Prepare input for convex hull. */
345 const float(*source)[2] = reinterpret_cast<const float(*)[2]>(triangle_vertices_.data());
346
347 /* Compute convex hull. */
348 int convex_len = BLI_convexhull_2d(source, vert_count, index_map);
349 if (convex_len >= 3) {
350 /* Write back. */
351 triangle_vertices_.clear();
352 float2 *convex_verts = static_cast<float2 *>(
353 BLI_memarena_alloc(arena, sizeof(*convex_verts) * convex_len));
354 for (int i = 0; i < convex_len; i++) {
355 convex_verts[i] = source[index_map[i]];
356 }
357 add_polygon(Span(convex_verts, convex_len), arena, heap);
358 }
359 }
360
361 /* Pivot calculation might be performed multiple times during pre-processing.
362 * To ensure the `pivot_` used during packing includes any changes, we also calculate
363 * the pivot *last* to ensure it is correct.
364 */
365 calculate_pivot_();
366}
367
368void PackIsland::calculate_pivot_()
369{
370 /* The meaning of `pivot_` is somewhat ambiguous, as technically, the only restriction is that it
371 * can't be *outside* the convex hull of the shape. Anywhere in the interior, or even on the
372 * boundary of the convex hull is fine.
373 * (The GJK support function for every direction away from `pivot_` is numerically >= 0.0f)
374 *
375 * Ideally, `pivot_` would be the center of the shape's minimum covering circle (MCC). That would
376 * improve packing performance, and potentially even improve packing efficiency.
377 *
378 * However, computing the MCC *efficiently* is somewhat complicated.
379 *
380 * Instead, we compromise, and `pivot_` is currently calculated as the center of the AABB.
381 *
382 * If we later special-case circle packing, *AND* we can preserve the
383 * numerically-not-outside-the-convex-hull property, we may want to revisit this choice.
384 */
385 Bounds<float2> triangle_bounds = *bounds::min_max(triangle_vertices_.as_span());
386 pivot_ = (triangle_bounds.min + triangle_bounds.max) * 0.5f;
387 half_diagonal_ = (triangle_bounds.max - triangle_bounds.min) * 0.5f;
388 BLI_assert(half_diagonal_.x >= 0.0f);
389 BLI_assert(half_diagonal_.y >= 0.0f);
390}
391
392void PackIsland::place_(const float scale, const UVPhi phi)
393{
394 angle = phi.rotation + pre_rotate_;
395
396 float matrix_inverse[2][2];
397 build_inverse_transformation(scale, phi.rotation, matrix_inverse);
398 mul_v2_m2v2(pre_translate, matrix_inverse, phi.translation);
400
401 if (pre_rotate_) {
402 build_inverse_transformation(1.0f, pre_rotate_, matrix_inverse);
403 mul_m2_v2(matrix_inverse, pre_translate);
404 }
405}
406
408{
410 scale_to_fit = true;
411 only_selected_uvs = false;
412 only_selected_faces = false;
413 use_seams = false;
414 correct_aspect = false;
416 pin_unselected = false;
417 merge_overlap = false;
418 margin = 0.001f;
420 udim_base_offset[0] = 0.0f;
421 udim_base_offset[1] = 0.0f;
422 target_extent = 1.0f; /* Assume unit square. */
423 target_aspect_y = 1.0f; /* Assume unit square. */
425 stop = nullptr;
426 do_update = nullptr;
427 progress = nullptr;
428}
429
430/* Compact representation for AABB packers. */
437
449static void pack_islands_alpaca_turbo(const int64_t exclude_index,
450 const rctf &exclude,
451 const Span<std::unique_ptr<UVAABBIsland>> islands,
452 const float target_aspect_y,
453 MutableSpan<UVPhi> r_phis,
454 rctf *r_extent)
455{
456 /* Exclude an initial AABB near the origin. */
457 float next_u1 = exclude.xmax;
458 float next_v1 = exclude.ymax;
459 bool zigzag = next_u1 < next_v1 * target_aspect_y; /* Horizontal or Vertical strip? */
460
461 float u0 = zigzag ? next_u1 : 0.0f;
462 float v0 = zigzag ? 0.0f : next_v1;
463
464 /* Visit every island in order, except the excluded islands at the start. */
465 for (int64_t index = exclude_index; index < islands.size(); index++) {
466 UVAABBIsland &island = *islands[index];
467 const float dsm_u = island.uv_diagonal.x;
468 const float dsm_v = island.uv_diagonal.y;
469
470 bool restart = false;
471 if (zigzag) {
472 restart = (next_v1 < v0 + dsm_v);
473 }
474 else {
475 restart = (next_u1 < u0 + dsm_u);
476 }
477 if (restart) {
478 /* We're at the end of a strip. Restart from U axis or V axis. */
479 zigzag = next_u1 < next_v1 * target_aspect_y;
480 u0 = zigzag ? next_u1 : 0.0f;
481 v0 = zigzag ? 0.0f : next_v1;
482 }
483
484 /* Place the island. */
485 UVPhi &phi = r_phis[island.index];
486 phi.rotation = 0.0f;
487 phi.translation.x = u0 + dsm_u * 0.5f;
488 phi.translation.y = v0 + dsm_v * 0.5f;
489 if (zigzag) {
490 /* Move upwards. */
491 v0 += dsm_v;
492 next_u1 = max_ff(next_u1, u0 + dsm_u);
493 next_v1 = max_ff(next_v1, v0);
494 }
495 else {
496 /* Move sideways. */
497 u0 += dsm_u;
498 next_v1 = max_ff(next_v1, v0 + dsm_v);
499 next_u1 = max_ff(next_u1, u0);
500 }
501 }
502
503 /* Write back extent. */
504 *r_extent = {0.0f, next_u1, 0.0f, next_v1};
505}
506
518static void update_hole_rotate(float2 &hole,
519 float2 &hole_diagonal,
520 bool &hole_rotate,
521 const float u0,
522 const float v0,
523 const float u1,
524 const float v1)
525{
526 BLI_assert(hole_diagonal.x <= hole_diagonal.y); /* Confirm invariants. */
527
528 const float hole_area = hole_diagonal.x * hole_diagonal.y;
529 const float quad_area = (u1 - u0) * (v1 - v0);
530 if (quad_area <= hole_area) {
531 return; /* No update, existing hole is larger than candidate. */
532 }
533 hole.x = u0;
534 hole.y = v0;
535 hole_diagonal.x = u1 - u0;
536 hole_diagonal.y = v1 - v0;
537 if (hole_diagonal.y < hole_diagonal.x) {
538 std::swap(hole_diagonal.x, hole_diagonal.y);
539 hole_rotate = true;
540 }
541 else {
542 hole_rotate = false;
543 }
544
545 const float updated_area = hole_diagonal.x * hole_diagonal.y;
546 BLI_assert(hole_area < updated_area); /* Confirm hole grew in size. */
547 UNUSED_VARS(updated_area);
548
549 BLI_assert(hole_diagonal.x <= hole_diagonal.y); /* Confirm invariants. */
550}
551
560static void pack_islands_alpaca_rotate(const int64_t exclude_index,
561 const rctf &exclude,
562 const Span<std::unique_ptr<UVAABBIsland>> islands,
563 const float target_aspect_y,
564 MutableSpan<UVPhi> r_phis,
565 rctf *r_extent)
566{
567 /* Exclude an initial AABB near the origin. */
568 float next_u1 = exclude.xmax;
569 float next_v1 = exclude.ymax;
570 bool zigzag = next_u1 / target_aspect_y < next_v1; /* Horizontal or Vertical strip? */
571
572 /* Track an AABB "hole" which may be filled at any time. */
573 float2 hole(0.0f);
574 float2 hole_diagonal(0.0f);
575 bool hole_rotate = false;
576
577 float u0 = zigzag ? next_u1 : 0.0f;
578 float v0 = zigzag ? 0.0f : next_v1;
579
580 /* Visit every island in order, except the excluded islands at the start. */
581 for (int64_t index = exclude_index; index < islands.size(); index++) {
582 UVAABBIsland &island = *islands[index];
583 UVPhi &phi = r_phis[island.index];
584 const float uvdiag_x = island.uv_diagonal.x * island.aspect_y;
585 float min_dsm = std::min(uvdiag_x, island.uv_diagonal.y);
586 float max_dsm = std::max(uvdiag_x, island.uv_diagonal.y);
587
588 if (min_dsm < hole_diagonal.x && max_dsm < hole_diagonal.y) {
589 /* Place island in the hole. */
590 if (hole_rotate == (min_dsm == island.uv_diagonal.x)) {
591 phi.rotation = DEG2RADF(90.0f);
592 phi.translation.x = hole[0] + island.uv_diagonal.y * 0.5f / island.aspect_y;
593 phi.translation.y = hole[1] + island.uv_diagonal.x * 0.5f * island.aspect_y;
594 }
595 else {
596 phi.rotation = 0.0f;
597 phi.translation.x = hole[0] + island.uv_diagonal.x * 0.5f;
598 phi.translation.y = hole[1] + island.uv_diagonal.y * 0.5f;
599 }
600
601 /* Update space left in the hole. */
602 float p[6];
603 p[0] = hole[0];
604 p[1] = hole[1];
605 p[2] = hole[0] + (hole_rotate ? max_dsm : min_dsm) / island.aspect_y;
606 p[3] = hole[1] + (hole_rotate ? min_dsm : max_dsm);
607 p[4] = hole[0] + (hole_rotate ? hole_diagonal.y : hole_diagonal.x);
608 p[5] = hole[1] + (hole_rotate ? hole_diagonal.x : hole_diagonal.y);
609 hole_diagonal.x = 0; /* Invalidate old hole. */
610 update_hole_rotate(hole, hole_diagonal, hole_rotate, p[0], p[3], p[4], p[5]);
611 update_hole_rotate(hole, hole_diagonal, hole_rotate, p[2], p[1], p[4], p[5]);
612
613 /* Island is placed in the hole, no need to check for restart, or process movement. */
614 continue;
615 }
616
617 bool restart = false;
618 if (zigzag) {
619 restart = (next_v1 < v0 + min_dsm);
620 }
621 else {
622 restart = (next_u1 < u0 + min_dsm / island.aspect_y);
623 }
624 if (restart) {
625 update_hole_rotate(hole, hole_diagonal, hole_rotate, u0, v0, next_u1, next_v1);
626 /* We're at the end of a strip. Restart from U axis or V axis. */
627 zigzag = next_u1 / target_aspect_y < next_v1;
628 u0 = zigzag ? next_u1 : 0.0f;
629 v0 = zigzag ? 0.0f : next_v1;
630 }
631
632 /* Place the island. */
633 if (zigzag == (min_dsm == uvdiag_x)) {
634 phi.rotation = DEG2RADF(90.0f);
635 phi.translation.x = u0 + island.uv_diagonal.y * 0.5f / island.aspect_y;
636 phi.translation.y = v0 + island.uv_diagonal.x * 0.5f * island.aspect_y;
637 }
638 else {
639 phi.rotation = 0.0f;
640 phi.translation.x = u0 + island.uv_diagonal.x * 0.5f;
641 phi.translation.y = v0 + island.uv_diagonal.y * 0.5f;
642 }
643
644 /* Move according to the "Alpaca rules", with rotation. */
645 if (zigzag) {
646 /* Move upwards. */
647 v0 += min_dsm;
648 next_u1 = max_ff(next_u1, u0 + max_dsm / island.aspect_y);
649 next_v1 = max_ff(next_v1, v0);
650 }
651 else {
652 /* Move sideways. */
653 u0 += min_dsm / island.aspect_y;
654 next_v1 = max_ff(next_v1, v0 + max_dsm);
655 next_u1 = max_ff(next_u1, u0);
656 }
657 }
658
659 /* Write back total pack AABB. */
660 *r_extent = {0.0f, next_u1, 0.0f, next_v1};
661}
662
666static void pack_islands_fast(const int64_t exclude_index,
667 const rctf &exclude,
668 const Span<std::unique_ptr<UVAABBIsland>> aabbs,
669 const bool rotate,
670 const float target_aspect_y,
671 MutableSpan<UVPhi> r_phis,
672 rctf *r_extent)
673{
674 if (rotate) {
675 pack_islands_alpaca_rotate(exclude_index, exclude, aabbs, target_aspect_y, r_phis, r_extent);
676 }
677 else {
678 pack_islands_alpaca_turbo(exclude_index, exclude, aabbs, target_aspect_y, r_phis, r_extent);
679 }
680}
681
683static void pack_gobel(const Span<std::unique_ptr<UVAABBIsland>> aabbs,
684 const float scale,
685 const int m,
686 MutableSpan<UVPhi> r_phis)
687{
688 for (const int64_t i : aabbs.index_range()) {
689 UVPhi &phi = *(UVPhi *)&r_phis[aabbs[i]->index];
690 phi.rotation = 0.0f;
691 if (i == 0) {
692 phi.translation.x = 0.5f * scale;
693 phi.translation.y = 0.5f * scale;
694 continue;
695 }
696 int xx = (i - 1) % m;
697 int yy = int(i - 1) / m;
698 phi.translation.x = (xx + 0.5f) * scale;
699 phi.translation.y = (yy + 0.5f) * scale;
700 if (xx >= yy) {
701 phi.translation.x += (1 + sqrtf(0.5f)) * scale;
702 }
703 else {
704 phi.translation.y += sqrtf(0.5f) * scale;
705 }
706
707 if (i == m * (m + 1) + 1) {
708 phi.translation.x += (m + sqrtf(0.5f)) * scale;
709 phi.translation.y -= scale;
710 }
711 else if (i > m * (m + 1) + 1) {
712 phi.rotation = DEG2RADF(45.0f);
713 phi.translation.x = ((i - m * (m + 1) - 1.5f) * cosf(phi.rotation) + 1.0f) * scale;
714 phi.translation.y = phi.translation.x;
715 }
716 }
717}
718
719static bool pack_islands_optimal_pack_table(const int table_count,
720 const float max_extent,
721 const float *optimal,
722 const char * /*unused_comment*/,
723 int64_t island_count,
724 const float large_uv,
725 const Span<std::unique_ptr<UVAABBIsland>> aabbs,
727 MutableSpan<UVPhi> r_phis,
728 rctf *r_extent)
729{
730 if (table_count < island_count) {
731 return false;
732 }
733 rctf extent = {0.0f, large_uv * max_extent, 0.0f, large_uv * max_extent};
734 if (is_larger(extent, *r_extent, params)) {
735 return false;
736 }
737 *r_extent = extent;
738
739 for (int i = 0; i < island_count; i++) {
740 UVPhi &phi = r_phis[aabbs[i]->index];
741 phi.translation.x = optimal[i * 3 + 0] * large_uv;
742 phi.translation.y = optimal[i * 3 + 1] * large_uv;
743 phi.rotation = optimal[i * 3 + 2];
744 }
745 return true;
746}
747
748/* Attempt to find an "Optimal" packing of the islands, e.g. assuming squares or circles. */
749static void pack_islands_optimal_pack(const Span<std::unique_ptr<UVAABBIsland>> aabbs,
751 MutableSpan<UVPhi> r_phis,
752 rctf *r_extent)
753{
754 if (params.shape_method == ED_UVPACK_SHAPE_AABB) {
755 return;
756 }
757 if (params.target_aspect_y != 1.0f) {
758 return;
759 }
760 if (params.rotate_method != ED_UVPACK_ROTATION_ANY) {
761 return;
762 }
763
764 float large_uv = 0.0f;
765 for (const int64_t i : aabbs.index_range()) {
766 large_uv = max_ff(large_uv, aabbs[i]->uv_diagonal.x);
767 large_uv = max_ff(large_uv, aabbs[i]->uv_diagonal.y);
768 }
769
770 int64_t island_count_patch = aabbs.size();
771
772 const float opt_11[] = {
773 /* Walter Trump, 1979. */
774 2.6238700165660708840676f,
775 2.4365065643739085565755f,
776 0.70130710554829878145f,
777 1.9596047386700836678841f,
778 1.6885655318806973568257f,
779 0.70130710554829878145f,
780 1.9364970731945949644626f,
781 3.1724566890997589752033f,
782 0.70130710554829878145f,
783 1.2722458068219282267819f,
784 2.4245322476118422727609f,
785 0.70130710554829878145f,
786 3.1724918301381124230431f,
787 1.536261617698265524723f,
788 0.70130710554829878145f,
789 3.3770999999999999907629f,
790 3.3770999999999999907629f,
791 0.0f,
792 0.5f,
793 1.5f,
794 0.0f,
795 2.5325444557069398676674f,
796 0.5f,
797 0.0f,
798 0.5f,
799 3.3770999999999999907629f,
800 0.0f,
801 1.5f,
802 0.5f,
803 0.0f,
804 0.5f,
805 0.5f,
806 0.0f,
807 };
809 3.8770999999999999907629f,
810 opt_11,
811 "Walter Trump, 1979",
812 island_count_patch,
813 large_uv,
814 aabbs,
815 params,
816 r_phis,
817 r_extent);
818
819 const float opt_18[] = {
820 /* Pertti Hamalainen, 1979. */
821 2.4700161985907582717914f,
822 2.4335783708246112588824f,
823 0.42403103949074028022892f,
824 1.3528594569415370862941f,
825 2.3892972847076845432923f,
826 0.42403103949074028022892f,
827 2.0585783708246108147932f,
828 1.5221405430584633577951f,
829 0.42403103949074028022892f,
830 1.7642972847076845432923f,
831 3.3007351124738324443797f,
832 0.42403103949074028022892f,
833 3.3228756555322949139963f,
834 1.5f,
835 0.0f,
836 3.3228756555322949139963f,
837 3.3228756555322949139963f,
838 0.0f,
839 0.5f,
840 1.5f,
841 0.0f,
842 2.3228756555322949139963f,
843 4.3228756555322949139963f,
844 0.0f,
845 0.5f,
846 3.3228756555322949139963f,
847 0.0f,
848 1.5f,
849 0.5f,
850 0.0f,
851 3.3228756555322949139963f,
852 0.5f,
853 0.0f,
854 3.3228756555322949139963f,
855 4.3228756555322949139963f,
856 0.0f,
857 4.3228756555322949139963f,
858 1.5f,
859 0.0f,
860 4.3228756555322949139963f,
861 3.3228756555322949139963f,
862 0.0f,
863 0.5f,
864 0.5f,
865 0.0f,
866 0.5f,
867 4.3228756555322949139963f,
868 0.0f,
869 4.3228756555322949139963f,
870 0.5f,
871 0.0f,
872 4.3228756555322949139963f,
873 4.3228756555322949139963f,
874 0.0f,
875 };
877 4.8228756555322949139963f,
878 opt_18,
879 "Pertti Hamalainen, 1979",
880 island_count_patch,
881 large_uv,
882 aabbs,
883 params,
884 r_phis,
885 r_extent);
886
887 const float opt_19[] = {
888 /* Robert Wainwright, 1979. */
889 2.1785113019775792508881f,
890 1.9428090415820631342569f,
891 0.78539816339744827899949f,
892 1.4714045207910317891731f,
893 2.6499158227686105959719f,
894 0.78539816339744827899949f,
895 2.9428090415820640224354f,
896 2.7071067811865479058042f,
897 0.78539816339744827899949f,
898 2.2357022603955165607204f,
899 3.4142135623730953675192f,
900 0.78539816339744827899949f,
901 1.4428090415820635783462f,
902 1.2642977396044836613243f,
903 0.78539816339744827899949f,
904 3.3856180831641271566923f,
905 1.5f,
906 0.0f,
907 0.73570226039551600560884f,
908 1.9714045207910311230393f,
909 0.78539816339744827899949f,
910 3.6213203435596432733234f,
911 3.4428090415820635783462f,
912 0.78539816339744827899949f,
913 2.9142135623730958116084f,
914 4.1499158227686105959719f,
915 0.78539816339744827899949f,
916 2.3856180831641271566923f,
917 0.5f,
918 0.0f,
919 0.5f,
920 3.3856180831641271566923f,
921 0.0f,
922 1.5f,
923 4.3856180831641271566923f,
924 0.0f,
925 4.3856180831641271566923f,
926 2.5f,
927 0.0f,
928 3.3856180831641271566923f,
929 0.5f,
930 0.0f,
931 4.3856180831641271566923f,
932 1.5f,
933 0.0f,
934 0.5f,
935 0.5f,
936 0.0f,
937 0.5f,
938 4.3856180831641271566923f,
939 0.0f,
940 4.3856180831641271566923f,
941 0.5f,
942 0.0f,
943 4.3856180831641271566923f,
944 4.3856180831641271566923f,
945 0.0f,
946 };
948 4.8856180831641271566923f,
949 opt_19,
950 "Robert Wainwright, 1979",
951 island_count_patch,
952 large_uv,
953 aabbs,
954 params,
955 r_phis,
956 r_extent);
957
958 const float opt_26[] = {
959 /* Erich Friedman, 1997. */
960 2.3106601717798209705279f,
961 2.8106601717798214146171f,
962 0.78539816339744827899949f,
963 1.6035533905932735088129f,
964 2.1035533905932739529021f,
965 0.78539816339744827899949f,
966 3.0177669529663684322429f,
967 2.1035533905932739529021f,
968 0.78539816339744827899949f,
969 2.3106601717798209705279f,
970 1.3964466094067264911871f,
971 0.78539816339744827899949f,
972 1.6035533905932735088129f,
973 3.5177669529663688763321f,
974 0.78539816339744827899949f,
975 0.89644660940672593607559f,
976 2.8106601717798214146171f,
977 0.78539816339744827899949f,
978 3.0177669529663684322429f,
979 3.5177669529663688763321f,
980 0.78539816339744827899949f,
981 3.7248737341529158939579f,
982 2.8106601717798214146171f,
983 0.78539816339744827899949f,
984 2.3106601717798209705279f,
985 4.2248737341529167821363f,
986 0.78539816339744827899949f,
987 0.5f,
988 1.5f,
989 0.0f,
990 1.5f,
991 0.5f,
992 0.0f,
993 3.1213203435596419410558f,
994 0.5f,
995 0.0f,
996 4.1213203435596419410558f,
997 1.5f,
998 0.0f,
999 0.5f,
1000 4.1213203435596419410558f,
1001 0.0f,
1002 0.5f,
1003 0.5f,
1004 0.0f,
1005 4.1213203435596419410558f,
1006 4.1213203435596419410558f,
1007 0.0f,
1008 4.1213203435596419410558f,
1009 0.5f,
1010 0.0f,
1011 1.5f,
1012 5.1213203435596419410558f,
1013 0.0f,
1014 3.1213203435596419410558f,
1015 5.1213203435596419410558f,
1016 0.0f,
1017 5.1213203435596419410558f,
1018 2.5f,
1019 0.0f,
1020 5.1213203435596419410558f,
1021 1.5f,
1022 0.0f,
1023 0.5f,
1024 5.1213203435596419410558f,
1025 0.0f,
1026 4.1213203435596419410558f,
1027 5.1213203435596419410558f,
1028 0.0f,
1029 5.1213203435596419410558f,
1030 4.1213203435596419410558f,
1031 0.0f,
1032 5.1213203435596419410558f,
1033 0.5f,
1034 0.0f,
1035 5.1213203435596419410558f,
1036 5.1213203435596419410558f,
1037 0.0f,
1038 };
1040 5.6213203435596419410558f,
1041 opt_26,
1042 "Erich Friedman, 1997",
1043 island_count_patch,
1044 large_uv,
1045 aabbs,
1046 params,
1047 r_phis,
1048 r_extent);
1049
1050 if (island_count_patch == 37) {
1051 island_count_patch = 38; /* TODO, Cantrell 2002. */
1052 }
1053 if (island_count_patch == 50) {
1054 island_count_patch = 52; /* TODO, Cantrell 2002. */
1055 }
1056 if (island_count_patch == 51) {
1057 island_count_patch = 52; /* TODO, Hajba 2009. */
1058 }
1059 if (island_count_patch == 65) {
1060 island_count_patch = 67; /* TODO, Gobel 1979. */
1061 }
1062 if (island_count_patch == 66) {
1063 island_count_patch = 67; /* TODO, Stenlund 1980. */
1064 }
1065 /* See https://www.combinatorics.org/files/Surveys/ds7/ds7v5-2009/ds7-2009.html
1066 * https://erich-friedman.github.io/packing/squinsqu */
1067 for (int a = 1; a < 20; a++) {
1068 int n = a * a + a + 3 + floorf((a - 1) * sqrtf(2.0f));
1069 if (island_count_patch == n) {
1070 float max_uv_gobel = large_uv * (a + 1 + sqrtf(0.5f));
1071 rctf extent = {0.0f, max_uv_gobel, 0.0f, max_uv_gobel};
1072 if (is_larger(*r_extent, extent, params)) {
1073 *r_extent = extent;
1074 pack_gobel(aabbs, large_uv, a, r_phis);
1075 }
1076 return;
1077 }
1078 }
1079}
1080
1081/* Wrapper around #BLI_box_pack_2d. */
1082static void pack_island_box_pack_2d(const Span<std::unique_ptr<UVAABBIsland>> aabbs,
1084 MutableSpan<UVPhi> r_phis,
1085 rctf *r_extent)
1086{
1087 /* Allocate storage. */
1088 BoxPack *box_array = MEM_malloc_arrayN<BoxPack>(size_t(aabbs.size()), __func__);
1089
1090 /* Prepare for box_pack_2d. */
1091 for (const int64_t i : aabbs.index_range()) {
1092 BoxPack *box = box_array + i;
1093 box->w = aabbs[i]->uv_diagonal.x / params.target_aspect_y;
1094 box->h = aabbs[i]->uv_diagonal.y;
1095 }
1096
1097 const bool sort_boxes = false; /* Use existing ordering from `aabbs`. */
1098
1099 float box_max_u = 0.0f;
1100 float box_max_v = 0.0f;
1101 BLI_box_pack_2d(box_array, int(aabbs.size()), sort_boxes, &box_max_u, &box_max_v);
1102 box_max_u *= params.target_aspect_y;
1103 rctf extent = {0.0f, box_max_u, 0.0f, box_max_v};
1104
1105 if (is_larger(*r_extent, extent, params)) {
1106 *r_extent = extent;
1107 /* Write back box_pack UVs. */
1108 for (const int64_t i : aabbs.index_range()) {
1109 BoxPack *box = box_array + i;
1110 UVPhi &phi = *(UVPhi *)&r_phis[aabbs[i]->index];
1111 phi.rotation = 0.0f; /* #BLI_box_pack_2d never rotates. */
1112 phi.translation.x = (box->x + box->w * 0.5f) * params.target_aspect_y;
1113 phi.translation.y = (box->y + box->h * 0.5f);
1114 }
1115 }
1116
1117 /* Housekeeping. */
1118 MEM_freeN(box_array);
1119}
1120
1129 public:
1130 Occupancy(const float initial_scale);
1131
1132 void increase_scale(); /* Resize the scale of the bitmap and clear it. */
1133 void clear(); /* Clear occupancy information. */
1134
1135 /* Write or Query a triangle on the bitmap. */
1136 float trace_triangle(const float2 &uv0,
1137 const float2 &uv1,
1138 const float2 &uv2,
1139 const float margin,
1140 const bool write) const;
1141
1142 /* Write or Query an island on the bitmap. */
1143 float trace_island(const PackIsland *island,
1144 const UVPhi phi,
1145 const float scale,
1146 const float margin,
1147 const bool write) const;
1148
1149 int bitmap_radix = 800; /* Width and Height of `bitmap`. */
1150 float bitmap_scale_reciprocal = 1.0f; /* == 1.0f / `bitmap_scale`. */
1151 private:
1152 mutable Array<float> bitmap_;
1153
1154 mutable float2 witness_; /* Witness to a previously known occupied pixel. */
1155 mutable float witness_distance_; /* Signed distance to nearest placed island. */
1156 mutable uint triangle_hint_; /* Hint to a previously suspected overlapping triangle. */
1157
1158 const float terminal = 1048576.0f; /* 4 * bitmap_radix < terminal < INT_MAX / 4. */
1159};
1160
1161Occupancy::Occupancy(const float initial_scale) : bitmap_(bitmap_radix * bitmap_radix, false)
1162{
1164 bitmap_scale_reciprocal = bitmap_radix / initial_scale; /* Actually set the value. */
1165}
1166
1168{
1169 BLI_assert(bitmap_scale_reciprocal > 0.0f); /* TODO: Packing has failed, report error. */
1170
1172 clear();
1173}
1174
1176{
1177 for (int i = 0; i < bitmap_radix * bitmap_radix; i++) {
1178 bitmap_[i] = terminal;
1179 }
1180 witness_.x = -1;
1181 witness_.y = -1;
1182 witness_distance_ = 0.0f;
1183 triangle_hint_ = 0;
1184}
1185
1186static float signed_distance_fat_triangle(const float2 probe,
1187 const float2 uv0,
1188 const float2 uv1,
1189 const float2 uv2)
1190{
1191 /* Be careful with ordering, uv0 <- uv1 <- uv2 <- uv0 <- uv1 etc. */
1192 const float dist01_ssq = dist_signed_squared_to_edge(probe, uv0, uv1);
1193 const float dist12_ssq = dist_signed_squared_to_edge(probe, uv1, uv2);
1194 const float dist20_ssq = dist_signed_squared_to_edge(probe, uv2, uv0);
1195 float result_ssq = max_fff(dist01_ssq, dist12_ssq, dist20_ssq);
1196 if (result_ssq < 0.0f) {
1197 return -sqrtf(-result_ssq);
1198 }
1199 BLI_assert(result_ssq >= 0.0f);
1200 result_ssq = std::min(result_ssq, math::length_squared(probe - uv0));
1201 result_ssq = std::min(result_ssq, math::length_squared(probe - uv1));
1202 result_ssq = std::min(result_ssq, math::length_squared(probe - uv2));
1203 BLI_assert(result_ssq >= 0.0f);
1204 return sqrtf(result_ssq);
1205}
1206
1208 const float2 &uv1,
1209 const float2 &uv2,
1210 const float margin,
1211 const bool write) const
1212{
1213 const float x0 = min_fff(uv0.x, uv1.x, uv2.x);
1214 const float y0 = min_fff(uv0.y, uv1.y, uv2.y);
1215 const float x1 = max_fff(uv0.x, uv1.x, uv2.x);
1216 const float y1 = max_fff(uv0.y, uv1.y, uv2.y);
1217 float spread = write ? margin * 2 : 0.0f;
1218 int ix0 = std::max(int(floorf((x0 - spread) * bitmap_scale_reciprocal)), 0);
1219 int iy0 = std::max(int(floorf((y0 - spread) * bitmap_scale_reciprocal)), 0);
1220 int ix1 = std::min(int(floorf((x1 + spread) * bitmap_scale_reciprocal + 2)), bitmap_radix);
1221 int iy1 = std::min(int(floorf((y1 + spread) * bitmap_scale_reciprocal + 2)), bitmap_radix);
1222
1223 const float2 uv0s = uv0 * bitmap_scale_reciprocal;
1224 const float2 uv1s = uv1 * bitmap_scale_reciprocal;
1225 const float2 uv2s = uv2 * bitmap_scale_reciprocal;
1226
1227 /* TODO: Better epsilon handling here could reduce search size. */
1228 float epsilon = 0.7071f; /* == sqrt(0.5f), rounded up by 0.00002f. */
1229 epsilon = std::max(epsilon, 2 * margin * bitmap_scale_reciprocal);
1230
1231 if (!write) {
1232 if (ix0 <= witness_.x && witness_.x < ix1) {
1233 if (iy0 <= witness_.y && witness_.y < iy1) {
1234 const float distance = signed_distance_fat_triangle(witness_, uv0s, uv1s, uv2s);
1235 const float extent = epsilon - distance - witness_distance_;
1236 const float pixel_round_off = -0.1f; /* Go faster on nearly-axis aligned edges. */
1237 if (extent > pixel_round_off) {
1238 return std::max(0.0f, extent); /* Witness observes occupied. */
1239 }
1240 }
1241 }
1242 }
1243
1244 /* Iterate in opposite direction to outer search to improve witness effectiveness. */
1245 for (int y = iy1 - 1; y >= iy0; y--) {
1246 for (int x = ix1 - 1; x >= ix0; x--) {
1247 float *hotspot = &bitmap_[y * bitmap_radix + x];
1248 if (!write && *hotspot > epsilon) {
1249 continue;
1250 }
1251 const float2 probe(x, y);
1252 const float distance = signed_distance_fat_triangle(probe, uv0s, uv1s, uv2s);
1253 if (write) {
1254 *hotspot = min_ff(distance, *hotspot);
1255 continue;
1256 }
1257 const float extent = epsilon - distance - *hotspot;
1258 if (extent > 0.0f) {
1259 witness_ = probe;
1260 witness_distance_ = *hotspot;
1261 return extent; /* Occupied. */
1262 }
1263 }
1264 }
1265 return -1.0f; /* Available. */
1266}
1267
1269 const float rotation,
1270 /* const bool reflection, */
1271 const float margin) const
1272{
1273 /* Caution: Only "Dihedral Group D4" transforms are calculated exactly.
1274 * if the transform is Non-D4, an upper bound will be returned instead. */
1275
1276 if (rotation == DEG2RADF(-180.0f) || rotation == 0.0f || rotation == DEG2RADF(180.0f)) {
1277 return half_diagonal_ * scale + margin;
1278 }
1279
1280 if (rotation == DEG2RADF(-90.0f) || rotation == DEG2RADF(90.0f) || rotation == DEG2RADF(270.0f))
1281 {
1282 return float2(half_diagonal_.y / aspect_y, half_diagonal_.x * aspect_y) * scale + margin;
1283 }
1284
1285 float matrix[2][2];
1286 build_transformation(scale, rotation, matrix);
1287
1288 /* TODO: Use convex hull to calculate support. */
1289 float diagonal_rotated[2];
1290 mul_v2_m2v2(diagonal_rotated, matrix, half_diagonal_);
1291 float sx = fabsf(diagonal_rotated[0]);
1292 float sy = fabsf(diagonal_rotated[1]);
1293
1294 return float2(sx + sy * 0.7071f + margin, sx * 0.7071f + sy + margin); /* Upper bound. */
1295}
1296
1298 const UVPhi phi,
1299 const float scale,
1300 const float margin,
1301 const bool write) const
1302{
1303 const float2 diagonal_support = island->get_diagonal_support(scale, phi.rotation, margin);
1304
1305 if (!write) {
1306 if (phi.translation.x < diagonal_support.x || phi.translation.y < diagonal_support.y) {
1307 return terminal; /* Occupied. */
1308 }
1309 }
1310 float matrix[2][2];
1311 island->build_transformation(scale, phi.rotation, matrix);
1312 float2 pivot_transformed;
1313 mul_v2_m2v2(pivot_transformed, matrix, island->pivot_);
1314
1315 /* TODO: Support `ED_UVPACK_SHAPE_AABB`. */
1316
1317 /* TODO: If the PackIsland has the same shape as it's convex hull, we can trace the hull instead
1318 * of the individual triangles, which is faster and provides a better value of `extent`.
1319 */
1320
1321 const float2 delta = phi.translation - pivot_transformed;
1322 const uint vert_count = uint(
1323 island->triangle_vertices_.size()); /* `uint` is faster than `int`. */
1324 for (uint i = 0; i < vert_count; i += 3) {
1325 const uint j = (i + triangle_hint_) % vert_count;
1326 float2 uv0;
1327 float2 uv1;
1328 float2 uv2;
1329 mul_v2_m2v2(uv0, matrix, island->triangle_vertices_[j]);
1330 mul_v2_m2v2(uv1, matrix, island->triangle_vertices_[j + 1]);
1331 mul_v2_m2v2(uv2, matrix, island->triangle_vertices_[j + 2]);
1332 const float extent = trace_triangle(uv0 + delta, uv1 + delta, uv2 + delta, margin, write);
1333
1334 if (!write && extent >= 0.0f) {
1335 triangle_hint_ = j;
1336 return extent; /* Occupied. */
1337 }
1338 }
1339 return -1.0f; /* Available. */
1340}
1341
1343 const int scan_line,
1344 const Occupancy &occupancy,
1345 const float scale,
1346 const int angle_90_multiple,
1347 /* TODO: const bool reflect, */
1348 const float margin,
1349 const float target_aspect_y)
1350{
1351 /* Discussion: Different xatlas implementation make different choices here, either
1352 * fixing the output bitmap size before packing begins, or sometimes allowing
1353 * for non-square outputs which can make the resulting algorithm a little simpler.
1354 *
1355 * The current implementation is to grow using the "Alpaca Rules" as described above, with calls
1356 * to increase_scale() if the particular packing instance is badly conditioned.
1357 *
1358 * (This particular choice is largely a result of the way packing is used inside the Blender API,
1359 * and isn't strictly required by the xatlas algorithm.)
1360 *
1361 * One nice extension to the xatlas algorithm might be to grow in all 4 directions, i.e. both
1362 * increasing and *decreasing* in the horizontal and vertical axes. The `scan_line` parameter
1363 * would become a #rctf, the occupancy bitmap would be 4x larger, and there will be a translation
1364 * to move the origin back to `(0,0)` at the end.
1365 *
1366 * This `plus-atlas` algorithm, which grows in a "+" shape, will likely have better packing
1367 * efficiency for many real world inputs, at a cost of increased complexity and memory.
1368 */
1369
1370 const float bitmap_scale = 1.0f / occupancy.bitmap_scale_reciprocal;
1371
1372 /* TODO: If `target_aspect_y != 1.0f`, to avoid aliasing issues, we should probably iterate
1373 * Separately on `scan_line_x` and `scan_line_y`. See also: Bresenham's algorithm. */
1374 const float sqrt_target_aspect_y = sqrtf(target_aspect_y);
1375 const int scan_line_x = int(scan_line * sqrt_target_aspect_y);
1376 const int scan_line_y = int(scan_line / sqrt_target_aspect_y);
1377
1378 UVPhi phi;
1379 phi.rotation = DEG2RADF(angle_90_multiple * 90);
1380 // phi.reflect = reflect;
1381 float matrix[2][2];
1382 island->build_transformation(scale, phi.rotation, matrix);
1383
1384 /* Caution, margin is zero for `support_diagonal` as we're tracking the top-right corner. */
1385 float2 support_diagonal = island->get_diagonal_support(scale, phi.rotation, 0.0f);
1386
1387 /* Scan using an "Alpaca"-style search, first horizontally using "less-than". */
1388 int t = int(ceilf((2 * support_diagonal.x + margin) * occupancy.bitmap_scale_reciprocal));
1389 while (t < scan_line_x) { /* "less-than" */
1390 phi.translation = float2(t * bitmap_scale, scan_line_y * bitmap_scale) - support_diagonal;
1391 const float extent = occupancy.trace_island(island, phi, scale, margin, false);
1392 if (extent < 0.0f) {
1393 return phi; /* Success. */
1394 }
1395 t = t + std::max(1, int(extent));
1396 }
1397
1398 /* Then scan vertically using "less-than-or-equal" */
1399 t = int(ceilf((2 * support_diagonal.y + margin) * occupancy.bitmap_scale_reciprocal));
1400 while (t <= scan_line_y) { /* "less-than-or-equal" */
1401 phi.translation = float2(scan_line_x * bitmap_scale, t * bitmap_scale) - support_diagonal;
1402 const float extent = occupancy.trace_island(island, phi, scale, margin, false);
1403 if (extent < 0.0f) {
1404 return phi; /* Success. */
1405 }
1406 t = t + std::max(1, int(extent));
1407 }
1408
1409 return UVPhi(); /* Unable to find a place to fit. */
1410}
1411
1412static float guess_initial_scale(const Span<PackIsland *> islands,
1413 const float scale,
1414 const float margin)
1415{
1416 float sum = 1e-40f;
1417 for (int64_t i : islands.index_range()) {
1418 PackIsland *island = islands[i];
1419 sum += island->half_diagonal_.x * 2 * scale + 2 * margin;
1420 sum += island->half_diagonal_.y * 2 * scale + 2 * margin;
1421 }
1422 return sqrtf(sum) / 6.0f;
1423}
1424
1427 public:
1428 const float scale_;
1429 const float margin_;
1431
1435
1438
1440 const float margin,
1442 : scale_(scale), margin_(margin), params_(params)
1443 {
1444 best_angle = 0.0f;
1445 best_quad = 0.0f;
1446 }
1447
1450
1451 float update(const double angle)
1452 {
1453 float2 dir(cos(angle), sin(angle));
1454
1455 /* TODO: Once convexhull_2d bugs are fixed, we can use "rotating calipers" to go faster. */
1456 rctf bounds;
1458 for (const int64_t i : indices.index_range()) {
1459 const float2 &p = points[indices[i]];
1460 const float uv[2] = {p.x * dir.x + p.y * dir.y, -p.x * dir.y + p.y * dir.x};
1462 }
1463 bounds.xmin -= margin_;
1464 bounds.ymin -= margin_;
1465 bounds.xmax += margin_;
1466 bounds.ymax += margin_;
1467 const float current_quad = get_aspect_scaled_extent(bounds, *params_);
1468 if (best_quad > current_quad) {
1469 best_quad = current_quad;
1470 best_angle = angle;
1472 }
1473 return current_quad;
1474 }
1475
1477 void update_recursive(const float angle0,
1478 const float quad0,
1479 const float angle1,
1480 const float quad1)
1481 {
1482 const float angle_mid = (angle0 + angle1) * 0.5f;
1483 const float quad_mid = update(angle_mid);
1484 const float angle_separation = angle1 - angle0;
1485
1486 if (angle_separation < DEG2RADF(0.002f)) {
1487 return; /* Sufficient accuracy achieved. */
1488 }
1489
1490 bool search_mode = DEG2RADF(10.0f) < angle_separation; /* In linear search mode. */
1491
1492 /* TODO: Degenerate inputs could have poor performance here. */
1493 if (search_mode || (quad0 <= quad1)) {
1494 update_recursive(angle0, quad0, angle_mid, quad_mid);
1495 }
1496 if (search_mode || (quad1 <= quad0)) {
1497 update_recursive(angle_mid, quad_mid, angle1, quad1);
1498 }
1499 }
1500};
1501
1507static bool rotate_inside_square(const Span<std::unique_ptr<UVAABBIsland>> island_indices,
1508 const Span<PackIsland *> islands,
1510 const float scale,
1511 const float margin,
1512 MutableSpan<UVPhi> r_phis,
1513 rctf *r_extent)
1514{
1515 if (island_indices.is_empty()) {
1516 return false; /* Nothing to do. */
1517 }
1518 if (params.rotate_method != ED_UVPACK_ROTATION_ANY) {
1519 return false; /* Unable to rotate by arbitrary angle. */
1520 }
1521 if (params.shape_method == ED_UVPACK_SHAPE_AABB) {
1522 /* AABB margin calculations are not preserved under rotations. */
1523 if (island_indices.size() > 1) { /* Unless there's only one island. */
1524
1525 if (params.target_aspect_y != 1.0f) {
1526 /* TODO: Check for possible 90 degree rotation. */
1527 }
1528 return false;
1529 }
1530 }
1531
1532 UVMinimumEnclosingSquareFinder square_finder(scale, margin, &params);
1533 square_finder.best_quad = get_aspect_scaled_extent(*r_extent, params) * 0.999f;
1534
1535 float matrix[2][2];
1536
1537 const float aspect_y = 1.0f; /* TODO: Use `islands[0]->aspect_y`. */
1538 for (const int64_t j : island_indices.index_range()) {
1539 const int64_t i = island_indices[j]->index;
1540 const PackIsland *island = islands[i];
1541 if (island->aspect_y != aspect_y) {
1542 return false; /* Aspect ratios are not preserved under rotation. */
1543 }
1544 const float island_scale = island->can_scale_(params) ? scale : 1.0f;
1545 island->build_transformation(island_scale, r_phis[i].rotation, matrix);
1546 float2 pivot_transformed;
1547 mul_v2_m2v2(pivot_transformed, matrix, island->pivot_);
1548 float2 delta = r_phis[i].translation - pivot_transformed;
1549
1550 for (const int64_t k : island->triangle_vertices_.index_range()) {
1551 float2 p = island->triangle_vertices_[k];
1552 mul_m2_v2(matrix, p);
1553 square_finder.points.append(p + delta);
1554 }
1555 }
1556
1557 /* Now we have all the points in the correct space, compute the 2D convex hull. */
1558 const float(*source)[2] = reinterpret_cast<const float(*)[2]>(square_finder.points.data());
1559
1560 square_finder.indices.resize(square_finder.points.size()); /* Allocate worst-case. */
1561 int convex_size = BLI_convexhull_2d(
1562 source, int(square_finder.points.size()), square_finder.indices.data());
1563 square_finder.indices.resize(convex_size); /* Resize to actual size. */
1564
1565 /* Run the computation to find the best angle. (Slow!) */
1566 const float quad_180 = square_finder.update(DEG2RADF(-180.0f));
1567 square_finder.update_recursive(DEG2RADF(-180.0f), quad_180, DEG2RADF(180.0f), quad_180);
1568
1569 if (square_finder.best_angle == 0.0f) {
1570 return false; /* Nothing to do. */
1571 }
1572
1573 /* Transform phis, rotate by best_angle, then translate back to the origin. No scale. */
1574 for (const int64_t j : island_indices.index_range()) {
1575 const int64_t i = island_indices[j]->index;
1576 const PackIsland *island = islands[i];
1577 const float identity_scale = 1.0f; /* Don't rescale the placement, just rotate. */
1578 island->build_transformation(identity_scale, square_finder.best_angle, matrix);
1579 r_phis[i].rotation += square_finder.best_angle;
1580 mul_m2_v2(matrix, r_phis[i].translation);
1581 r_phis[i].translation.x -= square_finder.best_bounds.xmin;
1582 r_phis[i].translation.y -= square_finder.best_bounds.ymin;
1583 }
1584
1585 /* Write back new extent, translated to the origin. */
1586 r_extent->xmin = 0.0f;
1587 r_extent->ymin = 0.0f;
1588 r_extent->xmax = BLI_rctf_size_x(&square_finder.best_bounds);
1589 r_extent->ymax = BLI_rctf_size_y(&square_finder.best_bounds);
1590 return true; /* `r_phis` and `r_extent` were modified. */
1591}
1592
1609
1610static int64_t pack_island_xatlas(const Span<std::unique_ptr<UVAABBIsland>> island_indices,
1611 const Span<PackIsland *> islands,
1612 const float scale,
1613 const float margin,
1615 MutableSpan<UVPhi> r_phis,
1616 rctf *r_extent)
1617{
1618 if (params.shape_method == ED_UVPACK_SHAPE_AABB) {
1619 return 0; /* Not yet supported. */
1620 }
1621 Array<UVPhi> phis(r_phis.size());
1622 Occupancy occupancy(guess_initial_scale(islands, scale, margin));
1623 rctf extent = {0.0f, 0.0f, 0.0f, 0.0f};
1624
1625 /* A heuristic to improve final layout efficiency by making an
1626 * intermediate call to #rotate_inside_square. */
1627 int64_t square_milestone = sqrt(island_indices.size()) / 4 + 2;
1628
1629 int scan_line = 0; /* Current "scan_line" of occupancy bitmap. */
1630 int traced_islands = 0; /* Which islands are currently traced in `occupancy`. */
1631 int i = 0;
1632 bool placed_can_rotate = true;
1633
1634 /* The following `while` loop is setting up a three-way race:
1635 * `for (scan_line = 0; scan_line < bitmap_radix; scan_line++)`
1636 * `for (i : island_indices.index_range())`
1637 * `while (bitmap_scale_reciprocal > 0) { bitmap_scale_reciprocal *= 0.5f; }`
1638 */
1639
1640 while (i < island_indices.size()) {
1641
1642 if (params.stop && G.is_break) {
1643 *params.stop = true;
1644 }
1645 if (params.isCancelled()) {
1646 break;
1647 }
1648
1649 while (traced_islands < i) {
1650 /* Trace an island that's been solved. (Greedy.) */
1651 const int64_t island_index = island_indices[traced_islands]->index;
1652 PackIsland *island = islands[island_index];
1653 const float island_scale = island->can_scale_(params) ? scale : 1.0f;
1654 occupancy.trace_island(island, phis[island_index], island_scale, margin, true);
1655 traced_islands++;
1656 }
1657
1658 PackIsland *island = islands[island_indices[i]->index];
1659 UVPhi phi; /* Create an identity transform. */
1660
1661 if (!island->can_translate_(params)) {
1662 /* Move the pinned island into the correct coordinate system. */
1663 phi.translation = island->pivot_;
1664 sub_v2_v2(phi.translation, params.udim_base_offset);
1665 phi.rotation = 0.0f;
1666 phis[island_indices[i]->index] = phi;
1667 i++;
1668 placed_can_rotate = false; /* Further rotation will cause a translation. */
1669 continue; /* `island` is now completed. */
1670 }
1671 const float island_scale = island->can_scale_(params) ? scale : 1.0f;
1672
1673 int max_90_multiple = 1;
1674 if (island->can_rotate_(params)) {
1675 if (i && (i < 50)) {
1676 max_90_multiple = 4;
1677 }
1678 }
1679 else {
1680 placed_can_rotate = false;
1681 }
1682
1683 for (int angle_90_multiple = 0; angle_90_multiple < max_90_multiple; angle_90_multiple++) {
1684 phi = find_best_fit_for_island(island,
1685 scan_line,
1686 occupancy,
1687 island_scale,
1688 angle_90_multiple,
1689 margin,
1690 params.target_aspect_y);
1691 if (phi.is_valid()) {
1692 break;
1693 }
1694 }
1695
1696 if (!phi.is_valid()) {
1697 /* Unable to find a fit on this scan_line. */
1698
1699 island = nullptr; /* Just mark it as null, we won't use it further. */
1700
1701 if (i < 10) {
1702 scan_line++;
1703 }
1704 else {
1705 /* Increasing by 2 here has the effect of changing the sampling pattern.
1706 * The parameter '2' is not "free" in the sense that changing it requires
1707 * a change to `bitmap_radix` and then re-tuning `alpaca_cutoff`.
1708 * Possible values here *could* be 1, 2 or 3, however the only *reasonable*
1709 * choice is 2. */
1710 scan_line += 2;
1711 }
1712 if (scan_line < occupancy.bitmap_radix *
1713 sqrtf(std::min(params.target_aspect_y, 1.0f / params.target_aspect_y)))
1714 {
1715 continue; /* Try again on next scan_line. */
1716 }
1717
1718 /* Enlarge search parameters. */
1719 scan_line = 0;
1720 occupancy.increase_scale();
1721 traced_islands = 0; /* Will trigger a re-trace of previously solved islands. */
1722 continue;
1723 }
1724
1725 /* Place island. */
1726 phis[island_indices[i]->index] = phi;
1727 i++; /* Next island. */
1728
1729 if (i == square_milestone && placed_can_rotate) {
1731 island_indices.take_front(i), islands, params, scale, margin, phis, &extent))
1732 {
1733 scan_line = 0;
1734 traced_islands = 0;
1735 occupancy.clear();
1736 continue;
1737 }
1738 }
1739
1740 /* Update top-right corner. */
1741 float2 top_right = island->get_diagonal_support(island_scale, phi.rotation, margin) +
1742 phi.translation;
1743 extent.xmax = std::max(top_right.x, extent.xmax);
1744 extent.ymax = std::max(top_right.y, extent.ymax);
1745
1746 if (!is_larger(*r_extent, extent, params)) {
1747 if (i >= square_milestone) {
1748 return 0; /* Early exit, we already have a better layout. */
1749 }
1750 }
1751
1752 /* Heuristics to reduce size of brute-force search. */
1753 if (i < 128 || (i & 31) == 16) {
1754 scan_line = 0; /* Restart completely. */
1755 }
1756 else {
1757 scan_line = std::max(0, scan_line - 25); /* `-25` must by odd. */
1758 }
1759
1760 if (params.progress) {
1761 /* We don't (yet) have a good model for how long the pack operation is going
1762 * to take, so just update the progress a little bit. */
1763 const float previous_progress = *params.progress;
1764 *params.do_update = true;
1765 const float reduction = island_indices.size() / (island_indices.size() + 0.5f);
1766 *params.progress = 1.0f - (1.0f - previous_progress) * reduction;
1767 }
1768 }
1769
1770 /* TODO: if (i != island_indices.size()) { ??? } */
1771
1772 if (!is_larger(*r_extent, extent, params)) {
1773 return 0;
1774 }
1775
1776 /* Our pack is an improvement on the one passed in. Write it back. */
1777 *r_extent = extent;
1778 for (int64_t j = 0; j < i; j++) {
1779 const int64_t island_index = island_indices[j]->index;
1780 r_phis[island_index] = phis[island_index];
1781 }
1782 return i; /* Return the number of islands which were packed. */
1783}
1784
1795 const float scale,
1796 const float margin,
1798 MutableSpan<UVPhi> r_phis)
1799{
1800 /* #BLI_box_pack_2d produces layouts with high packing efficiency, but has `O(n^3)`
1801 * time complexity, causing poor performance if there are lots of islands. See: #102843.
1802 * #pack_islands_alpaca_turbo is designed to be the fastest packing method, `O(nlogn)`,
1803 * but has poor packing efficiency if the AABBs have a spread of sizes and aspect ratios.
1804 * Here, we merge the best properties of both packers into one combined packer.
1805 *
1806 * The free tuning parameter, `alpaca_cutoff` will determine how many islands are packed
1807 * using each method.
1808 *
1809 * The current strategy is:
1810 * - Sort islands in size order.
1811 * - Try #pack_island_optimal_pack packer first
1812 * - Call #pack_island_xatlas on the first `alpaca_cutoff` islands.
1813 * - Also call #BLI_box_pack_2d on the first `alpaca_cutoff` islands.
1814 * - Choose the best layout so far.
1815 * - Rotate into the minimum bounding square.
1816 * - Call #pack_islands_alpaca_* on the remaining islands.
1817 */
1818
1819 const bool all_can_rotate = can_rotate(islands, params);
1820
1821 /* First, copy information from our input into the AABB structure. */
1822 Array<std::unique_ptr<UVAABBIsland>> aabbs(islands.size());
1823 for (const int64_t i : islands.index_range()) {
1824 PackIsland *pack_island = islands[i];
1825 float island_scale = scale;
1826 if (!pack_island->can_scale_(params)) {
1827 island_scale = 1.0f;
1828 }
1829 std::unique_ptr<UVAABBIsland> aabb = std::make_unique<UVAABBIsland>();
1830 aabb->index = i;
1831 aabb->uv_diagonal.x = pack_island->half_diagonal_.x * 2 * island_scale + 2 * margin;
1832 aabb->uv_diagonal.y = pack_island->half_diagonal_.y * 2 * island_scale + 2 * margin;
1833 aabb->aspect_y = pack_island->aspect_y;
1834 aabbs[i] = std::move(aabb);
1835 }
1836
1837 /* Sort from "biggest" to "smallest". */
1838
1839 if (all_can_rotate) {
1840 std::stable_sort(
1841 aabbs.begin(),
1842 aabbs.end(),
1843 [&](const std::unique_ptr<UVAABBIsland> &a, const std::unique_ptr<UVAABBIsland> &b) {
1844 const bool can_translate_a = islands[a->index]->can_translate_(params);
1845 const bool can_translate_b = islands[b->index]->can_translate_(params);
1846 if (can_translate_a != can_translate_b) {
1847 return can_translate_b; /* Locked islands are placed first. */
1848 }
1849 /* TODO: Fix when (params.target_aspect_y != 1.0f) */
1850
1851 /* Choose the AABB with the longest large edge. */
1852 float a_u = a->uv_diagonal.x * a->aspect_y;
1853 float a_v = a->uv_diagonal.y;
1854 float b_u = b->uv_diagonal.x * b->aspect_y;
1855 float b_v = b->uv_diagonal.y;
1856 if (a_u > a_v) {
1857 std::swap(a_u, a_v);
1858 }
1859 if (b_u > b_v) {
1860 std::swap(b_u, b_v);
1861 }
1862 float diff_u = a_u - b_u;
1863 float diff_v = a_v - b_v;
1864 diff_v += diff_u * 0.05f; /* Robust sort, smooth over round-off errors. */
1865 if (diff_v == 0.0f) { /* Tie break. */
1866 return diff_u > 0.0f;
1867 }
1868 return diff_v > 0.0f;
1869 });
1870 }
1871 else {
1872 std::stable_sort(
1873 aabbs.begin(),
1874 aabbs.end(),
1875 [&](const std::unique_ptr<UVAABBIsland> &a, const std::unique_ptr<UVAABBIsland> &b) {
1876 const bool can_translate_a = islands[a->index]->can_translate_(params);
1877 const bool can_translate_b = islands[b->index]->can_translate_(params);
1878 if (can_translate_a != can_translate_b) {
1879 return can_translate_b; /* Locked islands are placed first. */
1880 }
1881
1882 /* Choose the AABB with larger rectangular area. */
1883 return b->uv_diagonal.x * b->uv_diagonal.y < a->uv_diagonal.x * a->uv_diagonal.y;
1884 });
1885 }
1886
1887 /* If some of the islands are locked, we build a summary about them here. */
1888 rctf locked_bounds = {0.0f}; /* AABB of islands which can't translate. */
1889 int64_t locked_island_count = 0; /* Index of first non-locked island. */
1890 for (int64_t i = 0; i < islands.size(); i++) {
1891 PackIsland *pack_island = islands[aabbs[i]->index];
1892 if (pack_island->can_translate_(params)) {
1893 break;
1894 }
1895 float2 bottom_left = pack_island->pivot_ - pack_island->half_diagonal_;
1896 float2 top_right = pack_island->pivot_ + pack_island->half_diagonal_;
1897 if (i == 0) {
1898 locked_bounds.xmin = bottom_left.x;
1899 locked_bounds.xmax = top_right.x;
1900 locked_bounds.ymin = bottom_left.y;
1901 locked_bounds.ymax = top_right.y;
1902 }
1903 else {
1904 BLI_rctf_do_minmax_v(&locked_bounds, bottom_left);
1905 BLI_rctf_do_minmax_v(&locked_bounds, top_right);
1906 }
1907
1908 UVPhi &phi = r_phis[aabbs[i]->index]; /* Lock in place. */
1909 phi.translation = pack_island->pivot_;
1910 sub_v2_v2(phi.translation, params.udim_base_offset);
1911 phi.rotation = 0.0f;
1912
1913 locked_island_count = i + 1;
1914 }
1915
1916 /* Partition `islands`, largest islands will go to a slow packer, the rest the fast packer.
1917 * See discussion above for details. */
1918 int64_t alpaca_cutoff = 1024; /* Regular situation, pack `32 * 32` islands with slow packer. */
1919 int64_t alpaca_cutoff_fast = 81; /* Reduce problem size, only `N = 9 * 9` with slow packer. */
1920 if (params.margin_method == ED_UVPACK_MARGIN_FRACTION) {
1921 if (margin > 0.0f) {
1922 alpaca_cutoff = alpaca_cutoff_fast;
1923 }
1924 }
1925
1926 alpaca_cutoff = std::max(alpaca_cutoff, locked_island_count); /* ...TODO... */
1927
1928 Span<std::unique_ptr<UVAABBIsland>> slow_aabbs = aabbs.as_span().take_front(
1929 std::min(alpaca_cutoff, islands.size()));
1930 rctf extent = {0.0f, 1e30f, 0.0f, 1e30f};
1931
1932 /* Call the "fast" packer, which can sometimes give optimal results. */
1933 pack_islands_fast(locked_island_count,
1934 locked_bounds,
1935 slow_aabbs,
1936 all_can_rotate,
1937 params.target_aspect_y,
1938 r_phis,
1939 &extent);
1940 rctf fast_extent = extent; /* Remember how large the "fast" packer was. */
1941
1942 /* Call the "optimal" packer. */
1943 if (locked_island_count == 0) {
1944 pack_islands_optimal_pack(slow_aabbs, params, r_phis, &extent);
1945 }
1946
1947 /* Call box_pack_2d (slow for large N.) */
1948 if (locked_island_count == 0) { /* box_pack_2d doesn't yet support locked islands. */
1949 pack_island_box_pack_2d(slow_aabbs, params, r_phis, &extent);
1950 }
1951
1952 /* Call xatlas (slow for large N.) */
1953 int64_t max_xatlas = pack_island_xatlas(
1954 slow_aabbs, islands, scale, margin, params, r_phis, &extent);
1955 if (max_xatlas) {
1956 slow_aabbs = aabbs.as_span().take_front(max_xatlas);
1957 }
1958
1959 /* At this stage, `extent` contains the fast/optimal/box_pack/xatlas UVs. */
1960
1961 /* If more islands remain to be packed, attempt to improve the layout further by finding the
1962 * minimal-bounding-square. Disabled for other cases as users often prefer to avoid diagonal
1963 * islands. */
1964 if (all_can_rotate && aabbs.size() > slow_aabbs.size()) {
1965 rotate_inside_square(slow_aabbs, islands, params, scale, margin, r_phis, &extent);
1966 }
1967
1968 if (BLI_rctf_compare(&extent, &fast_extent, 0.0f)) {
1969 /* The fast packer was the best so far. Lets just use the fast packer for everything. */
1970 slow_aabbs = slow_aabbs.take_front(locked_island_count);
1971 extent = locked_bounds;
1972 }
1973
1974 /* Call fast packer for remaining islands, excluding everything already placed. */
1975 rctf final_extent = {0.0f, 1e30f, 0.0f, 1e30f};
1976 pack_islands_fast(slow_aabbs.size(),
1977 extent,
1978 aabbs,
1979 all_can_rotate,
1980 params.target_aspect_y,
1981 r_phis,
1982 &final_extent);
1983
1984 return get_aspect_scaled_extent(final_extent, params);
1985}
1986
1992 const float margin_fraction,
1993 const bool rescale_margin,
1995{
1996 /*
1997 * Root finding using a combined search / modified-secant method.
1998 * First, use a robust search procedure to bracket the root within a factor of 10.
1999 * Then, use a modified-secant method to converge.
2000 *
2001 * This is a specialized solver using domain knowledge to accelerate convergence. */
2002
2003 float scale_low = 0.0f;
2004 float value_low = 0.0f;
2005 float scale_high = 0.0f;
2006 float value_high = 0.0f;
2007
2008 Array<UVPhi> phis_a(islands.size());
2009 Array<UVPhi> phis_b(islands.size());
2010 Array<UVPhi> *phis_low = nullptr;
2011
2012 /* Scaling smaller than `min_scale_roundoff` is unlikely to fit and
2013 * will destroy information in existing UVs. */
2014 const float min_scale_roundoff = 1e-5f;
2015
2016 /* Certain inputs might have poor convergence properties.
2017 * Use `max_iteration` to prevent an infinite loop. */
2018 const int max_iteration = 25;
2019 for (int iteration = 0; iteration < max_iteration; iteration++) {
2020 float scale = 1.0f;
2021
2022 if (iteration == 0) {
2023 BLI_assert(iteration == 0);
2024 BLI_assert(scale == 1.0f);
2025 BLI_assert(scale_low == 0.0f);
2026 BLI_assert(scale_high == 0.0f);
2027 }
2028 else if (scale_low == 0.0f) {
2029 BLI_assert(scale_high > 0.0f);
2030 /* Search mode, shrink layout until we can find a scale that fits. */
2031 scale = scale_high * 0.1f;
2032 }
2033 else if (scale_high == 0.0f) {
2034 BLI_assert(scale_low > 0.0f);
2035 /* Search mode, grow layout until we can find a scale that doesn't fit. */
2036 scale = scale_low * 10.0f;
2037 }
2038 else {
2039 /* Bracket mode, use modified secant method to find root. */
2040 BLI_assert(scale_low > 0.0f);
2041 BLI_assert(scale_high > 0.0f);
2042 BLI_assert(value_low <= 0.0f);
2043 BLI_assert(value_high >= 0.0f);
2044 if (scale_high < scale_low * 1.0001f) {
2045 /* Convergence. */
2046 break;
2047 }
2048
2049 /* Secant method for area. */
2050 scale = (sqrtf(scale_low) * value_high - sqrtf(scale_high) * value_low) /
2051 (value_high - value_low);
2052 scale = scale * scale;
2053
2054 if (iteration & 1) {
2055 /* Modified binary-search to improve robustness. */
2056 scale = sqrtf(scale * sqrtf(scale_low * scale_high));
2057 }
2058
2059 BLI_assert(scale_low < scale);
2060 BLI_assert(scale < scale_high);
2061 }
2062
2063 scale = std::max(scale, min_scale_roundoff);
2064
2065 /* Evaluate our `f`. */
2066 Array<UVPhi> *phis_target = (phis_low == &phis_a) ? &phis_b : &phis_a;
2067 const float margin = rescale_margin ? margin_fraction * scale : margin_fraction;
2068 const float max_uv = pack_islands_scale_margin(islands, scale, margin, params, *phis_target) /
2069 params.target_extent;
2070 const float value = sqrtf(max_uv) - 1.0f;
2071
2072 if (value <= 0.0f) {
2073 scale_low = scale;
2074 value_low = value;
2075 phis_low = phis_target;
2076 if (value == 0.0f) {
2077 break; /* Target hit exactly. */
2078 }
2079 }
2080 else {
2081 scale_high = scale;
2082 value_high = value;
2083 if (scale == min_scale_roundoff) {
2084 /* Unable to pack without damaging UVs. */
2085 scale_low = scale;
2086 break;
2087 }
2088 if (!phis_low) {
2089 phis_low = phis_target; /* May as well do "something", even if it's wrong. */
2090 }
2091 }
2092 }
2093
2094 if (phis_low) {
2095 /* Write back best pack as a side-effect. */
2096 for (const int64_t i : islands.index_range()) {
2097 PackIsland *island = islands[i];
2098 const float island_scale = island->can_scale_(params) ? scale_low : 1.0f;
2099 island->place_(island_scale, (*phis_low)[i]);
2100 }
2101 }
2102 return scale_low;
2103}
2104
2107{
2108 /* Logic matches previous behavior from #geometry::uv_parametrizer_pack.
2109 * Attempt to give predictable results not dependent on current UV scale by using
2110 * `aabb_length_sum` (was "`area`") to multiply the margin by the length (was "area"). */
2111 double aabb_length_sum = 0.0f;
2112 for (PackIsland *island : island_vector) {
2113 float w = island->half_diagonal_.x * 2.0f;
2114 float h = island->half_diagonal_.y * 2.0f;
2115 aabb_length_sum += sqrtf(w * h);
2116 }
2117 return params.margin * aabb_length_sum * 0.1f;
2118}
2119
2120/* -------------------------------------------------------------------- */
2124
2125static bool overlap_aabb(const float2 &pivot_a,
2126 const float2 &half_diagonal_a,
2127 const float2 &pivot_b,
2128 const float2 &half_diagonal_b)
2129{
2130 if (pivot_a.x + half_diagonal_a.x <= pivot_b.x - half_diagonal_b.x) {
2131 return false;
2132 }
2133 if (pivot_a.y + half_diagonal_a.y <= pivot_b.y - half_diagonal_b.y) {
2134 return false;
2135 }
2136 if (pivot_b.x + half_diagonal_b.x <= pivot_a.x - half_diagonal_a.x) {
2137 return false;
2138 }
2139 if (pivot_b.y + half_diagonal_b.y <= pivot_a.y - half_diagonal_a.y) {
2140 return false;
2141 }
2142 return true;
2143}
2144
2146 public:
2147 static bool overlap(PackIsland *a, PackIsland *b)
2148 {
2149 if (a->aspect_y != b->aspect_y) {
2150 return false; /* Cannot merge islands with different aspect ratios. */
2151 }
2152 if (!overlap_aabb(a->pivot_, a->half_diagonal_, b->pivot_, b->half_diagonal_)) {
2153 return false; /* AABBs are disjoint => islands are separate. */
2154 }
2155 for (int i = 0; i < a->triangle_vertices_.size(); i += 3) {
2156 for (int j = 0; j < b->triangle_vertices_.size(); j += 3) {
2158 a->triangle_vertices_[i + 1],
2159 a->triangle_vertices_[i + 2],
2160 b->triangle_vertices_[j + 0],
2161 b->triangle_vertices_[j + 1],
2162 b->triangle_vertices_[j + 2]))
2163 {
2164 return true; /* Two triangles overlap => islands overlap. */
2165 }
2166 }
2167 }
2168
2169 return false; /* Separate. */
2170 }
2171
2172 static void add_geometry(PackIsland *dest, const PackIsland *source)
2173 {
2174 for (int64_t i = 0; i < source->triangle_vertices_.size(); i += 3) {
2175 dest->add_triangle(source->triangle_vertices_[i],
2176 source->triangle_vertices_[i + 1],
2177 source->triangle_vertices_[i + 2]);
2178 }
2179 }
2180
2183 {
2184 PackIsland *result = new PackIsland();
2185 result->aspect_y = sqrtf(a->aspect_y * b->aspect_y);
2186 result->caller_index = -1;
2187 result->pinned = a->pinned || b->pinned;
2188 add_geometry(result, a);
2190 result->calculate_pivot_();
2191 return result;
2192 }
2193
2194 static float pack_islands_overlap(const Span<PackIsland *> islands,
2196 {
2197
2198 /* Building the binary-tree of merges is complicated to do in a single pass if we proceed in
2199 * the forward order. Instead we'll continuously update the tree as we descend, with
2200 * `sub_islands` doing the work of our stack. See #merge_islands for details.
2201 *
2202 * Technically, performance is O(n^2). In practice, should be fast enough. */
2203
2204 Vector<PackIsland *> sub_islands; /* Pack these islands instead. */
2205 Vector<PackIsland *> merge_trace; /* Trace merge information. */
2206 for (const int64_t i : islands.index_range()) {
2207 PackIsland *island = islands[i];
2208 island->calculate_pivot_();
2209
2210 /* Loop backwards, building a binary tree of all merged islands as we descend. */
2211 for (int64_t j = sub_islands.size() - 1; j >= 0; j--) {
2212 if (overlap(island, sub_islands[j])) {
2213 merge_trace.append(island);
2214 merge_trace.append(sub_islands[j]);
2215 island = merge_islands(island, sub_islands[j]);
2216 merge_trace.append(island);
2217 sub_islands.remove(j);
2218 }
2219 }
2220 sub_islands.append(island);
2221 }
2222
2223 /* Recursively call pack_islands with `merge_overlap = false`. */
2224 UVPackIsland_Params sub_params(params);
2225 sub_params.merge_overlap = false;
2226 const float result = pack_islands(sub_islands, sub_params);
2227
2228 /* Must loop backwards, or we will miss sub-sub-islands. */
2229 for (int64_t i = merge_trace.size() - 3; i >= 0; i -= 3) {
2230 PackIsland *sub_a = merge_trace[i];
2231 PackIsland *sub_b = merge_trace[i + 1];
2232 PackIsland *merge = merge_trace[i + 2];
2233
2234 /* Copy `angle`, `pre_translate` and `pre_rotate` from merged island to sub islands. */
2235 sub_a->angle = merge->angle;
2236 sub_b->angle = merge->angle;
2237 sub_a->pre_translate = merge->pre_translate;
2238 sub_b->pre_translate = merge->pre_translate;
2239 sub_a->pre_rotate_ = merge->pre_rotate_;
2240 sub_b->pre_rotate_ = merge->pre_rotate_;
2241
2242 /* If the merged island is pinned, the sub-islands are also pinned to correct scaling. */
2243 if (merge->pinned) {
2244 sub_a->pinned = true;
2245 sub_b->pinned = true;
2246 }
2247 delete merge;
2248 }
2249
2250 return result;
2251 }
2252};
2253
2255{
2257 Heap *heap = BLI_heap_new();
2258 for (const int64_t i : islands.index_range()) {
2259 islands[i]->finalize_geometry_(params, arena, heap);
2260 BLI_memarena_clear(arena);
2261 }
2262
2263 BLI_heap_free(heap, nullptr);
2264 BLI_memarena_free(arena);
2265}
2266
2268{
2269 BLI_assert(0.0f <= params.margin);
2270 BLI_assert(0.0f <= params.target_aspect_y);
2271
2272 if (islands.is_empty()) {
2273 return 1.0f; /* Nothing to do, just create a safe default. */
2274 }
2275
2276 if (params.merge_overlap) {
2278 }
2279
2280 finalize_geometry(islands, params);
2281
2282 /* Count the number of islands which can scale and which can translate. */
2283 int64_t can_scale_count = 0;
2284 int64_t can_translate_count = 0;
2285 for (const int64_t i : islands.index_range()) {
2286 if (islands[i]->can_scale_(params)) {
2287 can_scale_count++;
2288 }
2289 if (islands[i]->can_translate_(params)) {
2290 can_translate_count++;
2291 }
2292 }
2293
2294 if (can_translate_count == 0) {
2295 return 1.0f; /* Nothing to do, all islands are locked. */
2296 }
2297
2298 if (params.margin_method == ED_UVPACK_MARGIN_FRACTION && params.margin > 0.0f &&
2299 can_scale_count > 0)
2300 {
2301 /* Uses a line search on scale. ~10x slower than other method. */
2302 return pack_islands_margin_fraction(islands, params.margin, false, params);
2303 }
2304
2305 float margin = params.margin;
2306 switch (params.margin_method) {
2307 case ED_UVPACK_MARGIN_ADD: /* Default for Blender 2.8 and earlier. */
2308 break; /* Nothing to do. */
2309 case ED_UVPACK_MARGIN_SCALED: /* Default for Blender 3.3 and later. */
2310 margin = calc_margin_from_aabb_length_sum(islands, params);
2311 break;
2312 case ED_UVPACK_MARGIN_FRACTION: /* Added as an option in Blender 3.4. */
2313 /* Most other cases are handled above, unless pinning is involved. */
2314 break;
2315 default:
2317 }
2318
2319 if (can_scale_count > 0 && can_scale_count != islands.size()) {
2320 /* Search for the best scale parameter. (slow) */
2321 return pack_islands_margin_fraction(islands, margin, true, params);
2322 }
2323
2324 /* Either all of the islands can scale, or none of them can.
2325 * In either case, we pack them all tight to the origin. */
2326 Array<UVPhi> phis(islands.size());
2327 const float scale = 1.0f;
2328 const float max_uv = pack_islands_scale_margin(islands, scale, margin, params, phis);
2329 const float result = can_scale_count && max_uv > 1e-14f ? params.target_extent / max_uv : 1.0f;
2330 for (const int64_t i : islands.index_range()) {
2331 BLI_assert(result == 1.0f || islands[i]->can_scale_(params));
2332 islands[i]->place_(scale, phis[i]);
2333 }
2334 return result;
2335}
2336
2338
2340 const double angle,
2341 float (*r_matrix)[2]) const
2342{
2343 const double cos_angle = cos(angle);
2344 const double sin_angle = sin(angle);
2345 r_matrix[0][0] = cos_angle * scale;
2346 r_matrix[0][1] = -sin_angle * scale * aspect_y;
2347 r_matrix[1][0] = sin_angle * scale / aspect_y;
2348 r_matrix[1][1] = cos_angle * scale;
2349#if 0
2350 if (reflect) {
2351 r_matrix[0][0] *= -1.0f;
2352 r_matrix[0][1] *= -1.0f;
2353 }
2354#endif
2355}
2356
2358 const double angle,
2359 float (*r_matrix)[2]) const
2360{
2361 const double cos_angle = cos(angle);
2362 const double sin_angle = sin(angle);
2363
2364 r_matrix[0][0] = cos_angle / scale;
2365 r_matrix[0][1] = sin_angle / scale * aspect_y;
2366 r_matrix[1][0] = -sin_angle / scale / aspect_y;
2367 r_matrix[1][1] = cos_angle / scale;
2368#if 0
2369 if (reflect) {
2370 r_matrix[0][0] *= -1.0f;
2371 r_matrix[1][0] *= -1.0f;
2372 }
2373#endif
2374}
2375
2376static bool can_rotate_with_method(const PackIsland &island,
2378 const eUVPackIsland_RotationMethod rotate_method)
2379{
2380 /* When axis aligned along X/Y coordinates, rotation is performed once early on,
2381 * but no rotation is allowed when packing. */
2382 if (ELEM(rotate_method,
2386 {
2387 return false;
2388 }
2389 if (!island.pinned) {
2390 return true;
2391 }
2392 switch (params.pin_method) {
2396 return false;
2397 default:
2398 return true;
2399 }
2400}
2401
2403{
2404 eUVPackIsland_RotationMethod rotate_method = params.rotate_method;
2406 rotate_method = ED_UVPACK_ROTATION_AXIS_ALIGNED;
2407 }
2408 return can_rotate_with_method(*this, params, rotate_method);
2409}
2410
2412{
2413 return can_rotate_with_method(*this, params, params.rotate_method);
2414}
2415
2417{
2418 if (!params.scale_to_fit) {
2419 return false;
2420 }
2421 if (!pinned) {
2422 return true;
2423 }
2424 switch (params.pin_method) {
2428 return false;
2429 default:
2430 return true;
2431 }
2432}
2433
2435{
2436 if (!pinned) {
2437 return true;
2438 }
2439 switch (params.pin_method) {
2441 return false;
2442 default:
2443 return true;
2444 }
2445}
2446
2447} // namespace blender::geometry
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLI_box_pack_2d(BoxPack *boxarray, unsigned int len, bool sort_boxes, float *r_tot_x, float *r_tot_y)
int BLI_convexhull_2d(const float(*points)[2], int points_num, int r_points[])
float BLI_convexhull_aabb_fit_points_2d(const float(*points)[2], int points_num)
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.cc:191
unsigned int BLI_heap_len(const Heap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:274
void BLI_heap_clear(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.cc:213
Heap * BLI_heap_new(void) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.cc:186
MINLINE float max_fff(float a, float b, float c)
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE float min_fff(float a, float b, float c)
MINLINE int compare_ff_relative(float a, float b, float max_diff, int max_ulps)
#define DEG2RADF(_deg)
#define M_PI_2
#define M_PI
bool isect_tri_tri_v2(const float t_a0[2], const float t_a1[2], const float t_a2[2], const float t_b0[2], const float t_b1[2], const float t_b2[2])
void mul_v2_m2v2(float r[2], const float mat[2][2], const float vec[2])
void mul_m2_v2(const float mat[2][2], float vec[2])
void angle_to_mat2(float R[2][2], float angle)
MINLINE void sub_v2_v2(float r[2], const float a[2])
#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_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)
void BLI_polyfill_beautify(const float(*coords)[2], unsigned int coords_num, unsigned int(*tris)[3], struct MemArena *arena, struct Heap *eheap)
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
void BLI_rctf_do_minmax_v(struct rctf *rect, const float xy[2])
Definition rct.cc:510
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
bool BLI_rctf_compare(const struct rctf *rect_a, const struct rctf *rect_b, float limit)
void BLI_rctf_init_minmax(struct rctf *rect)
Definition rct.cc:480
unsigned int uint
#define UNUSED_VARS(...)
#define ELEM(...)
struct rctf rctf
@ ED_UVPACK_MARGIN_FRACTION
@ ED_UVPACK_MARGIN_SCALED
@ ED_UVPACK_MARGIN_ADD
eUVPackIsland_ShapeMethod
@ ED_UVPACK_SHAPE_AABB
@ ED_UVPACK_SHAPE_CONVEX
@ ED_UVPACK_PIN_NONE
@ ED_UVPACK_PIN_LOCK_ROTATION_SCALE
@ ED_UVPACK_PIN_LOCK_SCALE
@ ED_UVPACK_PIN_LOCK_ROTATION
@ ED_UVPACK_PIN_LOCK_ALL
eUVPackIsland_RotationMethod
@ ED_UVPACK_ROTATION_ANY
@ ED_UVPACK_ROTATION_AXIS_ALIGNED_X
@ ED_UVPACK_ROTATION_AXIS_ALIGNED
@ ED_UVPACK_ROTATION_CARDINAL
@ ED_UVPACK_ROTATION_NONE
@ ED_UVPACK_ROTATION_AXIS_ALIGNED_Y
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
for(;discarded_id_iter !=nullptr;discarded_id_iter=static_cast< ID * >(discarded_id_iter->next))
Definition blendfile.cc:634
long long int int64_t
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
static T sum(const btAlignedObjectArray< T > &items)
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr Span take_front(int64_t n) const
Definition BLI_span.hh:193
const T * end() const
Definition BLI_array.hh:314
const T * begin() const
Definition BLI_array.hh:310
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr Span take_front(int64_t n) const
Definition BLI_span.hh:193
constexpr bool is_empty() const
Definition BLI_span.hh:260
int64_t size() const
void append(const T &value)
void remove(const int64_t index)
void resize(const int64_t new_size)
float trace_triangle(const float2 &uv0, const float2 &uv1, const float2 &uv2, const float margin, const bool write) const
Definition uv_pack.cc:1207
Occupancy(const float initial_scale)
Definition uv_pack.cc:1161
float trace_island(const PackIsland *island, const UVPhi phi, const float scale, const float margin, const bool write) const
Definition uv_pack.cc:1297
static float pack_islands_overlap(const Span< PackIsland * > islands, const UVPackIsland_Params &params)
Definition uv_pack.cc:2194
static void add_geometry(PackIsland *dest, const PackIsland *source)
Definition uv_pack.cc:2172
static bool overlap(PackIsland *a, PackIsland *b)
Definition uv_pack.cc:2147
static PackIsland * merge_islands(const PackIsland *a, const PackIsland *b)
Definition uv_pack.cc:2182
void place_(float scale, UVPhi phi)
Definition uv_pack.cc:392
bool can_scale_(const UVPackIsland_Params &params) const
Definition uv_pack.cc:2416
void finalize_geometry_(const UVPackIsland_Params &params, MemArena *arena, Heap *heap)
Definition uv_pack.cc:315
bool can_rotate_before_pack_(const UVPackIsland_Params &params) const
Definition uv_pack.cc:2402
void add_polygon(Span< float2 > uvs, MemArena *arena, Heap *heap)
Definition uv_pack.cc:155
void build_transformation(float scale, double angle, float r_matrix[2][2]) const
Definition uv_pack.cc:2339
void build_inverse_transformation(float scale, double angle, float r_matrix[2][2]) const
Definition uv_pack.cc:2357
bool can_rotate_(const UVPackIsland_Params &params) const
Definition uv_pack.cc:2411
Vector< float2 > triangle_vertices_
bool can_translate_(const UVPackIsland_Params &params) const
Definition uv_pack.cc:2434
void add_triangle(float2 uv0, float2 uv1, float2 uv2)
Definition uv_pack.cc:140
float2 get_diagonal_support(float scale, float rotation, float margin) const
Definition uv_pack.cc:1268
UVMinimumEnclosingSquareFinder(const float scale, const float margin, const UVPackIsland_Params *params)
Definition uv_pack.cc:1439
void update_recursive(const float angle0, const float quad0, const float angle1, const float quad1)
Definition uv_pack.cc:1477
eUVPackIsland_RotationMethod rotate_method
eUVPackIsland_MarginMethod margin_method
eUVPackIsland_ShapeMethod shape_method
eUVPackIsland_PinMethod pin_method
bool is_valid() const
Definition uv_pack.cc:41
#define cosf(x)
#define ceilf(x)
#define floorf(x)
#define fabsf(x)
#define sqrtf(x)
#define sin
VecBase< T, D > reflect(VecOp< T, D >, VecOp< T, D >) RET
#define cos
#define sqrt
float distance(VecOp< float, D >, VecOp< float, D >) RET
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
OrientationBounds merge(const OrientationBounds &cone_a, const OrientationBounds &cone_b)
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:55
static int64_t pack_island_xatlas(const Span< std::unique_ptr< UVAABBIsland > > island_indices, const Span< PackIsland * > islands, const float scale, const float margin, const UVPackIsland_Params &params, MutableSpan< UVPhi > r_phis, rctf *r_extent)
Definition uv_pack.cc:1610
static void pack_islands_optimal_pack(const Span< std::unique_ptr< UVAABBIsland > > aabbs, const UVPackIsland_Params &params, MutableSpan< UVPhi > r_phis, rctf *r_extent)
Definition uv_pack.cc:749
static float angle_match(float angle_radians, float target_radians)
Definition uv_pack.cc:202
static float get_aspect_scaled_extent(const rctf &extent, const UVPackIsland_Params &params)
Definition uv_pack.cc:92
static void pack_gobel(const Span< std::unique_ptr< UVAABBIsland > > aabbs, const float scale, const int m, MutableSpan< UVPhi > r_phis)
Definition uv_pack.cc:683
static float angle_wrap(float angle_radians)
Definition uv_pack.cc:210
static float plusminus_90_angle(float angle_radians)
Definition uv_pack.cc:219
static void pack_islands_alpaca_rotate(const int64_t exclude_index, const rctf &exclude, const Span< std::unique_ptr< UVAABBIsland > > islands, const float target_aspect_y, MutableSpan< UVPhi > r_phis, rctf *r_extent)
Definition uv_pack.cc:560
static float dist_signed_squared_to_edge(const float2 probe, const float2 uva, const float2 uvb)
Definition uv_pack.cc:72
static float pack_islands_margin_fraction(const Span< PackIsland * > islands, const float margin_fraction, const bool rescale_margin, const UVPackIsland_Params &params)
Definition uv_pack.cc:1991
static void pack_islands_alpaca_turbo(const int64_t exclude_index, const rctf &exclude, const Span< std::unique_ptr< UVAABBIsland > > islands, const float target_aspect_y, MutableSpan< UVPhi > r_phis, rctf *r_extent)
Definition uv_pack.cc:449
static void pack_island_box_pack_2d(const Span< std::unique_ptr< UVAABBIsland > > aabbs, const UVPackIsland_Params &params, MutableSpan< UVPhi > r_phis, rctf *r_extent)
Definition uv_pack.cc:1082
static float get_aspect_scaled_area(const rctf &extent, const UVPackIsland_Params &params)
Definition uv_pack.cc:102
static bool is_larger(const rctf &a, const rctf &b, const UVPackIsland_Params &params)
Definition uv_pack.cc:112
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 float signed_distance_fat_triangle(const float2 probe, const float2 uv0, const float2 uv1, const float2 uv2)
Definition uv_pack.cc:1186
static float pack_islands_scale_margin(const Span< PackIsland * > islands, const float scale, const float margin, const UVPackIsland_Params &params, MutableSpan< UVPhi > r_phis)
Definition uv_pack.cc:1794
static bool overlap_aabb(const float2 &pivot_a, const float2 &half_diagonal_a, const float2 &pivot_b, const float2 &half_diagonal_b)
Definition uv_pack.cc:2125
static bool can_rotate(const Span< PackIsland * > islands, const UVPackIsland_Params &params)
Definition uv_pack.cc:191
static void update_hole_rotate(float2 &hole, float2 &hole_diagonal, bool &hole_rotate, const float u0, const float v0, const float u1, const float v1)
Definition uv_pack.cc:518
static bool pack_islands_optimal_pack_table(const int table_count, const float max_extent, const float *optimal, const char *, int64_t island_count, const float large_uv, const Span< std::unique_ptr< UVAABBIsland > > aabbs, const UVPackIsland_Params &params, MutableSpan< UVPhi > r_phis, rctf *r_extent)
Definition uv_pack.cc:719
static UVPhi find_best_fit_for_island(const PackIsland *island, const int scan_line, const Occupancy &occupancy, const float scale, const int angle_90_multiple, const float margin, const float target_aspect_y)
Definition uv_pack.cc:1342
static bool can_rotate_with_method(const PackIsland &island, const UVPackIsland_Params &params, const eUVPackIsland_RotationMethod rotate_method)
Definition uv_pack.cc:2376
static bool rotate_inside_square(const Span< std::unique_ptr< UVAABBIsland > > island_indices, const Span< PackIsland * > islands, const UVPackIsland_Params &params, const float scale, const float margin, MutableSpan< UVPhi > r_phis, rctf *r_extent)
Definition uv_pack.cc:1507
float pack_islands(Span< PackIsland * > islands, const UVPackIsland_Params &params)
static void pack_islands_fast(const int64_t exclude_index, const rctf &exclude, const Span< std::unique_ptr< UVAABBIsland > > aabbs, const bool rotate, const float target_aspect_y, MutableSpan< UVPhi > r_phis, rctf *r_extent)
Definition uv_pack.cc:666
static float guess_initial_scale(const Span< PackIsland * > islands, const float scale, const float margin)
Definition uv_pack.cc:1412
static void finalize_geometry(const Span< PackIsland * > islands, const UVPackIsland_Params &params)
Definition uv_pack.cc:2254
static float calc_margin_from_aabb_length_sum(const Span< PackIsland * > island_vector, const UVPackIsland_Params &params)
Definition uv_pack.cc:2105
T length_squared(const VecBase< T, Size > &a)
VecBase< float, 2 > float2
static void rotate(float new_co[3], float a, const float ax[3], const float co[3])
float x
float y
float xmax
float xmin
float ymax
float ymin
i
Definition text_draw.cc:230