Blender V4.3
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
9#include "MEM_guardedalloc.h"
10
11#include "DNA_mesh_types.h"
12#include "DNA_object_types.h"
13#include "DNA_scene_types.h"
15
16#ifdef WITH_FREESTYLE
17# include "DNA_meshdata_types.h"
18#endif
19
20#include "BLI_linklist.h"
21#include "BLI_math_vector.h"
22
23#include "BKE_context.hh"
24#include "BKE_customdata.hh"
25#include "BKE_editmesh.hh"
26#include "BKE_layer.hh"
27#include "BKE_mesh_types.hh"
28#include "BKE_report.hh"
29
30#include "ED_mesh.hh"
31#include "ED_object.hh"
32#include "ED_screen.hh"
33#include "ED_select_utils.hh"
34#include "ED_uvedit.hh"
35#include "ED_view3d.hh"
36
37#include "RNA_access.hh"
38#include "RNA_define.hh"
39
40#include "WM_api.hh"
41#include "WM_types.hh"
42
43#include "bmesh.hh"
44#include "bmesh_tools.hh"
45
46#include "DEG_depsgraph.hh"
47
48#include "mesh_intern.hh" /* own include */
49
50using blender::Vector;
51
52/* -------------------------------------------------------------------- */
56enum {
63};
64
74
76{
77 static const EnumPropertyItem edge_tag_items[] = {
78 {EDGE_MODE_SELECT, "SELECT", 0, "Select", ""},
79 {EDGE_MODE_TAG_SEAM, "SEAM", 0, "Tag Seam", ""},
80 {EDGE_MODE_TAG_SHARP, "SHARP", 0, "Tag Sharp", ""},
81 {EDGE_MODE_TAG_CREASE, "CREASE", 0, "Tag Crease", ""},
82 {EDGE_MODE_TAG_BEVEL, "BEVEL", 0, "Tag Bevel", ""},
83 {EDGE_MODE_TAG_FREESTYLE, "FREESTYLE", 0, "Tag Freestyle Edge Mark", ""},
84 {0, nullptr, 0, nullptr, nullptr},
85 };
86
88 "edge_mode",
89 edge_tag_items,
91 "Edge Tag",
92 "The edge flag to tag when selecting the shortest path");
93
95 "use_face_step",
96 false,
97 "Face Stepping",
98 "Traverse connected faces (includes diagonals and edge-rings)");
100 "use_topology_distance",
101 false,
102 "Topology Distance",
103 "Find the minimum number of steps, ignoring spatial distance");
105 "use_fill",
106 false,
107 "Fill Region",
108 "Select all paths between the source/destination elements");
110}
111
113 ToolSettings *ts,
114 PathSelectParams *op_params)
115{
116 {
117 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "edge_mode");
118 if (RNA_property_is_set(op->ptr, prop)) {
119 op_params->edge_mode = RNA_property_enum_get(op->ptr, prop);
120 if (op->flag & OP_IS_INVOKE) {
121 ts->edge_mode = op_params->edge_mode;
122 }
123 }
124 else {
125 op_params->edge_mode = ts->edge_mode;
126 RNA_property_enum_set(op->ptr, prop, op_params->edge_mode);
127 }
128 }
129
130 op_params->track_active = false;
131 op_params->use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
132 op_params->use_fill = RNA_boolean_get(op->ptr, "use_fill");
133 op_params->use_topology_distance = RNA_boolean_get(op->ptr, "use_topology_distance");
135}
136
138 wmOperator * /*op*/,
139 const PropertyRNA *prop)
140{
141 const char *prop_id = RNA_property_identifier(prop);
142 if (STREQ(prop_id, "edge_mode")) {
143 const Scene *scene = CTX_data_scene(C);
144 ToolSettings *ts = scene->toolsettings;
145 if ((ts->selectmode & SCE_SELECT_EDGE) == 0) {
146 return false;
147 }
148 }
149 return true;
150}
151
158
161/* -------------------------------------------------------------------- */
165/* callbacks */
166static bool verttag_filter_cb(BMVert *v, void * /*user_data_v*/)
167{
169}
170static bool verttag_test_cb(BMVert *v, void * /*user_data_v*/)
171{
173}
174static void verttag_set_cb(BMVert *v, bool val, void *user_data_v)
175{
176 UserData *user_data = static_cast<UserData *>(user_data_v);
177 BM_vert_select_set(user_data->bm, v, val);
178}
179
180static void mouse_mesh_shortest_path_vert(Scene * /*scene*/,
181 Object *obedit,
182 const PathSelectParams *op_params,
183 BMVert *v_act,
184 BMVert *v_dst)
185{
187 BMesh *bm = em->bm;
188
189 int cd_offset = -1;
190 switch (op_params->edge_mode) {
191 case EDGE_MODE_SELECT:
194#ifdef WITH_FREESTYLE
196#endif
197 break;
199 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
200 break;
202 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
203 break;
204 }
205
206 UserData user_data = {bm, static_cast<Mesh *>(obedit->data), cd_offset, op_params};
207 LinkNode *path = nullptr;
208 bool is_path_ordered = false;
209
210 if (v_act && (v_act != v_dst)) {
211 if (op_params->use_fill) {
213 bm, (BMElem *)v_act, (BMElem *)v_dst, verttag_filter_cb, &user_data);
214 }
215 else {
216 is_path_ordered = true;
218 params.use_topology_distance = op_params->use_topology_distance;
219 params.use_step_face = op_params->use_face_step;
220 path = BM_mesh_calc_path_vert(bm, v_act, v_dst, &params, verttag_filter_cb, &user_data);
221 }
222
223 if (path) {
224 if (op_params->track_active) {
226 }
227 }
228 }
229
230 BMVert *v_dst_last = v_dst;
231
232 if (path) {
233 /* toggle the flag */
234 bool all_set = true;
235 LinkNode *node;
236
237 node = path;
238 do {
239 if (!verttag_test_cb((BMVert *)node->link, &user_data)) {
240 all_set = false;
241 break;
242 }
243 } while ((node = node->next));
244
245 int depth = -1;
246 node = path;
247 do {
248 if ((is_path_ordered == false) ||
250 {
251 verttag_set_cb((BMVert *)node->link, !all_set, &user_data);
252 if (is_path_ordered) {
253 v_dst_last = static_cast<BMVert *>(node->link);
254 }
255 }
256 } while ((void)depth++, (node = node->next));
257
258 BLI_linklist_free(path, nullptr);
259 }
260 else {
261 const bool is_act = !verttag_test_cb(v_dst, &user_data);
262 verttag_set_cb(v_dst, is_act, &user_data); /* switch the face option */
263 }
264
266
267 if (op_params->track_active) {
268 /* even if this is selected it may not be in the selection list */
269 if (BM_elem_flag_test(v_dst_last, BM_ELEM_SELECT) == 0) {
270 BM_select_history_remove(bm, v_dst_last);
271 }
272 else {
273 BM_select_history_store(bm, v_dst_last);
274 }
275 }
276
278 params.calc_looptris = false;
279 params.calc_normals = false;
280 params.is_destructive = false;
281 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
282}
283
286/* -------------------------------------------------------------------- */
290/* callbacks */
291static bool edgetag_filter_cb(BMEdge *e, void * /*user_data_v*/)
292{
294}
295static bool edgetag_test_cb(BMEdge *e, void *user_data_v)
296{
297 UserData *user_data = static_cast<UserData *>(user_data_v);
298 const char edge_mode = user_data->op_params->edge_mode;
299
300 switch (edge_mode) {
301 case EDGE_MODE_SELECT:
302 return BM_elem_flag_test(e, BM_ELEM_SELECT) ? true : false;
304 return BM_elem_flag_test(e, BM_ELEM_SEAM) ? true : false;
306 return BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? false : true;
309 return BM_ELEM_CD_GET_FLOAT(e, user_data->cd_offset);
310#ifdef WITH_FREESTYLE
312 BMesh *bm = user_data->bm;
313 FreestyleEdge *fed = static_cast<FreestyleEdge *>(
315 return (!fed) ? false : (fed->flag & FREESTYLE_EDGE_MARK) ? true : false;
316 }
317#endif
318 }
319 return false;
320}
321static void edgetag_set_cb(BMEdge *e, bool val, void *user_data_v)
322{
323 UserData *user_data = static_cast<UserData *>(user_data_v);
324 const char edge_mode = user_data->op_params->edge_mode;
325 BMesh *bm = user_data->bm;
326
327 switch (edge_mode) {
328 case EDGE_MODE_SELECT:
329 BM_edge_select_set(bm, e, val);
330 break;
333 break;
336 break;
339 BM_ELEM_CD_SET_FLOAT(e, user_data->cd_offset, (val) ? 1.0f : 0.0f);
340 break;
341#ifdef WITH_FREESTYLE
343 FreestyleEdge *fed;
344 fed = static_cast<FreestyleEdge *>(
346 if (!val) {
347 fed->flag &= ~FREESTYLE_EDGE_MARK;
348 }
349 else {
351 }
352 break;
353 }
354#endif
355 }
356}
357
358static void edgetag_ensure_cd_flag(Mesh *mesh, const char edge_mode)
359{
360 BMesh *bm = mesh->runtime->edit_mesh->bm;
361
362 switch (edge_mode) {
364 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_FLOAT, "crease_edge")) {
365 BM_data_layer_add_named(bm, &bm->edata, CD_PROP_FLOAT, "crease_edge");
366 }
367 break;
369 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge")) {
370 BM_data_layer_add_named(bm, &bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
371 }
372 break;
373#ifdef WITH_FREESTYLE
377 }
378 break;
379#endif
380 default:
381 break;
382 }
383}
384
385/* Mesh shortest path select, uses previously-selected edge. */
386
387/* since you want to create paths with multiple selects, it doesn't have extend option */
389 Scene *scene, Object *obedit, const PathSelectParams *op_params, BMEdge *e_act, BMEdge *e_dst)
390{
392 BMesh *bm = em->bm;
393
394 int cd_offset = -1;
395 switch (op_params->edge_mode) {
396 case EDGE_MODE_SELECT:
399#ifdef WITH_FREESTYLE
401#endif
402 break;
404 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
405 break;
407 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
408 break;
409 }
410
411 UserData user_data = {bm, static_cast<Mesh *>(obedit->data), cd_offset, op_params};
412 LinkNode *path = nullptr;
413 bool is_path_ordered = false;
414
415 edgetag_ensure_cd_flag(static_cast<Mesh *>(obedit->data), op_params->edge_mode);
416
417 if (e_act && (e_act != e_dst)) {
418 if (op_params->use_fill) {
420 bm, (BMElem *)e_act, (BMElem *)e_dst, edgetag_filter_cb, &user_data);
421 }
422 else {
423 is_path_ordered = true;
425 params.use_topology_distance = op_params->use_topology_distance;
426 params.use_step_face = op_params->use_face_step;
427 path = BM_mesh_calc_path_edge(bm, e_act, e_dst, &params, edgetag_filter_cb, &user_data);
428 }
429
430 if (path) {
431 if (op_params->track_active) {
433 }
434 }
435 }
436
437 BMEdge *e_dst_last = e_dst;
438
439 if (path) {
440 /* toggle the flag */
441 bool all_set = true;
442 LinkNode *node;
443
444 node = path;
445 do {
446 if (!edgetag_test_cb((BMEdge *)node->link, &user_data)) {
447 all_set = false;
448 break;
449 }
450 } while ((node = node->next));
451
452 int depth = -1;
453 node = path;
454 do {
455 if ((is_path_ordered == false) ||
457 {
458 edgetag_set_cb((BMEdge *)node->link, !all_set, &user_data);
459 if (is_path_ordered) {
460 e_dst_last = static_cast<BMEdge *>(node->link);
461 }
462 }
463 } while ((void)depth++, (node = node->next));
464
465 BLI_linklist_free(path, nullptr);
466 }
467 else {
468 const bool is_act = !edgetag_test_cb(e_dst, &user_data);
469 edgetag_ensure_cd_flag(static_cast<Mesh *>(obedit->data), op_params->edge_mode);
470 edgetag_set_cb(e_dst, is_act, &user_data); /* switch the edge option */
471 }
472
473 if (op_params->edge_mode != EDGE_MODE_SELECT) {
474 if (op_params->track_active) {
475 /* simple rules - last edge is _always_ active and selected */
476 if (e_act) {
477 BM_edge_select_set(bm, e_act, false);
478 }
479 BM_edge_select_set(bm, e_dst_last, true);
480 BM_select_history_store(bm, e_dst_last);
481 }
482 }
483
485
486 if (op_params->track_active) {
487 /* even if this is selected it may not be in the selection list */
488 if (op_params->edge_mode == EDGE_MODE_SELECT) {
489 if (edgetag_test_cb(e_dst_last, &user_data) == 0) {
490 BM_select_history_remove(bm, e_dst_last);
491 }
492 else {
493 BM_select_history_store(bm, e_dst_last);
494 }
495 }
496 }
497
499 params.calc_looptris = false;
500 params.calc_normals = false;
501 params.is_destructive = false;
502 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
503
504 if (op_params->edge_mode == EDGE_MODE_TAG_SEAM) {
505 ED_uvedit_live_unwrap(scene, {obedit});
506 }
507}
508
511/* -------------------------------------------------------------------- */
515/* callbacks */
516static bool facetag_filter_cb(BMFace *f, void * /*user_data_v*/)
517{
519}
520// static bool facetag_test_cb(Scene * /*scene*/, BMesh * /*bm*/, BMFace *f)
521static bool facetag_test_cb(BMFace *f, void * /*user_data_v*/)
522{
524}
525// static void facetag_set_cb(BMesh *bm, Scene * /*scene*/, BMFace *f, const bool val)
526static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
527{
528 UserData *user_data = static_cast<UserData *>(user_data_v);
529 BM_face_select_set(user_data->bm, f, val);
530}
531
532static void mouse_mesh_shortest_path_face(Scene * /*scene*/,
533 Object *obedit,
534 const PathSelectParams *op_params,
535 BMFace *f_act,
536 BMFace *f_dst)
537{
539 BMesh *bm = em->bm;
540
541 int cd_offset = -1;
542 switch (op_params->edge_mode) {
543 case EDGE_MODE_SELECT:
546#ifdef WITH_FREESTYLE
548#endif
549 break;
551 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
552 break;
554 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
555 break;
556 }
557
558 UserData user_data = {bm, static_cast<Mesh *>(obedit->data), cd_offset, op_params};
559 LinkNode *path = nullptr;
560 bool is_path_ordered = false;
561
562 if (f_act) {
563 if (op_params->use_fill) {
565 bm, (BMElem *)f_act, (BMElem *)f_dst, facetag_filter_cb, &user_data);
566 }
567 else {
568 is_path_ordered = true;
570 params.use_topology_distance = op_params->use_topology_distance;
571 params.use_step_face = op_params->use_face_step;
572 path = BM_mesh_calc_path_face(bm, f_act, f_dst, &params, facetag_filter_cb, &user_data);
573 }
574
575 if (f_act != f_dst) {
576 if (path) {
577 if (op_params->track_active) {
579 }
580 }
581 }
582 }
583
584 BMFace *f_dst_last = f_dst;
585
586 if (path) {
587 /* toggle the flag */
588 bool all_set = true;
589 LinkNode *node;
590
591 node = path;
592 do {
593 if (!facetag_test_cb((BMFace *)node->link, &user_data)) {
594 all_set = false;
595 break;
596 }
597 } while ((node = node->next));
598
599 int depth = -1;
600 node = path;
601 do {
602 if ((is_path_ordered == false) ||
604 {
605 facetag_set_cb((BMFace *)node->link, !all_set, &user_data);
606 if (is_path_ordered) {
607 f_dst_last = static_cast<BMFace *>(node->link);
608 }
609 }
610 } while ((void)depth++, (node = node->next));
611
612 BLI_linklist_free(path, nullptr);
613 }
614 else {
615 const bool is_act = !facetag_test_cb(f_dst, &user_data);
616 facetag_set_cb(f_dst, is_act, &user_data); /* switch the face option */
617 }
618
620
621 if (op_params->track_active) {
622 /* even if this is selected it may not be in the selection list */
623 if (facetag_test_cb(f_dst_last, &user_data) == 0) {
624 BM_select_history_remove(bm, f_dst_last);
625 }
626 else {
627 BM_select_history_store(bm, f_dst_last);
628 }
629 BM_mesh_active_face_set(bm, f_dst_last);
630 }
631
633 params.calc_looptris = false;
634 params.calc_normals = false;
635 params.is_destructive = false;
636 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
637}
638
641/* -------------------------------------------------------------------- */
646 Object *obedit,
647 const PathSelectParams *op_params,
648 BMElem *ele_src,
649 BMElem *ele_dst)
650{
651 bool ok = false;
652
653 if (ELEM(nullptr, ele_src, ele_dst) || (ele_src->head.htype != ele_dst->head.htype)) {
654 /* pass */
655 }
656 else if (ele_src->head.htype == BM_VERT) {
657 mouse_mesh_shortest_path_vert(scene, obedit, op_params, (BMVert *)ele_src, (BMVert *)ele_dst);
658 ok = true;
659 }
660 else if (ele_src->head.htype == BM_EDGE) {
661 mouse_mesh_shortest_path_edge(scene, obedit, op_params, (BMEdge *)ele_src, (BMEdge *)ele_dst);
662 ok = true;
663 }
664 else if (ele_src->head.htype == BM_FACE) {
665 mouse_mesh_shortest_path_face(scene, obedit, op_params, (BMFace *)ele_src, (BMFace *)ele_dst);
666 ok = true;
667 }
668
669 if (ok) {
670 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
672 }
673
674 return ok;
675}
676
678
679static BMElem *edbm_elem_find_nearest(ViewContext *vc, const char htype)
680{
681 BMEditMesh *em = vc->em;
682 float dist = ED_view3d_select_dist_px();
683
684 if ((em->selectmode & SCE_SELECT_VERTEX) && (htype == BM_VERT)) {
685 return (BMElem *)EDBM_vert_find_nearest(vc, &dist);
686 }
687 if ((em->selectmode & SCE_SELECT_EDGE) && (htype == BM_EDGE)) {
688 return (BMElem *)EDBM_edge_find_nearest(vc, &dist);
689 }
690 if ((em->selectmode & SCE_SELECT_FACE) && (htype == BM_FACE)) {
691 return (BMElem *)EDBM_face_find_nearest(vc, &dist);
692 }
693
694 return nullptr;
695}
696
698{
700
701 if ((ele == nullptr) && bm->act_face && BM_elem_flag_test(bm->act_face, BM_ELEM_SELECT)) {
702 ele = (BMElem *)bm->act_face;
703 }
704
705 return ele;
706}
707
709{
710 if (RNA_struct_property_is_set(op->ptr, "index")) {
711 return edbm_shortest_path_pick_exec(C, op);
712 }
713
714 BMVert *eve = nullptr;
715 BMEdge *eed = nullptr;
716 BMFace *efa = nullptr;
717
718 bool track_active = true;
719
721 copy_v2_v2_int(vc.mval, event->mval);
724 BMEditMesh *em = vc.em;
725
727
728 {
729 int base_index = -1;
731 vc.scene, vc.view_layer, vc.v3d);
732 if (EDBM_unified_findnearest(&vc, bases, &base_index, &eve, &eed, &efa)) {
733 basact = bases[base_index];
735 em = vc.em;
736 }
737 }
738
739 /* If nothing is selected, let's select the picked vertex/edge/face. */
740 if ((vc.em->bm->totvertsel == 0) && (eve || eed || efa)) {
741 /* TODO(dfelinto): right now we try to find the closest element twice.
742 * The ideal is to refactor EDBM_select_pick so it doesn't
743 * have to pick the nearest vert/edge/face again. */
745 params.sel_op = SEL_OP_ADD;
746 EDBM_select_pick(C, event->mval, &params);
747 return OPERATOR_FINISHED;
748 }
749
750 PathSelectParams op_params;
751 path_select_params_from_op(op, vc.scene->toolsettings, &op_params);
752
753 BMElem *ele_src, *ele_dst;
754 if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) ||
755 !(ele_dst = edbm_elem_find_nearest(&vc, ele_src->head.htype)))
756 {
757 /* special case, toggle edge tags even when we don't have a path */
758 if (((em->selectmode & SCE_SELECT_EDGE) && (op_params.edge_mode != EDGE_MODE_SELECT)) &&
759 /* check if we only have a destination edge */
760 ((ele_src == nullptr) && (ele_dst = edbm_elem_find_nearest(&vc, BM_EDGE))))
761 {
762 ele_src = ele_dst;
763 track_active = false;
764 }
765 else {
767 }
768 }
769
770 op_params.track_active = track_active;
771
772 if (!edbm_shortest_path_pick_ex(vc.scene, vc.obedit, &op_params, ele_src, ele_dst)) {
774 }
775
777 if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
779 }
780
781 /* to support redo */
782 BM_mesh_elem_index_ensure(em->bm, ele_dst->head.htype);
783 int index = EDBM_elem_to_index_any(em, ele_dst);
784
785 RNA_int_set(op->ptr, "index", index);
786
787 return OPERATOR_FINISHED;
788}
789
791{
792 Scene *scene = CTX_data_scene(C);
793 Object *obedit = CTX_data_edit_object(C);
795 BMesh *bm = em->bm;
796
797 const int index = RNA_int_get(op->ptr, "index");
798 if (index < 0 || index >= (bm->totvert + bm->totedge + bm->totface)) {
799 return OPERATOR_CANCELLED;
800 }
801
802 BMElem *ele_src, *ele_dst;
803 if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) ||
804 !(ele_dst = EDBM_elem_from_index_any(em, index)))
805 {
806 return OPERATOR_CANCELLED;
807 }
808
809 PathSelectParams op_params;
810 path_select_params_from_op(op, scene->toolsettings, &op_params);
811 op_params.track_active = true;
812
813 if (!edbm_shortest_path_pick_ex(scene, obedit, &op_params, ele_src, ele_dst)) {
814 return OPERATOR_CANCELLED;
815 }
816
817 return OPERATOR_FINISHED;
818}
819
821{
822 PropertyRNA *prop;
823
824 /* identifiers */
825 ot->name = "Pick Shortest Path";
826 ot->idname = "MESH_OT_shortest_path_pick";
827 ot->description = "Select shortest path between two selections";
828
829 /* api callbacks */
834
835 /* flags */
837
838 /* properties */
840
841 /* use for redo */
842 prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
844}
845
848/* -------------------------------------------------------------------- */
853{
854 Scene *scene = CTX_data_scene(C);
855 bool found_valid_elements = false;
856
857 ViewLayer *view_layer = CTX_data_view_layer(C);
859 scene, view_layer, CTX_wm_view3d(C));
860 for (Object *obedit : objects) {
862 BMesh *bm = em->bm;
863 BMIter iter;
864 BMEditSelection *ese_src, *ese_dst;
865 BMElem *ele_src = nullptr, *ele_dst = nullptr, *ele;
866
867 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
868 continue;
869 }
870
871 /* first try to find vertices in edit selection */
872 ese_src = static_cast<BMEditSelection *>(bm->selected.last);
873 if (ese_src && (ese_dst = ese_src->prev) && (ese_src->htype == ese_dst->htype)) {
874 ele_src = ese_src->ele;
875 ele_dst = ese_dst->ele;
876 }
877 else {
878 /* if selection history isn't available, find two selected elements */
879 ele_src = ele_dst = nullptr;
880 if ((em->selectmode & SCE_SELECT_VERTEX) && (bm->totvertsel >= 2)) {
881 BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
883 if (ele_src == nullptr) {
884 ele_src = ele;
885 }
886 else if (ele_dst == nullptr) {
887 ele_dst = ele;
888 }
889 else {
890 break;
891 }
892 }
893 }
894 }
895
896 if ((ele_dst == nullptr) && (em->selectmode & SCE_SELECT_EDGE) && (bm->totedgesel >= 2)) {
897 ele_src = nullptr;
898 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
900 if (ele_src == nullptr) {
901 ele_src = ele;
902 }
903 else if (ele_dst == nullptr) {
904 ele_dst = ele;
905 }
906 else {
907 break;
908 }
909 }
910 }
911 }
912
913 if ((ele_dst == nullptr) && (em->selectmode & SCE_SELECT_FACE) && (bm->totfacesel >= 2)) {
914 ele_src = nullptr;
915 BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) {
917 if (ele_src == nullptr) {
918 ele_src = ele;
919 }
920 else if (ele_dst == nullptr) {
921 ele_dst = ele;
922 }
923 else {
924 break;
925 }
926 }
927 }
928 }
929 }
930
931 if (ele_src && ele_dst) {
932 PathSelectParams op_params;
933 path_select_params_from_op(op, scene->toolsettings, &op_params);
934
935 edbm_shortest_path_pick_ex(scene, obedit, &op_params, ele_src, ele_dst);
936
937 found_valid_elements = true;
938 }
939 }
940
941 if (!found_valid_elements) {
943 op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected");
944 return OPERATOR_CANCELLED;
945 }
946
947 return OPERATOR_FINISHED;
948}
949
951{
952 /* identifiers */
953 ot->name = "Select Shortest Path";
954 ot->idname = "MESH_OT_shortest_path_select";
955 ot->description = "Selected shortest path between two vertices/edges/faces";
956
957 /* api callbacks */
961
962 /* flags */
964
965 /* properties */
967}
968
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)
void * CustomData_bmesh_get(const CustomData *data, void *block, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
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)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
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:1068
@ CD_PROP_FLOAT
@ CD_FREESTYLE_EDGE
@ FREESTYLE_EDGE_MARK
Object is a sort of wrapper for general info.
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ 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)
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_opengl(const bContext *C)
void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact)
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ 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
#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_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(BMesh *bm, CustomData *data, int type)
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
ATTR_WARN_UNUSED_RESULT BMesh * bm
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
OperationNode * node
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)
static int edbm_shortest_path_select_exec(bContext *C, wmOperator *op)
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 bool edbm_shortest_path_pick_ex(Scene *scene, Object *obedit, const PathSelectParams *op_params, BMElem *ele_src, BMElem *ele_dst)
static int edbm_shortest_path_pick_exec(bContext *C, wmOperator *op)
static bool facetag_filter_cb(BMFace *f, void *)
static void path_select_params_from_op(wmOperator *op, ToolSettings *ts, PathSelectParams *op_params)
void MESH_OT_shortest_path_select(wmOperatorType *ot)
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 int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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)
@ EDGE_MODE_SELECT
@ EDGE_MODE_TAG_SEAM
@ EDGE_MODE_TAG_BEVEL
@ EDGE_MODE_TAG_FREESTYLE
@ EDGE_MODE_TAG_SHARP
@ EDGE_MODE_TAG_CREASE
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)
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
void * data
BMHeader head
int totvert
int totfacesel
int totedge
ListBase selected
CustomData edata
int totvertsel
BMFace * act_face
int totedgesel
int totface
struct Object * object
Definition DNA_ID.h:413
void * last
CheckerIntervalParams interval_params
struct ToolSettings * toolsettings
const PathSelectParams * op_params
int mval[2]
Definition ED_view3d.hh:78
Scene * scene
Definition ED_view3d.hh:69
BMEditMesh * em
Definition ED_view3d.hh:77
ViewLayer * view_layer
Definition ED_view3d.hh:70
View3D * v3d
Definition ED_view3d.hh:74
Object * obedit
Definition ED_view3d.hh:72
int mval[2]
Definition WM_types.hh:728
const char * name
Definition WM_types.hh:990
bool(* poll_property)(const bContext *C, wmOperator *op, const PropertyRNA *prop) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1048
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
void 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)