Blender V4.3
uvedit_rip.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 <cmath>
10#include <cstdlib>
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_ghash.h"
16#include "BLI_linklist_stack.h"
17#include "BLI_math_vector.h"
18#include "BLI_utildefines.h"
19
20#include "DNA_image_types.h"
21#include "DNA_mesh_types.h"
22#include "DNA_node_types.h"
23#include "DNA_object_types.h"
24#include "DNA_scene_types.h"
25#include "DNA_space_types.h"
26
27#include "BKE_context.hh"
28#include "BKE_customdata.hh"
29#include "BKE_editmesh.hh"
30#include "BKE_layer.hh"
31#include "BKE_mesh_types.hh"
32#include "BKE_report.hh"
33
34#include "DEG_depsgraph.hh"
35
36#include "ED_screen.hh"
37#include "ED_transform.hh"
38#include "ED_uvedit.hh"
39
40#include "RNA_access.hh"
41#include "RNA_define.hh"
42
43#include "WM_api.hh"
44#include "WM_types.hh"
45
46#include "UI_view2d.hh"
47
48#include "uvedit_intern.hh"
49
50using blender::Vector;
51
52/* -------------------------------------------------------------------- */
88
90BLI_STATIC_ASSERT(sizeof(ULData) <= sizeof(int), "");
91
93{
94 return (ULData *)&l->head.index;
95}
96
99/* -------------------------------------------------------------------- */
104 const int cd_loop_uv_offset)
105{
106 BMLoop *l_other = nullptr;
107 BMLoop *l_iter = l_src->radial_next;
108 if (l_iter != l_src) {
109 do {
110 if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG) && UL(l_iter)->is_select_edge &&
111 BM_loop_uv_share_edge_check(l_src, l_iter, cd_loop_uv_offset))
112 {
113 /* Check UVs are contiguous. */
114 if (l_other == nullptr) {
115 l_other = l_iter;
116 }
117 else {
118 /* Only use when there is a single alternative. */
119 l_other = nullptr;
120 break;
121 }
122 }
123 } while ((l_iter = l_iter->radial_next) != l_src);
124 }
125 return l_other;
126}
127
129 BMVert *v_src,
130 const int cd_loop_uv_offset)
131{
132 BLI_assert(BM_vert_in_edge(l_src->e, v_src));
133 BMLoop *l_other = nullptr;
134 BMLoop *l_iter = l_src->radial_next;
135 if (l_iter != l_src) {
136 do {
137 if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG) &&
138 BM_loop_uv_share_edge_check(l_src, l_iter, cd_loop_uv_offset))
139 {
140 /* Check UVs are contiguous. */
141 if (l_other == nullptr) {
142 l_other = l_iter;
143 }
144 else {
145 /* Only use when there is a single alternative. */
146 l_other = nullptr;
147 break;
148 }
149 }
150 } while ((l_iter = l_iter->radial_next) != l_src);
151 }
152 if (l_other != nullptr) {
153 if (l_other->v == v_src) {
154 /* do nothing. */
155 }
156 else if (l_other->next->v == v_src) {
157 l_other = l_other->next;
158 }
159 else if (l_other->prev->v == v_src) {
160 l_other = l_other->prev;
161 }
162 else {
164 }
165 }
166 return l_other;
167}
168
172static BMLoop *bm_vert_step_fan_loop_uv(BMLoop *l, BMEdge **e_step, const int cd_loop_uv_offset)
173{
174 BMEdge *e_prev = *e_step;
175 BMLoop *l_next;
176 if (l->e == e_prev) {
177 l_next = l->prev;
178 }
179 else if (l->prev->e == e_prev) {
180 l_next = l;
181 }
182 else {
184 return nullptr;
185 }
186
187 *e_step = l_next->e;
188
189 return bm_loop_find_other_fan_loop_with_visible_face(l_next, l->v, cd_loop_uv_offset);
190}
191
192static void bm_loop_uv_select_single_vert_validate(BMLoop *l_init, const int cd_loop_uv_offset)
193{
194 const float *luv_init = BM_ELEM_CD_GET_FLOAT_P(l_init, cd_loop_uv_offset);
195 BMIter liter;
196 BMLoop *l;
197 bool is_single_vert = true;
198 BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
199 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
200 if (equals_v2v2(luv_init, luv)) {
201 if (UL(l->prev)->is_select_edge || UL(l)->is_select_edge) {
202 is_single_vert = false;
203 break;
204 }
205 }
206 }
207 if (is_single_vert == false) {
208 BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
209 if (UL(l)->is_select_vert_single) {
210 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
211 if (equals_v2v2(luv_init, luv)) {
212 UL(l)->is_select_vert_single = false;
213 }
214 }
215 }
216 }
217}
218
226 const float dir[2],
227 const float aspect_y,
228 const int cd_loop_uv_offset,
229 float *r_corner_angle,
230 float *r_edge_angle,
231 int *r_edge_index)
232{
233 /* Calculate 3 directions, return the shortest angle. */
234 float dir_test[3][2];
235 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
236 const float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l->prev, cd_loop_uv_offset);
237 const float *luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
238
239 sub_v2_v2v2(dir_test[0], luv, luv_prev);
240 sub_v2_v2v2(dir_test[2], luv, luv_next);
241 dir_test[0][1] /= aspect_y;
242 dir_test[2][1] /= aspect_y;
243
244 normalize_v2(dir_test[0]);
245 normalize_v2(dir_test[2]);
246
247 /* Calculate the orthogonal line (same as negating one, then adding). */
248 sub_v2_v2v2(dir_test[1], dir_test[0], dir_test[2]);
249 normalize_v2(dir_test[1]);
250
251 /* Rotate 90 degrees. */
252 std::swap(dir_test[1][0], dir_test[1][1]);
253 dir_test[1][1] *= -1.0f;
254
255 if (BM_face_uv_calc_cross(l->f, cd_loop_uv_offset) > 0.0f) {
256 negate_v2(dir_test[1]);
257 }
258
259 const float angles[3] = {
260 angle_v2v2(dir, dir_test[0]),
261 angle_v2v2(dir, dir_test[1]),
262 angle_v2v2(dir, dir_test[2]),
263 };
264
265 /* Set the corner values. */
266 *r_corner_angle = angles[1];
267
268 /* Set the edge values. */
269 if (angles[0] < angles[2]) {
270 *r_edge_angle = angles[0];
271 *r_edge_index = -1;
272 }
273 else {
274 *r_edge_angle = angles[2];
275 *r_edge_index = 1;
276 }
277}
278
281/* -------------------------------------------------------------------- */
288};
289
305 const float co[2],
306 const float aspect_y,
307 const int cd_loop_uv_offset)
308{
309 UVRipSingle *rip = MEM_cnew<UVRipSingle>(__func__);
310 const float *co_center = BM_ELEM_CD_GET_FLOAT_P(l_init_orig, cd_loop_uv_offset);
311 rip->loops = BLI_gset_ptr_new(__func__);
312
313 /* Track the closest loop, start walking from this so in the event we have multiple
314 * disconnected fans, we can rip away loops connected to this one. */
315 BMLoop *l_init = nullptr;
316 BMLoop *l_init_edge = nullptr;
317 float corner_angle_best = FLT_MAX;
318 float edge_angle_best = FLT_MAX;
319 int edge_index_best = 0; /* -1 or +1 (never center). */
320
321 /* Calculate the direction from the cursor with aspect correction. */
322 float dir_co[2];
323 sub_v2_v2v2(dir_co, co_center, co);
324 dir_co[1] /= aspect_y;
325 if (UNLIKELY(normalize_v2(dir_co) == 0.0)) {
326 dir_co[1] = 1.0f;
327 }
328
329 int uv_fan_count_all = 0;
330 {
331 BMIter liter;
332 BMLoop *l;
333 BM_ITER_ELEM (l, &liter, l_init_orig->v, BM_LOOPS_OF_VERT) {
335 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
336 if (equals_v2v2(co_center, luv)) {
337 uv_fan_count_all += 1;
338 /* Clear at the same time. */
339 UL(l)->is_select_vert_single = true;
340 UL(l)->side = 0;
341 BLI_gset_add(rip->loops, l);
342
343 /* Update `l_init_close` */
344 float corner_angle_test;
345 float edge_angle_test;
346 int edge_index_test;
348 dir_co,
349 aspect_y,
350 cd_loop_uv_offset,
351 &corner_angle_test,
352 &edge_angle_test,
353 &edge_index_test);
354 if ((corner_angle_best == FLT_MAX) || (corner_angle_test < corner_angle_best)) {
355 corner_angle_best = corner_angle_test;
356 l_init = l;
357 }
358
359 /* Trick so we don't consider concave corners further away than they should be. */
360 edge_angle_test = min_ff(corner_angle_test, edge_angle_test);
361
362 if ((edge_angle_best == FLT_MAX) || (edge_angle_test < edge_angle_best)) {
363 edge_angle_best = edge_angle_test;
364 edge_index_best = edge_index_test;
365 l_init_edge = l;
366 }
367 }
368 }
369 }
370 }
371
372 /* Walk around the `l_init` in both directions of the UV fan. */
373 int uv_fan_count_contiguous = 1;
374 UL(l_init)->side = 1;
375 for (int i = 0; i < 2; i += 1) {
376 BMEdge *e_prev = i ? l_init->e : l_init->prev->e;
377 BMLoop *l_iter = l_init;
378 while (((l_iter = bm_vert_step_fan_loop_uv(l_iter, &e_prev, cd_loop_uv_offset)) != l_init) &&
379 (l_iter != nullptr) && (UL(l_iter)->side == 0))
380 {
381 uv_fan_count_contiguous += 1;
382 /* Keep. */
383 UL(l_iter)->side = 1;
384 }
385 /* May be useful to know if the fan is closed, currently it's not needed. */
386#if 0
387 if (l_iter == l_init) {
388 is_closed = true;
389 }
390#endif
391 }
392
393 if (uv_fan_count_contiguous != uv_fan_count_all) {
394 /* Simply rip off the current fan, all tagging is done. */
395 }
396 else {
397 GSetIterator gs_iter;
398 GSET_ITER (gs_iter, rip->loops) {
399 BMLoop *l = static_cast<BMLoop *>(BLI_gsetIterator_getKey(&gs_iter));
400 UL(l)->side = 0;
401 }
402
403 if (uv_fan_count_contiguous <= 2) {
404 /* Simple case, rip away the closest loop. */
405 UL(l_init)->side = 1;
406 }
407 else {
408 /* Rip away from the closest edge. */
409 BMLoop *l_radial_init = (edge_index_best == -1) ? l_init_edge->prev : l_init_edge;
410 BMLoop *l_radial_iter = l_radial_init;
411 do {
412 if (BM_loop_uv_share_edge_check(l_radial_init, l_radial_iter, cd_loop_uv_offset)) {
413 BMLoop *l = (l_radial_iter->v == l_init->v) ? l_radial_iter : l_radial_iter->next;
414 BLI_assert(l->v == l_init->v);
415 /* Keep. */
416 UL(l)->side = 1;
417 }
418 } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_init);
419 }
420 }
421
422 return rip;
423}
424
426{
427 BLI_gset_free(rip->loops, nullptr);
428 MEM_freeN(rip);
429}
430
433/* -------------------------------------------------------------------- */
440};
441
443{
444 ULData *ul = UL(l);
446 BLI_assert(ul->in_rip_pairs == false);
447 ul->in_rip_pairs = true;
448 BLI_gset_add(rip->loops, l);
449}
450
452{
453 ULData *ul = UL(l);
455 BLI_assert(ul->in_rip_pairs == true);
456 ul->in_rip_pairs = false;
457 BLI_gset_remove(rip->loops, l, nullptr);
458}
459
465 uint side,
466 const float aspect_y,
467 const int cd_loop_uv_offset)
468{
469 BMIter liter;
470 const float *luv_init = BM_ELEM_CD_GET_FLOAT_P(l_init, cd_loop_uv_offset);
471 float angle_of_side = 0.0f;
472 BMLoop *l;
473 BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
474 if (UL(l)->in_rip_pairs) {
475 if (UL(l)->side == side) {
476 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
477 if (equals_v2v2(luv_init, luv)) {
478 const float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l->prev, cd_loop_uv_offset);
479 const float *luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
480 float dir_prev[2], dir_next[2];
481 sub_v2_v2v2(dir_prev, luv_prev, luv);
482 sub_v2_v2v2(dir_next, luv_next, luv);
483 dir_prev[1] /= aspect_y;
484 dir_next[1] /= aspect_y;
485 const float luv_angle = angle_v2v2(dir_prev, dir_next);
486 if (LIKELY(isfinite(luv_angle))) {
487 angle_of_side += luv_angle;
488 }
489 }
490 }
491 }
492 }
493 return angle_of_side;
494}
495
496static int uv_rip_pairs_loop_count_on_side(BMLoop *l_init, uint side, const int cd_loop_uv_offset)
497{
498 const float *luv_init = BM_ELEM_CD_GET_FLOAT_P(l_init, cd_loop_uv_offset);
499 int count = 0;
500 BMIter liter;
501 BMLoop *l;
502 BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
503 if (UL(l)->in_rip_pairs) {
504 if (UL(l)->side == side) {
505 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
506 if (equals_v2v2(luv_init, luv)) {
507 count += 1;
508 }
509 }
510 }
511 }
512 return count;
513}
514
516 BMLoop *l_target,
517 const float aspect_y,
518 const int cd_loop_uv_offset)
519{
520 const int side_a = UL(l_switch)->side;
521 const int side_b = UL(l_target)->side;
522
523 BLI_assert(UL(l_switch)->side != UL(l_target)->side);
524
525 /* First, check if this is a simple grid topology,
526 * in that case always choose the adjacent edge. */
527 const int count_a = uv_rip_pairs_loop_count_on_side(l_switch, side_a, cd_loop_uv_offset);
528 const int count_b = uv_rip_pairs_loop_count_on_side(l_target, side_b, cd_loop_uv_offset);
529 if (count_a + count_b == 4) {
530 return count_a > count_b;
531 }
532
533 const float angle_a_before = uv_rip_pairs_calc_uv_angle(
534 l_switch, side_a, aspect_y, cd_loop_uv_offset);
535 const float angle_b_before = uv_rip_pairs_calc_uv_angle(
536 l_target, side_b, aspect_y, cd_loop_uv_offset);
537
538 UL(l_switch)->side = side_b;
539
540 const float angle_a_after = uv_rip_pairs_calc_uv_angle(
541 l_switch, side_a, aspect_y, cd_loop_uv_offset);
542 const float angle_b_after = uv_rip_pairs_calc_uv_angle(
543 l_target, side_b, aspect_y, cd_loop_uv_offset);
544
545 UL(l_switch)->side = side_a;
546
547 return fabsf(angle_a_before - angle_b_before) > fabsf(angle_a_after - angle_b_after);
548}
549
559 const float aspect_y,
560 const int cd_loop_uv_offset)
561{
562 UVRipPairs *rip = MEM_cnew<UVRipPairs>(__func__);
563 rip->loops = BLI_gset_ptr_new(__func__);
564
565 /* We can rely on this stack being small, as we're walking down two sides of an edge loop,
566 * so the stack won't be much larger than the total number of fans at any one vertex. */
568
569 /* Needed for cases when we walk onto loops which already have a side assigned,
570 * in this case we need to pick a better side (see #uv_rip_pairs_loop_change_sides_test)
571 * and put the loop back in the stack,
572 * which is needed in the case adjacent loops should also switch sides. */
573#define UV_SET_SIDE_AND_REMOVE_FROM_RAIL(loop, side_value) \
574 { \
575 BLI_assert(UL(loop)->side_was_swapped == false); \
576 BLI_assert(UL(loop)->side != side_value); \
577 if (!UL(loop)->in_stack) { \
578 BLI_SMALLSTACK_PUSH(stack, loop); \
579 UL(loop)->in_stack = true; \
580 } \
581 if (UL(loop)->in_rip_pairs) { \
582 uv_rip_pairs_remove(rip, loop); \
583 } \
584 UL(loop)->side = side_value; \
585 UL(loop)->side_was_swapped = true; \
586 }
587
588 /* Initialize the stack. */
589 BLI_SMALLSTACK_PUSH(stack, l_init);
590 UL(l_init)->in_stack = true;
591
592 BMLoop *l_step;
593 while ((l_step = static_cast<BMLoop *>(BLI_SMALLSTACK_POP(stack)))) {
594 int side = UL(l_step)->side;
595 UL(l_step)->in_stack = false;
596
597 /* Note that we could add all loops into the rip-pairs when adding into the stack,
598 * however this complicates removal, so add into the rip-pairs when popping from the stack. */
599 uv_rip_pairs_add(rip, l_step);
600
601 /* Add to the other side if it exists. */
602 if (UL(l_step)->is_select_edge) {
604 cd_loop_uv_offset);
605 if (l_other != nullptr) {
606 if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) {
607 BLI_SMALLSTACK_PUSH(stack, l_other);
608 UL(l_other)->in_stack = true;
609 UL(l_other)->side = !side;
610 }
611 else {
612 if (UL(l_other)->side == side) {
613 if (UL(l_other)->side_was_swapped == false) {
614 UV_SET_SIDE_AND_REMOVE_FROM_RAIL(l_other, !side);
615 }
616 }
617 }
618 }
619
620 /* Add the next loop along the edge on the same side. */
621 l_other = l_step->next;
622 if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) {
623 BLI_SMALLSTACK_PUSH(stack, l_other);
624 UL(l_other)->in_stack = true;
625 UL(l_other)->side = side;
626 }
627 else {
628 if (UL(l_other)->side != side) {
629 if ((UL(l_other)->side_was_swapped == false) &&
630 uv_rip_pairs_loop_change_sides_test(l_other, l_step, aspect_y, cd_loop_uv_offset))
631 {
633 }
634 }
635 }
636 }
637
638 /* Walk over the fan of loops, starting from `l_step` in both directions. */
639 for (int i = 0; i < 2; i++) {
640 BMLoop *l_radial_first = i ? l_step : l_step->prev;
641 if (l_radial_first != l_radial_first->radial_next) {
642 BMEdge *e_radial = l_radial_first->e;
643 BMLoop *l_radial_iter = l_radial_first->radial_next;
644 do {
645 /* Not a boundary and visible. */
646 if (!UL(l_radial_iter)->is_select_edge &&
647 BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG))
648 {
649 BMLoop *l_other = (l_radial_iter->v == l_step->v) ? l_radial_iter :
650 l_radial_iter->next;
651 BLI_assert(l_other->v == l_step->v);
652 if (BM_edge_uv_share_vert_check(e_radial, l_other, l_step, cd_loop_uv_offset)) {
653 if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) {
654 BLI_SMALLSTACK_PUSH(stack, l_other);
655 UL(l_other)->in_stack = true;
656 UL(l_other)->side = side;
657 }
658 else {
659 if (UL(l_other)->side != side) {
660 if ((UL(l_other)->side_was_swapped == false) &&
662 l_other, l_step, aspect_y, cd_loop_uv_offset))
663 {
665 }
666 }
667 }
668 }
669 }
670 } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first);
671 }
672 }
673 }
674
675#undef UV_SET_SIDE_AND_REMOVE_FROM_RAIL
676
677 return rip;
678}
679
681{
682 BLI_gset_free(rip->loops, nullptr);
683 MEM_freeN(rip);
684}
685
690 const int cd_loop_uv_offset,
691 float r_center[2],
692 float r_dir_side[2][2])
693{
694 zero_v2(r_center);
695 int center_total = 0;
696 int side_total[2] = {0, 0};
697
698 for (int i = 0; i < 2; i++) {
699 zero_v2(r_dir_side[i]);
700 }
701 GSetIterator gs_iter;
702 GSET_ITER (gs_iter, rip->loops) {
703 BMLoop *l = static_cast<BMLoop *>(BLI_gsetIterator_getKey(&gs_iter));
704 int side = UL(l)->side;
705 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
706 add_v2_v2(r_center, luv);
707
708 float dir[2];
709 if (!UL(l)->is_select_edge) {
710 const float *luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
711 sub_v2_v2v2(dir, luv_next, luv);
712 add_v2_v2(r_dir_side[side], dir);
713 }
714 if (!UL(l->prev)->is_select_edge) {
715 const float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l->prev, cd_loop_uv_offset);
716 sub_v2_v2v2(dir, luv_prev, luv);
717 add_v2_v2(r_dir_side[side], dir);
718 }
719 side_total[side] += 1;
720 }
721 center_total += BLI_gset_len(rip->loops);
722
723 for (int i = 0; i < 2; i++) {
724 normalize_v2(r_dir_side[i]);
725 }
726 mul_v2_fl(r_center, 1.0f / center_total);
727
728 /* If only a single side is selected, don't handle this rip-pairs. */
729 return side_total[0] && side_total[1];
730}
731
734/* -------------------------------------------------------------------- */
741static bool uv_rip_object(Scene *scene, Object *obedit, const float co[2], const float aspect_y)
742{
743 Mesh *mesh = (Mesh *)obedit->data;
744 BMEditMesh *em = mesh->runtime->edit_mesh.get();
745 BMesh *bm = em->bm;
746 const char *active_uv_name = CustomData_get_active_layer_name(&bm->ldata, CD_PROP_FLOAT2);
747 BM_uv_map_ensure_vert_select_attr(bm, active_uv_name);
748 BM_uv_map_ensure_edge_select_attr(bm, active_uv_name);
749 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
750
751 BMFace *efa;
752 BMIter iter, liter;
753 BMLoop *l;
754
755 const ULData ul_clear = {0};
756
757 bool changed = false;
758
759 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
761 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
762 ULData *ul = UL(l);
763 *ul = ul_clear;
764 }
765 }
767
768 bool is_select_all_any = false;
769 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
770 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
771 bool is_all = true;
772 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
773 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert)) {
774 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_edge)) {
775 UL(l)->is_select_edge = true;
776 }
777 else if (!BM_ELEM_CD_GET_BOOL(l->prev, offsets.select_edge)) {
778 /* #bm_loop_uv_select_single_vert_validate validates below. */
779 UL(l)->is_select_vert_single = true;
780 is_all = false;
781 }
782 else {
783 /* Cases where all vertices of a face are selected but not all edges are selected. */
784 is_all = false;
785 }
786 }
787 else {
788 is_all = false;
789 }
790 }
791 if (is_all) {
792 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
793 UL(l)->is_select_all = true;
794 }
795 is_select_all_any = true;
796 }
797 }
798 }
799
800 /* Remove #ULData.is_select_vert_single when connected to selected edges. */
801 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
802 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
803 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
804 if (UL(l)->is_select_vert_single) {
806 }
807 }
808 }
809 }
810
811 /* Special case: if we have selected faces, isolate them.
812 * This isn't a rip, however it's useful for users as a quick way
813 * to detach the selection.
814 *
815 * We could also extract an edge loop from the boundary
816 * however in practice it's not that useful, see #78751. */
817 if (is_select_all_any) {
818 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
819 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
820 if (!UL(l)->is_select_all) {
821 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert)) {
822 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
823 changed = true;
824 }
825 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_edge)) {
826 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
827 changed = true;
828 }
829 }
830 }
831 }
832 return changed;
833 }
834
835 /* Extract loop pairs or single loops. */
836 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
837 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
838 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
839 if (UL(l)->is_select_edge) {
840 if (!UL(l)->in_rip_pairs) {
841 UVRipPairs *rip = uv_rip_pairs_from_loop(l, aspect_y, offsets.uv);
842 float center[2];
843 float dir_cursor[2];
844 float dir_side[2][2];
845 int side_from_cursor = -1;
846 if (uv_rip_pairs_calc_center_and_direction(rip, offsets.uv, center, dir_side)) {
847 for (int i = 0; i < 2; i++) {
848 sub_v2_v2v2(dir_cursor, center, co);
849 normalize_v2(dir_cursor);
850 }
851 side_from_cursor = (dot_v2v2(dir_side[0], dir_cursor) -
852 dot_v2v2(dir_side[1], dir_cursor)) < 0.0f;
853 }
854 GSetIterator gs_iter;
855 GSET_ITER (gs_iter, rip->loops) {
856 BMLoop *l_iter = static_cast<BMLoop *>(BLI_gsetIterator_getKey(&gs_iter));
857 ULData *ul = UL(l_iter);
858 if (ul->side == side_from_cursor) {
859 uvedit_uv_select_disable(scene, bm, l_iter, offsets);
860 changed = true;
861 }
862 /* Ensure we don't operate on these again. */
863 *ul = ul_clear;
864 }
866 }
867 }
868 else if (UL(l)->is_select_vert_single) {
869 UVRipSingle *rip = uv_rip_single_from_loop(l, co, aspect_y, offsets.uv);
870 /* We only ever use one side. */
871 const int side_from_cursor = 0;
872 GSetIterator gs_iter;
873 GSET_ITER (gs_iter, rip->loops) {
874 BMLoop *l_iter = static_cast<BMLoop *>(BLI_gsetIterator_getKey(&gs_iter));
875 ULData *ul = UL(l_iter);
876 if (ul->side == side_from_cursor) {
877 uvedit_uv_select_disable(scene, bm, l_iter, offsets);
878 changed = true;
879 }
880 /* Ensure we don't operate on these again. */
881 *ul = ul_clear;
882 }
884 }
885 }
886 }
887 }
888 if (changed) {
889 uvedit_deselect_flush(scene, em);
890 }
891 return changed;
892}
893
896/* -------------------------------------------------------------------- */
900static int uv_rip_exec(bContext *C, wmOperator *op)
901{
903 Scene *scene = CTX_data_scene(C);
904 ViewLayer *view_layer = CTX_data_view_layer(C);
905
906 bool changed_multi = false;
907
908 float co[2];
909 RNA_float_get_array(op->ptr, "location", co);
910
911 float aspx, aspy;
912 {
913 /* Note that we only want to run this on the. */
914 Object *obedit = CTX_data_edit_object(C);
915 ED_uvedit_get_aspect(obedit, &aspx, &aspy);
916 }
917 const float aspect_y = aspx / aspy;
918
920 scene, view_layer, nullptr);
921
922 for (Object *obedit : objects) {
923 if (uv_rip_object(scene, obedit, co, aspect_y)) {
924 changed_multi = true;
925 uvedit_live_unwrap_update(sima, scene, obedit);
926 DEG_id_tag_update(static_cast<ID *>(obedit->data), 0);
927 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
928 }
929 }
930
931 if (!changed_multi) {
932 BKE_report(op->reports, RPT_ERROR, "Rip failed");
933 return OPERATOR_CANCELLED;
934 }
935 return OPERATOR_FINISHED;
936}
937
938static int uv_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
939{
940 ARegion *region = CTX_wm_region(C);
941 float co[2];
942
943 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
944 RNA_float_set_array(op->ptr, "location", co);
945
946 return uv_rip_exec(C, op);
947}
948
950{
951 /* identifiers */
952 ot->name = "UV Rip";
953 ot->description = "Rip selected vertices or a selected region";
954 ot->idname = "UV_OT_rip";
956
957 /* api callbacks */
961
962 /* translation data */
964
965 /* properties */
967 ot->srna,
968 "location",
969 2,
970 nullptr,
971 -FLT_MAX,
972 FLT_MAX,
973 "Location",
974 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
975 -100.0f,
976 100.0f);
977}
978
SpaceImage * CTX_wm_space_image(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:87
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_INLINE
struct GSet GSet
Definition BLI_ghash.h:341
GSet * BLI_gset_ptr_new(const char *info)
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:1004
unsigned int BLI_gset_len(const GSet *gs) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:954
BLI_INLINE void * BLI_gsetIterator_getKey(GSetIterator *gsi)
Definition BLI_ghash.h:459
#define GSET_ITER(gs_iter_, gset_)
Definition BLI_ghash.h:472
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1034
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.c:966
bool BLI_gset_remove(GSet *gs, const void *key, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:999
MINLINE float min_ff(float a, float b)
float angle_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void negate_v2(float r[2])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void zero_v2(float r[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float normalize_v2(float n[2])
unsigned int uint
#define UNLIKELY(x)
#define LIKELY(x)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ CD_PROP_FLOAT2
Object is a sort of wrapper for general info.
bool ED_operator_uvedit(bContext *C)
void Transform_Properties(wmOperatorType *ot, int flags)
#define P_MIRROR_DUMMY
void uvedit_deselect_flush(const Scene *scene, BMEditMesh *em)
void ED_uvedit_get_aspect(Object *obedit, float *r_aspx, float *r_aspy)
bool uvedit_face_visible_test(const Scene *scene, BMFace *efa)
void uvedit_uv_select_disable(const Scene *scene, BMesh *bm, BMLoop *l, BMUVOffsets offsets)
Read Guarded memory(de)allocation.
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
#define BM_ELEM_CD_GET_BOOL(ele, offset)
@ BM_LOOP
@ BM_ELEM_TAG
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
#define BM_ELEM_CD_SET_BOOL(ele, offset, f)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_flag_test(ele, hflag)
void BM_uv_map_ensure_vert_select_attr(BMesh *bm, const char *uv_map_name)
void BM_uv_map_ensure_edge_select_attr(BMesh *bm, const char *uv_map_name)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_FACE
ATTR_WARN_UNUSED_RESULT BMesh * bm
BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
bool BM_edge_uv_share_vert_check(BMEdge *e, BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
BMUVOffsets BM_uv_map_get_offsets(const BMesh *bm)
bool BM_loop_uv_share_edge_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
float BM_face_uv_calc_cross(const BMFace *f, const int cd_loop_uv_offset)
#define fabsf(x)
int count
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
PropertyRNA * RNA_def_float_vector(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
#define FLT_MAX
Definition stdcycles.h:14
BMHeader head
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
char elem_index_dirty
CustomData ldata
Definition DNA_ID.h:413
uint side_was_swapped
Definition uvedit_rip.cc:86
uint in_rip_pairs
Definition uvedit_rip.cc:78
uint is_select_vert_single
Definition uvedit_rip.cc:72
uint in_stack
Definition uvedit_rip.cc:76
uint is_select_edge
Definition uvedit_rip.cc:59
uint side
Definition uvedit_rip.cc:80
uint is_select_all
Definition uvedit_rip.cc:74
GSet * loops
int mval[2]
Definition WM_types.hh:728
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit)
static int uv_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static BMLoop * bm_loop_find_other_fan_loop_with_visible_face(BMLoop *l_src, BMVert *v_src, const int cd_loop_uv_offset)
#define UV_SET_SIDE_AND_REMOVE_FROM_RAIL(loop, side_value)
static void bm_loop_uv_select_single_vert_validate(BMLoop *l_init, const int cd_loop_uv_offset)
static void uv_rip_pairs_free(UVRipPairs *rip)
BLI_INLINE ULData * UL(BMLoop *l)
Definition uvedit_rip.cc:92
static void uv_rip_single_free(UVRipSingle *rip)
static int uv_rip_exec(bContext *C, wmOperator *op)
static void uv_rip_pairs_remove(UVRipPairs *rip, BMLoop *l)
static void uv_rip_pairs_add(UVRipPairs *rip, BMLoop *l)
static int uv_rip_pairs_loop_count_on_side(BMLoop *l_init, uint side, const int cd_loop_uv_offset)
static UVRipSingle * uv_rip_single_from_loop(BMLoop *l_init_orig, const float co[2], const float aspect_y, const int cd_loop_uv_offset)
static bool uv_rip_object(Scene *scene, Object *obedit, const float co[2], const float aspect_y)
static BMLoop * bm_loop_find_other_radial_loop_with_visible_face(BMLoop *l_src, const int cd_loop_uv_offset)
void UV_OT_rip(wmOperatorType *ot)
static UVRipPairs * uv_rip_pairs_from_loop(BMLoop *l_init, const float aspect_y, const int cd_loop_uv_offset)
static float uv_rip_pairs_calc_uv_angle(BMLoop *l_init, uint side, const float aspect_y, const int cd_loop_uv_offset)
static bool uv_rip_pairs_calc_center_and_direction(UVRipPairs *rip, const int cd_loop_uv_offset, float r_center[2], float r_dir_side[2][2])
static bool uv_rip_pairs_loop_change_sides_test(BMLoop *l_switch, BMLoop *l_target, const float aspect_y, const int cd_loop_uv_offset)
static void bm_loop_calc_uv_angle_from_dir(BMLoop *l, const float dir[2], const float aspect_y, const int cd_loop_uv_offset, float *r_corner_angle, float *r_edge_angle, int *r_edge_index)
static BMLoop * bm_vert_step_fan_loop_uv(BMLoop *l, BMEdge **e_step, const int cd_loop_uv_offset)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125