Blender V5.0
uvedit_path.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
10
11#include <cstdlib>
12#include <cstring>
13
14#include "BLI_linklist.h"
15#include "BLI_math_vector.h"
16#include "BLI_utildefines.h"
17
18#include "MEM_guardedalloc.h"
19
20#include "DNA_mesh_types.h"
21#include "DNA_object_types.h"
22#include "DNA_scene_types.h"
24
25#include "BKE_context.hh"
26#include "BKE_customdata.hh"
27#include "BKE_editmesh.hh"
28#include "BKE_layer.hh"
29#include "BKE_mesh.hh"
30#include "BKE_report.hh"
31
32#include "DEG_depsgraph.hh"
34
35#include "ED_object.hh"
36#include "ED_screen.hh"
37#include "ED_uvedit.hh"
38
39#include "RNA_access.hh"
40#include "RNA_define.hh"
41
42#include "WM_api.hh"
43#include "WM_types.hh"
44
45#include "UI_view2d.hh"
46
48#include "uvedit_intern.hh"
49
50#include "bmesh_tools.hh"
51
52using blender::Vector;
53
54/* -------------------------------------------------------------------- */
57
58namespace {
59
60struct PathSelectParams {
62 bool track_active;
64 bool use_face_step;
65 bool use_fill;
66 CheckerIntervalParams interval_params;
67};
68
69struct UserData_UV {
70 Scene *scene;
71 BMesh *bm;
72 BMUVOffsets offsets;
73};
74
75} // namespace
76
78{
79 RNA_def_boolean(ot->srna,
80 "use_face_step",
81 false,
82 "Face Stepping",
83 "Traverse connected faces (includes diagonals and edge-rings)");
84 RNA_def_boolean(ot->srna,
85 "use_topology_distance",
86 false,
87 "Topology Distance",
88 "Find the minimum number of steps, ignoring spatial distance");
89 RNA_def_boolean(ot->srna,
90 "use_fill",
91 false,
92 "Fill Region",
93 "Select all paths between the source/destination elements");
94
96}
97
99{
100 op_params->track_active = false;
101 op_params->use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
102 op_params->use_fill = RNA_boolean_get(op->ptr, "use_fill");
103 op_params->use_topology_distance = RNA_boolean_get(op->ptr, "use_topology_distance");
105}
106
108
109/* -------------------------------------------------------------------- */
112
113/* callbacks */
114static bool verttag_filter_cb(BMLoop *l, void *user_data_v)
115{
116 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
117 return uvedit_face_visible_test(user_data->scene, l->f);
118}
119static bool verttag_test_cb(BMLoop *l, void *user_data_v)
120{
121 /* All connected loops are selected or we return false. */
122 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
123 const Scene *scene = user_data->scene;
124 const int cd_loop_uv_offset = user_data->offsets.uv;
125 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
126 BMIter iter;
127 BMLoop *l_iter;
128 BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
129 if (verttag_filter_cb(l_iter, user_data)) {
130 const float *luv_iter = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
131 if (equals_v2v2(luv, luv_iter)) {
132 if (!uvedit_uv_select_test(scene, user_data->bm, l_iter, user_data->offsets)) {
133 return false;
134 }
135 }
136 }
137 }
138 return true;
139}
140static void verttag_set_cb(BMLoop *l, bool val, void *user_data_v)
141{
142 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
143 const Scene *scene = user_data->scene;
144 BMesh *bm = user_data->bm;
145 const uint cd_loop_uv_offset = user_data->offsets.uv;
146 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
147 BMIter iter;
148 BMLoop *l_iter;
149 BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
150 if (verttag_filter_cb(l_iter, user_data)) {
151 const float *luv_iter = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
152 if (equals_v2v2(luv, luv_iter)) {
153 uvedit_uv_select_set(scene, bm, l_iter, val);
154 }
155 }
156 }
157}
158
160 Object *obedit,
161 const PathSelectParams *op_params,
162 BMLoop *l_src,
163 BMLoop *l_dst,
164 const float aspect_y,
165 const BMUVOffsets &offsets)
166{
168 BMesh *bm = em->bm;
169 int flush = 0;
170
171 UserData_UV user_data = {};
172 user_data.scene = scene;
173 user_data.bm = bm;
174 user_data.offsets = offsets;
175
177 params.use_topology_distance = op_params->use_topology_distance;
178 params.use_step_face = op_params->use_face_step;
179 params.aspect_y = aspect_y;
180 params.cd_loop_uv_offset = offsets.uv;
181
182 LinkNode *path = nullptr;
183 bool is_path_ordered = false;
184
185 if (l_src != l_dst) {
186 if (op_params->use_fill) {
188 (BMElem *)l_src,
189 (BMElem *)l_dst,
190 params.cd_loop_uv_offset,
192 &user_data);
193 }
194 else {
195 is_path_ordered = true;
196 path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, &params, verttag_filter_cb, &user_data);
197 }
198 }
199
200 BMLoop *l_dst_last = l_dst;
201
202 if (path) {
203 /* toggle the flag */
204 bool all_set = true;
205 LinkNode *node = path;
206 do {
207 if (!verttag_test_cb((BMLoop *)node->link, &user_data)) {
208 all_set = false;
209 break;
210 }
211 } while ((node = node->next));
212
213 int depth = -1;
214 node = path;
215 do {
216 if ((is_path_ordered == false) ||
218 {
219 verttag_set_cb((BMLoop *)node->link, !all_set, &user_data);
220 if (is_path_ordered) {
221 l_dst_last = static_cast<BMLoop *>(node->link);
222 }
223 }
224 } while ((void)depth++, (node = node->next));
225
226 BLI_linklist_free(path, nullptr);
227 flush = all_set ? -1 : 1;
228 }
229 else {
230 const bool is_act = !verttag_test_cb(l_dst, &user_data);
231 verttag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
232 }
233
234 if (op_params->track_active) {
236 }
237 return flush;
238}
239
241
242/* -------------------------------------------------------------------- */
245
246/* callbacks */
247static bool edgetag_filter_cb(BMLoop *l, void *user_data_v)
248{
249 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
250 return uvedit_face_visible_test(user_data->scene, l->f);
251}
252static bool edgetag_test_cb(BMLoop *l, void *user_data_v)
253{
254 /* All connected loops (UV) are selected or we return false. */
255 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
256 const Scene *scene = user_data->scene;
257 BMIter iter;
258 BMLoop *l_iter;
259 BM_ITER_ELEM (l_iter, &iter, l->e, BM_LOOPS_OF_EDGE) {
260 if (edgetag_filter_cb(l_iter, user_data)) {
261 if (BM_loop_uv_share_edge_check(l, l_iter, user_data->offsets.uv)) {
262 if (!uvedit_edge_select_test(scene, user_data->bm, l_iter, user_data->offsets)) {
263 return false;
264 }
265 }
266 }
267 }
268 return true;
269}
270static void edgetag_set_cb(BMLoop *l, bool val, void *user_data_v)
271{
272 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
273 const Scene *scene = user_data->scene;
274 BMesh *bm = user_data->bm;
275 uvedit_edge_select_set_with_sticky(scene, bm, l, val, user_data->offsets);
276}
277
279 Object *obedit,
280 const PathSelectParams *op_params,
281 BMLoop *l_src,
282 BMLoop *l_dst,
283 const float aspect_y,
284 const BMUVOffsets &offsets)
285{
287 BMesh *bm = em->bm;
288 int flush = 0;
289
290 UserData_UV user_data = {};
291 user_data.scene = scene;
292 user_data.bm = bm;
293 user_data.offsets = offsets;
294
296 params.use_topology_distance = op_params->use_topology_distance;
297 params.use_step_face = op_params->use_face_step;
298 params.aspect_y = aspect_y;
299 params.cd_loop_uv_offset = offsets.uv;
300
301 LinkNode *path = nullptr;
302 bool is_path_ordered = false;
303
304 if (l_src != l_dst) {
305 if (op_params->use_fill) {
307 (BMElem *)l_src,
308 (BMElem *)l_dst,
309 params.cd_loop_uv_offset,
311 &user_data);
312 }
313 else {
314 is_path_ordered = true;
315 path = BM_mesh_calc_path_uv_edge(bm, l_src, l_dst, &params, edgetag_filter_cb, &user_data);
316 }
317 }
318
319 BMLoop *l_dst_last = l_dst;
320
321 if (path) {
322 /* toggle the flag */
323 bool all_set = true;
324 LinkNode *node = path;
325 do {
326 if (!edgetag_test_cb((BMLoop *)node->link, &user_data)) {
327 all_set = false;
328 break;
329 }
330 } while ((node = node->next));
331
332 int depth = -1;
333 node = path;
334 do {
335 if ((is_path_ordered == false) ||
337 {
338 edgetag_set_cb((BMLoop *)node->link, !all_set, &user_data);
339 if (is_path_ordered) {
340 l_dst_last = static_cast<BMLoop *>(node->link);
341 }
342 }
343 } while ((void)depth++, (node = node->next));
344
345 BLI_linklist_free(path, nullptr);
346 flush = all_set ? -1 : 1;
347 }
348 else {
349 const bool is_act = !edgetag_test_cb(l_dst, &user_data);
350 edgetag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
351 }
352
353 if (op_params->track_active) {
355 }
356 return flush;
357}
358
360
361/* -------------------------------------------------------------------- */
364
365/* callbacks */
366static bool facetag_filter_cb(BMFace *f, void *user_data_v)
367{
368 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
369 return uvedit_face_visible_test(user_data->scene, f);
370}
371static bool facetag_test_cb(BMFace *f, void *user_data_v)
372{
373 /* All connected loops are selected or we return false. */
374 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
375 const Scene *scene = user_data->scene;
376 BMIter iter;
377 BMLoop *l_iter;
378 BM_ITER_ELEM (l_iter, &iter, f, BM_LOOPS_OF_FACE) {
379 if (!uvedit_edge_select_test(scene, user_data->bm, l_iter, user_data->offsets)) {
380 return false;
381 }
382 }
383 return true;
384}
385static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
386{
387 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
388 const Scene *scene = user_data->scene;
389 BMesh *bm = user_data->bm;
390 uvedit_face_select_set_with_sticky(scene, bm, f, val, user_data->offsets);
391}
392
394 Object *obedit,
395 const PathSelectParams *op_params,
396 BMFace *f_src,
397 BMFace *f_dst,
398 const float aspect_y,
399 const BMUVOffsets &offsets)
400{
402 BMesh *bm = em->bm;
403 int flush = 0;
404
405 UserData_UV user_data = {};
406 user_data.scene = scene;
407 user_data.bm = bm;
408 user_data.offsets = offsets;
409
411 params.use_topology_distance = op_params->use_topology_distance;
412 params.use_step_face = op_params->use_face_step;
413 params.aspect_y = aspect_y;
414 params.cd_loop_uv_offset = offsets.uv;
415
416 LinkNode *path = nullptr;
417 bool is_path_ordered = false;
418
419 if (f_src != f_dst) {
420 if (op_params->use_fill) {
422 (BMElem *)f_src,
423 (BMElem *)f_dst,
424 params.cd_loop_uv_offset,
426 &user_data);
427 }
428 else {
429 is_path_ordered = true;
430 path = BM_mesh_calc_path_uv_face(bm, f_src, f_dst, &params, facetag_filter_cb, &user_data);
431 }
432 }
433
434 BMFace *f_dst_last = f_dst;
435
436 if (path) {
437 /* toggle the flag */
438 bool all_set = true;
439 LinkNode *node = path;
440 do {
441 if (!facetag_test_cb((BMFace *)node->link, &user_data)) {
442 all_set = false;
443 break;
444 }
445 } while ((node = node->next));
446
447 int depth = -1;
448 node = path;
449 do {
450 if ((is_path_ordered == false) ||
452 {
453 facetag_set_cb((BMFace *)node->link, !all_set, &user_data);
454 if (is_path_ordered) {
455 f_dst_last = static_cast<BMFace *>(node->link);
456 }
457 }
458 } while ((void)depth++, (node = node->next));
459
460 BLI_linklist_free(path, nullptr);
461 flush = all_set ? -1 : 1;
462 }
463 else {
464 const bool is_act = !facetag_test_cb(f_dst, &user_data);
465 facetag_set_cb(f_dst, is_act, &user_data); /* switch the face option */
466 }
467
468 if (op_params->track_active) {
469 /* Unlike other types, we can track active without it being selected. */
470 BM_mesh_active_face_set(bm, f_dst_last);
471 }
472 return flush;
473}
474
476
477/* -------------------------------------------------------------------- */
480
482
484 Depsgraph *depsgraph,
485 Object *obedit,
486 const PathSelectParams *op_params,
487 BMElem *ele_src,
488 BMElem *ele_dst,
489 const float aspect_y,
490 const BMUVOffsets &offsets)
491{
492 const ToolSettings *ts = scene->toolsettings;
493 const char uv_selectmode = ED_uvedit_select_mode_get(scene);
494 bool ok = false;
495 int flush = 0;
496
497 if (ELEM(nullptr, ele_src, ele_dst) || (ele_src->head.htype != ele_dst->head.htype)) {
498 /* pass */
499 }
500 else if (ele_src->head.htype == BM_FACE) {
502 scene, obedit, op_params, (BMFace *)ele_src, (BMFace *)ele_dst, aspect_y, offsets);
503 ok = true;
504 }
505 else if (ele_src->head.htype == BM_LOOP) {
506 if (uv_selectmode & UV_SELECT_EDGE) {
508 scene, obedit, op_params, (BMLoop *)ele_src, (BMLoop *)ele_dst, aspect_y, offsets);
509 }
510 else {
512 scene, obedit, op_params, (BMLoop *)ele_src, (BMLoop *)ele_dst, aspect_y, offsets);
513 }
514 ok = true;
515 }
516
517 if (ok) {
518 if (flush != 0) {
519 const bool select = (flush == 1);
521 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
523 }
524 else {
526 }
527 }
528
529 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
530 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
531 }
532 else {
533 Object *obedit_eval = DEG_get_evaluated(depsgraph, obedit);
534 BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(obedit_eval->data),
536 }
537 /* Only for region redraw. */
539 }
540
541 return ok;
542}
543
545 wmOperator *op,
546 const wmEvent *event)
547{
548 Scene *scene = CTX_data_scene(C);
549 const ToolSettings *ts = scene->toolsettings;
550 const char uv_selectmode = ED_uvedit_select_mode_get(scene);
551
552 /* We could support this, it needs further testing. */
553 if (RNA_struct_property_is_set(op->ptr, "index")) {
554 return uv_shortest_path_pick_exec(C, op);
555 }
556
557 PathSelectParams op_params;
558 path_select_params_from_op(op, &op_params);
559
560 /* Set false if we support edge tagging. */
561 op_params.track_active = true;
562
564 ViewLayer *view_layer = CTX_data_view_layer(C);
566 scene, view_layer, nullptr);
567
568 float co[2];
569
570 const ARegion *region = CTX_wm_region(C);
571
572 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
573
574 BMElem *ele_src = nullptr, *ele_dst = nullptr;
575
576 /* Detect the hit. */
578 bool hit_found = false;
579 if (uv_selectmode == UV_SELECT_FACE) {
580 if (uv_find_nearest_face_multi(scene, objects, co, &hit)) {
581 hit_found = true;
582 }
583 }
584 else if (uv_selectmode & UV_SELECT_EDGE) {
585 if (uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit)) {
586 hit_found = true;
587 }
588 }
589 else {
590 if (uv_find_nearest_vert_multi(scene, objects, co, 0.0f, &hit)) {
591 hit_found = true;
592 }
593 }
594
595 bool changed = false;
596 if (hit_found) {
597 /* This may not be the active object. */
598 Object *obedit = hit.ob;
600 BMesh *bm = em->bm;
601 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
602
603 /* Respond to the hit. */
604 if (uv_selectmode == UV_SELECT_FACE) {
605 /* Face selection. */
606 BMFace *f_src = BM_mesh_active_face_get(bm, false, false);
607 /* Check selection? */
608 ele_src = (BMElem *)f_src;
609 ele_dst = (BMElem *)hit.efa;
610 }
611 else if (uv_selectmode & UV_SELECT_EDGE) {
612 /* Edge selection. */
613 BMLoop *l_src = nullptr;
614 if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) && (bm->uv_select_sync_valid == false)) {
616 if (e_src != nullptr) {
617 l_src = uv_find_nearest_loop_from_edge(scene, obedit, e_src, co);
618 }
619 }
620 else {
622 if (l_src != nullptr) {
623 if (!uvedit_uv_select_test(scene, bm, l_src, offsets) &&
624 !uvedit_uv_select_test(scene, bm, l_src->next, offsets))
625 {
626 l_src = nullptr;
627 }
628 ele_src = (BMElem *)l_src;
629 }
630 }
631 ele_src = (BMElem *)l_src;
632 ele_dst = (BMElem *)hit.l;
633 }
634 else {
635 /* Vertex selection. */
636 BMLoop *l_src = nullptr;
637 if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) && (bm->uv_select_sync_valid == false)) {
639 if (v_src != nullptr) {
640 l_src = uv_find_nearest_loop_from_vert(scene, obedit, v_src, co);
641 }
642 }
643 else {
645 if (l_src != nullptr) {
646 if (!uvedit_uv_select_test(scene, bm, l_src, offsets)) {
647 l_src = nullptr;
648 }
649 }
650 }
651 ele_src = (BMElem *)l_src;
652 ele_dst = (BMElem *)hit.l;
653 }
654
655 if (ele_src && ele_dst) {
656 /* Always use the active object, not `obedit` as the active defines the UV display. */
657 const float aspect_y = ED_uvedit_get_aspect_y(CTX_data_edit_object(C));
659 scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, offsets);
660
661 /* Store the object and it's index so redo is possible. */
662 int index;
663 if (uv_selectmode & UV_SELECT_FACE) {
665 index = BM_elem_index_get(ele_dst);
666 }
667 else if (uv_selectmode & UV_SELECT_EDGE) {
669 index = BM_elem_index_get(ele_dst);
670 }
671 else {
673 index = BM_elem_index_get(ele_dst);
674 }
675
676 const int object_index = blender::ed::object::object_in_mode_to_index(
677 scene, view_layer, OB_MODE_EDIT, obedit);
678 BLI_assert(object_index != -1);
679 RNA_int_set(op->ptr, "object_index", object_index);
680 RNA_int_set(op->ptr, "index", index);
681 changed = true;
682 }
683 }
684
685 return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
686}
687
689{
691 Scene *scene = CTX_data_scene(C);
692 const ToolSettings *ts = scene->toolsettings;
693 ViewLayer *view_layer = CTX_data_view_layer(C);
694 const char uv_selectmode = ED_uvedit_select_mode_get(scene);
695
696 const int object_index = RNA_int_get(op->ptr, "object_index");
697 const int index = RNA_int_get(op->ptr, "index");
698 if (object_index == -1) {
699 return OPERATOR_CANCELLED;
700 }
701
703 scene, view_layer, OB_MODE_EDIT, object_index);
704 if (obedit == nullptr) {
705 return OPERATOR_CANCELLED;
706 }
707
709 BMesh *bm = em->bm;
710 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
711
712 BMElem *ele_src, *ele_dst;
713
714 /* NOLINTBEGIN: bugprone-assignment-in-if-condition */
715 if (uv_selectmode & UV_SELECT_FACE) {
716 if (index < 0 || index >= bm->totface) {
717 return OPERATOR_CANCELLED;
718 }
719 if (!(ele_src = (BMElem *)BM_mesh_active_face_get(bm, false, false)) ||
720 !(ele_dst = (BMElem *)BM_face_at_index_find_or_table(bm, index)))
721 {
722 return OPERATOR_CANCELLED;
723 }
724 }
725 else if (uv_selectmode & UV_SELECT_EDGE) {
726 if (index < 0 || index >= bm->totloop) {
727 return OPERATOR_CANCELLED;
728 }
729 if (!(ele_src = (BMElem *)ED_uvedit_active_edge_loop_get(ts, bm)) ||
730 !(ele_dst = (BMElem *)BM_loop_at_index_find(bm, index)))
731 {
732 return OPERATOR_CANCELLED;
733 }
734 }
735 else {
736 if (index < 0 || index >= bm->totloop) {
737 return OPERATOR_CANCELLED;
738 }
739 if (!(ele_src = (BMElem *)ED_uvedit_active_vert_loop_get(ts, bm)) ||
740 !(ele_dst = (BMElem *)BM_loop_at_index_find(bm, index)))
741 {
742 return OPERATOR_CANCELLED;
743 }
744 }
745 /* NOLINTEND: bugprone-assignment-in-if-condition */
746
747 /* Always use the active object, not `obedit` as the active defines the UV display. */
748 const float aspect_y = ED_uvedit_get_aspect_y(CTX_data_edit_object(C));
749
750 PathSelectParams op_params;
751 path_select_params_from_op(op, &op_params);
752 op_params.track_active = true;
753
755 scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, offsets))
756 {
757 return OPERATOR_CANCELLED;
758 }
759
760 return OPERATOR_FINISHED;
761}
762
764{
765 PropertyRNA *prop;
766
767 /* identifiers */
768 ot->name = "Pick Shortest Path";
769 ot->idname = "UV_OT_shortest_path_pick";
770 ot->description = "Select shortest path between two selections";
771
772 /* API callbacks. */
776
777 /* flags */
779
780 /* properties */
782
783 /* use for redo */
784 prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
786 prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
788}
789
791
792/* -------------------------------------------------------------------- */
795
797{
799 Scene *scene = CTX_data_scene(C);
800 const char uv_selectmode = ED_uvedit_select_mode_get(scene);
801 bool found_valid_elements = false;
802
803 const float aspect_y = ED_uvedit_get_aspect_y(CTX_data_edit_object(C));
804
805 ViewLayer *view_layer = CTX_data_view_layer(C);
807 scene, view_layer, nullptr);
808 for (Object *obedit : objects) {
810
811 const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
812
813 BMElem *ele_src = nullptr, *ele_dst = nullptr;
814
815 /* Find 2x elements. */
816 {
817 BMElem **ele_array = nullptr;
818 int ele_array_len = 0;
819 if (uv_selectmode & UV_SELECT_FACE) {
820 ele_array = (BMElem **)ED_uvedit_selected_faces(scene, bm, 3, &ele_array_len);
821 }
822 else if (uv_selectmode & UV_SELECT_EDGE) {
823 ele_array = (BMElem **)ED_uvedit_selected_edges(scene, bm, 3, &ele_array_len);
824 }
825 else {
826 ele_array = (BMElem **)ED_uvedit_selected_verts(scene, bm, 3, &ele_array_len);
827 }
828
829 if (ele_array_len == 2) {
830 ele_src = ele_array[0];
831 ele_dst = ele_array[1];
832 }
833 MEM_freeN(ele_array);
834 }
835
836 if (ele_src && ele_dst) {
837 PathSelectParams op_params;
838 path_select_params_from_op(op, &op_params);
839
841 scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, offsets);
842
843 found_valid_elements = true;
844 }
845 }
846
847 if (!found_valid_elements) {
849 op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected");
850 return OPERATOR_CANCELLED;
851 }
852
853 return OPERATOR_FINISHED;
854}
855
857{
858 /* identifiers */
859 ot->name = "Select Shortest Path";
860 ot->idname = "UV_OT_shortest_path_select";
861 ot->description = "Selected shortest path between two vertices/edges/faces";
862
863 /* API callbacks. */
866
867 /* flags */
869
870 /* properties */
872}
873
Depsgraph * CTX_data_ensure_evaluated_depsgraph(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.
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
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_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
@ BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT
Definition BKE_mesh.h:43
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
unsigned int uint
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ OB_MODE_EDIT
Object is a sort of wrapper for general info.
@ UV_SELECT_FACE
@ UV_SELECT_EDGE
struct Scene Scene
@ UV_FLAG_SELECT_SYNC
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
bool ED_operator_uvedit_space_image(bContext *C)
BMLoop * ED_uvedit_active_vert_loop_get(const ToolSettings *ts, BMesh *bm)
bool uvedit_edge_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
float ED_uvedit_get_aspect_y(Object *obedit)
void ED_uvedit_active_edge_loop_set(BMesh *bm, BMLoop *l)
void uvedit_face_select_set_with_sticky(const Scene *scene, BMesh *bm, BMFace *efa, bool select, const BMUVOffsets &offsets)
BMLoop ** ED_uvedit_selected_edges(const Scene *scene, BMesh *bm, int len_max, int *r_edges_len)
char ED_uvedit_select_mode_get(const Scene *scene)
void ED_uvedit_selectmode_flush(const Scene *scene, BMesh *bm)
UV Select Mode Flush.
bool uvedit_uv_select_test(const Scene *scene, const BMesh *bm, const BMLoop *l, const BMUVOffsets &offsets)
void uvedit_edge_select_set_with_sticky(const Scene *scene, BMesh *bm, BMLoop *l, bool select, const BMUVOffsets &offsets)
void uvedit_uv_select_set(const Scene *scene, BMesh *bm, BMLoop *l, bool select)
Select UV Vertex.
void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMesh *bm, bool select)
bool uvedit_face_visible_test(const Scene *scene, const BMFace *efa)
BMLoop * ED_uvedit_active_edge_loop_get(const ToolSettings *ts, BMesh *bm)
BMFace ** ED_uvedit_selected_faces(const Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
BMLoop ** ED_uvedit_selected_verts(const Scene *scene, BMesh *bm, int len_max, int *r_verts_len)
void ED_uvedit_active_vert_loop_set(BMesh *bm, BMLoop *l)
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
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:1668
#define NC_GEOM
Definition WM_types.hh:393
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SELECT
Definition WM_types.hh:508
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
@ BM_LOOP
#define BM_elem_index_get(ele)
#define BM_ITER_ELEM(ele, iter, data, itype)
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_EDGE
@ BM_LOOPS_OF_FACE
BMesh * bm
BMVert * BM_mesh_active_vert_get(BMesh *bm)
BMEdge * BM_mesh_active_edge_get(BMesh *bm)
BMFace * BM_mesh_active_face_get(BMesh *bm, const bool is_sloppy, const bool is_selected)
void BM_mesh_active_face_set(BMesh *bm, BMFace *f)
BMLoop * BM_loop_at_index_find(BMesh *bm, const int index)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMFace * BM_face_at_index_find_or_table(BMesh *bm, const int index)
#define BM_FACE
LinkNode * BM_mesh_calc_path_uv_region_vert(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const int cd_loop_uv_offset, bool(*filter_fn)(BMLoop *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_uv_region_edge(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const int cd_loop_uv_offset, bool(*filter_fn)(BMLoop *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_uv_region_face(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, const int cd_loop_uv_offset, bool(*filter_fn)(BMFace *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_uv_edge(BMesh *bm, BMLoop *l_src, BMLoop *l_dst, const BMCalcPathUVParams *params, bool(*filter_fn)(BMLoop *, void *), void *user_data)
LinkNode * BM_mesh_calc_path_uv_vert(BMesh *bm, BMLoop *l_src, BMLoop *l_dst, const BMCalcPathUVParams *params, bool(*filter_fn)(BMLoop *, void *), void *user_data)
LinkNode * BM_mesh_calc_path_uv_face(BMesh *bm, BMFace *f_src, BMFace *f_dst, const BMCalcPathUVParams *params, bool(*filter_fn)(BMFace *, void *), void *user_data)
ATTR_WARN_UNUSED_RESULT const BMLoop * l
BMUVOffsets BM_uv_map_offsets_get(const BMesh *bm)
bool BM_loop_uv_share_edge_check(const BMLoop *l_a, const BMLoop *l_b, const int cd_loop_uv_offset)
BPy_StructRNA * depsgraph
static bool verttag_filter_cb(BMVert *v, void *)
static bool facetag_filter_cb(BMFace *f, void *)
static bool edgetag_filter_cb(BMEdge *e, void *)
#define select(A, B, C)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
int object_in_mode_to_index(const Scene *scene, ViewLayer *view_layer, eObjectMode mode, const Object *ob)
Object * object_in_mode_from_index(const Scene *scene, ViewLayer *view_layer, eObjectMode mode, int index)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
int RNA_int_get(PointerRNA *ptr, const char *name)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
BMHeader head
struct BMLoop * next
Definition DNA_ID.h:414
void * link
struct LinkNode * next
CheckerIntervalParams interval_params
struct ToolSettings * toolsettings
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
bool uv_find_nearest_vert_multi(Scene *scene, blender::Span< Object * > objects, const float co[2], float penalty_dist, UvNearestHit *hit)
bool uv_find_nearest_face_multi(Scene *scene, blender::Span< Object * > objects, const float co[2], UvNearestHit *hit)
UvNearestHit uv_nearest_hit_init_max(const View2D *v2d)
BMLoop * uv_find_nearest_loop_from_edge(Scene *scene, Object *obedit, BMEdge *e, const float co[2])
BMLoop * uv_find_nearest_loop_from_vert(Scene *scene, Object *obedit, BMVert *v, const float co[2])
bool uv_find_nearest_edge_multi(Scene *scene, blender::Span< Object * > objects, const float co[2], float penalty, UvNearestHit *hit)
static int mouse_mesh_uv_shortest_path_vert(Scene *scene, Object *obedit, const PathSelectParams *op_params, BMLoop *l_src, BMLoop *l_dst, const float aspect_y, const BMUVOffsets &offsets)
static bool facetag_filter_cb(BMFace *f, void *user_data_v)
void UV_OT_shortest_path_pick(wmOperatorType *ot)
static bool uv_shortest_path_pick_ex(Scene *scene, Depsgraph *depsgraph, Object *obedit, const PathSelectParams *op_params, BMElem *ele_src, BMElem *ele_dst, const float aspect_y, const BMUVOffsets &offsets)
static bool facetag_test_cb(BMFace *f, void *user_data_v)
static bool edgetag_test_cb(BMLoop *l, void *user_data_v)
static void path_select_params_from_op(wmOperator *op, PathSelectParams *op_params)
static wmOperatorStatus uv_shortest_path_select_exec(bContext *C, wmOperator *op)
static bool verttag_test_cb(BMLoop *l, void *user_data_v)
static bool edgetag_filter_cb(BMLoop *l, void *user_data_v)
static int mouse_mesh_uv_shortest_path_edge(Scene *scene, Object *obedit, const PathSelectParams *op_params, BMLoop *l_src, BMLoop *l_dst, const float aspect_y, const BMUVOffsets &offsets)
static void verttag_set_cb(BMLoop *l, bool val, void *user_data_v)
static int mouse_mesh_uv_shortest_path_face(Scene *scene, Object *obedit, const PathSelectParams *op_params, BMFace *f_src, BMFace *f_dst, const float aspect_y, const BMUVOffsets &offsets)
static void path_select_properties(wmOperatorType *ot)
static wmOperatorStatus uv_shortest_path_pick_exec(bContext *C, wmOperator *op)
void UV_OT_shortest_path_select(wmOperatorType *ot)
static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
static void edgetag_set_cb(BMLoop *l, bool val, void *user_data_v)
static bool verttag_filter_cb(BMLoop *l, void *user_data_v)
static wmOperatorStatus uv_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_main_add_notifier(uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_operator_properties_checker_interval_from_op(wmOperator *op, CheckerIntervalParams *op_params)
void WM_operator_properties_checker_interval(wmOperatorType *ot, bool nth_can_disable)
bool WM_operator_properties_checker_interval_test(const CheckerIntervalParams *op_params, int depth)