Blender V4.3
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
11#include <cmath>
12#include <cstdlib>
13#include <cstring>
14
15#include "BLI_linklist.h"
17#include "MEM_guardedalloc.h"
18
19#include "BLI_ghash.h"
20#include "BLI_math_vector.h"
21#include "BLI_utildefines.h"
22
23#include "DNA_image_types.h"
24#include "DNA_mesh_types.h"
25#include "DNA_node_types.h"
26#include "DNA_object_types.h"
27#include "DNA_scene_types.h"
28#include "DNA_space_types.h"
29
30#include "BKE_context.hh"
31#include "BKE_customdata.hh"
32#include "BKE_editmesh.hh"
33#include "BKE_layer.hh"
34#include "BKE_mesh.hh"
35#include "BKE_report.hh"
36
37#include "DEG_depsgraph.hh"
39
40#include "ED_object.hh"
41#include "ED_screen.hh"
42#include "ED_uvedit.hh"
43
44#include "RNA_access.hh"
45#include "RNA_define.hh"
46
47#include "WM_api.hh"
48#include "WM_types.hh"
49
50#include "UI_view2d.hh"
51
53#include "uvedit_intern.hh"
54
55#include "bmesh_tools.hh"
56
57using blender::Vector;
58
59/* -------------------------------------------------------------------- */
63struct PathSelectParams {
65 bool track_active;
67 bool use_face_step;
68 bool use_fill;
70};
71
77
79{
81 "use_face_step",
82 false,
83 "Face Stepping",
84 "Traverse connected faces (includes diagonals and edge-rings)");
86 "use_topology_distance",
87 false,
88 "Topology Distance",
89 "Find the minimum number of steps, ignoring spatial distance");
91 "use_fill",
92 false,
93 "Fill Region",
94 "Select all paths between the source/destination elements");
95
97}
98
100{
101 op_params->track_active = false;
102 op_params->use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
103 op_params->use_fill = RNA_boolean_get(op->ptr, "use_fill");
104 op_params->use_topology_distance = RNA_boolean_get(op->ptr, "use_topology_distance");
106}
107
110/* -------------------------------------------------------------------- */
114/* callbacks */
115static bool verttag_filter_cb(BMLoop *l, void *user_data_v)
116{
117 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
118 return uvedit_face_visible_test(user_data->scene, l->f);
119}
120static bool verttag_test_cb(BMLoop *l, void *user_data_v)
121{
122 /* All connected loops are selected or we return false. */
123 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
124 const Scene *scene = user_data->scene;
125 const int cd_loop_uv_offset = user_data->offsets.uv;
126 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
127 BMIter iter;
128 BMLoop *l_iter;
129 BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
130 if (verttag_filter_cb(l_iter, user_data)) {
131 const float *luv_iter = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
132 if (equals_v2v2(luv, luv_iter)) {
133 if (!uvedit_uv_select_test(scene, l_iter, user_data->offsets)) {
134 return false;
135 }
136 }
137 }
138 }
139 return true;
140}
141static void verttag_set_cb(BMLoop *l, bool val, void *user_data_v)
142{
143 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
144 const Scene *scene = user_data->scene;
145 BMEditMesh *em = user_data->em;
146 const uint cd_loop_uv_offset = user_data->offsets.uv;
147 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
148 BMIter iter;
149 BMLoop *l_iter;
150 BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
151 if (verttag_filter_cb(l_iter, user_data)) {
152 const float *luv_iter = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
153 if (equals_v2v2(luv, luv_iter)) {
154 uvedit_uv_select_set(scene, em->bm, l_iter, val, false, user_data->offsets);
155 }
156 }
157 }
158}
159
161 Object *obedit,
162 const PathSelectParams *op_params,
163 BMLoop *l_src,
164 BMLoop *l_dst,
165 const float aspect_y,
166 const BMUVOffsets offsets)
167{
169 BMesh *bm = em->bm;
170 int flush = 0;
171
172 UserData_UV user_data = {};
173 user_data.scene = scene;
174 user_data.em = em;
175 user_data.offsets = offsets;
176
178 params.use_topology_distance = op_params->use_topology_distance;
179 params.use_step_face = op_params->use_face_step;
180 params.aspect_y = aspect_y;
181 params.cd_loop_uv_offset = offsets.uv;
182
183 LinkNode *path = nullptr;
184 bool is_path_ordered = false;
185
186 if (l_src != l_dst) {
187 if (op_params->use_fill) {
189 (BMElem *)l_src,
190 (BMElem *)l_dst,
191 params.cd_loop_uv_offset,
193 &user_data);
194 }
195 else {
196 is_path_ordered = true;
197 path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, &params, verttag_filter_cb, &user_data);
198 }
199 }
200
201 BMLoop *l_dst_last = l_dst;
202
203 if (path) {
204 /* toggle the flag */
205 bool all_set = true;
206 LinkNode *node = path;
207 do {
208 if (!verttag_test_cb((BMLoop *)node->link, &user_data)) {
209 all_set = false;
210 break;
211 }
212 } while ((node = node->next));
213
214 int depth = -1;
215 node = path;
216 do {
217 if ((is_path_ordered == false) ||
219 {
220 verttag_set_cb((BMLoop *)node->link, !all_set, &user_data);
221 if (is_path_ordered) {
222 l_dst_last = static_cast<BMLoop *>(node->link);
223 }
224 }
225 } while ((void)depth++, (node = node->next));
226
227 BLI_linklist_free(path, nullptr);
228 flush = all_set ? -1 : 1;
229 }
230 else {
231 const bool is_act = !verttag_test_cb(l_dst, &user_data);
232 verttag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
233 }
234
235 if (op_params->track_active) {
237 }
238 return flush;
239}
240
243/* -------------------------------------------------------------------- */
247/* callbacks */
248static bool edgetag_filter_cb(BMLoop *l, void *user_data_v)
249{
250 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
251 return uvedit_face_visible_test(user_data->scene, l->f);
252}
253static bool edgetag_test_cb(BMLoop *l, void *user_data_v)
254{
255 /* All connected loops (UV) are selected or we return false. */
256 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
257 const Scene *scene = user_data->scene;
258 BMIter iter;
259 BMLoop *l_iter;
260 BM_ITER_ELEM (l_iter, &iter, l->e, BM_LOOPS_OF_EDGE) {
261 if (edgetag_filter_cb(l_iter, user_data)) {
262 if (BM_loop_uv_share_edge_check(l, l_iter, user_data->offsets.uv)) {
263 if (!uvedit_edge_select_test(scene, l_iter, user_data->offsets)) {
264 return false;
265 }
266 }
267 }
268 }
269 return true;
270}
271static void edgetag_set_cb(BMLoop *l, bool val, void *user_data_v)
272{
273 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
274 const Scene *scene = user_data->scene;
275 BMEditMesh *em = user_data->em;
276 uvedit_edge_select_set_with_sticky(scene, em, l, val, false, user_data->offsets);
277}
278
280 Object *obedit,
281 const PathSelectParams *op_params,
282 BMLoop *l_src,
283 BMLoop *l_dst,
284 const float aspect_y,
285 const BMUVOffsets offsets)
286{
288 BMesh *bm = em->bm;
289 int flush = 0;
290
291 UserData_UV user_data = {};
292 user_data.scene = scene;
293 user_data.em = em;
294 user_data.offsets = offsets;
295
297 params.use_topology_distance = op_params->use_topology_distance;
298 params.use_step_face = op_params->use_face_step;
299 params.aspect_y = aspect_y;
300 params.cd_loop_uv_offset = offsets.uv;
301
302 LinkNode *path = nullptr;
303 bool is_path_ordered = false;
304
305 if (l_src != l_dst) {
306 if (op_params->use_fill) {
308 (BMElem *)l_src,
309 (BMElem *)l_dst,
310 params.cd_loop_uv_offset,
312 &user_data);
313 }
314 else {
315 is_path_ordered = true;
316 path = BM_mesh_calc_path_uv_edge(bm, l_src, l_dst, &params, edgetag_filter_cb, &user_data);
317 }
318 }
319
320 BMLoop *l_dst_last = l_dst;
321
322 if (path) {
323 /* toggle the flag */
324 bool all_set = true;
325 LinkNode *node = path;
326 do {
327 if (!edgetag_test_cb((BMLoop *)node->link, &user_data)) {
328 all_set = false;
329 break;
330 }
331 } while ((node = node->next));
332
333 int depth = -1;
334 node = path;
335 do {
336 if ((is_path_ordered == false) ||
338 {
339 edgetag_set_cb((BMLoop *)node->link, !all_set, &user_data);
340 if (is_path_ordered) {
341 l_dst_last = static_cast<BMLoop *>(node->link);
342 }
343 }
344 } while ((void)depth++, (node = node->next));
345
346 BLI_linklist_free(path, nullptr);
347 flush = all_set ? -1 : 1;
348 }
349 else {
350 const bool is_act = !edgetag_test_cb(l_dst, &user_data);
351 edgetag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
352 }
353
354 if (op_params->track_active) {
356 }
357 return flush;
358}
359
362/* -------------------------------------------------------------------- */
366/* callbacks */
367static bool facetag_filter_cb(BMFace *f, void *user_data_v)
368{
369 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
370 return uvedit_face_visible_test(user_data->scene, f);
371}
372static bool facetag_test_cb(BMFace *f, void *user_data_v)
373{
374 /* All connected loops are selected or we return false. */
375 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
376 const Scene *scene = user_data->scene;
377 BMIter iter;
378 BMLoop *l_iter;
379 BM_ITER_ELEM (l_iter, &iter, f, BM_LOOPS_OF_FACE) {
380 if (!uvedit_edge_select_test(scene, l_iter, user_data->offsets)) {
381 return false;
382 }
383 }
384 return true;
385}
386static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
387{
388 UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
389 const Scene *scene = user_data->scene;
390 BMEditMesh *em = user_data->em;
391 uvedit_face_select_set_with_sticky(scene, em, f, val, false, user_data->offsets);
392}
393
395 Object *obedit,
396 const PathSelectParams *op_params,
397 BMFace *f_src,
398 BMFace *f_dst,
399 const float aspect_y,
400 const BMUVOffsets offsets)
401{
403 BMesh *bm = em->bm;
404 int flush = 0;
405
406 UserData_UV user_data = {};
407 user_data.scene = scene;
408 user_data.em = em;
409 user_data.offsets = offsets;
410
412 params.use_topology_distance = op_params->use_topology_distance;
413 params.use_step_face = op_params->use_face_step;
414 params.aspect_y = aspect_y;
415 params.cd_loop_uv_offset = offsets.uv;
416
417 LinkNode *path = nullptr;
418 bool is_path_ordered = false;
419
420 if (f_src != f_dst) {
421 if (op_params->use_fill) {
423 (BMElem *)f_src,
424 (BMElem *)f_dst,
425 params.cd_loop_uv_offset,
427 &user_data);
428 }
429 else {
430 is_path_ordered = true;
431 path = BM_mesh_calc_path_uv_face(bm, f_src, f_dst, &params, facetag_filter_cb, &user_data);
432 }
433 }
434
435 BMFace *f_dst_last = f_dst;
436
437 if (path) {
438 /* toggle the flag */
439 bool all_set = true;
440 LinkNode *node = path;
441 do {
442 if (!facetag_test_cb((BMFace *)node->link, &user_data)) {
443 all_set = false;
444 break;
445 }
446 } while ((node = node->next));
447
448 int depth = -1;
449 node = path;
450 do {
451 if ((is_path_ordered == false) ||
453 {
454 facetag_set_cb((BMFace *)node->link, !all_set, &user_data);
455 if (is_path_ordered) {
456 f_dst_last = static_cast<BMFace *>(node->link);
457 }
458 }
459 } while ((void)depth++, (node = node->next));
460
461 BLI_linklist_free(path, nullptr);
462 flush = all_set ? -1 : 1;
463 }
464 else {
465 const bool is_act = !facetag_test_cb(f_dst, &user_data);
466 facetag_set_cb(f_dst, is_act, &user_data); /* switch the face option */
467 }
468
469 if (op_params->track_active) {
470 /* Unlike other types, we can track active without it being selected. */
471 BM_mesh_active_face_set(bm, f_dst_last);
472 }
473 return flush;
474}
475
478/* -------------------------------------------------------------------- */
483
485 Depsgraph *depsgraph,
486 Object *obedit,
487 const PathSelectParams *op_params,
488 BMElem *ele_src,
489 BMElem *ele_dst,
490 const float aspect_y,
491 const BMUVOffsets offsets)
492{
493 const ToolSettings *ts = scene->toolsettings;
494 const char uv_selectmode = ED_uvedit_select_mode_get(scene);
495 bool ok = false;
496 int flush = 0;
497
498 if (ELEM(nullptr, ele_src, ele_dst) || (ele_src->head.htype != ele_dst->head.htype)) {
499 /* pass */
500 }
501 else if (ele_src->head.htype == BM_FACE) {
503 scene, obedit, op_params, (BMFace *)ele_src, (BMFace *)ele_dst, aspect_y, offsets);
504 ok = true;
505 }
506 else if (ele_src->head.htype == BM_LOOP) {
507 if (uv_selectmode & UV_SELECT_EDGE) {
509 scene, obedit, op_params, (BMLoop *)ele_src, (BMLoop *)ele_dst, aspect_y, offsets);
510 }
511 else {
513 scene, obedit, op_params, (BMLoop *)ele_src, (BMLoop *)ele_dst, aspect_y, offsets);
514 }
515 ok = true;
516 }
517
518 if (ok) {
519 if (flush != 0) {
520 const bool select = (flush == 1);
522 if (ts->uv_flag & UV_SYNC_SELECTION) {
523 ED_uvedit_select_sync_flush(scene->toolsettings, em, select);
524 }
525 else {
527 }
528 }
529
530 if (ts->uv_flag & UV_SYNC_SELECTION) {
531 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
532 }
533 else {
534 Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
535 BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(obedit_eval->data),
537 }
538 /* Only for region redraw. */
540 }
541
542 return ok;
543}
544
546{
547 Scene *scene = CTX_data_scene(C);
548 const ToolSettings *ts = scene->toolsettings;
549 const char uv_selectmode = ED_uvedit_select_mode_get(scene);
550
551 /* We could support this, it needs further testing. */
552 if (RNA_struct_property_is_set(op->ptr, "index")) {
553 return uv_shortest_path_pick_exec(C, op);
554 }
555
556 PathSelectParams op_params;
557 path_select_params_from_op(op, &op_params);
558
559 /* Set false if we support edge tagging. */
560 op_params.track_active = true;
561
563 ViewLayer *view_layer = CTX_data_view_layer(C);
565 scene, view_layer, nullptr);
566
567 float co[2];
568
569 const ARegion *region = CTX_wm_region(C);
570
571 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
572
573 BMElem *ele_src = nullptr, *ele_dst = nullptr;
574
575 /* Detect the hit. */
576 UvNearestHit hit = uv_nearest_hit_init_max(&region->v2d);
577 bool hit_found = false;
578 if (uv_selectmode == UV_SELECT_FACE) {
579 if (uv_find_nearest_face_multi(scene, objects, co, &hit)) {
580 hit_found = true;
581 }
582 }
583 else if (uv_selectmode & UV_SELECT_EDGE) {
584 if (uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit)) {
585 hit_found = true;
586 }
587 }
588 else {
589 if (uv_find_nearest_vert_multi(scene, objects, co, 0.0f, &hit)) {
590 hit_found = true;
591 }
592 }
593
594 bool changed = false;
595 if (hit_found) {
596 /* This may not be the active object. */
597 Object *obedit = hit.ob;
599 BMesh *bm = em->bm;
600 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
601
602 /* Respond to the hit. */
603 if (uv_selectmode == UV_SELECT_FACE) {
604 /* Face selection. */
605 BMFace *f_src = BM_mesh_active_face_get(bm, false, false);
606 /* Check selection? */
607 ele_src = (BMElem *)f_src;
608 ele_dst = (BMElem *)hit.efa;
609 }
610 else if (uv_selectmode & UV_SELECT_EDGE) {
611 /* Edge selection. */
612 BMLoop *l_src = nullptr;
613 if (ts->uv_flag & UV_SYNC_SELECTION) {
615 if (e_src != nullptr) {
616 l_src = uv_find_nearest_loop_from_edge(scene, obedit, e_src, co);
617 }
618 }
619 else {
621 if (l_src != nullptr) {
622 if (!uvedit_uv_select_test(scene, l_src, offsets) &&
623 !uvedit_uv_select_test(scene, l_src->next, offsets))
624 {
625 l_src = nullptr;
626 }
627 ele_src = (BMElem *)l_src;
628 }
629 }
630 ele_src = (BMElem *)l_src;
631 ele_dst = (BMElem *)hit.l;
632 }
633 else {
634 /* Vertex selection. */
635 BMLoop *l_src = nullptr;
636 if (ts->uv_flag & UV_SYNC_SELECTION) {
638 if (v_src != nullptr) {
639 l_src = uv_find_nearest_loop_from_vert(scene, obedit, v_src, co);
640 }
641 }
642 else {
644 if (l_src != nullptr) {
645 if (!uvedit_uv_select_test(scene, l_src, offsets)) {
646 l_src = nullptr;
647 }
648 }
649 }
650 ele_src = (BMElem *)l_src;
651 ele_dst = (BMElem *)hit.l;
652 }
653
654 if (ele_src && ele_dst) {
655 /* Always use the active object, not `obedit` as the active defines the UV display. */
656 const float aspect_y = ED_uvedit_get_aspect_y(CTX_data_edit_object(C));
658 scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, offsets);
659
660 /* Store the object and it's index so redo is possible. */
661 int index;
662 if (uv_selectmode & UV_SELECT_FACE) {
664 index = BM_elem_index_get(ele_dst);
665 }
666 else if (uv_selectmode & UV_SELECT_EDGE) {
668 index = BM_elem_index_get(ele_dst);
669 }
670 else {
672 index = BM_elem_index_get(ele_dst);
673 }
674
675 const int object_index = blender::ed::object::object_in_mode_to_index(
676 scene, view_layer, OB_MODE_EDIT, obedit);
677 BLI_assert(object_index != -1);
678 RNA_int_set(op->ptr, "object_index", object_index);
679 RNA_int_set(op->ptr, "index", index);
680 changed = true;
681 }
682 }
683
684 return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
685}
686
688{
690 Scene *scene = CTX_data_scene(C);
691 ViewLayer *view_layer = CTX_data_view_layer(C);
692 const char uv_selectmode = ED_uvedit_select_mode_get(scene);
693
694 const int object_index = RNA_int_get(op->ptr, "object_index");
695 const int index = RNA_int_get(op->ptr, "index");
696 if (object_index == -1) {
697 return OPERATOR_CANCELLED;
698 }
699
701 scene, view_layer, OB_MODE_EDIT, object_index);
702 if (obedit == nullptr) {
703 return OPERATOR_CANCELLED;
704 }
705
707 BMesh *bm = em->bm;
708 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
709
710 BMElem *ele_src, *ele_dst;
711
712 /* NOLINTBEGIN: bugprone-assignment-in-if-condition */
713 if (uv_selectmode & UV_SELECT_FACE) {
714 if (index < 0 || index >= bm->totface) {
715 return OPERATOR_CANCELLED;
716 }
717 if (!(ele_src = (BMElem *)BM_mesh_active_face_get(bm, false, false)) ||
718 !(ele_dst = (BMElem *)BM_face_at_index_find_or_table(bm, index)))
719 {
720 return OPERATOR_CANCELLED;
721 }
722 }
723 else if (uv_selectmode & UV_SELECT_EDGE) {
724 if (index < 0 || index >= bm->totloop) {
725 return OPERATOR_CANCELLED;
726 }
727 if (!(ele_src = (BMElem *)ED_uvedit_active_edge_loop_get(bm)) ||
728 !(ele_dst = (BMElem *)BM_loop_at_index_find(bm, index)))
729 {
730 return OPERATOR_CANCELLED;
731 }
732 }
733 else {
734 if (index < 0 || index >= bm->totloop) {
735 return OPERATOR_CANCELLED;
736 }
737 if (!(ele_src = (BMElem *)ED_uvedit_active_vert_loop_get(bm)) ||
738 !(ele_dst = (BMElem *)BM_loop_at_index_find(bm, index)))
739 {
740 return OPERATOR_CANCELLED;
741 }
742 }
743 /* NOLINTEND: bugprone-assignment-in-if-condition */
744
745 /* Always use the active object, not `obedit` as the active defines the UV display. */
746 const float aspect_y = ED_uvedit_get_aspect_y(CTX_data_edit_object(C));
747
748 PathSelectParams op_params;
749 path_select_params_from_op(op, &op_params);
750 op_params.track_active = true;
751
753 scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, offsets))
754 {
755 return OPERATOR_CANCELLED;
756 }
757
758 return OPERATOR_FINISHED;
759}
760
762{
763 PropertyRNA *prop;
764
765 /* identifiers */
766 ot->name = "Pick Shortest Path";
767 ot->idname = "UV_OT_shortest_path_pick";
768 ot->description = "Select shortest path between two selections";
769
770 /* api callbacks */
774
775 /* flags */
777
778 /* properties */
780
781 /* use for redo */
782 prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
784 prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
786}
787
790/* -------------------------------------------------------------------- */
795{
797 Scene *scene = CTX_data_scene(C);
798 const char uv_selectmode = ED_uvedit_select_mode_get(scene);
799 bool found_valid_elements = false;
800
801 const float aspect_y = ED_uvedit_get_aspect_y(CTX_data_edit_object(C));
802
803 ViewLayer *view_layer = CTX_data_view_layer(C);
805 scene, view_layer, nullptr);
806 for (Object *obedit : objects) {
808 BMesh *bm = em->bm;
809
810 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
811
812 BMElem *ele_src = nullptr, *ele_dst = nullptr;
813
814 /* Find 2x elements. */
815 {
816 BMElem **ele_array = nullptr;
817 int ele_array_len = 0;
818 if (uv_selectmode & UV_SELECT_FACE) {
819 ele_array = (BMElem **)ED_uvedit_selected_faces(scene, bm, 3, &ele_array_len);
820 }
821 else if (uv_selectmode & UV_SELECT_EDGE) {
822 ele_array = (BMElem **)ED_uvedit_selected_edges(scene, bm, 3, &ele_array_len);
823 }
824 else {
825 ele_array = (BMElem **)ED_uvedit_selected_verts(scene, bm, 3, &ele_array_len);
826 }
827
828 if (ele_array_len == 2) {
829 ele_src = ele_array[0];
830 ele_dst = ele_array[1];
831 }
832 MEM_freeN(ele_array);
833 }
834
835 if (ele_src && ele_dst) {
836 PathSelectParams op_params;
837 path_select_params_from_op(op, &op_params);
838
840 scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, offsets);
841
842 found_valid_elements = true;
843 }
844 }
845
846 if (!found_valid_elements) {
848 op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected");
849 return OPERATOR_CANCELLED;
850 }
851
852 return OPERATOR_FINISHED;
853}
854
856{
857 /* identifiers */
858 ot->name = "Select Shortest Path";
859 ot->idname = "UV_OT_shortest_path_select";
860 ot->description = "Selected shortest path between two vertices/edges/faces";
861
862 /* api callbacks */
865
866 /* flags */
868
869 /* properties */
871}
872
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:63
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
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert(a)
Definition BLI_assert.h:50
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)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ OB_MODE_EDIT
Object is a sort of wrapper for general info.
@ UV_SELECT_FACE
@ UV_SELECT_EDGE
@ UV_SYNC_SELECTION
bool ED_operator_uvedit_space_image(bContext *C)
void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, bool select)
float ED_uvedit_get_aspect_y(Object *obedit)
void ED_uvedit_active_edge_loop_set(BMesh *bm, BMLoop *l)
bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, 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 uvedit_edge_select_set_with_sticky(const Scene *scene, BMEditMesh *em, BMLoop *l, bool select, bool do_history, BMUVOffsets offsets)
void ED_uvedit_selectmode_flush(const Scene *scene, BMEditMesh *em)
UV Select Mode Flush.
BMLoop * ED_uvedit_active_edge_loop_get(BMesh *bm)
bool uvedit_face_visible_test(const Scene *scene, BMFace *efa)
void uvedit_uv_select_set(const Scene *scene, BMesh *bm, BMLoop *l, bool select, bool do_history, BMUVOffsets offsets)
Select UV Vertex.
void uvedit_face_select_set_with_sticky(const Scene *scene, BMEditMesh *em, BMFace *efa, bool select, bool do_history, BMUVOffsets offsets)
BMLoop * ED_uvedit_active_vert_loop_get(BMesh *bm)
BMFace ** ED_uvedit_selected_faces(const Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, BMUVOffsets offsets)
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.
PropertyFlag
Definition RNA_types.hh:201
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
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_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_SELECT
Definition WM_types.hh:474
@ BM_LOOP
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
#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
ATTR_WARN_UNUSED_RESULT 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_get_offsets(const BMesh *bm)
bool BM_loop_uv_share_edge_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
const Depsgraph * depsgraph
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
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 BMVert * v
struct BMEdge * e
struct BMFace * f
struct BMLoop * next
int totloop
int totface
Definition DNA_ID.h:413
CheckerIntervalParams interval_params
Scene * scene
BMUVOffsets offsets
BMEditMesh * em
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
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 bool facetag_filter_cb(BMFace *f, void *user_data_v)
void UV_OT_shortest_path_pick(wmOperatorType *ot)
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 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 bool verttag_test_cb(BMLoop *l, void *user_data_v)
static bool edgetag_filter_cb(BMLoop *l, void *user_data_v)
static void verttag_set_cb(BMLoop *l, bool val, void *user_data_v)
static void path_select_properties(wmOperatorType *ot)
static int uv_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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)
void UV_OT_shortest_path_select(wmOperatorType *ot)
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 int uv_shortest_path_select_exec(bContext *C, wmOperator *op)
static void facetag_set_cb(BMFace *f, bool val, 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 edgetag_set_cb(BMLoop *l, bool val, void *user_data_v)
static bool verttag_filter_cb(BMLoop *l, void *user_data_v)
static int uv_shortest_path_pick_exec(bContext *C, wmOperator *op)
void WM_main_add_notifier(uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
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)