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