Blender V4.5
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
264
265 if (op_params->track_active) {
266 /* even if this is selected it may not be in the selection list */
267 if (BM_elem_flag_test(v_dst_last, BM_ELEM_SELECT) == 0) {
268 BM_select_history_remove(bm, v_dst_last);
269 }
270 else {
271 BM_select_history_store(bm, v_dst_last);
272 }
273 }
274
276 params.calc_looptris = false;
277 params.calc_normals = false;
278 params.is_destructive = false;
279 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
280}
281
283
284/* -------------------------------------------------------------------- */
287
288/* callbacks */
289static bool edgetag_filter_cb(BMEdge *e, void * /*user_data_v*/)
290{
292}
293static bool edgetag_test_cb(BMEdge *e, void *user_data_v)
294{
295 UserData *user_data = static_cast<UserData *>(user_data_v);
296 const char edge_mode = user_data->op_params->edge_mode;
297
298 switch (edge_mode) {
299 case EDGE_MODE_SELECT:
300 return BM_elem_flag_test(e, BM_ELEM_SELECT) ? true : false;
302 return BM_elem_flag_test(e, BM_ELEM_SEAM) ? true : false;
304 return BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? false : true;
307 return BM_ELEM_CD_GET_FLOAT(e, user_data->cd_offset);
308#ifdef WITH_FREESTYLE
310 BMesh *bm = user_data->bm;
311 FreestyleEdge *fed = static_cast<FreestyleEdge *>(
312 CustomData_bmesh_get(&bm->edata, e->head.data, CD_FREESTYLE_EDGE));
313 return (!fed) ? false : (fed->flag & FREESTYLE_EDGE_MARK) ? true : false;
314 }
315#endif
316 }
317 return false;
318}
319static void edgetag_set_cb(BMEdge *e, bool val, void *user_data_v)
320{
321 UserData *user_data = static_cast<UserData *>(user_data_v);
322 const char edge_mode = user_data->op_params->edge_mode;
323 BMesh *bm = user_data->bm;
324
325 switch (edge_mode) {
326 case EDGE_MODE_SELECT:
327 BM_edge_select_set(bm, e, val);
328 break;
331 break;
334 break;
337 BM_ELEM_CD_SET_FLOAT(e, user_data->cd_offset, (val) ? 1.0f : 0.0f);
338 break;
339#ifdef WITH_FREESTYLE
341 FreestyleEdge *fed;
342 fed = static_cast<FreestyleEdge *>(
343 CustomData_bmesh_get(&bm->edata, e->head.data, CD_FREESTYLE_EDGE));
344 if (!val) {
346 }
347 else {
349 }
350 break;
351 }
352#endif
353 }
354}
355
356static void edgetag_ensure_cd_flag(Mesh *mesh, const char edge_mode)
357{
358 BMesh *bm = mesh->runtime->edit_mesh->bm;
359
360 switch (edge_mode) {
362 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_FLOAT, "crease_edge")) {
363 BM_data_layer_add_named(bm, &bm->edata, CD_PROP_FLOAT, "crease_edge");
364 }
365 break;
367 if (!CustomData_has_layer_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge")) {
368 BM_data_layer_add_named(bm, &bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
369 }
370 break;
371#ifdef WITH_FREESTYLE
375 }
376 break;
377#endif
378 default:
379 break;
380 }
381}
382
383/* Mesh shortest path select, uses previously-selected edge. */
384
385/* since you want to create paths with multiple selects, it doesn't have extend option */
387 Scene *scene, Object *obedit, const PathSelectParams *op_params, BMEdge *e_act, BMEdge *e_dst)
388{
390 BMesh *bm = em->bm;
391
392 int cd_offset = -1;
393 switch (op_params->edge_mode) {
394 case EDGE_MODE_SELECT:
397#ifdef WITH_FREESTYLE
399#endif
400 break;
402 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
403 break;
405 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
406 break;
407 }
408
409 UserData user_data = {bm, static_cast<Mesh *>(obedit->data), cd_offset, op_params};
410 LinkNode *path = nullptr;
411 bool is_path_ordered = false;
412
413 edgetag_ensure_cd_flag(static_cast<Mesh *>(obedit->data), op_params->edge_mode);
414
415 if (e_act && (e_act != e_dst)) {
416 if (op_params->use_fill) {
418 bm, (BMElem *)e_act, (BMElem *)e_dst, edgetag_filter_cb, &user_data);
419 }
420 else {
421 is_path_ordered = true;
423 params.use_topology_distance = op_params->use_topology_distance;
424 params.use_step_face = op_params->use_face_step;
425 path = BM_mesh_calc_path_edge(bm, e_act, e_dst, &params, edgetag_filter_cb, &user_data);
426 }
427
428 if (path) {
429 if (op_params->track_active) {
431 }
432 }
433 }
434
435 BMEdge *e_dst_last = e_dst;
436
437 if (path) {
438 /* toggle the flag */
439 bool all_set = true;
440 LinkNode *node;
441
442 node = path;
443 do {
444 if (!edgetag_test_cb((BMEdge *)node->link, &user_data)) {
445 all_set = false;
446 break;
447 }
448 } while ((node = node->next));
449
450 int depth = -1;
451 node = path;
452 do {
453 if ((is_path_ordered == false) ||
455 {
456 edgetag_set_cb((BMEdge *)node->link, !all_set, &user_data);
457 if (is_path_ordered) {
458 e_dst_last = static_cast<BMEdge *>(node->link);
459 }
460 }
461 } while ((void)depth++, (node = node->next));
462
463 BLI_linklist_free(path, nullptr);
464 }
465 else {
466 const bool is_act = !edgetag_test_cb(e_dst, &user_data);
467 edgetag_ensure_cd_flag(static_cast<Mesh *>(obedit->data), op_params->edge_mode);
468 edgetag_set_cb(e_dst, is_act, &user_data); /* switch the edge option */
469 }
470
471 if (op_params->edge_mode != EDGE_MODE_SELECT) {
472 if (op_params->track_active) {
473 /* simple rules - last edge is _always_ active and selected */
474 if (e_act) {
475 BM_edge_select_set(bm, e_act, false);
476 }
477 BM_edge_select_set(bm, e_dst_last, true);
478 BM_select_history_store(bm, e_dst_last);
479 }
480 }
481
483
484 if (op_params->track_active) {
485 /* even if this is selected it may not be in the selection list */
486 if (op_params->edge_mode == EDGE_MODE_SELECT) {
487 if (edgetag_test_cb(e_dst_last, &user_data) == 0) {
488 BM_select_history_remove(bm, e_dst_last);
489 }
490 else {
491 BM_select_history_store(bm, e_dst_last);
492 }
493 }
494 }
495
497 params.calc_looptris = false;
498 params.calc_normals = false;
499 params.is_destructive = false;
500 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
501
502 if (op_params->edge_mode == EDGE_MODE_TAG_SEAM) {
503 ED_uvedit_live_unwrap(scene, {obedit});
504 }
505}
506
508
509/* -------------------------------------------------------------------- */
512
513/* callbacks */
514static bool facetag_filter_cb(BMFace *f, void * /*user_data_v*/)
515{
517}
518// static bool facetag_test_cb(Scene * /*scene*/, BMesh * /*bm*/, BMFace *f)
519static bool facetag_test_cb(BMFace *f, void * /*user_data_v*/)
520{
522}
523// static void facetag_set_cb(BMesh *bm, Scene * /*scene*/, BMFace *f, const bool val)
524static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
525{
526 UserData *user_data = static_cast<UserData *>(user_data_v);
527 BM_face_select_set(user_data->bm, f, val);
528}
529
530static void mouse_mesh_shortest_path_face(Scene * /*scene*/,
531 Object *obedit,
532 const PathSelectParams *op_params,
533 BMFace *f_act,
534 BMFace *f_dst)
535{
537 BMesh *bm = em->bm;
538
539 int cd_offset = -1;
540 switch (op_params->edge_mode) {
541 case EDGE_MODE_SELECT:
544#ifdef WITH_FREESTYLE
546#endif
547 break;
549 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "crease_edge");
550 break;
552 cd_offset = CustomData_get_offset_named(&bm->edata, CD_PROP_FLOAT, "bevel_weight_edge");
553 break;
554 }
555
556 UserData user_data = {bm, static_cast<Mesh *>(obedit->data), cd_offset, op_params};
557 LinkNode *path = nullptr;
558 bool is_path_ordered = false;
559
560 if (f_act) {
561 if (op_params->use_fill) {
563 bm, (BMElem *)f_act, (BMElem *)f_dst, facetag_filter_cb, &user_data);
564 }
565 else {
566 is_path_ordered = true;
568 params.use_topology_distance = op_params->use_topology_distance;
569 params.use_step_face = op_params->use_face_step;
570 path = BM_mesh_calc_path_face(bm, f_act, f_dst, &params, facetag_filter_cb, &user_data);
571 }
572
573 if (f_act != f_dst) {
574 if (path) {
575 if (op_params->track_active) {
577 }
578 }
579 }
580 }
581
582 BMFace *f_dst_last = f_dst;
583
584 if (path) {
585 /* toggle the flag */
586 bool all_set = true;
587 LinkNode *node;
588
589 node = path;
590 do {
591 if (!facetag_test_cb((BMFace *)node->link, &user_data)) {
592 all_set = false;
593 break;
594 }
595 } while ((node = node->next));
596
597 int depth = -1;
598 node = path;
599 do {
600 if ((is_path_ordered == false) ||
602 {
603 facetag_set_cb((BMFace *)node->link, !all_set, &user_data);
604 if (is_path_ordered) {
605 f_dst_last = static_cast<BMFace *>(node->link);
606 }
607 }
608 } while ((void)depth++, (node = node->next));
609
610 BLI_linklist_free(path, nullptr);
611 }
612 else {
613 const bool is_act = !facetag_test_cb(f_dst, &user_data);
614 facetag_set_cb(f_dst, is_act, &user_data); /* switch the face option */
615 }
616
618
619 if (op_params->track_active) {
620 /* even if this is selected it may not be in the selection list */
621 if (facetag_test_cb(f_dst_last, &user_data) == 0) {
622 BM_select_history_remove(bm, f_dst_last);
623 }
624 else {
625 BM_select_history_store(bm, f_dst_last);
626 }
627 BM_mesh_active_face_set(bm, f_dst_last);
628
630 em->mat_nr = f_dst_last->mat_nr;
631 }
632
634 params.calc_looptris = false;
635 params.calc_normals = false;
636 params.is_destructive = false;
637 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
638}
639
641
642/* -------------------------------------------------------------------- */
645
647 Object *obedit,
648 const PathSelectParams *op_params,
649 BMElem *ele_src,
650 BMElem *ele_dst)
651{
652 bool ok = false;
653
654 if (ELEM(nullptr, ele_src, ele_dst) || (ele_src->head.htype != ele_dst->head.htype)) {
655 /* pass */
656 }
657 else if (ele_src->head.htype == BM_VERT) {
658 mouse_mesh_shortest_path_vert(scene, obedit, op_params, (BMVert *)ele_src, (BMVert *)ele_dst);
659 ok = true;
660 }
661 else if (ele_src->head.htype == BM_EDGE) {
662 mouse_mesh_shortest_path_edge(scene, obedit, op_params, (BMEdge *)ele_src, (BMEdge *)ele_dst);
663 ok = true;
664 }
665 else if (ele_src->head.htype == BM_FACE) {
666 mouse_mesh_shortest_path_face(scene, obedit, op_params, (BMFace *)ele_src, (BMFace *)ele_dst);
667 ok = true;
668 }
669
670 if (ok) {
671 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
673 }
674
675 return ok;
676}
677
679
680static BMElem *edbm_elem_find_nearest(ViewContext *vc, const char htype)
681{
682 BMEditMesh *em = vc->em;
683 float dist = ED_view3d_select_dist_px();
684
685 if ((em->selectmode & SCE_SELECT_VERTEX) && (htype == BM_VERT)) {
686 return (BMElem *)EDBM_vert_find_nearest(vc, &dist);
687 }
688 if ((em->selectmode & SCE_SELECT_EDGE) && (htype == BM_EDGE)) {
689 return (BMElem *)EDBM_edge_find_nearest(vc, &dist);
690 }
691 if ((em->selectmode & SCE_SELECT_FACE) && (htype == BM_FACE)) {
692 return (BMElem *)EDBM_face_find_nearest(vc, &dist);
693 }
694
695 return nullptr;
696}
697
699{
701
702 if ((ele == nullptr) && bm->act_face && BM_elem_flag_test(bm->act_face, BM_ELEM_SELECT)) {
703 ele = (BMElem *)bm->act_face;
704 }
705
706 return ele;
707}
708
710 wmOperator *op,
711 const wmEvent *event)
712{
713 if (RNA_struct_property_is_set(op->ptr, "index")) {
715 }
716
717 BMVert *eve = nullptr;
718 BMEdge *eed = nullptr;
719 BMFace *efa = nullptr;
720
721 bool track_active = true;
722
724 copy_v2_v2_int(vc.mval, event->mval);
727 BMEditMesh *em = vc.em;
728
730
731 {
732 int base_index = -1;
734 vc.scene, vc.view_layer, vc.v3d);
735 if (EDBM_unified_findnearest(&vc, bases, &base_index, &eve, &eed, &efa)) {
736 basact = bases[base_index];
738 em = vc.em;
739 }
740 }
741
742 /* If nothing is selected, let's select the picked vertex/edge/face. */
743 if ((vc.em->bm->totvertsel == 0) && (eve || eed || efa)) {
744 /* TODO(dfelinto): right now we try to find the closest element twice.
745 * The ideal is to refactor EDBM_select_pick so it doesn't
746 * have to pick the nearest vert/edge/face again. */
747 const SelectPick_Params params = {
748 /*sel_op*/ SEL_OP_ADD,
749 };
750 EDBM_select_pick(C, event->mval, params);
751 return OPERATOR_FINISHED;
752 }
753
754 PathSelectParams op_params;
755 path_select_params_from_op(op, vc.scene->toolsettings, &op_params);
756
757 BMElem *ele_src, *ele_dst;
758 if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) ||
759 !(ele_dst = edbm_elem_find_nearest(&vc, ele_src->head.htype)))
760 {
761 /* special case, toggle edge tags even when we don't have a path */
762 if (((em->selectmode & SCE_SELECT_EDGE) && (op_params.edge_mode != EDGE_MODE_SELECT)) &&
763 /* check if we only have a destination edge */
764 ((ele_src == nullptr) && (ele_dst = edbm_elem_find_nearest(&vc, BM_EDGE))))
765 {
766 ele_src = ele_dst;
767 track_active = false;
768 }
769 else {
771 }
772 }
773
774 op_params.track_active = track_active;
775
776 if (!edbm_shortest_path_pick_ex(vc.scene, vc.obedit, &op_params, ele_src, ele_dst)) {
778 }
779
781 if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
783 }
784
785 /* to support redo */
786 BM_mesh_elem_index_ensure(em->bm, ele_dst->head.htype);
787 int index = EDBM_elem_to_index_any(em, ele_dst);
788
789 RNA_int_set(op->ptr, "index", index);
790
791 return OPERATOR_FINISHED;
792}
793
795{
796 Scene *scene = CTX_data_scene(C);
797 Object *obedit = CTX_data_edit_object(C);
799 BMesh *bm = em->bm;
800
801 const int index = RNA_int_get(op->ptr, "index");
802 if (index < 0 || index >= (bm->totvert + bm->totedge + bm->totface)) {
803 return OPERATOR_CANCELLED;
804 }
805
806 BMElem *ele_src, *ele_dst;
807 if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) ||
808 !(ele_dst = EDBM_elem_from_index_any(em, index)))
809 {
810 return OPERATOR_CANCELLED;
811 }
812
813 PathSelectParams op_params;
814 path_select_params_from_op(op, scene->toolsettings, &op_params);
815 op_params.track_active = true;
816
817 if (!edbm_shortest_path_pick_ex(scene, obedit, &op_params, ele_src, ele_dst)) {
818 return OPERATOR_CANCELLED;
819 }
820
821 return OPERATOR_FINISHED;
822}
823
825{
826 PropertyRNA *prop;
827
828 /* identifiers */
829 ot->name = "Pick Shortest Path";
830 ot->idname = "MESH_OT_shortest_path_pick";
831 ot->description = "Select shortest path between two selections";
832
833 /* API callbacks. */
837 ot->poll_property = path_select_poll_property;
838
839 /* flags */
841
842 /* properties */
844
845 /* use for redo */
846 prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
848}
849
851
852/* -------------------------------------------------------------------- */
855
857{
858 Scene *scene = CTX_data_scene(C);
859 bool found_valid_elements = false;
860
861 ViewLayer *view_layer = CTX_data_view_layer(C);
863 scene, view_layer, CTX_wm_view3d(C));
864 for (Object *obedit : objects) {
866 BMesh *bm = em->bm;
867 BMIter iter;
868 BMEditSelection *ese_src, *ese_dst;
869 BMElem *ele_src = nullptr, *ele_dst = nullptr, *ele;
870
871 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
872 continue;
873 }
874
875 /* first try to find vertices in edit selection */
876 ese_src = static_cast<BMEditSelection *>(bm->selected.last);
877 if (ese_src && (ese_dst = ese_src->prev) && (ese_src->htype == ese_dst->htype)) {
878 ele_src = ese_src->ele;
879 ele_dst = ese_dst->ele;
880 }
881 else {
882 /* if selection history isn't available, find two selected elements */
883 ele_src = ele_dst = nullptr;
884 if ((em->selectmode & SCE_SELECT_VERTEX) && (bm->totvertsel >= 2)) {
885 BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
887 if (ele_src == nullptr) {
888 ele_src = ele;
889 }
890 else if (ele_dst == nullptr) {
891 ele_dst = ele;
892 }
893 else {
894 break;
895 }
896 }
897 }
898 }
899
900 if ((ele_dst == nullptr) && (em->selectmode & SCE_SELECT_EDGE) && (bm->totedgesel >= 2)) {
901 ele_src = nullptr;
902 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
904 if (ele_src == nullptr) {
905 ele_src = ele;
906 }
907 else if (ele_dst == nullptr) {
908 ele_dst = ele;
909 }
910 else {
911 break;
912 }
913 }
914 }
915 }
916
917 if ((ele_dst == nullptr) && (em->selectmode & SCE_SELECT_FACE) && (bm->totfacesel >= 2)) {
918 ele_src = nullptr;
919 BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) {
921 if (ele_src == nullptr) {
922 ele_src = ele;
923 }
924 else if (ele_dst == nullptr) {
925 ele_dst = ele;
926 }
927 else {
928 break;
929 }
930 }
931 }
932 }
933 }
934
935 if (ele_src && ele_dst) {
936 PathSelectParams op_params;
937 path_select_params_from_op(op, scene->toolsettings, &op_params);
938
939 edbm_shortest_path_pick_ex(scene, obedit, &op_params, ele_src, ele_dst);
940
941 found_valid_elements = true;
942 }
943 }
944
945 if (!found_valid_elements) {
947 op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected");
948 return OPERATOR_CANCELLED;
949 }
950
951 return OPERATOR_FINISHED;
952}
953
955{
956 /* identifiers */
957 ot->name = "Select Shortest Path";
958 ot->idname = "MESH_OT_shortest_path_select";
959 ot->description = "Selected shortest path between two vertices/edges/faces";
960
961 /* API callbacks. */
963 ot->poll = ED_operator_editmesh;
964 ot->poll_property = path_select_poll_property;
965
966 /* flags */
968
969 /* properties */
971}
972
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: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)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
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:1009
@ 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_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)
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:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:390
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SELECT
Definition WM_types.hh:505
#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_named(BMesh *bm, CustomData *data, int type, const StringRef name)
void BM_data_layer_add(BMesh *bm, CustomData *data, int type)
#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)
@ EDGE_MODE_SELECT
@ EDGE_MODE_TAG_SEAM
@ EDGE_MODE_TAG_BEVEL
@ EDGE_MODE_TAG_FREESTYLE
@ EDGE_MODE_TAG_SHARP
@ EDGE_MODE_TAG_CREASE
static void mouse_mesh_shortest_path_face(Scene *, Object *obedit, const PathSelectParams *op_params, BMFace *f_act, BMFace *f_dst)
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:404
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:760
struct ReportList * reports
struct PointerRNA * ptr
void WM_main_add_notifier(uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4225
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)