Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / mesh / editmesh_path.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2004 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/mesh/editmesh_path.c
29  *  \ingroup edmesh
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "DNA_scene_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_mesh_types.h"
37 #include "DNA_windowmanager_types.h"
38
39 #ifdef WITH_FREESTYLE
40 #  include "DNA_meshdata_types.h"
41 #endif
42
43 #include "BLI_math.h"
44 #include "BLI_linklist.h"
45
46 #include "BKE_layer.h"
47 #include "BKE_context.h"
48 #include "BKE_editmesh.h"
49 #include "BKE_report.h"
50
51 #include "ED_mesh.h"
52 #include "ED_screen.h"
53 #include "ED_uvedit.h"
54 #include "ED_view3d.h"
55
56 #include "RNA_access.h"
57 #include "RNA_define.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 #include "bmesh.h"
63 #include "bmesh_tools.h"
64
65 #include "DEG_depsgraph.h"
66
67 #include "mesh_intern.h"  /* own include */
68
69 /* -------------------------------------------------------------------- */
70 /** \name Path Select Struct & Properties
71  * \{ */
72
73 struct PathSelectParams {
74         bool track_active;  /* ensure the active element is the last selected item (handy for picking) */
75         bool use_topology_distance;
76         bool use_face_step;
77         bool use_fill;
78         char edge_mode;
79         struct CheckerIntervalParams interval_params;
80 };
81
82 static void path_select_properties(wmOperatorType *ot)
83 {
84         RNA_def_boolean(
85                 ot->srna, "use_face_step", false, "Face Stepping",
86                 "Traverse connected faces (includes diagonals and edge-rings)");
87         RNA_def_boolean(
88                 ot->srna, "use_topology_distance", false, "Topology Distance",
89                 "Find the minimum number of steps, ignoring spatial distance");
90         RNA_def_boolean(
91                 ot->srna, "use_fill", false, "Fill Region",
92                 "Select all paths between the source/destination elements");
93         WM_operator_properties_checker_interval(ot, true);
94 }
95
96 static void path_select_params_from_op(wmOperator *op, struct PathSelectParams *op_params)
97 {
98         op_params->edge_mode = EDGE_MODE_SELECT;
99         op_params->track_active = false;
100         op_params->use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
101         op_params->use_fill = RNA_boolean_get(op->ptr, "use_fill");
102         op_params->use_topology_distance = RNA_boolean_get(op->ptr, "use_topology_distance");
103         WM_operator_properties_checker_interval_from_op(op, &op_params->interval_params);
104 }
105
106 struct UserData {
107         BMesh *bm;
108         Mesh  *me;
109         const struct PathSelectParams *op_params;
110 };
111
112 /** \} */
113
114 /* -------------------------------------------------------------------- */
115 /** \name Vert Path
116  * \{ */
117
118 /* callbacks */
119 static bool verttag_filter_cb(BMVert *v, void *UNUSED(user_data_v))
120 {
121         return !BM_elem_flag_test(v, BM_ELEM_HIDDEN);
122 }
123 static bool verttag_test_cb(BMVert *v, void *UNUSED(user_data_v))
124 {
125         return BM_elem_flag_test_bool(v, BM_ELEM_SELECT);
126 }
127 static void verttag_set_cb(BMVert *v, bool val, void *user_data_v)
128 {
129         struct UserData *user_data = user_data_v;
130         BM_vert_select_set(user_data->bm, v, val);
131 }
132
133 static void mouse_mesh_shortest_path_vert(
134         Scene *UNUSED(scene), Object *obedit, const struct PathSelectParams *op_params,
135         BMVert *v_act, BMVert *v_dst)
136 {
137         BMEditMesh *em = BKE_editmesh_from_object(obedit);
138         BMesh *bm = em->bm;
139
140         struct UserData user_data = {bm, obedit->data, op_params};
141         LinkNode *path = NULL;
142         bool is_path_ordered = false;
143
144         if (v_act && (v_act != v_dst)) {
145                 if (op_params->use_fill) {
146                         path = BM_mesh_calc_path_region_vert(
147                                 bm, (BMElem *)v_act, (BMElem *)v_dst,
148                                 verttag_filter_cb, &user_data);
149                 }
150                 else {
151                         is_path_ordered = true;
152                         path = BM_mesh_calc_path_vert(
153                                 bm, v_act, v_dst,
154                                 &(const struct BMCalcPathParams) {
155                                     .use_topology_distance = op_params->use_topology_distance,
156                                     .use_step_face = op_params->use_face_step,
157                                 },
158                                 verttag_filter_cb, &user_data);
159                 }
160
161                 if (path) {
162                         if (op_params->track_active) {
163                                 BM_select_history_remove(bm, v_act);
164                         }
165                 }
166         }
167
168         BMVert *v_dst_last = v_dst;
169
170         if (path) {
171                 /* toggle the flag */
172                 bool all_set = true;
173                 LinkNode *node;
174
175                 node = path;
176                 do {
177                         if (!verttag_test_cb((BMVert *)node->link, &user_data)) {
178                                 all_set = false;
179                                 break;
180                         }
181                 } while ((node = node->next));
182
183                 /* We need to start as if just *after* a 'skip' block... */
184                 int depth = op_params->interval_params.skip;
185                 node = path;
186                 do {
187                         if ((is_path_ordered == false) ||
188                             WM_operator_properties_checker_interval_test(&op_params->interval_params, depth))
189                         {
190                                 verttag_set_cb((BMVert *)node->link, !all_set, &user_data);
191                                 if (is_path_ordered) {
192                                         v_dst_last = node->link;
193                                 }
194                         }
195                 } while ((void)depth++, (node = node->next));
196
197                 BLI_linklist_free(path, NULL);
198         }
199         else {
200                 const bool is_act = !verttag_test_cb(v_dst, &user_data);
201                 verttag_set_cb(v_dst, is_act, &user_data); /* switch the face option */
202         }
203
204         EDBM_selectmode_flush(em);
205
206         if (op_params->track_active) {
207                 /* even if this is selected it may not be in the selection list */
208                 if (BM_elem_flag_test(v_dst_last, BM_ELEM_SELECT) == 0) {
209                         BM_select_history_remove(bm, v_dst_last);
210                 }
211                 else {
212                         BM_select_history_store(bm, v_dst_last);
213                 }
214         }
215
216         EDBM_update_generic(em, false, false);
217 }
218
219 /** \} */
220
221 /* -------------------------------------------------------------------- */
222 /** \name Edge Path
223  * \{ */
224
225 /* callbacks */
226 static bool edgetag_filter_cb(BMEdge *e, void *UNUSED(user_data_v))
227 {
228         return !BM_elem_flag_test(e, BM_ELEM_HIDDEN);
229 }
230 static bool edgetag_test_cb(BMEdge *e, void *user_data_v)
231 {
232         struct UserData *user_data = user_data_v;
233         const char edge_mode = user_data->op_params->edge_mode;
234         BMesh *bm = user_data->bm;
235
236         switch (edge_mode) {
237                 case EDGE_MODE_SELECT:
238                         return BM_elem_flag_test(e, BM_ELEM_SELECT) ? true : false;
239                 case EDGE_MODE_TAG_SEAM:
240                         return BM_elem_flag_test(e, BM_ELEM_SEAM) ? true : false;
241                 case EDGE_MODE_TAG_SHARP:
242                         return BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? false : true;
243                 case EDGE_MODE_TAG_CREASE:
244                         return BM_elem_float_data_get(&bm->edata, e, CD_CREASE) ? true : false;
245                 case EDGE_MODE_TAG_BEVEL:
246                         return BM_elem_float_data_get(&bm->edata, e, CD_BWEIGHT) ? true : false;
247 #ifdef WITH_FREESTYLE
248                 case EDGE_MODE_TAG_FREESTYLE:
249                 {
250                         FreestyleEdge *fed = CustomData_bmesh_get(&bm->edata, e->head.data, CD_FREESTYLE_EDGE);
251                         return (!fed) ? false : (fed->flag & FREESTYLE_EDGE_MARK) ? true : false;
252                 }
253 #endif
254         }
255         return 0;
256 }
257 static void edgetag_set_cb(BMEdge *e, bool val, void *user_data_v)
258 {
259         struct UserData *user_data = user_data_v;
260         const char edge_mode = user_data->op_params->edge_mode;
261         BMesh *bm = user_data->bm;
262
263         switch (edge_mode) {
264                 case EDGE_MODE_SELECT:
265                         BM_edge_select_set(bm, e, val);
266                         break;
267                 case EDGE_MODE_TAG_SEAM:
268                         BM_elem_flag_set(e, BM_ELEM_SEAM, val);
269                         break;
270                 case EDGE_MODE_TAG_SHARP:
271                         BM_elem_flag_set(e, BM_ELEM_SMOOTH, !val);
272                         break;
273                 case EDGE_MODE_TAG_CREASE:
274                         BM_elem_float_data_set(&bm->edata, e, CD_CREASE, (val) ? 1.0f : 0.0f);
275                         break;
276                 case EDGE_MODE_TAG_BEVEL:
277                         BM_elem_float_data_set(&bm->edata, e, CD_BWEIGHT, (val) ? 1.0f : 0.0f);
278                         break;
279 #ifdef WITH_FREESTYLE
280                 case EDGE_MODE_TAG_FREESTYLE:
281                 {
282                         FreestyleEdge *fed;
283                         fed = CustomData_bmesh_get(&bm->edata, e->head.data, CD_FREESTYLE_EDGE);
284                         if (!val)
285                                 fed->flag &= ~FREESTYLE_EDGE_MARK;
286                         else
287                                 fed->flag |= FREESTYLE_EDGE_MARK;
288                         break;
289                 }
290 #endif
291         }
292 }
293
294 static void edgetag_ensure_cd_flag(Mesh *me, const char edge_mode)
295 {
296         BMesh *bm = me->edit_btmesh->bm;
297
298         switch (edge_mode) {
299                 case EDGE_MODE_TAG_CREASE:
300                         BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_CREASE);
301                         break;
302                 case EDGE_MODE_TAG_BEVEL:
303                         BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT);
304                         break;
305 #ifdef WITH_FREESTYLE
306                 case EDGE_MODE_TAG_FREESTYLE:
307                         if (!CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
308                                 BM_data_layer_add(bm, &bm->edata, CD_FREESTYLE_EDGE);
309                         }
310                         break;
311 #endif
312                 default:
313                         break;
314         }
315 }
316
317 /* mesh shortest path select, uses prev-selected edge */
318
319 /* since you want to create paths with multiple selects, it doesn't have extend option */
320 static void mouse_mesh_shortest_path_edge(
321         Scene *scene, Object *obedit, const struct PathSelectParams *op_params,
322         BMEdge *e_act, BMEdge *e_dst)
323 {
324         BMEditMesh *em = BKE_editmesh_from_object(obedit);
325         BMesh *bm = em->bm;
326
327         struct UserData user_data = {bm, obedit->data, op_params};
328         LinkNode *path = NULL;
329         Mesh *me = obedit->data;
330         bool is_path_ordered = false;
331
332         edgetag_ensure_cd_flag(obedit->data, op_params->edge_mode);
333
334         if (e_act && (e_act != e_dst)) {
335                 if (op_params->use_fill) {
336                         path = BM_mesh_calc_path_region_edge(
337                                 bm, (BMElem *)e_act, (BMElem *)e_dst,
338                                 edgetag_filter_cb, &user_data);
339                 }
340                 else {
341                         is_path_ordered = true;
342                         path = BM_mesh_calc_path_edge(
343                                 bm, e_act, e_dst,
344                                 &(const struct BMCalcPathParams) {
345                                     .use_topology_distance = op_params->use_topology_distance,
346                                     .use_step_face = op_params->use_face_step,
347                                 },
348                                 edgetag_filter_cb, &user_data);
349                 }
350
351                 if (path) {
352                         if (op_params->track_active) {
353                                 BM_select_history_remove(bm, e_act);
354                         }
355                 }
356         }
357
358         BMEdge *e_dst_last = e_dst;
359
360         if (path) {
361                 /* toggle the flag */
362                 bool all_set = true;
363                 LinkNode *node;
364
365                 node = path;
366                 do {
367                         if (!edgetag_test_cb((BMEdge *)node->link, &user_data)) {
368                                 all_set = false;
369                                 break;
370                         }
371                 } while ((node = node->next));
372
373                 /* We need to start as if just *after* a 'skip' block... */
374                 int depth = op_params->interval_params.skip;
375                 node = path;
376                 do {
377                         if ((is_path_ordered == false) ||
378                             WM_operator_properties_checker_interval_test(&op_params->interval_params, depth))
379                         {
380                                 edgetag_set_cb((BMEdge *)node->link, !all_set, &user_data);
381                                 if (is_path_ordered) {
382                                         e_dst_last = node->link;
383                                 }
384                         }
385                 } while ((void)depth++, (node = node->next));
386
387                 BLI_linklist_free(path, NULL);
388         }
389         else {
390                 const bool is_act = !edgetag_test_cb(e_dst, &user_data);
391                 edgetag_ensure_cd_flag(obedit->data, op_params->edge_mode);
392                 edgetag_set_cb(e_dst, is_act, &user_data); /* switch the edge option */
393         }
394
395         if (op_params->edge_mode != EDGE_MODE_SELECT) {
396                 if (op_params->track_active) {
397                         /* simple rules - last edge is _always_ active and selected */
398                         if (e_act)
399                                 BM_edge_select_set(bm, e_act, false);
400                         BM_edge_select_set(bm, e_dst_last, true);
401                         BM_select_history_store(bm, e_dst_last);
402                 }
403         }
404
405         EDBM_selectmode_flush(em);
406
407         if (op_params->track_active) {
408                 /* even if this is selected it may not be in the selection list */
409                 if (op_params->edge_mode == EDGE_MODE_SELECT) {
410                         if (edgetag_test_cb(e_dst_last, &user_data) == 0)
411                                 BM_select_history_remove(bm, e_dst_last);
412                         else
413                                 BM_select_history_store(bm, e_dst_last);
414                 }
415         }
416
417         /* force drawmode for mesh */
418         switch (op_params->edge_mode) {
419
420                 case EDGE_MODE_TAG_SEAM:
421                         me->drawflag |= ME_DRAWSEAMS;
422                         ED_uvedit_live_unwrap(scene, obedit);
423                         break;
424                 case EDGE_MODE_TAG_SHARP:
425                         me->drawflag |= ME_DRAWSHARP;
426                         break;
427                 case EDGE_MODE_TAG_CREASE:
428                         me->drawflag |= ME_DRAWCREASES;
429                         break;
430                 case EDGE_MODE_TAG_BEVEL:
431                         me->drawflag |= ME_DRAWBWEIGHTS;
432                         break;
433 #ifdef WITH_FREESTYLE
434                 case EDGE_MODE_TAG_FREESTYLE:
435                         me->drawflag |= ME_DRAW_FREESTYLE_EDGE;
436                         break;
437 #endif
438         }
439
440         EDBM_update_generic(em, false, false);
441 }
442
443 /** \} */
444
445 /* -------------------------------------------------------------------- */
446 /** \name Face Path
447  * \{ */
448
449 /* callbacks */
450 static bool facetag_filter_cb(BMFace *f, void *UNUSED(user_data_v))
451 {
452         return !BM_elem_flag_test(f, BM_ELEM_HIDDEN);
453 }
454 //static bool facetag_test_cb(Scene *UNUSED(scene), BMesh *UNUSED(bm), BMFace *f)
455 static bool facetag_test_cb(BMFace *f, void *UNUSED(user_data_v))
456 {
457         return BM_elem_flag_test_bool(f, BM_ELEM_SELECT);
458 }
459 //static void facetag_set_cb(BMesh *bm, Scene *UNUSED(scene), BMFace *f, const bool val)
460 static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
461 {
462         struct UserData *user_data = user_data_v;
463         BM_face_select_set(user_data->bm, f, val);
464 }
465
466 static void mouse_mesh_shortest_path_face(
467         Scene *UNUSED(scene), Object *obedit, const struct PathSelectParams *op_params,
468         BMFace *f_act, BMFace *f_dst)
469 {
470         BMEditMesh *em = BKE_editmesh_from_object(obedit);
471         BMesh *bm = em->bm;
472
473         struct UserData user_data = {bm, obedit->data, op_params};
474         LinkNode *path = NULL;
475         bool is_path_ordered = false;
476
477         if (f_act) {
478                 if (op_params->use_fill) {
479                         path = BM_mesh_calc_path_region_face(
480                                 bm, (BMElem *)f_act, (BMElem *)f_dst,
481                                 facetag_filter_cb, &user_data);
482                 }
483                 else {
484                         is_path_ordered = true;
485                         path = BM_mesh_calc_path_face(
486                                 bm, f_act, f_dst,
487                                 &(const struct BMCalcPathParams) {
488                                     .use_topology_distance = op_params->use_topology_distance,
489                                     .use_step_face = op_params->use_face_step,
490                                 },
491                                 facetag_filter_cb, &user_data);
492                 }
493
494                 if (f_act != f_dst) {
495                         if (path) {
496                                 if (op_params->track_active) {
497                                         BM_select_history_remove(bm, f_act);
498                                 }
499                         }
500                 }
501         }
502
503         BMFace *f_dst_last = f_dst;
504
505         if (path) {
506                 /* toggle the flag */
507                 bool all_set = true;
508                 LinkNode *node;
509
510                 node = path;
511                 do {
512                         if (!facetag_test_cb((BMFace *)node->link, &user_data)) {
513                                 all_set = false;
514                                 break;
515                         }
516                 } while ((node = node->next));
517
518                 /* We need to start as if just *after* a 'skip' block... */
519                 int depth = op_params->interval_params.skip;
520                 node = path;
521                 do {
522                         if ((is_path_ordered == false) ||
523                             WM_operator_properties_checker_interval_test(&op_params->interval_params, depth))
524                         {
525                                 facetag_set_cb((BMFace *)node->link, !all_set, &user_data);
526                                 if (is_path_ordered) {
527                                         f_dst_last = node->link;
528                                 }
529                         }
530                 } while ((void)depth++, (node = node->next));
531
532                 BLI_linklist_free(path, NULL);
533         }
534         else {
535                 const bool is_act = !facetag_test_cb(f_dst, &user_data);
536                 facetag_set_cb(f_dst, is_act, &user_data); /* switch the face option */
537         }
538
539         EDBM_selectmode_flush(em);
540
541         if (op_params->track_active) {
542                 /* even if this is selected it may not be in the selection list */
543                 if (facetag_test_cb(f_dst_last, &user_data) == 0) {
544                         BM_select_history_remove(bm, f_dst_last);
545                 }
546                 else {
547                         BM_select_history_store(bm, f_dst_last);
548                 }
549                 BM_mesh_active_face_set(bm, f_dst_last);
550         }
551
552         EDBM_update_generic(em, false, false);
553 }
554
555 /** \} */
556
557 /* -------------------------------------------------------------------- */
558 /** \name Main Operator for vert/edge/face tag
559  * \{ */
560
561 static bool edbm_shortest_path_pick_ex(
562         Scene *scene, Object *obedit, const struct PathSelectParams *op_params,
563         BMElem *ele_src, BMElem *ele_dst)
564 {
565
566         if (ELEM(NULL, ele_src, ele_dst) && (ele_src->head.htype != ele_dst->head.htype)) {
567                 /* pass */
568         }
569         else if (ele_src->head.htype == BM_VERT) {
570                 mouse_mesh_shortest_path_vert(scene, obedit, op_params, (BMVert *)ele_src, (BMVert *)ele_dst);
571                 return true;
572         }
573         else if (ele_src->head.htype == BM_EDGE) {
574                 mouse_mesh_shortest_path_edge(scene, obedit, op_params, (BMEdge *)ele_src, (BMEdge *)ele_dst);
575                 return true;
576         }
577         else if (ele_src->head.htype == BM_FACE) {
578                 mouse_mesh_shortest_path_face(scene, obedit, op_params, (BMFace *)ele_src, (BMFace *)ele_dst);
579                 return true;
580         }
581
582         return false;
583 }
584
585 static int edbm_shortest_path_pick_exec(bContext *C, wmOperator *op);
586
587 static BMElem *edbm_elem_find_nearest(ViewContext *vc, const char htype)
588 {
589         BMEditMesh *em = vc->em;
590         float dist = ED_view3d_select_dist_px();
591
592         if ((em->selectmode & SCE_SELECT_VERTEX) && (htype == BM_VERT)) {
593                 return (BMElem *)EDBM_vert_find_nearest(vc, &dist);
594         }
595         else if ((em->selectmode & SCE_SELECT_EDGE) && (htype == BM_EDGE)) {
596                 return (BMElem *)EDBM_edge_find_nearest(vc, &dist);
597         }
598         else if ((em->selectmode & SCE_SELECT_FACE) && (htype == BM_FACE)) {
599                 return (BMElem *)EDBM_face_find_nearest(vc, &dist);
600         }
601
602         return NULL;
603 }
604
605 static BMElem *edbm_elem_active_elem_or_face_get(BMesh *bm)
606 {
607         BMElem *ele = BM_mesh_active_elem_get(bm);
608
609         if ((ele == NULL) && bm->act_face && BM_elem_flag_test(bm->act_face, BM_ELEM_SELECT)) {
610                 ele = (BMElem *)bm->act_face;
611         }
612
613         return ele;
614 }
615
616 static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
617 {
618         if (RNA_struct_property_is_set(op->ptr, "index")) {
619                 return edbm_shortest_path_pick_exec(C, op);
620         }
621
622         ViewContext vc;
623         BMEditMesh *em;
624         bool track_active = true;
625
626         em_setup_viewcontext(C, &vc);
627         copy_v2_v2_int(vc.mval, event->mval);
628         em = vc.em;
629
630         view3d_operator_needs_opengl(C);
631
632         BMElem *ele_src, *ele_dst;
633         if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) ||
634             !(ele_dst = edbm_elem_find_nearest(&vc, ele_src->head.htype)))
635         {
636                 /* special case, toggle edge tags even when we don't have a path */
637                 if (((em->selectmode & SCE_SELECT_EDGE) &&
638                      (vc.scene->toolsettings->edge_mode != EDGE_MODE_SELECT)) &&
639                     /* check if we only have a destination edge */
640                     ((ele_src == NULL) &&
641                      (ele_dst = edbm_elem_find_nearest(&vc, BM_EDGE))))
642                 {
643                         ele_src = ele_dst;
644                         track_active = false;
645                 }
646                 else {
647                         return OPERATOR_PASS_THROUGH;
648                 }
649         }
650
651         struct PathSelectParams op_params;
652
653         path_select_params_from_op(op, &op_params);
654         op_params.track_active = track_active;
655         op_params.edge_mode = vc.scene->toolsettings->edge_mode;
656
657         if (!edbm_shortest_path_pick_ex(vc.scene, vc.obedit, &op_params, ele_src, ele_dst)) {
658                 return OPERATOR_PASS_THROUGH;
659         }
660
661         /* to support redo */
662         BM_mesh_elem_index_ensure(em->bm, ele_dst->head.htype);
663         int index = EDBM_elem_to_index_any(em, ele_dst);
664
665         RNA_int_set(op->ptr, "index", index);
666
667         return OPERATOR_FINISHED;
668 }
669
670 static int edbm_shortest_path_pick_exec(bContext *C, wmOperator *op)
671 {
672         Scene *scene = CTX_data_scene(C);
673         Object *obedit = CTX_data_edit_object(C);
674         BMEditMesh *em = BKE_editmesh_from_object(obedit);
675         BMesh *bm = em->bm;
676
677         const int index = RNA_int_get(op->ptr, "index");
678         if (index < 0 || index >= (bm->totvert + bm->totedge + bm->totface)) {
679                 return OPERATOR_CANCELLED;
680         }
681
682         BMElem *ele_src, *ele_dst;
683         if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) ||
684             !(ele_dst = EDBM_elem_from_index_any(em, index)))
685         {
686                 return OPERATOR_CANCELLED;
687         }
688
689         struct PathSelectParams op_params;
690         path_select_params_from_op(op, &op_params);
691         op_params.track_active = true;
692         op_params.edge_mode = scene->toolsettings->edge_mode;
693
694         if (!edbm_shortest_path_pick_ex(scene, obedit, &op_params, ele_src, ele_dst)) {
695                 return OPERATOR_CANCELLED;
696         }
697
698         return OPERATOR_FINISHED;
699 }
700
701 void MESH_OT_shortest_path_pick(wmOperatorType *ot)
702 {
703         PropertyRNA *prop;
704
705         /* identifiers */
706         ot->name = "Pick Shortest Path";
707         ot->idname = "MESH_OT_shortest_path_pick";
708         ot->description = "Select shortest path between two selections";
709
710         /* api callbacks */
711         ot->invoke = edbm_shortest_path_pick_invoke;
712         ot->exec = edbm_shortest_path_pick_exec;
713         ot->poll = ED_operator_editmesh_region_view3d;
714
715         /* flags */
716         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
717
718         /* properties */
719         path_select_properties(ot);
720
721         /* use for redo */
722         prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
723         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
724 }
725
726 /** \} */
727
728 /* -------------------------------------------------------------------- */
729 /** \name Select Path Between Existing Selection
730  * \{ */
731
732 static int edbm_shortest_path_select_exec(bContext *C, wmOperator *op)
733 {
734         Scene *scene = CTX_data_scene(C);
735         bool found_valid_elements = false;
736
737         ViewLayer *view_layer = CTX_data_view_layer(C);
738         uint objects_len = 0;
739         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
740         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
741                 Object *obedit = objects[ob_index];
742                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
743                 BMesh *bm = em->bm;
744                 BMIter iter;
745                 BMEditSelection *ese_src, *ese_dst;
746                 BMElem *ele_src = NULL, *ele_dst = NULL, *ele;
747
748                 if ((em->bm->totvertsel == 0) &&
749                     (em->bm->totedgesel == 0) &&
750                     (em->bm->totfacesel == 0))
751                 {
752                         continue;
753                 }
754
755                 /* first try to find vertices in edit selection */
756                 ese_src = bm->selected.last;
757                 if (ese_src && (ese_dst = ese_src->prev) && (ese_src->htype  == ese_dst->htype)) {
758                         ele_src = ese_src->ele;
759                         ele_dst = ese_dst->ele;
760                 }
761                 else {
762                         /* if selection history isn't available, find two selected elements */
763                         ele_src = ele_dst = NULL;
764                         if ((em->selectmode & SCE_SELECT_VERTEX) && (bm->totvertsel >= 2)) {
765                                 BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
766                                         if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
767                                                 if      (ele_src == NULL) ele_src = ele;
768                                                 else if (ele_dst == NULL) ele_dst = ele;
769                                                 else                      break;
770                                         }
771                                 }
772                         }
773
774                         if ((ele_dst == NULL) && (em->selectmode & SCE_SELECT_EDGE) && (bm->totedgesel >= 2)) {
775                                 ele_src = NULL;
776                                 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
777                                         if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
778                                                 if      (ele_src == NULL) ele_src = ele;
779                                                 else if (ele_dst == NULL) ele_dst = ele;
780                                                 else                      break;
781                                         }
782                                 }
783                         }
784
785                         if ((ele_dst == NULL) && (em->selectmode & SCE_SELECT_FACE) && (bm->totfacesel >= 2)) {
786                                 ele_src = NULL;
787                                 BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) {
788                                         if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
789                                                 if      (ele_src == NULL) ele_src = ele;
790                                                 else if (ele_dst == NULL) ele_dst = ele;
791                                                 else                      break;
792                                         }
793                                 }
794                         }
795                 }
796
797                 if (ele_src && ele_dst) {
798                         struct PathSelectParams op_params;
799                         path_select_params_from_op(op, &op_params);
800
801                         edbm_shortest_path_pick_ex(scene, obedit, &op_params, ele_src, ele_dst);
802
803                         found_valid_elements = true;
804                 }
805         }
806         MEM_freeN(objects);
807
808         if (!found_valid_elements) {
809                 BKE_report(op->reports,
810                            RPT_WARNING,
811                            "Path selection requires two matching elements to be selected");
812                 return OPERATOR_CANCELLED;
813         }
814
815         return OPERATOR_FINISHED;
816 }
817
818 void MESH_OT_shortest_path_select(wmOperatorType *ot)
819 {
820         /* identifiers */
821         ot->name = "Select Shortest Path";
822         ot->idname = "MESH_OT_shortest_path_select";
823         ot->description = "Selected shortest path between two vertices/edges/faces";
824
825         /* api callbacks */
826         ot->exec = edbm_shortest_path_select_exec;
827         ot->poll = ED_operator_editmesh;
828
829         /* flags */
830         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
831
832         /* properties */
833         path_select_properties(ot);
834 }
835
836 /** \} */