Blender V5.0
editmesh_path.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "DNA_mesh_types.h"
10#include "DNA_object_types.h"
11#include "DNA_scene_types.h"
13
14#ifdef WITH_FREESTYLE
15# include "DNA_meshdata_types.h"
16#endif
17
18#include "BLI_linklist.h"
19#include "BLI_math_vector.h"
20
21#include "BKE_context.hh"
22#include "BKE_customdata.hh"
23#include "BKE_editmesh.hh"
24#include "BKE_layer.hh"
25#include "BKE_mesh_types.hh"
26#include "BKE_report.hh"
27
28#include "ED_mesh.hh"
29#include "ED_object.hh"
30#include "ED_screen.hh"
31#include "ED_select_utils.hh"
32#include "ED_uvedit.hh"
33#include "ED_view3d.hh"
34
35#include "RNA_access.hh"
36#include "RNA_define.hh"
37
38#include "WM_api.hh"
39#include "WM_types.hh"
40
41#include "bmesh.hh"
42#include "bmesh_tools.hh"
43
44#include "DEG_depsgraph.hh"
45
46#include "mesh_intern.hh" /* own include */
47
48using blender::Vector;
49
50/* -------------------------------------------------------------------- */
53
54enum {
61};
62
72
74{
75 static const EnumPropertyItem edge_tag_items[] = {
76 {EDGE_MODE_SELECT, "SELECT", 0, "Select", ""},
77 {EDGE_MODE_TAG_SEAM, "SEAM", 0, "Tag Seam", ""},
78 {EDGE_MODE_TAG_SHARP, "SHARP", 0, "Tag Sharp", ""},
79 {EDGE_MODE_TAG_CREASE, "CREASE", 0, "Tag Crease", ""},
80 {EDGE_MODE_TAG_BEVEL, "BEVEL", 0, "Tag Bevel", ""},
81 {EDGE_MODE_TAG_FREESTYLE, "FREESTYLE", 0, "Tag Freestyle Edge Mark", ""},
82 {0, nullptr, 0, nullptr, nullptr},
83 };
84
85 RNA_def_enum(ot->srna,
86 "edge_mode",
87 edge_tag_items,
89 "Edge Tag",
90 "The edge flag to tag when selecting the shortest path");
91
92 RNA_def_boolean(ot->srna,
93 "use_face_step",
94 false,
95 "Face Stepping",
96 "Traverse connected faces (includes diagonals and edge-rings)");
97 RNA_def_boolean(ot->srna,
98 "use_topology_distance",
99 false,
100 "Topology Distance",
101 "Find the minimum number of steps, ignoring spatial distance");
102 RNA_def_boolean(ot->srna,
103 "use_fill",
104 false,
105 "Fill Region",
106 "Select all paths between the source/destination elements");
108}
109
111 ToolSettings *ts,
112 PathSelectParams *op_params)
113{
114 {
115 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "edge_mode");
116 if (RNA_property_is_set(op->ptr, prop)) {
117 op_params->edge_mode = RNA_property_enum_get(op->ptr, prop);
118 if (op->flag & OP_IS_INVOKE) {
119 ts->edge_mode = op_params->edge_mode;
120 }
121 }
122 else {
123 op_params->edge_mode = ts->edge_mode;
124 RNA_property_enum_set(op->ptr, prop, op_params->edge_mode);
125 }
126 }
127
128 op_params->track_active = false;
129 op_params->use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
130 op_params->use_fill = RNA_boolean_get(op->ptr, "use_fill");
131 op_params->use_topology_distance = RNA_boolean_get(op->ptr, "use_topology_distance");
133}
134
136 wmOperator * /*op*/,
137 const PropertyRNA *prop)
138{
139 const char *prop_id = RNA_property_identifier(prop);
140 if (STREQ(prop_id, "edge_mode")) {
141 const Scene *scene = CTX_data_scene(C);
142 ToolSettings *ts = scene->toolsettings;
143 if ((ts->selectmode & SCE_SELECT_EDGE) == 0) {
144 return false;
145 }
146 }
147 return true;
148}
149
156
158
159/* -------------------------------------------------------------------- */
162
163/* callbacks */
164static bool verttag_filter_cb(BMVert *v, void * /*user_data_v*/)
165{
167}
168static bool verttag_test_cb(BMVert *v, void * /*user_data_v*/)
169{
171}
172static void verttag_set_cb(BMVert *v, bool val, void *user_data_v)
173{
174 UserData *user_data = static_cast<UserData *>(user_data_v);
175 BM_vert_select_set(user_data->bm, v, val);
176}
177
178static void mouse_mesh_shortest_path_vert(Scene * /*scene*/,
179 Object *obedit,
180 const PathSelectParams *op_params,
181 BMVert *v_act,
182 BMVert *v_dst)
183{
185 BMesh *bm = em->bm;
186
187 int cd_offset = -1;
188 switch (op_params->edge_mode) {
189 case EDGE_MODE_SELECT:
192#ifdef WITH_FREESTYLE
194#endif
195 break;
197 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
198 break;
200 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
201 break;
202 }
203
204 UserData user_data = {bm, static_cast<Mesh *>(obedit->data), cd_offset, op_params};
205 LinkNode *path = nullptr;
206 bool is_path_ordered = false;
207
208 if (v_act && (v_act != v_dst)) {
209 if (op_params->use_fill) {
211 bm, (BMElem *)v_act, (BMElem *)v_dst, verttag_filter_cb, &user_data);
212 }
213 else {
214 is_path_ordered = true;
216 params.use_topology_distance = op_params->use_topology_distance;
217 params.use_step_face = op_params->use_face_step;
218 path = BM_mesh_calc_path_vert(bm, v_act, v_dst, &params, verttag_filter_cb, &user_data);
219 }
220
221 if (path) {
222 if (op_params->track_active) {
224 }
225 }
226 }
227
228 BMVert *v_dst_last = v_dst;
229
230 if (path) {
231 /* toggle the flag */
232 bool all_set = true;
233 LinkNode *node;
234
235 node = path;
236 do {
237 if (!verttag_test_cb((BMVert *)node->link, &user_data)) {
238 all_set = false;
239 break;
240 }
241 } while ((node = node->next));
242
243 int depth = -1;
244 node = path;
245 do {
246 if ((is_path_ordered == false) ||
248 {
249 verttag_set_cb((BMVert *)node->link, !all_set, &user_data);
250 if (is_path_ordered) {
251 v_dst_last = static_cast<BMVert *>(node->link);
252 }
253 }
254 } while ((void)depth++, (node = node->next));
255
256 BLI_linklist_free(path, nullptr);
257 }
258 else {
259 const bool is_act = !verttag_test_cb(v_dst, &user_data);
260 verttag_set_cb(v_dst, is_act, &user_data); /* switch the face option */
261 }
262
265
266 if (op_params->track_active) {
267 /* even if this is selected it may not be in the selection list */
268 if (BM_elem_flag_test(v_dst_last, BM_ELEM_SELECT) == 0) {
269 BM_select_history_remove(bm, v_dst_last);
270 }
271 else {
272 BM_select_history_store(bm, v_dst_last);
273 }
274 }
275
277 params.calc_looptris = false;
278 params.calc_normals = false;
279 params.is_destructive = false;
280 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
281}
282
284
285/* -------------------------------------------------------------------- */
288
289/* callbacks */
290static bool edgetag_filter_cb(BMEdge *e, void * /*user_data_v*/)
291{
293}
294static bool edgetag_test_cb(BMEdge *e, void *user_data_v)
295{
296 UserData *user_data = static_cast<UserData *>(user_data_v);
297 const char edge_mode = user_data->op_params->edge_mode;
298
299 switch (edge_mode) {
300 case EDGE_MODE_SELECT:
301 return BM_elem_flag_test(e, BM_ELEM_SELECT) ? true : false;
303 return BM_elem_flag_test(e, BM_ELEM_SEAM) ? true : false;
305 return BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? false : true;
308 return BM_ELEM_CD_GET_FLOAT(e, user_data->cd_offset);
309#ifdef WITH_FREESTYLE
311 return BM_ELEM_CD_GET_BOOL(e, user_data->cd_offset);
312 }
313#endif
314 }
315 return false;
316}
317static void edgetag_set_cb(BMEdge *e, bool val, void *user_data_v)
318{
319 UserData *user_data = static_cast<UserData *>(user_data_v);
320 const char edge_mode = user_data->op_params->edge_mode;
321 BMesh *bm = user_data->bm;
322
323 switch (edge_mode) {
324 case EDGE_MODE_SELECT:
325 BM_edge_select_set(bm, e, val);
326 break;
329 break;
332 break;
335 BM_ELEM_CD_SET_FLOAT(e, user_data->cd_offset, (val) ? 1.0f : 0.0f);
336 break;
337#ifdef WITH_FREESTYLE
339 BM_ELEM_CD_SET_BOOL(e, user_data->cd_offset, val);
340 break;
341 }
342#endif
343 }
344}
345
346static void edgetag_ensure_cd_flag(Mesh *mesh, const char edge_mode)
347{
348 BMesh *bm = mesh->runtime->edit_mesh->bm;
349
350 switch (edge_mode) {
352 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_FLOAT, "crease_edge")) {
353 BM_data_layer_add_named(bm, &bm->edata, CD_PROP_FLOAT, "crease_edge");
354 }
355 break;
357 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge")) {
358 BM_data_layer_add_named(bm, &bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
359 }
360 break;
361#ifdef WITH_FREESTYLE
363 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_BOOL, "freestyle_edge")) {
364 BM_data_layer_add_named(bm, &bm->edata, CD_PROP_BOOL, "freestyle_edge");
365 }
366 break;
367#endif
368 default:
369 break;
370 }
371}
372
373/* Mesh shortest path select, uses previously-selected edge. */
374
375/* since you want to create paths with multiple selects, it doesn't have extend option */
377 Scene *scene, Object *obedit, const PathSelectParams *op_params, BMEdge *e_act, BMEdge *e_dst)
378{
380 BMesh *bm = em->bm;
381
382 int cd_offset = -1;
383 switch (op_params->edge_mode) {
384 case EDGE_MODE_SELECT:
387#ifdef WITH_FREESTYLE
389 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_BOOL, "freestyle_edge");
390#endif
391 break;
393 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
394 break;
396 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
397 break;
398 }
399
400 UserData user_data = {bm, static_cast<Mesh *>(obedit->data), cd_offset, op_params};
401 LinkNode *path = nullptr;
402 bool is_path_ordered = false;
403
404 edgetag_ensure_cd_flag(static_cast<Mesh *>(obedit->data), op_params->edge_mode);
405
406 if (e_act && (e_act != e_dst)) {
407 if (op_params->use_fill) {
409 bm, (BMElem *)e_act, (BMElem *)e_dst, edgetag_filter_cb, &user_data);
410 }
411 else {
412 is_path_ordered = true;
414 params.use_topology_distance = op_params->use_topology_distance;
415 params.use_step_face = op_params->use_face_step;
416 path = BM_mesh_calc_path_edge(bm, e_act, e_dst, &params, edgetag_filter_cb, &user_data);
417 }
418
419 if (path) {
420 if (op_params->track_active) {
422 }
423 }
424 }
425
426 BMEdge *e_dst_last = e_dst;
427
428 if (path) {
429 /* toggle the flag */
430 bool all_set = true;
431 LinkNode *node;
432
433 node = path;
434 do {
435 if (!edgetag_test_cb((BMEdge *)node->link, &user_data)) {
436 all_set = false;
437 break;
438 }
439 } while ((node = node->next));
440
441 int depth = -1;
442 node = path;
443 do {
444 if ((is_path_ordered == false) ||
446 {
447 edgetag_set_cb((BMEdge *)node->link, !all_set, &user_data);
448 if (is_path_ordered) {
449 e_dst_last = static_cast<BMEdge *>(node->link);
450 }
451 }
452 } while ((void)depth++, (node = node->next));
453
454 BLI_linklist_free(path, nullptr);
455 }
456 else {
457 const bool is_act = !edgetag_test_cb(e_dst, &user_data);
458 edgetag_ensure_cd_flag(static_cast<Mesh *>(obedit->data), op_params->edge_mode);
459 edgetag_set_cb(e_dst, is_act, &user_data); /* switch the edge option */
460 }
461
462 if (op_params->edge_mode != EDGE_MODE_SELECT) {
463 if (op_params->track_active) {
464 /* simple rules - last edge is _always_ active and selected */
465 if (e_act) {
466 BM_edge_select_set(bm, e_act, false);
467 }
468 BM_edge_select_set(bm, e_dst_last, true);
469 BM_select_history_store(bm, e_dst_last);
470 }
471 }
472
475
476 if (op_params->track_active) {
477 /* even if this is selected it may not be in the selection list */
478 if (op_params->edge_mode == EDGE_MODE_SELECT) {
479 if (edgetag_test_cb(e_dst_last, &user_data) == 0) {
480 BM_select_history_remove(bm, e_dst_last);
481 }
482 else {
483 BM_select_history_store(bm, e_dst_last);
484 }
485 }
486 }
487
489 params.calc_looptris = false;
490 params.calc_normals = false;
491 params.is_destructive = false;
492 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
493
494 if (op_params->edge_mode == EDGE_MODE_TAG_SEAM) {
495 ED_uvedit_live_unwrap(scene, {obedit});
496 }
497}
498
500
501/* -------------------------------------------------------------------- */
504
505/* callbacks */
506static bool facetag_filter_cb(BMFace *f, void * /*user_data_v*/)
507{
509}
510// static bool facetag_test_cb(Scene * /*scene*/, BMesh * /*bm*/, BMFace *f)
511static bool facetag_test_cb(BMFace *f, void * /*user_data_v*/)
512{
514}
515// static void facetag_set_cb(BMesh *bm, Scene * /*scene*/, BMFace *f, const bool val)
516static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
517{
518 UserData *user_data = static_cast<UserData *>(user_data_v);
519 BM_face_select_set(user_data->bm, f, val);
520}
521
522static void mouse_mesh_shortest_path_face(Scene * /*scene*/,
523 Object *obedit,
524 const PathSelectParams *op_params,
525 BMFace *f_act,
526 BMFace *f_dst)
527{
529 BMesh *bm = em->bm;
530
531 int cd_offset = -1;
532 switch (op_params->edge_mode) {
533 case EDGE_MODE_SELECT:
536#ifdef WITH_FREESTYLE
538#endif
539 break;
541 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
542 break;
544 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
545 break;
546 }
547
548 UserData user_data = {bm, static_cast<Mesh *>(obedit->data), cd_offset, op_params};
549 LinkNode *path = nullptr;
550 bool is_path_ordered = false;
551
552 if (f_act) {
553 if (op_params->use_fill) {
555 bm, (BMElem *)f_act, (BMElem *)f_dst, facetag_filter_cb, &user_data);
556 }
557 else {
558 is_path_ordered = true;
560 params.use_topology_distance = op_params->use_topology_distance;
561 params.use_step_face = op_params->use_face_step;
562 path = BM_mesh_calc_path_face(bm, f_act, f_dst, &params, facetag_filter_cb, &user_data);
563 }
564
565 if (f_act != f_dst) {
566 if (path) {
567 if (op_params->track_active) {
569 }
570 }
571 }
572 }
573
574 BMFace *f_dst_last = f_dst;
575
576 if (path) {
577 /* toggle the flag */
578 bool all_set = true;
579 LinkNode *node;
580
581 node = path;
582 do {
583 if (!facetag_test_cb((BMFace *)node->link, &user_data)) {
584 all_set = false;
585 break;
586 }
587 } while ((node = node->next));
588
589 int depth = -1;
590 node = path;
591 do {
592 if ((is_path_ordered == false) ||
594 {
595 facetag_set_cb((BMFace *)node->link, !all_set, &user_data);
596 if (is_path_ordered) {
597 f_dst_last = static_cast<BMFace *>(node->link);
598 }
599 }
600 } while ((void)depth++, (node = node->next));
601
602 BLI_linklist_free(path, nullptr);
603 }
604 else {
605 const bool is_act = !facetag_test_cb(f_dst, &user_data);
606 facetag_set_cb(f_dst, is_act, &user_data); /* switch the face option */
607 }
608
611
612 if (op_params->track_active) {
613 /* even if this is selected it may not be in the selection list */
614 if (facetag_test_cb(f_dst_last, &user_data) == 0) {
615 BM_select_history_remove(bm, f_dst_last);
616 }
617 else {
618 BM_select_history_store(bm, f_dst_last);
619 }
620 BM_mesh_active_face_set(bm, f_dst_last);
621
623 em->mat_nr = f_dst_last->mat_nr;
624 }
625
627 params.calc_looptris = false;
628 params.calc_normals = false;
629 params.is_destructive = false;
630 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
631}
632
634
635/* -------------------------------------------------------------------- */
638
640 Object *obedit,
641 const PathSelectParams *op_params,
642 BMElem *ele_src,
643 BMElem *ele_dst)
644{
645 bool ok = false;
646
647 if (ELEM(nullptr, ele_src, ele_dst) || (ele_src->head.htype != ele_dst->head.htype)) {
648 /* pass */
649 }
650 else if (ele_src->head.htype == BM_VERT) {
651 mouse_mesh_shortest_path_vert(scene, obedit, op_params, (BMVert *)ele_src, (BMVert *)ele_dst);
652 ok = true;
653 }
654 else if (ele_src->head.htype == BM_EDGE) {
655 mouse_mesh_shortest_path_edge(scene, obedit, op_params, (BMEdge *)ele_src, (BMEdge *)ele_dst);
656 ok = true;
657 }
658 else if (ele_src->head.htype == BM_FACE) {
659 mouse_mesh_shortest_path_face(scene, obedit, op_params, (BMFace *)ele_src, (BMFace *)ele_dst);
660 ok = true;
661 }
662
663 if (ok) {
664 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
666 }
667
668 return ok;
669}
670
672
673static BMElem *edbm_elem_find_nearest(ViewContext *vc, const char htype)
674{
675 BMEditMesh *em = vc->em;
676 float dist = ED_view3d_select_dist_px();
677
678 if ((em->selectmode & SCE_SELECT_VERTEX) && (htype == BM_VERT)) {
679 return (BMElem *)EDBM_vert_find_nearest(vc, &dist);
680 }
681 if ((em->selectmode & SCE_SELECT_EDGE) && (htype == BM_EDGE)) {
682 return (BMElem *)EDBM_edge_find_nearest(vc, &dist);
683 }
684 if ((em->selectmode & SCE_SELECT_FACE) && (htype == BM_FACE)) {
685 return (BMElem *)EDBM_face_find_nearest(vc, &dist);
686 }
687
688 return nullptr;
689}
690
692{
694
695 if ((ele == nullptr) && bm->act_face && BM_elem_flag_test(bm->act_face, BM_ELEM_SELECT)) {
696 ele = (BMElem *)bm->act_face;
697 }
698
699 return ele;
700}
701
703 wmOperator *op,
704 const wmEvent *event)
705{
706 if (RNA_struct_property_is_set(op->ptr, "index")) {
708 }
709
710 BMVert *eve = nullptr;
711 BMEdge *eed = nullptr;
712 BMFace *efa = nullptr;
713
714 bool track_active = true;
715
717 copy_v2_v2_int(vc.mval, event->mval);
720 BMEditMesh *em = vc.em;
721
723
724 {
725 int base_index = -1;
727 vc.scene, vc.view_layer, vc.v3d);
728 if (EDBM_unified_findnearest(&vc, bases, &base_index, &eve, &eed, &efa)) {
729 basact = bases[base_index];
731 em = vc.em;
732 }
733 }
734
735 /* If nothing is selected, let's select the picked vertex/edge/face. */
736 if ((vc.em->bm->totvertsel == 0) && (eve || eed || efa)) {
737 /* TODO(dfelinto): right now we try to find the closest element twice.
738 * The ideal is to refactor EDBM_select_pick so it doesn't
739 * have to pick the nearest vert/edge/face again. */
740 const SelectPick_Params params = {
741 /*sel_op*/ SEL_OP_ADD,
742 };
743 EDBM_select_pick(C, event->mval, params);
744 return OPERATOR_FINISHED;
745 }
746
747 PathSelectParams op_params;
748 path_select_params_from_op(op, vc.scene->toolsettings, &op_params);
749
750 BMElem *ele_src, *ele_dst;
751 if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) ||
752 !(ele_dst = edbm_elem_find_nearest(&vc, ele_src->head.htype)))
753 {
754 /* special case, toggle edge tags even when we don't have a path */
755 if (((em->selectmode & SCE_SELECT_EDGE) && (op_params.edge_mode != EDGE_MODE_SELECT)) &&
756 /* check if we only have a destination edge */
757 ((ele_src == nullptr) && (ele_dst = edbm_elem_find_nearest(&vc, BM_EDGE))))
758 {
759 ele_src = ele_dst;
760 track_active = false;
761 }
762 else {
764 }
765 }
766
767 op_params.track_active = track_active;
768
769 if (!edbm_shortest_path_pick_ex(vc.scene, vc.obedit, &op_params, ele_src, ele_dst)) {
771 }
772
774 if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
776 }
777
778 /* to support redo */
779 BM_mesh_elem_index_ensure(em->bm, ele_dst->head.htype);
780 int index = EDBM_elem_to_index_any(em, ele_dst);
781
782 RNA_int_set(op->ptr, "index", index);
783
784 return OPERATOR_FINISHED;
785}
786
788{
789 Scene *scene = CTX_data_scene(C);
790 Object *obedit = CTX_data_edit_object(C);
792 BMesh *bm = em->bm;
793
794 const int index = RNA_int_get(op->ptr, "index");
795 if (index < 0 || index >= (bm->totvert + bm->totedge + bm->totface)) {
796 return OPERATOR_CANCELLED;
797 }
798
799 BMElem *ele_src, *ele_dst;
800 if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) ||
801 !(ele_dst = EDBM_elem_from_index_any(em, index)))
802 {
803 return OPERATOR_CANCELLED;
804 }
805
806 PathSelectParams op_params;
807 path_select_params_from_op(op, scene->toolsettings, &op_params);
808 op_params.track_active = true;
809
810 if (!edbm_shortest_path_pick_ex(scene, obedit, &op_params, ele_src, ele_dst)) {
811 return OPERATOR_CANCELLED;
812 }
813
814 return OPERATOR_FINISHED;
815}
816
818{
819 PropertyRNA *prop;
820
821 /* identifiers */
822 ot->name = "Pick Shortest Path";
823 ot->idname = "MESH_OT_shortest_path_pick";
824 ot->description = "Select shortest path between two selections";
825
826 /* API callbacks. */
830 ot->poll_property = path_select_poll_property;
831
832 /* flags */
834
835 /* properties */
837
838 /* use for redo */
839 prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
841}
842
844
845/* -------------------------------------------------------------------- */
848
850{
851 Scene *scene = CTX_data_scene(C);
852 bool found_valid_elements = false;
853
854 ViewLayer *view_layer = CTX_data_view_layer(C);
856 scene, view_layer, CTX_wm_view3d(C));
857 for (Object *obedit : objects) {
859 BMesh *bm = em->bm;
860 BMIter iter;
861 BMEditSelection *ese_src, *ese_dst;
862 BMElem *ele_src = nullptr, *ele_dst = nullptr, *ele;
863
864 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
865 continue;
866 }
867
868 /* first try to find vertices in edit selection */
869 ese_src = static_cast<BMEditSelection *>(bm->selected.last);
870 if (ese_src && (ese_dst = ese_src->prev) && (ese_src->htype == ese_dst->htype)) {
871 ele_src = ese_src->ele;
872 ele_dst = ese_dst->ele;
873 }
874 else {
875 /* if selection history isn't available, find two selected elements */
876 ele_src = ele_dst = nullptr;
877 if ((em->selectmode & SCE_SELECT_VERTEX) && (bm->totvertsel >= 2)) {
878 BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
880 if (ele_src == nullptr) {
881 ele_src = ele;
882 }
883 else if (ele_dst == nullptr) {
884 ele_dst = ele;
885 }
886 else {
887 break;
888 }
889 }
890 }
891 }
892
893 if ((ele_dst == nullptr) && (em->selectmode & SCE_SELECT_EDGE) && (bm->totedgesel >= 2)) {
894 ele_src = nullptr;
895 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
897 if (ele_src == nullptr) {
898 ele_src = ele;
899 }
900 else if (ele_dst == nullptr) {
901 ele_dst = ele;
902 }
903 else {
904 break;
905 }
906 }
907 }
908 }
909
910 if ((ele_dst == nullptr) && (em->selectmode & SCE_SELECT_FACE) && (bm->totfacesel >= 2)) {
911 ele_src = nullptr;
912 BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) {
914 if (ele_src == nullptr) {
915 ele_src = ele;
916 }
917 else if (ele_dst == nullptr) {
918 ele_dst = ele;
919 }
920 else {
921 break;
922 }
923 }
924 }
925 }
926 }
927
928 if (ele_src && ele_dst) {
929 PathSelectParams op_params;
930 path_select_params_from_op(op, scene->toolsettings, &op_params);
931
932 edbm_shortest_path_pick_ex(scene, obedit, &op_params, ele_src, ele_dst);
933
934 found_valid_elements = true;
935 }
936 }
937
938 if (!found_valid_elements) {
940 op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected");
941 return OPERATOR_CANCELLED;
942 }
943
944 return OPERATOR_FINISHED;
945}
946
948{
949 /* identifiers */
950 ot->name = "Select Shortest Path";
951 ot->idname = "MESH_OT_shortest_path_select";
952 ot->description = "Selected shortest path between two vertices/edges/faces";
953
954 /* API callbacks. */
956 ot->poll = ED_operator_editmesh;
957 ot->poll_property = path_select_poll_property;
958
959 /* flags */
961
962 /* properties */
964}
965
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
bool CustomData_has_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
int CustomData_get_offset_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
Base * BKE_view_layer_active_base_get(ViewLayer *view_layer)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
#define ELEM(...)
#define STREQ(a, b)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ CD_PROP_FLOAT
Object is a sort of wrapper for general info.
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
BMEdge * EDBM_edge_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
BMVert * EDBM_vert_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
bool EDBM_uvselect_clear(BMEditMesh *em)
BMFace * EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
bool EDBM_unified_findnearest(ViewContext *vc, blender::Span< Base * > bases, int *r_base_index, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
ViewContext em_setup_viewcontext(bContext *C)
void EDBM_selectmode_flush(BMEditMesh *em)
bool EDBM_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
bool ED_operator_editmesh_region_view3d(bContext *C)
bool ED_operator_editmesh(bContext *C)
@ SEL_OP_ADD
void ED_uvedit_live_unwrap(const Scene *scene, blender::Span< Object * > objects)
float ED_view3d_select_dist_px()
void view3d_operator_needs_gpu(const bContext *C)
void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
#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_BOOL(ele, offset)
#define BM_ELEM_CD_SET_FLOAT(ele, offset, f)
#define BM_ELEM_CD_GET_FLOAT(ele, offset)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
#define BM_ELEM_CD_SET_BOOL(ele, offset, f)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_test_bool(ele, hflag)
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const StringRef name)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
BMesh * bm
return true
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
BMElem * BM_mesh_active_elem_get(BMesh *bm)
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
void BM_mesh_active_face_set(BMesh *bm, BMFace *f)
#define BM_select_history_store(bm, ele)
#define BM_select_history_remove(bm, ele)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
LinkNode * BM_mesh_calc_path_edge(BMesh *bm, BMEdge *e_src, BMEdge *e_dst, const BMCalcPathParams *params, bool(*filter_fn)(BMEdge *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_vert(BMesh *bm, BMVert *v_src, BMVert *v_dst, const BMCalcPathParams *params, bool(*filter_fn)(BMVert *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_face(BMesh *bm, BMFace *f_src, BMFace *f_dst, const BMCalcPathParams *params, bool(*filter_fn)(BMFace *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_region_vert(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, bool(*filter_fn)(BMVert *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_region_edge(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, bool(*filter_fn)(BMEdge *, void *user_data), void *user_data)
LinkNode * BM_mesh_calc_path_region_face(BMesh *bm, BMElem *ele_src, BMElem *ele_dst, bool(*filter_fn)(BMFace *, void *user_data), void *user_data)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static bool edgetag_test_cb(BMEdge *e, void *user_data_v)
static BMElem * edbm_elem_find_nearest(ViewContext *vc, const char htype)
static void mouse_mesh_shortest_path_face(Scene *, Object *obedit, const PathSelectParams *op_params, BMFace *f_act, BMFace *f_dst)
@ EDGE_MODE_SELECT
@ EDGE_MODE_TAG_SEAM
@ EDGE_MODE_TAG_BEVEL
@ EDGE_MODE_TAG_FREESTYLE
@ EDGE_MODE_TAG_SHARP
@ EDGE_MODE_TAG_CREASE
static bool verttag_filter_cb(BMVert *v, void *)
static void verttag_set_cb(BMVert *v, bool val, void *user_data_v)
static bool path_select_poll_property(const bContext *C, wmOperator *, const PropertyRNA *prop)
static wmOperatorStatus edbm_shortest_path_pick_exec(bContext *C, wmOperator *op)
static bool edbm_shortest_path_pick_ex(Scene *scene, Object *obedit, const PathSelectParams *op_params, BMElem *ele_src, BMElem *ele_dst)
static bool facetag_filter_cb(BMFace *f, void *)
static wmOperatorStatus edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void path_select_params_from_op(wmOperator *op, ToolSettings *ts, PathSelectParams *op_params)
void MESH_OT_shortest_path_select(wmOperatorType *ot)
static wmOperatorStatus edbm_shortest_path_select_exec(bContext *C, wmOperator *op)
static void path_select_properties(wmOperatorType *ot)
static void mouse_mesh_shortest_path_vert(Scene *, Object *obedit, const PathSelectParams *op_params, BMVert *v_act, BMVert *v_dst)
static void mouse_mesh_shortest_path_edge(Scene *scene, Object *obedit, const PathSelectParams *op_params, BMEdge *e_act, BMEdge *e_dst)
static bool facetag_test_cb(BMFace *f, void *)
static bool edgetag_filter_cb(BMEdge *e, void *)
static void edgetag_set_cb(BMEdge *e, bool val, void *user_data_v)
static bool verttag_test_cb(BMVert *v, void *)
static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
static void edgetag_ensure_cd_flag(Mesh *mesh, const char edge_mode)
void MESH_OT_shortest_path_pick(wmOperatorType *ot)
static BMElem * edbm_elem_active_elem_or_face_get(BMesh *bm)
BMElem * EDBM_elem_from_index_any(BMEditMesh *em, uint index)
int EDBM_elem_to_index_any(BMEditMesh *em, BMElem *ele)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void base_activate(bContext *C, Base *base)
bool material_active_index_set(Object *ob, int index)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
int RNA_int_get(PointerRNA *ptr, const char *name)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
const char * RNA_property_identifier(const PropertyRNA *prop)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
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)
short selectmode
struct BMEditSelection * prev
BMHeader head
short mat_nr
int totfacesel
int totvertsel
int totedgesel
struct Object * object
Definition DNA_ID.h:414
void * link
struct LinkNode * next
MeshRuntimeHandle * runtime
CheckerIntervalParams interval_params
struct ToolSettings * toolsettings
const PathSelectParams * op_params
int mval[2]
Definition ED_view3d.hh:82
Scene * scene
Definition ED_view3d.hh:73
BMEditMesh * em
Definition ED_view3d.hh:81
ViewLayer * view_layer
Definition ED_view3d.hh:74
View3D * v3d
Definition ED_view3d.hh:78
Object * obedit
Definition ED_view3d.hh:76
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
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)