2.8x branch added bContext arg in many places, pass eval-context instead since its not simple to reason about what what nested functions do when they can access and change almost anything. Also use const to prevent unexpected modifications. This fixes crash loading files with shadows, since off-screen buffers use a NULL context for rendering.
803 lines
22 KiB
C
803 lines
22 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_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "bmesh.h"
|
|
#include "bmesh_tools.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
|
|
#include "mesh_intern.h" /* own include */
|
|
|
|
struct PathSelectParams {
|
|
bool track_active; /* ensure the active element is the last selected item (handy for picking) */
|
|
bool use_topology_distance;
|
|
bool use_face_step;
|
|
bool use_fill;
|
|
char edge_mode;
|
|
struct CheckerIntervalParams interval_params;
|
|
};
|
|
|
|
static void path_select_properties(wmOperatorType *ot)
|
|
{
|
|
RNA_def_boolean(
|
|
ot->srna, "use_face_step", false, "Face Stepping",
|
|
"Traverse connected faces (includes diagonals and edge-rings)");
|
|
RNA_def_boolean(
|
|
ot->srna, "use_topology_distance", false, "Topology Distance",
|
|
"Find the minimum number of steps, ignoring spatial distance");
|
|
RNA_def_boolean(
|
|
ot->srna, "use_fill", false, "Fill Region",
|
|
"Select all paths between the source/destination elements");
|
|
WM_operator_properties_checker_interval(ot, true);
|
|
}
|
|
|
|
static void path_select_params_from_op(wmOperator *op, struct PathSelectParams *op_params)
|
|
{
|
|
op_params->edge_mode = EDGE_MODE_SELECT;
|
|
op_params->track_active = false;
|
|
op_params->use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
|
|
op_params->use_fill = RNA_boolean_get(op->ptr, "use_fill");
|
|
op_params->use_topology_distance = RNA_boolean_get(op->ptr, "use_topology_distance");
|
|
WM_operator_properties_checker_interval_from_op(op, &op_params->interval_params);
|
|
}
|
|
|
|
struct UserData {
|
|
BMesh *bm;
|
|
Mesh *me;
|
|
const struct PathSelectParams *op_params;
|
|
};
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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 void mouse_mesh_shortest_path_vert(
|
|
Scene *scene, const struct PathSelectParams *op_params,
|
|
BMVert *v_act, BMVert *v_dst)
|
|
{
|
|
Object *obedit = scene->obedit;
|
|
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
|
BMesh *bm = em->bm;
|
|
|
|
struct UserData user_data = {bm, obedit->data, op_params};
|
|
LinkNode *path = NULL;
|
|
bool is_path_ordered = false;
|
|
|
|
if (v_act && (v_act != v_dst)) {
|
|
if (op_params->use_fill) {
|
|
path = BM_mesh_calc_path_region_vert(
|
|
bm, (BMElem *)v_act, (BMElem *)v_dst,
|
|
verttag_filter_cb, &user_data);
|
|
}
|
|
else {
|
|
is_path_ordered = true;
|
|
path = BM_mesh_calc_path_vert(
|
|
bm, v_act, v_dst,
|
|
&(const struct BMCalcPathParams) {
|
|
.use_topology_distance = op_params->use_topology_distance,
|
|
.use_step_face = op_params->use_face_step,
|
|
},
|
|
verttag_filter_cb, &user_data);
|
|
}
|
|
|
|
if (path) {
|
|
if (op_params->track_active) {
|
|
BM_select_history_remove(bm, v_act);
|
|
}
|
|
}
|
|
}
|
|
|
|
BMVert *v_dst_last = v_dst;
|
|
|
|
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));
|
|
|
|
int depth = 1;
|
|
node = path;
|
|
do {
|
|
if ((is_path_ordered == false) ||
|
|
WM_operator_properties_checker_interval_test(&op_params->interval_params, depth))
|
|
{
|
|
verttag_set_cb((BMVert *)node->link, !all_set, &user_data);
|
|
if (is_path_ordered) {
|
|
v_dst_last = node->link;
|
|
}
|
|
}
|
|
} while ((void)depth++, (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);
|
|
|
|
if (op_params->track_active) {
|
|
/* even if this is selected it may not be in the selection list */
|
|
if (BM_elem_flag_test(v_dst_last, BM_ELEM_SELECT) == 0) {
|
|
BM_select_history_remove(bm, v_dst_last);
|
|
}
|
|
else {
|
|
BM_select_history_store(bm, v_dst_last);
|
|
}
|
|
}
|
|
|
|
EDBM_update_generic(em, false, 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;
|
|
const char edge_mode = user_data->op_params->edge_mode;
|
|
BMesh *bm = user_data->bm;
|
|
|
|
switch (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;
|
|
const char edge_mode = user_data->op_params->edge_mode;
|
|
BMesh *bm = user_data->bm;
|
|
|
|
switch (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 void mouse_mesh_shortest_path_edge(
|
|
Scene *scene, const struct PathSelectParams *op_params,
|
|
BMEdge *e_act, BMEdge *e_dst)
|
|
{
|
|
Object *obedit = scene->obedit;
|
|
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
|
BMesh *bm = em->bm;
|
|
|
|
struct UserData user_data = {bm, obedit->data, op_params};
|
|
LinkNode *path = NULL;
|
|
Mesh *me = obedit->data;
|
|
bool is_path_ordered = false;
|
|
|
|
edgetag_ensure_cd_flag(scene, obedit->data);
|
|
|
|
if (e_act && (e_act != e_dst)) {
|
|
if (op_params->use_fill) {
|
|
path = BM_mesh_calc_path_region_edge(
|
|
bm, (BMElem *)e_act, (BMElem *)e_dst,
|
|
edgetag_filter_cb, &user_data);
|
|
}
|
|
else {
|
|
is_path_ordered = true;
|
|
path = BM_mesh_calc_path_edge(
|
|
bm, e_act, e_dst,
|
|
&(const struct BMCalcPathParams) {
|
|
.use_topology_distance = op_params->use_topology_distance,
|
|
.use_step_face = op_params->use_face_step,
|
|
},
|
|
edgetag_filter_cb, &user_data);
|
|
}
|
|
|
|
if (path) {
|
|
if (op_params->track_active) {
|
|
BM_select_history_remove(bm, e_act);
|
|
}
|
|
}
|
|
}
|
|
|
|
BMEdge *e_dst_last = e_dst;
|
|
|
|
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));
|
|
|
|
int depth = 1;
|
|
node = path;
|
|
do {
|
|
if ((is_path_ordered == false) ||
|
|
WM_operator_properties_checker_interval_test(&op_params->interval_params, depth))
|
|
{
|
|
edgetag_set_cb((BMEdge *)node->link, !all_set, &user_data);
|
|
if (is_path_ordered) {
|
|
e_dst_last = node->link;
|
|
}
|
|
}
|
|
} while ((void)depth++, (node = node->next));
|
|
|
|
BLI_linklist_free(path, NULL);
|
|
}
|
|
else {
|
|
const bool is_act = !edgetag_test_cb(e_dst, &user_data);
|
|
edgetag_ensure_cd_flag(scene, obedit->data);
|
|
edgetag_set_cb(e_dst, is_act, &user_data); /* switch the edge option */
|
|
}
|
|
|
|
if (op_params->edge_mode != EDGE_MODE_SELECT) {
|
|
if (op_params->track_active) {
|
|
/* 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_last, true);
|
|
BM_select_history_store(bm, e_dst_last);
|
|
}
|
|
}
|
|
|
|
EDBM_selectmode_flush(em);
|
|
|
|
if (op_params->track_active) {
|
|
/* even if this is selected it may not be in the selection list */
|
|
if (op_params->edge_mode == EDGE_MODE_SELECT) {
|
|
if (edgetag_test_cb(e_dst_last, &user_data) == 0)
|
|
BM_select_history_remove(bm, e_dst_last);
|
|
else
|
|
BM_select_history_store(bm, e_dst_last);
|
|
}
|
|
}
|
|
|
|
/* force drawmode for mesh */
|
|
switch (op_params->edge_mode) {
|
|
|
|
case EDGE_MODE_TAG_SEAM:
|
|
me->drawflag |= ME_DRAWSEAMS;
|
|
ED_uvedit_live_unwrap(scene, 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);
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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 void mouse_mesh_shortest_path_face(
|
|
Scene *scene, const struct PathSelectParams *op_params,
|
|
BMFace *f_act, BMFace *f_dst)
|
|
{
|
|
Object *obedit = scene->obedit;
|
|
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
|
BMesh *bm = em->bm;
|
|
|
|
struct UserData user_data = {bm, obedit->data, op_params};
|
|
LinkNode *path = NULL;
|
|
bool is_path_ordered = false;
|
|
|
|
if (f_act) {
|
|
if (op_params->use_fill) {
|
|
path = BM_mesh_calc_path_region_face(
|
|
bm, (BMElem *)f_act, (BMElem *)f_dst,
|
|
facetag_filter_cb, &user_data);
|
|
}
|
|
else {
|
|
is_path_ordered = true;
|
|
path = BM_mesh_calc_path_face(
|
|
bm, f_act, f_dst,
|
|
&(const struct BMCalcPathParams) {
|
|
.use_topology_distance = op_params->use_topology_distance,
|
|
.use_step_face = op_params->use_face_step,
|
|
},
|
|
facetag_filter_cb, &user_data);
|
|
}
|
|
|
|
|
|
if (f_act != f_dst) {
|
|
if (path) {
|
|
if (op_params->track_active) {
|
|
BM_select_history_remove(bm, f_act);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BMFace *f_dst_last = f_dst;
|
|
|
|
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));
|
|
|
|
int depth = 1;
|
|
node = path;
|
|
do {
|
|
if ((is_path_ordered == false) ||
|
|
WM_operator_properties_checker_interval_test(&op_params->interval_params, depth))
|
|
{
|
|
facetag_set_cb((BMFace *)node->link, !all_set, &user_data);
|
|
if (is_path_ordered) {
|
|
f_dst_last = node->link;
|
|
}
|
|
}
|
|
} while ((void)depth++, (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);
|
|
|
|
if (op_params->track_active) {
|
|
/* even if this is selected it may not be in the selection list */
|
|
if (facetag_test_cb(f_dst_last, &user_data) == 0) {
|
|
BM_select_history_remove(bm, f_dst_last);
|
|
}
|
|
else {
|
|
BM_select_history_store(bm, f_dst_last);
|
|
}
|
|
BM_mesh_active_face_set(bm, f_dst_last);
|
|
}
|
|
|
|
EDBM_update_generic(em, false, false);
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Main Operator for vert/edge/face tag */
|
|
|
|
static bool edbm_shortest_path_pick_ex(
|
|
Scene *scene, const struct PathSelectParams *op_params,
|
|
BMElem *ele_src, BMElem *ele_dst)
|
|
{
|
|
|
|
if (ELEM(NULL, ele_src, ele_dst) && (ele_src->head.htype != ele_dst->head.htype)) {
|
|
/* pass */
|
|
}
|
|
else if (ele_src->head.htype == BM_VERT) {
|
|
mouse_mesh_shortest_path_vert(scene, op_params, (BMVert *)ele_src, (BMVert *)ele_dst);
|
|
return true;
|
|
}
|
|
else if (ele_src->head.htype == BM_EDGE) {
|
|
mouse_mesh_shortest_path_edge(scene, op_params, (BMEdge *)ele_src, (BMEdge *)ele_dst);
|
|
return true;
|
|
}
|
|
else if (ele_src->head.htype == BM_FACE) {
|
|
mouse_mesh_shortest_path_face(scene, op_params, (BMFace *)ele_src, (BMFace *)ele_dst);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int edbm_shortest_path_pick_exec(bContext *C, wmOperator *op);
|
|
|
|
static BMElem *edbm_elem_find_nearest(const struct EvaluationContext *eval_ctx, ViewContext *vc, const char htype)
|
|
{
|
|
BMEditMesh *em = vc->em;
|
|
float dist = ED_view3d_select_dist_px();
|
|
|
|
if ((em->selectmode & SCE_SELECT_VERTEX) && (htype == BM_VERT)) {
|
|
return (BMElem *)EDBM_vert_find_nearest(eval_ctx, vc, &dist);
|
|
}
|
|
else if ((em->selectmode & SCE_SELECT_EDGE) && (htype == BM_EDGE)) {
|
|
return (BMElem *)EDBM_edge_find_nearest(eval_ctx, vc, &dist);
|
|
}
|
|
else if ((em->selectmode & SCE_SELECT_FACE) && (htype == BM_FACE)) {
|
|
return (BMElem *)EDBM_face_find_nearest(eval_ctx, vc, &dist);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static BMElem *edbm_elem_active_elem_or_face_get(BMesh *bm)
|
|
{
|
|
BMElem *ele = BM_mesh_active_elem_get(bm);
|
|
|
|
if ((ele == NULL) && bm->act_face && BM_elem_flag_test(bm->act_face, BM_ELEM_SELECT)) {
|
|
ele = (BMElem *)bm->act_face;
|
|
}
|
|
|
|
return ele;
|
|
}
|
|
|
|
static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
if (RNA_struct_property_is_set(op->ptr, "index")) {
|
|
return edbm_shortest_path_pick_exec(C, op);
|
|
}
|
|
|
|
EvaluationContext eval_ctx;
|
|
ViewContext vc;
|
|
BMEditMesh *em;
|
|
bool track_active = true;
|
|
|
|
CTX_data_eval_ctx(C, &eval_ctx);
|
|
em_setup_viewcontext(C, &vc);
|
|
copy_v2_v2_int(vc.mval, event->mval);
|
|
em = vc.em;
|
|
|
|
view3d_operator_needs_opengl(C);
|
|
|
|
BMElem *ele_src, *ele_dst;
|
|
if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) ||
|
|
!(ele_dst = edbm_elem_find_nearest(&eval_ctx, &vc, ele_src->head.htype)))
|
|
{
|
|
/* special case, toggle edge tags even when we don't have a path */
|
|
if (((em->selectmode & SCE_SELECT_EDGE) &&
|
|
(vc.scene->toolsettings->edge_mode != EDGE_MODE_SELECT)) &&
|
|
/* check if we only have a destination edge */
|
|
((ele_src == NULL) &&
|
|
(ele_dst = edbm_elem_find_nearest(&eval_ctx, &vc, BM_EDGE))))
|
|
{
|
|
ele_src = ele_dst;
|
|
track_active = false;
|
|
}
|
|
else {
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
}
|
|
|
|
struct PathSelectParams op_params;
|
|
|
|
path_select_params_from_op(op, &op_params);
|
|
op_params.track_active = track_active;
|
|
op_params.edge_mode = vc.scene->toolsettings->edge_mode;
|
|
|
|
if (!edbm_shortest_path_pick_ex(vc.scene, &op_params, ele_src, ele_dst)) {
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
/* to support redo */
|
|
BM_mesh_elem_index_ensure(em->bm, ele_dst->head.htype);
|
|
int index = EDBM_elem_to_index_any(em, ele_dst);
|
|
|
|
RNA_int_set(op->ptr, "index", index);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int edbm_shortest_path_pick_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
|
BMesh *bm = em->bm;
|
|
|
|
const int index = RNA_int_get(op->ptr, "index");
|
|
if (index < 0 || index >= (bm->totvert + bm->totedge + bm->totface)) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
BMElem *ele_src, *ele_dst;
|
|
if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) ||
|
|
!(ele_dst = EDBM_elem_from_index_any(em, index)))
|
|
{
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
struct PathSelectParams op_params;
|
|
path_select_params_from_op(op, &op_params);
|
|
op_params.track_active = true;
|
|
op_params.edge_mode = scene->toolsettings->edge_mode;
|
|
|
|
if (!edbm_shortest_path_pick_ex(scene, &op_params, ele_src, ele_dst)) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
void MESH_OT_shortest_path_pick(wmOperatorType *ot)
|
|
{
|
|
PropertyRNA *prop;
|
|
|
|
/* 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->exec = edbm_shortest_path_pick_exec;
|
|
ot->poll = ED_operator_editmesh_region_view3d;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
/* properties */
|
|
path_select_properties(ot);
|
|
|
|
/* use for redo */
|
|
prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
|
|
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Select path between existing selection */
|
|
|
|
static int edbm_shortest_path_select_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
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;
|
|
|
|
/* 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) {
|
|
struct PathSelectParams op_params;
|
|
path_select_params_from_op(op, &op_params);
|
|
|
|
edbm_shortest_path_pick_ex(scene, &op_params, ele_src, ele_dst);
|
|
|
|
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 */
|
|
path_select_properties(ot);
|
|
}
|