This addresses warnings from Clang-Tidy's `readability-else-after-return` rule in the `source/blender/editors/mesh` module. No functional changes.
		
			
				
	
	
		
			928 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			928 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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.
 | |
|  */
 | |
| 
 | |
| /** \file
 | |
|  * \ingroup edmesh
 | |
|  */
 | |
| 
 | |
| #include "MEM_guardedalloc.h"
 | |
| 
 | |
| #include "DNA_mesh_types.h"
 | |
| #include "DNA_object_types.h"
 | |
| #include "DNA_scene_types.h"
 | |
| #include "DNA_windowmanager_types.h"
 | |
| 
 | |
| #ifdef WITH_FREESTYLE
 | |
| #  include "DNA_meshdata_types.h"
 | |
| #endif
 | |
| 
 | |
| #include "BLI_linklist.h"
 | |
| #include "BLI_math.h"
 | |
| 
 | |
| #include "BKE_context.h"
 | |
| #include "BKE_editmesh.h"
 | |
| #include "BKE_layer.h"
 | |
| #include "BKE_report.h"
 | |
| 
 | |
| #include "ED_mesh.h"
 | |
| #include "ED_object.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 */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Path Select Struct & Properties
 | |
|  * \{ */
 | |
| 
 | |
| enum {
 | |
|   EDGE_MODE_SELECT = 0,
 | |
|   EDGE_MODE_TAG_SEAM = 1,
 | |
|   EDGE_MODE_TAG_SHARP = 2,
 | |
|   EDGE_MODE_TAG_CREASE = 3,
 | |
|   EDGE_MODE_TAG_BEVEL = 4,
 | |
|   EDGE_MODE_TAG_FREESTYLE = 5,
 | |
| };
 | |
| 
 | |
| struct PathSelectParams {
 | |
|   /** ensure the active element is the last selected item (handy for picking) */
 | |
|   bool track_active;
 | |
|   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)
 | |
| {
 | |
|   static const EnumPropertyItem edge_tag_items[] = {
 | |
|       {EDGE_MODE_SELECT, "SELECT", 0, "Select", ""},
 | |
|       {EDGE_MODE_TAG_SEAM, "SEAM", 0, "Tag Seam", ""},
 | |
|       {EDGE_MODE_TAG_SHARP, "SHARP", 0, "Tag Sharp", ""},
 | |
|       {EDGE_MODE_TAG_CREASE, "CREASE", 0, "Tag Crease", ""},
 | |
|       {EDGE_MODE_TAG_BEVEL, "BEVEL", 0, "Tag Bevel", ""},
 | |
|       {EDGE_MODE_TAG_FREESTYLE, "FREESTYLE", 0, "Tag Freestyle Edge Mark", ""},
 | |
|       {0, NULL, 0, NULL, NULL},
 | |
|   };
 | |
| 
 | |
|   RNA_def_enum(ot->srna,
 | |
|                "edge_mode",
 | |
|                edge_tag_items,
 | |
|                EDGE_MODE_SELECT,
 | |
|                "Edge Tag",
 | |
|                "The edge flag to tag when selecting the shortest path");
 | |
| 
 | |
|   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,
 | |
|                                        ToolSettings *ts,
 | |
|                                        struct PathSelectParams *op_params)
 | |
| {
 | |
|   {
 | |
|     PropertyRNA *prop = RNA_struct_find_property(op->ptr, "edge_mode");
 | |
|     if (RNA_property_is_set(op->ptr, prop)) {
 | |
|       op_params->edge_mode = RNA_property_enum_get(op->ptr, prop);
 | |
|       if (op->flag & OP_IS_INVOKE) {
 | |
|         ts->edge_mode = op_params->edge_mode;
 | |
|       }
 | |
|     }
 | |
|     else {
 | |
|       op_params->edge_mode = ts->edge_mode;
 | |
|       RNA_property_enum_set(op->ptr, prop, op_params->edge_mode);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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);
 | |
| }
 | |
| 
 | |
| static bool path_select_poll_property(const bContext *C,
 | |
|                                       wmOperator *UNUSED(op),
 | |
|                                       const PropertyRNA *prop)
 | |
| {
 | |
|   const char *prop_id = RNA_property_identifier(prop);
 | |
|   if (STREQ(prop_id, "edge_mode")) {
 | |
|     const Scene *scene = CTX_data_scene(C);
 | |
|     ToolSettings *ts = scene->toolsettings;
 | |
|     if ((ts->selectmode & SCE_SELECT_EDGE) == 0) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| struct UserData {
 | |
|   BMesh *bm;
 | |
|   Mesh *me;
 | |
|   const struct PathSelectParams *op_params;
 | |
| };
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name 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 *UNUSED(scene),
 | |
|                                           Object *obedit,
 | |
|                                           const struct PathSelectParams *op_params,
 | |
|                                           BMVert *v_act,
 | |
|                                           BMVert *v_dst)
 | |
| {
 | |
|   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(obedit->data, false, false);
 | |
| }
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name 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(Mesh *me, const char edge_mode)
 | |
| {
 | |
|   BMesh *bm = me->edit_mesh->bm;
 | |
| 
 | |
|   switch (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,
 | |
|                                           Object *obedit,
 | |
|                                           const struct PathSelectParams *op_params,
 | |
|                                           BMEdge *e_act,
 | |
|                                           BMEdge *e_dst)
 | |
| {
 | |
|   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;
 | |
| 
 | |
|   edgetag_ensure_cd_flag(obedit->data, op_params->edge_mode);
 | |
| 
 | |
|   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(obedit->data, op_params->edge_mode);
 | |
|     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);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   EDBM_update_generic(obedit->data, false, false);
 | |
| 
 | |
|   if (op_params->edge_mode == EDGE_MODE_TAG_SEAM) {
 | |
|     ED_uvedit_live_unwrap(scene, &obedit, 1);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name 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 *UNUSED(scene),
 | |
|                                           Object *obedit,
 | |
|                                           const struct PathSelectParams *op_params,
 | |
|                                           BMFace *f_act,
 | |
|                                           BMFace *f_dst)
 | |
| {
 | |
|   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(obedit->data, false, false);
 | |
| }
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Main Operator for vert/edge/face tag
 | |
|  * \{ */
 | |
| 
 | |
| static bool edbm_shortest_path_pick_ex(Scene *scene,
 | |
|                                        Object *obedit,
 | |
|                                        const struct PathSelectParams *op_params,
 | |
|                                        BMElem *ele_src,
 | |
|                                        BMElem *ele_dst)
 | |
| {
 | |
|   bool ok = false;
 | |
| 
 | |
|   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, obedit, op_params, (BMVert *)ele_src, (BMVert *)ele_dst);
 | |
|     ok = true;
 | |
|   }
 | |
|   else if (ele_src->head.htype == BM_EDGE) {
 | |
|     mouse_mesh_shortest_path_edge(scene, obedit, op_params, (BMEdge *)ele_src, (BMEdge *)ele_dst);
 | |
|     ok = true;
 | |
|   }
 | |
|   else if (ele_src->head.htype == BM_FACE) {
 | |
|     mouse_mesh_shortest_path_face(scene, obedit, op_params, (BMFace *)ele_src, (BMFace *)ele_dst);
 | |
|     ok = true;
 | |
|   }
 | |
| 
 | |
|   if (ok) {
 | |
|     DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
 | |
|     WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data);
 | |
|   }
 | |
| 
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| static int edbm_shortest_path_pick_exec(bContext *C, wmOperator *op);
 | |
| 
 | |
| static BMElem *edbm_elem_find_nearest(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(vc, &dist);
 | |
|   }
 | |
|   if ((em->selectmode & SCE_SELECT_EDGE) && (htype == BM_EDGE)) {
 | |
|     return (BMElem *)EDBM_edge_find_nearest(vc, &dist);
 | |
|   }
 | |
|   if ((em->selectmode & SCE_SELECT_FACE) && (htype == BM_FACE)) {
 | |
|     return (BMElem *)EDBM_face_find_nearest(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);
 | |
|   }
 | |
| 
 | |
|   Base *basact = NULL;
 | |
|   BMVert *eve = NULL;
 | |
|   BMEdge *eed = NULL;
 | |
|   BMFace *efa = NULL;
 | |
| 
 | |
|   ViewContext vc;
 | |
|   BMEditMesh *em;
 | |
|   bool track_active = true;
 | |
| 
 | |
|   em_setup_viewcontext(C, &vc);
 | |
|   copy_v2_v2_int(vc.mval, event->mval);
 | |
|   em = vc.em;
 | |
| 
 | |
|   view3d_operator_needs_opengl(C);
 | |
| 
 | |
|   {
 | |
|     int base_index = -1;
 | |
|     uint bases_len = 0;
 | |
|     Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len);
 | |
|     if (EDBM_unified_findnearest(&vc, bases, bases_len, &base_index, &eve, &eed, &efa)) {
 | |
|       basact = bases[base_index];
 | |
|       ED_view3d_viewcontext_init_object(&vc, basact->object);
 | |
|       em = vc.em;
 | |
|     }
 | |
|     MEM_freeN(bases);
 | |
|   }
 | |
| 
 | |
|   /* If nothing is selected, let's select the picked vertex/edge/face. */
 | |
|   if ((vc.em->bm->totvertsel == 0) && (eve || eed || efa)) {
 | |
|     /* TODO (dfelinto) right now we try to find the closest element twice.
 | |
|      * The ideal is to refactor EDBM_select_pick so it doesn't
 | |
|      * have to pick the nearest vert/edge/face again.
 | |
|      */
 | |
|     EDBM_select_pick(C, event->mval, true, false, false);
 | |
|     return OPERATOR_FINISHED;
 | |
|   }
 | |
| 
 | |
|   struct PathSelectParams op_params;
 | |
|   path_select_params_from_op(op, vc.scene->toolsettings, &op_params);
 | |
| 
 | |
|   BMElem *ele_src, *ele_dst;
 | |
|   if (!(ele_src = edbm_elem_active_elem_or_face_get(em->bm)) ||
 | |
|       !(ele_dst = edbm_elem_find_nearest(&vc, ele_src->head.htype))) {
 | |
|     /* special case, toggle edge tags even when we don't have a path */
 | |
|     if (((em->selectmode & SCE_SELECT_EDGE) && (op_params.edge_mode != EDGE_MODE_SELECT)) &&
 | |
|         /* check if we only have a destination edge */
 | |
|         ((ele_src == NULL) && (ele_dst = edbm_elem_find_nearest(&vc, BM_EDGE)))) {
 | |
|       ele_src = ele_dst;
 | |
|       track_active = false;
 | |
|     }
 | |
|     else {
 | |
|       return OPERATOR_PASS_THROUGH;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   op_params.track_active = track_active;
 | |
| 
 | |
|   if (!edbm_shortest_path_pick_ex(vc.scene, vc.obedit, &op_params, ele_src, ele_dst)) {
 | |
|     return OPERATOR_PASS_THROUGH;
 | |
|   }
 | |
| 
 | |
|   if (vc.view_layer->basact != basact) {
 | |
|     ED_object_base_activate(C, basact);
 | |
|   }
 | |
| 
 | |
|   /* 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, scene->toolsettings, &op_params);
 | |
|   op_params.track_active = true;
 | |
| 
 | |
|   if (!edbm_shortest_path_pick_ex(scene, obedit, &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;
 | |
|   ot->poll_property = path_select_poll_property;
 | |
| 
 | |
|   /* 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);
 | |
| }
 | |
| 
 | |
| /** \} */
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /** \name Select Path Between Existing Selection
 | |
|  * \{ */
 | |
| 
 | |
| static int edbm_shortest_path_select_exec(bContext *C, wmOperator *op)
 | |
| {
 | |
|   Scene *scene = CTX_data_scene(C);
 | |
|   bool found_valid_elements = false;
 | |
| 
 | |
|   ViewLayer *view_layer = CTX_data_view_layer(C);
 | |
|   uint objects_len = 0;
 | |
|   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
 | |
|       view_layer, CTX_wm_view3d(C), &objects_len);
 | |
|   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
 | |
|     Object *obedit = objects[ob_index];
 | |
|     BMEditMesh *em = BKE_editmesh_from_object(obedit);
 | |
|     BMesh *bm = em->bm;
 | |
|     BMIter iter;
 | |
|     BMEditSelection *ese_src, *ese_dst;
 | |
|     BMElem *ele_src = NULL, *ele_dst = NULL, *ele;
 | |
| 
 | |
|     if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     /* 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, scene->toolsettings, &op_params);
 | |
| 
 | |
|       edbm_shortest_path_pick_ex(scene, obedit, &op_params, ele_src, ele_dst);
 | |
| 
 | |
|       found_valid_elements = true;
 | |
|     }
 | |
|   }
 | |
|   MEM_freeN(objects);
 | |
| 
 | |
|   if (!found_valid_elements) {
 | |
|     BKE_report(
 | |
|         op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected");
 | |
|     return OPERATOR_CANCELLED;
 | |
|   }
 | |
| 
 | |
|   return OPERATOR_FINISHED;
 | |
| }
 | |
| 
 | |
| void MESH_OT_shortest_path_select(wmOperatorType *ot)
 | |
| {
 | |
|   /* identifiers */
 | |
|   ot->name = "Select Shortest Path";
 | |
|   ot->idname = "MESH_OT_shortest_path_select";
 | |
|   ot->description = "Selected shortest path between two vertices/edges/faces";
 | |
| 
 | |
|   /* api callbacks */
 | |
|   ot->exec = edbm_shortest_path_select_exec;
 | |
|   ot->poll = ED_operator_editmesh;
 | |
|   ot->poll_property = path_select_poll_property;
 | |
| 
 | |
|   /* flags */
 | |
|   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 | |
| 
 | |
|   /* properties */
 | |
|   path_select_properties(ot);
 | |
| }
 | |
| 
 | |
| /** \} */
 |