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