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