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