Blender V4.3
editmesh_rip_edge.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include "MEM_guardedalloc.h"
12
13#include "DNA_object_types.h"
14
15#include "BKE_context.hh"
16#include "BKE_editmesh.hh"
17#include "BKE_layer.hh"
18#include "BKE_report.hh"
19
20#include "BLI_math_geom.h"
21#include "BLI_math_vector.h"
23
24#include "WM_types.hh"
25
26#include "ED_mesh.hh"
27#include "ED_transform.hh"
28#include "ED_view3d.hh"
29
30#include "bmesh.hh"
31
32#include "mesh_intern.hh" /* own include */
33
34using blender::float2;
35using blender::Vector;
36
37/* uses total number of selected edges around a vertex to choose how to extend */
38#define USE_TRICKY_EXTEND
39
40static int edbm_rip_edge_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
41{
42 ARegion *region = CTX_wm_region(C);
44 const Scene *scene = CTX_data_scene(C);
45 ViewLayer *view_layer = CTX_data_view_layer(C);
47 scene, view_layer, CTX_wm_view3d(C));
48
49 for (Object *obedit : objects) {
51 BMesh *bm = em->bm;
52
53 BMIter viter;
54 BMVert *v;
55 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
56 float cent_sco[2];
57 int cent_tot;
58 bool changed = false;
59
60 /* mouse direction to view center */
61 float mval_dir[2];
62
63 if (bm->totvertsel == 0) {
64 continue;
65 }
66
67 const blender::float4x4 projectMat = ED_view3d_ob_project_mat_get(rv3d, obedit);
68
69 zero_v2(cent_sco);
70 cent_tot = 0;
71
72 /* clear tags and calc screen center */
73 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
75
77 const float2 v_sco = ED_view3d_project_float_v2_m4(region, v->co, projectMat);
78
79 add_v2_v2(cent_sco, v_sco);
80 cent_tot += 1;
81 }
82 }
83 mul_v2_fl(cent_sco, 1.0f / float(cent_tot));
84
85 /* not essential, but gives more expected results with edge selection */
86 if (bm->totedgesel) {
87 /* angle against center can give odd result,
88 * try re-position the center to the closest edge */
89 BMIter eiter;
90 BMEdge *e;
91 float dist_sq_best = len_squared_v2v2(cent_sco, mval_fl);
92
93 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
95 float cent_sco_test[2];
96 float dist_sq_test;
97
98 const float2 e_sco_0 = ED_view3d_project_float_v2_m4(region, e->v1->co, projectMat);
99 const float2 e_sco_1 = ED_view3d_project_float_v2_m4(region, e->v2->co, projectMat);
100
101 closest_to_line_segment_v2(cent_sco_test, mval_fl, e_sco_0, e_sco_1);
102 dist_sq_test = len_squared_v2v2(cent_sco_test, mval_fl);
103 if (dist_sq_test < dist_sq_best) {
104 dist_sq_best = dist_sq_test;
105
106 /* we have a new screen center */
107 copy_v2_v2(cent_sco, cent_sco_test);
108 }
109 }
110 }
111 }
112
113 sub_v2_v2v2(mval_dir, mval_fl, cent_sco);
114 normalize_v2(mval_dir);
115
116 /* operate on selected verts */
117 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
118 BMIter eiter;
119 BMEdge *e;
120 float2 v_sco;
121
123 /* Rules for */
124 float angle_best = FLT_MAX;
125 BMEdge *e_best = nullptr;
126
127#ifdef USE_TRICKY_EXTEND
128 /* first check if we can select the edge to split based on selection-only */
129 int tot_sel = 0;
130
131 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
134 e_best = e;
135 tot_sel += 1;
136 }
137 }
138 }
139 if (tot_sel != 1) {
140 e_best = nullptr;
141 }
142
143 /* only one edge selected, operate on that */
144 if (e_best) {
145 goto found_edge;
146 }
147 /* none selected, fall through and find one */
148 else if (tot_sel == 0) {
149 /* pass */
150 }
151 /* selection not 0 or 1, do nothing */
152 else {
153 goto found_edge;
154 }
155#endif
156 v_sco = ED_view3d_project_float_v2_m4(region, v->co, projectMat);
157
158 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
160 BMVert *v_other = BM_edge_other_vert(e, v);
161 float angle_test;
162
163 float2 v_other_sco = ED_view3d_project_float_v2_m4(region, v_other->co, projectMat);
164
165 /* avoid comparing with view-axis aligned edges (less than a pixel) */
166 if (len_squared_v2v2(v_sco, v_other_sco) > 1.0f) {
167 float v_dir[2];
168
169 sub_v2_v2v2(v_dir, v_other_sco, v_sco);
170 normalize_v2(v_dir);
171
172 angle_test = angle_normalized_v2v2(mval_dir, v_dir);
173
174 if (angle_test < angle_best) {
175 angle_best = angle_test;
176 e_best = e;
177 }
178 }
179 }
180 }
181
182#ifdef USE_TRICKY_EXTEND
183 found_edge:
184#endif
185 if (e_best) {
186 const bool e_select = BM_elem_flag_test_bool(e_best, BM_ELEM_SELECT);
187 BMVert *v_new;
188 BMEdge *e_new;
189
190 v_new = BM_edge_split(bm, e_best, v, &e_new, 0.0f);
191
192 BM_vert_select_set(bm, v, false);
193 BM_edge_select_set(bm, e_new, false);
194
195 BM_vert_select_set(bm, v_new, true);
196 if (e_select) {
197 BM_edge_select_set(bm, e_best, true);
198 }
199 BM_elem_flag_enable(v_new, BM_ELEM_TAG); /* prevent further splitting */
200
201 changed = true;
202 }
203 }
204 }
205
206 if (changed) {
208
210
212 params.calc_looptris = true;
213 params.calc_normals = false;
214 params.is_destructive = true;
215 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
216 }
217 }
218
219 return OPERATOR_FINISHED;
220}
221
223{
224 /* identifiers */
225 ot->name = "Extend Vertices";
226 ot->idname = "MESH_OT_rip_edge";
227 ot->description = "Extend vertices along the edge closest to the cursor";
228
229 /* api callbacks */
232
233 /* flags */
235
236 /* to give to transform */
238}
Scene * CTX_data_scene(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
float closest_to_line_segment_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:363
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
float angle_normalized_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_fl(float r[2], float f)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void zero_v2(float r[2])
MINLINE float normalize_v2(float n[2])
Object is a sort of wrapper for general info.
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
#define P_PROPORTIONAL
void Transform_Properties(wmOperatorType *ot, int flags)
#define P_MIRROR_DUMMY
blender::float2 ED_view3d_project_float_v2_m4(const ARegion *region, const float co[3], const blender::float4x4 &mat)
blender::float4x4 ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, const Object *ob)
Read Guarded memory(de)allocation.
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_test_bool(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_EDGES_OF_VERT
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_select_history_clear(BMesh *bm)
void BM_mesh_select_mode_flush(BMesh *bm)
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
BMVert * BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
Edge Split.
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
static int edbm_rip_edge_invoke(bContext *C, wmOperator *, const wmEvent *event)
void MESH_OT_rip_edge(wmOperatorType *ot)
bool EDBM_view3d_poll(bContext *C)
draw_view in_light_buf[] float
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define FLT_MAX
Definition stdcycles.h:14
float co[3]
int totvertsel
int totedgesel
int mval[2]
Definition WM_types.hh:728
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
const char * description
Definition WM_types.hh:996
wmOperatorType * ot
Definition wm_files.cc:4125