When mixing vert/edge/face with select-visible, face selection could fail when not close enough to the center. This also fixes a bug where the bias for verts over edges would prefer faces over edges too, making edges harder to pick. Mixing edge with other selection modes works more predictably now.
634 lines
16 KiB
C
634 lines
16 KiB
C
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2004 Blender Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): none yet.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/editors/mesh/editmesh_path.c
|
|
* \ingroup edmesh
|
|
*/
|
|
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_windowmanager_types.h"
|
|
|
|
#ifdef WITH_FREESTYLE
|
|
# include "DNA_meshdata_types.h"
|
|
#endif
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_linklist.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_editmesh.h"
|
|
#include "BKE_report.h"
|
|
|
|
#include "ED_mesh.h"
|
|
#include "ED_screen.h"
|
|
#include "ED_uvedit.h"
|
|
#include "ED_view3d.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "RNA_define.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
#include "bmesh.h"
|
|
#include "bmesh_tools.h"
|
|
|
|
#include "mesh_intern.h" /* own include */
|
|
|
|
struct UserData {
|
|
BMesh *bm;
|
|
Mesh *me;
|
|
Scene *scene;
|
|
};
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Vert Path */
|
|
|
|
/* callbacks */
|
|
static bool verttag_filter_cb(BMVert *v, void *UNUSED(user_data_v))
|
|
{
|
|
return !BM_elem_flag_test(v, BM_ELEM_HIDDEN);
|
|
}
|
|
static bool verttag_test_cb(BMVert *v, void *UNUSED(user_data_v))
|
|
{
|
|
return BM_elem_flag_test_bool(v, BM_ELEM_SELECT);
|
|
}
|
|
static void verttag_set_cb(BMVert *v, bool val, void *user_data_v)
|
|
{
|
|
struct UserData *user_data = user_data_v;
|
|
BM_vert_select_set(user_data->bm, v, val);
|
|
}
|
|
|
|
static bool mouse_mesh_shortest_path_vert(ViewContext *vc)
|
|
{
|
|
/* unlike edge/face versions, this uses a bmesh operator */
|
|
|
|
BMEditMesh *em = vc->em;
|
|
BMesh *bm = em->bm;
|
|
BMVert *v_dst;
|
|
float dist = ED_view3d_select_dist_px();
|
|
const bool use_length = true;
|
|
|
|
v_dst = EDBM_vert_find_nearest(vc, &dist);
|
|
if (v_dst) {
|
|
struct UserData user_data = {bm, vc->obedit->data, vc->scene};
|
|
LinkNode *path = NULL;
|
|
BMVert *v_act = BM_mesh_active_vert_get(bm);
|
|
|
|
if (v_act && (v_act != v_dst)) {
|
|
if ((path = BM_mesh_calc_path_vert(bm, v_act, v_dst, use_length,
|
|
verttag_filter_cb, &user_data)))
|
|
{
|
|
BM_select_history_remove(bm, v_act);
|
|
}
|
|
}
|
|
|
|
if (path) {
|
|
/* toggle the flag */
|
|
bool all_set = true;
|
|
LinkNode *node;
|
|
|
|
node = path;
|
|
do {
|
|
if (!verttag_test_cb((BMVert *)node->link, &user_data)) {
|
|
all_set = false;
|
|
break;
|
|
}
|
|
} while ((node = node->next));
|
|
|
|
node = path;
|
|
do {
|
|
verttag_set_cb((BMVert *)node->link, !all_set, &user_data);
|
|
} while ((node = node->next));
|
|
|
|
BLI_linklist_free(path, NULL);
|
|
}
|
|
else {
|
|
const bool is_act = !verttag_test_cb(v_dst, &user_data);
|
|
verttag_set_cb(v_dst, is_act, &user_data); /* switch the face option */
|
|
}
|
|
|
|
EDBM_selectmode_flush(em);
|
|
|
|
/* even if this is selected it may not be in the selection list */
|
|
if (BM_elem_flag_test(v_dst, BM_ELEM_SELECT) == 0)
|
|
BM_select_history_remove(bm, v_dst);
|
|
else
|
|
BM_select_history_store(bm, v_dst);
|
|
|
|
EDBM_update_generic(em, false, false);
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Edge Path */
|
|
|
|
/* callbacks */
|
|
static bool edgetag_filter_cb(BMEdge *e, void *UNUSED(user_data_v))
|
|
{
|
|
return !BM_elem_flag_test(e, BM_ELEM_HIDDEN);
|
|
}
|
|
static bool edgetag_test_cb(BMEdge *e, void *user_data_v)
|
|
{
|
|
struct UserData *user_data = user_data_v;
|
|
Scene *scene = user_data->scene;
|
|
BMesh *bm = user_data->bm;
|
|
|
|
switch (scene->toolsettings->edge_mode) {
|
|
case EDGE_MODE_SELECT:
|
|
return BM_elem_flag_test(e, BM_ELEM_SELECT) ? true : false;
|
|
case EDGE_MODE_TAG_SEAM:
|
|
return BM_elem_flag_test(e, BM_ELEM_SEAM) ? true : false;
|
|
case EDGE_MODE_TAG_SHARP:
|
|
return BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? false : true;
|
|
case EDGE_MODE_TAG_CREASE:
|
|
return BM_elem_float_data_get(&bm->edata, e, CD_CREASE) ? true : false;
|
|
case EDGE_MODE_TAG_BEVEL:
|
|
return BM_elem_float_data_get(&bm->edata, e, CD_BWEIGHT) ? true : false;
|
|
#ifdef WITH_FREESTYLE
|
|
case EDGE_MODE_TAG_FREESTYLE:
|
|
{
|
|
FreestyleEdge *fed = CustomData_bmesh_get(&bm->edata, e->head.data, CD_FREESTYLE_EDGE);
|
|
return (!fed) ? false : (fed->flag & FREESTYLE_EDGE_MARK) ? true : false;
|
|
}
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
static void edgetag_set_cb(BMEdge *e, bool val, void *user_data_v)
|
|
{
|
|
struct UserData *user_data = user_data_v;
|
|
Scene *scene = user_data->scene;
|
|
BMesh *bm = user_data->bm;
|
|
|
|
switch (scene->toolsettings->edge_mode) {
|
|
case EDGE_MODE_SELECT:
|
|
BM_edge_select_set(bm, e, val);
|
|
break;
|
|
case EDGE_MODE_TAG_SEAM:
|
|
BM_elem_flag_set(e, BM_ELEM_SEAM, val);
|
|
break;
|
|
case EDGE_MODE_TAG_SHARP:
|
|
BM_elem_flag_set(e, BM_ELEM_SMOOTH, !val);
|
|
break;
|
|
case EDGE_MODE_TAG_CREASE:
|
|
BM_elem_float_data_set(&bm->edata, e, CD_CREASE, (val) ? 1.0f : 0.0f);
|
|
break;
|
|
case EDGE_MODE_TAG_BEVEL:
|
|
BM_elem_float_data_set(&bm->edata, e, CD_BWEIGHT, (val) ? 1.0f : 0.0f);
|
|
break;
|
|
#ifdef WITH_FREESTYLE
|
|
case EDGE_MODE_TAG_FREESTYLE:
|
|
{
|
|
FreestyleEdge *fed;
|
|
fed = CustomData_bmesh_get(&bm->edata, e->head.data, CD_FREESTYLE_EDGE);
|
|
if (!val)
|
|
fed->flag &= ~FREESTYLE_EDGE_MARK;
|
|
else
|
|
fed->flag |= FREESTYLE_EDGE_MARK;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void edgetag_ensure_cd_flag(Scene *scene, Mesh *me)
|
|
{
|
|
BMesh *bm = me->edit_btmesh->bm;
|
|
|
|
switch (scene->toolsettings->edge_mode) {
|
|
case EDGE_MODE_TAG_CREASE:
|
|
BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_CREASE);
|
|
break;
|
|
case EDGE_MODE_TAG_BEVEL:
|
|
BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT);
|
|
break;
|
|
#ifdef WITH_FREESTYLE
|
|
case EDGE_MODE_TAG_FREESTYLE:
|
|
if (!CustomData_has_layer(&bm->edata, CD_FREESTYLE_EDGE)) {
|
|
BM_data_layer_add(bm, &bm->edata, CD_FREESTYLE_EDGE);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* mesh shortest path select, uses prev-selected edge */
|
|
|
|
/* since you want to create paths with multiple selects, it doesn't have extend option */
|
|
static bool mouse_mesh_shortest_path_edge(ViewContext *vc)
|
|
{
|
|
BMEditMesh *em = vc->em;
|
|
BMesh *bm = em->bm;
|
|
BMEdge *e_dst;
|
|
float dist = ED_view3d_select_dist_px();
|
|
const bool use_length = true;
|
|
|
|
e_dst = EDBM_edge_find_nearest(vc, &dist);
|
|
if (e_dst) {
|
|
const char edge_mode = vc->scene->toolsettings->edge_mode;
|
|
struct UserData user_data = {bm, vc->obedit->data, vc->scene};
|
|
LinkNode *path = NULL;
|
|
Mesh *me = vc->obedit->data;
|
|
BMEdge *e_act = BM_mesh_active_edge_get(bm);
|
|
|
|
edgetag_ensure_cd_flag(vc->scene, em->ob->data);
|
|
|
|
if (e_act && (e_act != e_dst)) {
|
|
if ((path = BM_mesh_calc_path_edge(bm, e_act, e_dst, use_length,
|
|
edgetag_filter_cb, &user_data)))
|
|
{
|
|
BM_select_history_remove(bm, e_act);
|
|
}
|
|
}
|
|
|
|
if (path) {
|
|
/* toggle the flag */
|
|
bool all_set = true;
|
|
LinkNode *node;
|
|
|
|
node = path;
|
|
do {
|
|
if (!edgetag_test_cb((BMEdge *)node->link, &user_data)) {
|
|
all_set = false;
|
|
break;
|
|
}
|
|
} while ((node = node->next));
|
|
|
|
node = path;
|
|
do {
|
|
edgetag_set_cb((BMEdge *)node->link, !all_set, &user_data);
|
|
} while ((node = node->next));
|
|
|
|
BLI_linklist_free(path, NULL);
|
|
}
|
|
else {
|
|
const bool is_act = !edgetag_test_cb(e_dst, &user_data);
|
|
edgetag_ensure_cd_flag(vc->scene, vc->obedit->data);
|
|
edgetag_set_cb(e_dst, is_act, &user_data); /* switch the edge option */
|
|
}
|
|
|
|
if (edge_mode != EDGE_MODE_SELECT) {
|
|
/* simple rules - last edge is _always_ active and selected */
|
|
if (e_act)
|
|
BM_edge_select_set(bm, e_act, false);
|
|
BM_edge_select_set(bm, e_dst, true);
|
|
BM_select_history_store(bm, e_dst);
|
|
}
|
|
|
|
EDBM_selectmode_flush(em);
|
|
|
|
/* even if this is selected it may not be in the selection list */
|
|
if (edge_mode == EDGE_MODE_SELECT) {
|
|
if (edgetag_test_cb(e_dst, &user_data) == 0)
|
|
BM_select_history_remove(bm, e_dst);
|
|
else
|
|
BM_select_history_store(bm, e_dst);
|
|
}
|
|
|
|
/* force drawmode for mesh */
|
|
switch (edge_mode) {
|
|
|
|
case EDGE_MODE_TAG_SEAM:
|
|
me->drawflag |= ME_DRAWSEAMS;
|
|
ED_uvedit_live_unwrap(vc->scene, vc->obedit);
|
|
break;
|
|
case EDGE_MODE_TAG_SHARP:
|
|
me->drawflag |= ME_DRAWSHARP;
|
|
break;
|
|
case EDGE_MODE_TAG_CREASE:
|
|
me->drawflag |= ME_DRAWCREASES;
|
|
break;
|
|
case EDGE_MODE_TAG_BEVEL:
|
|
me->drawflag |= ME_DRAWBWEIGHTS;
|
|
break;
|
|
#ifdef WITH_FREESTYLE
|
|
case EDGE_MODE_TAG_FREESTYLE:
|
|
me->drawflag |= ME_DRAW_FREESTYLE_EDGE;
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
EDBM_update_generic(em, false, false);
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Face Path */
|
|
|
|
/* callbacks */
|
|
static bool facetag_filter_cb(BMFace *f, void *UNUSED(user_data_v))
|
|
{
|
|
return !BM_elem_flag_test(f, BM_ELEM_HIDDEN);
|
|
}
|
|
//static bool facetag_test_cb(Scene *UNUSED(scene), BMesh *UNUSED(bm), BMFace *f)
|
|
static bool facetag_test_cb(BMFace *f, void *UNUSED(user_data_v))
|
|
{
|
|
return BM_elem_flag_test_bool(f, BM_ELEM_SELECT);
|
|
}
|
|
//static void facetag_set_cb(BMesh *bm, Scene *UNUSED(scene), BMFace *f, const bool val)
|
|
static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
|
|
{
|
|
struct UserData *user_data = user_data_v;
|
|
BM_face_select_set(user_data->bm, f, val);
|
|
}
|
|
|
|
static bool mouse_mesh_shortest_path_face(ViewContext *vc)
|
|
{
|
|
BMEditMesh *em = vc->em;
|
|
BMesh *bm = em->bm;
|
|
BMFace *f_dst;
|
|
float dist = ED_view3d_select_dist_px();
|
|
const bool use_length = true;
|
|
|
|
f_dst = EDBM_face_find_nearest(vc, &dist);
|
|
if (f_dst) {
|
|
struct UserData user_data = {bm, vc->obedit->data, vc->scene};
|
|
LinkNode *path = NULL;
|
|
BMFace *f_act = BM_mesh_active_face_get(bm, false, true);
|
|
|
|
if (f_act) {
|
|
if (f_act != f_dst) {
|
|
if ((path = BM_mesh_calc_path_face(bm, f_act, f_dst, use_length,
|
|
facetag_filter_cb, &user_data)))
|
|
{
|
|
BM_select_history_remove(bm, f_act);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (path) {
|
|
/* toggle the flag */
|
|
bool all_set = true;
|
|
LinkNode *node;
|
|
|
|
node = path;
|
|
do {
|
|
if (!facetag_test_cb((BMFace *)node->link, &user_data)) {
|
|
all_set = false;
|
|
break;
|
|
}
|
|
} while ((node = node->next));
|
|
|
|
node = path;
|
|
do {
|
|
facetag_set_cb((BMFace *)node->link, !all_set, &user_data);
|
|
} while ((node = node->next));
|
|
|
|
BLI_linklist_free(path, NULL);
|
|
}
|
|
else {
|
|
const bool is_act = !facetag_test_cb(f_dst, &user_data);
|
|
facetag_set_cb(f_dst, is_act, &user_data); /* switch the face option */
|
|
}
|
|
|
|
EDBM_selectmode_flush(em);
|
|
|
|
/* even if this is selected it may not be in the selection list */
|
|
if (facetag_test_cb(f_dst, &user_data) == 0)
|
|
BM_select_history_remove(bm, f_dst);
|
|
else
|
|
BM_select_history_store(bm, f_dst);
|
|
|
|
BM_mesh_active_face_set(bm, f_dst);
|
|
|
|
EDBM_update_generic(em, false, false);
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Main Operator for vert/edge/face tag */
|
|
|
|
static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
|
|
{
|
|
ViewContext vc;
|
|
BMEditMesh *em;
|
|
BMElem *ele;
|
|
|
|
|
|
em_setup_viewcontext(C, &vc);
|
|
copy_v2_v2_int(vc.mval, event->mval);
|
|
em = vc.em;
|
|
|
|
ele = BM_mesh_active_elem_get(em->bm);
|
|
if (ele == NULL) {
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
view3d_operator_needs_opengl(C);
|
|
|
|
if ((em->selectmode & SCE_SELECT_VERTEX) && (ele->head.htype == BM_VERT)) {
|
|
if (mouse_mesh_shortest_path_vert(&vc)) {
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
else {
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
}
|
|
else if ((em->selectmode & SCE_SELECT_EDGE) && (ele->head.htype == BM_EDGE)) {
|
|
if (mouse_mesh_shortest_path_edge(&vc)) {
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
else {
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
}
|
|
else if ((em->selectmode & SCE_SELECT_FACE) && (ele->head.htype == BM_FACE)) {
|
|
if (mouse_mesh_shortest_path_face(&vc)) {
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
else {
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
}
|
|
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
void MESH_OT_shortest_path_pick(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Pick Shortest Path";
|
|
ot->idname = "MESH_OT_shortest_path_pick";
|
|
ot->description = "Select shortest path between two selections";
|
|
|
|
/* api callbacks */
|
|
ot->invoke = edbm_shortest_path_pick_invoke;
|
|
ot->poll = ED_operator_editmesh_region_view3d;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Select path between existing selection */
|
|
|
|
static int edbm_shortest_path_select_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Object *ob = CTX_data_edit_object(C);
|
|
BMEditMesh *em = BKE_editmesh_from_object(ob);
|
|
BMesh *bm = em->bm;
|
|
BMIter iter;
|
|
BMEditSelection *ese_src, *ese_dst;
|
|
BMElem *ele_src = NULL, *ele_dst = NULL, *ele;
|
|
|
|
const bool use_length = RNA_boolean_get(op->ptr, "use_length");
|
|
|
|
/* first try to find vertices in edit selection */
|
|
ese_src = bm->selected.last;
|
|
if (ese_src && (ese_dst = ese_src->prev) && (ese_src->htype == ese_dst->htype)) {
|
|
ele_src = ese_src->ele;
|
|
ele_dst = ese_dst->ele;
|
|
}
|
|
else {
|
|
/* if selection history isn't available, find two selected elements */
|
|
ele_src = ele_dst = NULL;
|
|
if ((em->selectmode & SCE_SELECT_VERTEX) && (bm->totvertsel >= 2)) {
|
|
BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
|
|
if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
|
|
if (ele_src == NULL) ele_src = ele;
|
|
else if (ele_dst == NULL) ele_dst = ele;
|
|
else break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ele_dst == NULL) && (em->selectmode & SCE_SELECT_EDGE) && (bm->totedgesel >= 2)) {
|
|
ele_src = NULL;
|
|
BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
|
|
if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
|
|
if (ele_src == NULL) ele_src = ele;
|
|
else if (ele_dst == NULL) ele_dst = ele;
|
|
else break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ele_dst == NULL) && (em->selectmode & SCE_SELECT_FACE) && (bm->totfacesel >= 2)) {
|
|
ele_src = NULL;
|
|
BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) {
|
|
if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
|
|
if (ele_src == NULL) ele_src = ele;
|
|
else if (ele_dst == NULL) ele_dst = ele;
|
|
else break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ele_src && ele_dst) {
|
|
LinkNode *path = NULL;
|
|
switch (ele_src->head.htype) {
|
|
case BM_VERT:
|
|
path = BM_mesh_calc_path_vert(
|
|
bm, (BMVert *)ele_src, (BMVert *)ele_dst, use_length,
|
|
BM_elem_cb_check_hflag_disabled_simple(BMVert *, BM_ELEM_HIDDEN));
|
|
break;
|
|
case BM_EDGE:
|
|
path = BM_mesh_calc_path_edge(
|
|
bm, (BMEdge *)ele_src, (BMEdge *)ele_dst, use_length,
|
|
BM_elem_cb_check_hflag_disabled_simple(BMEdge *, BM_ELEM_HIDDEN));
|
|
break;
|
|
case BM_FACE:
|
|
path = BM_mesh_calc_path_face(
|
|
bm, (BMFace *)ele_src, (BMFace *)ele_dst, use_length,
|
|
BM_elem_cb_check_hflag_disabled_simple(BMFace *, BM_ELEM_HIDDEN));
|
|
break;
|
|
}
|
|
|
|
if (path) {
|
|
LinkNode *node = path;
|
|
|
|
do {
|
|
BM_elem_select_set(bm, node->link, true);
|
|
} while ((node = node->next));
|
|
|
|
BLI_linklist_free(path, NULL);
|
|
}
|
|
else {
|
|
BKE_report(op->reports, RPT_WARNING, "Path can't be found");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
EDBM_selectmode_flush(em);
|
|
EDBM_update_generic(em, false, false);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
else {
|
|
BKE_report(op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
}
|
|
|
|
void MESH_OT_shortest_path_select(wmOperatorType *ot)
|
|
{
|
|
/* identifiers */
|
|
ot->name = "Select Shortest Path";
|
|
ot->idname = "MESH_OT_shortest_path_select";
|
|
ot->description = "Selected vertex path between two vertices";
|
|
|
|
/* api callbacks */
|
|
ot->exec = edbm_shortest_path_select_exec;
|
|
ot->poll = ED_operator_editmesh;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
RNA_def_boolean(ot->srna, "use_length", true, "Length", "Use length when measuring distance");
|
|
}
|