Blender V5.0
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
10
11#include "DNA_object_types.h"
12
13#include "BKE_context.hh"
14#include "BKE_editmesh.hh"
15#include "BKE_layer.hh"
16
17#include "BLI_math_geom.h"
18#include "BLI_math_vector.h"
20
21#include "WM_types.hh"
22
23#include "ED_mesh.hh"
24#include "ED_transform.hh"
25#include "ED_view3d.hh"
26
27#include "bmesh.hh"
28
29#include "mesh_intern.hh" /* own include */
30
31using blender::float2;
32using blender::Vector;
33
34/* uses total number of selected edges around a vertex to choose how to extend */
35#define USE_TRICKY_EXTEND
36
38 wmOperator * /*op*/,
39 const wmEvent *event)
40{
41 ARegion *region = CTX_wm_region(C);
43 const Scene *scene = CTX_data_scene(C);
44 ViewLayer *view_layer = CTX_data_view_layer(C);
46 scene, view_layer, CTX_wm_view3d(C));
47
48 for (Object *obedit : objects) {
50 BMesh *bm = em->bm;
51
52 BMIter viter;
53 BMVert *v;
54 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
55 float cent_sco[2];
56 int cent_tot;
57 bool changed = false;
58
59 /* mouse direction to view center */
60 float mval_dir[2];
61
62 if (bm->totvertsel == 0) {
63 continue;
64 }
65
66 const blender::float4x4 projectMat = ED_view3d_ob_project_mat_get(rv3d, obedit);
67
68 zero_v2(cent_sco);
69 cent_tot = 0;
70
71 /* clear tags and calc screen center */
72 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
74
76 const float2 v_sco = ED_view3d_project_float_v2_m4(region, v->co, projectMat);
77
78 add_v2_v2(cent_sco, v_sco);
79 cent_tot += 1;
80 }
81 }
82 mul_v2_fl(cent_sco, 1.0f / float(cent_tot));
83
84 /* not essential, but gives more expected results with edge selection */
85 if (bm->totedgesel) {
86 /* angle against center can give odd result,
87 * try re-position the center to the closest edge */
88 BMIter eiter;
89 BMEdge *e;
90 float dist_sq_best = len_squared_v2v2(cent_sco, mval_fl);
91
92 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
94 float cent_sco_test[2];
95 float dist_sq_test;
96
97 const float2 e_sco_0 = ED_view3d_project_float_v2_m4(region, e->v1->co, projectMat);
98 const float2 e_sco_1 = ED_view3d_project_float_v2_m4(region, e->v2->co, projectMat);
99
100 closest_to_line_segment_v2(cent_sco_test, mval_fl, e_sco_0, e_sco_1);
101 dist_sq_test = len_squared_v2v2(cent_sco_test, mval_fl);
102 if (dist_sq_test < dist_sq_best) {
103 dist_sq_best = dist_sq_test;
104
105 /* we have a new screen center */
106 copy_v2_v2(cent_sco, cent_sco_test);
107 }
108 }
109 }
110 }
111
112 sub_v2_v2v2(mval_dir, mval_fl, cent_sco);
113 normalize_v2(mval_dir);
114
115 /* operate on selected verts */
116 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
117 BMIter eiter;
118 BMEdge *e;
119 float2 v_sco;
120
122 /* Rules for */
123 float angle_best = FLT_MAX;
124 BMEdge *e_best = nullptr;
125
126#ifdef USE_TRICKY_EXTEND
127 /* first check if we can select the edge to split based on selection-only */
128 int tot_sel = 0;
129
130 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
133 e_best = e;
134 tot_sel += 1;
135 }
136 }
137 }
138 if (tot_sel != 1) {
139 e_best = nullptr;
140 }
141
142 /* only one edge selected, operate on that */
143 if (e_best) {
144 goto found_edge;
145 }
146 /* none selected, fall through and find one */
147 else if (tot_sel == 0) {
148 /* pass */
149 }
150 /* selection not 0 or 1, do nothing */
151 else {
152 goto found_edge;
153 }
154#endif
155 v_sco = ED_view3d_project_float_v2_m4(region, v->co, projectMat);
156
157 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
159 BMVert *v_other = BM_edge_other_vert(e, v);
160 float angle_test;
161
162 float2 v_other_sco = ED_view3d_project_float_v2_m4(region, v_other->co, projectMat);
163
164 /* avoid comparing with view-axis aligned edges (less than a pixel) */
165 if (len_squared_v2v2(v_sco, v_other_sco) > 1.0f) {
166 float v_dir[2];
167
168 sub_v2_v2v2(v_dir, v_other_sco, v_sco);
169 normalize_v2(v_dir);
170
171 angle_test = angle_normalized_v2v2(mval_dir, v_dir);
172
173 if (angle_test < angle_best) {
174 angle_best = angle_test;
175 e_best = e;
176 }
177 }
178 }
179 }
180
181#ifdef USE_TRICKY_EXTEND
182 found_edge:
183#endif
184 if (e_best) {
185 const bool e_select = BM_elem_flag_test_bool(e_best, BM_ELEM_SELECT);
186 BMVert *v_new;
187 BMEdge *e_new;
188
189 v_new = BM_edge_split(bm, e_best, v, &e_new, 0.0f);
190
191 BM_vert_select_set(bm, v, false);
192 BM_edge_select_set(bm, e_new, false);
193
194 BM_vert_select_set(bm, v_new, true);
195 if (e_select) {
196 BM_edge_select_set(bm, e_best, true);
197 }
198 BM_elem_flag_enable(v_new, BM_ELEM_TAG); /* prevent further splitting */
199
200 /* When UV sync select is enabled, the wrong UV's will be selected
201 * because the existing loops will have the selection and the new ones won't.
202 * transfer the selection state to the new loops. */
203 if (bm->uv_select_sync_valid) {
204 if (e_best->l) {
205 BMLoop *l_iter, *l_first;
206 l_iter = l_first = e_best->l;
207 do {
208 bool was_select = false;
209 if (l_iter->next->e == e_new) {
211 BM_loop_edge_uvselect_set(bm, l_iter->next, false);
212 was_select = true;
213 }
214 }
215 else {
216 BLI_assert(l_iter->prev->e == e_new);
218 BM_loop_edge_uvselect_set(bm, l_iter->prev, false);
219 was_select = true;
220 }
221 }
222 if (was_select) {
223 BM_loop_edge_uvselect_set(bm, l_iter, true);
224 }
225
226 } while ((l_iter = l_iter->radial_next) != l_first);
227 }
228 }
229
230 changed = true;
231 }
232 }
233 }
234
235 if (changed) {
237
239
241 params.calc_looptris = true;
242 params.calc_normals = false;
243 params.is_destructive = true;
244 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
245 }
246 }
247
248 return OPERATOR_FINISHED;
249}
250
252{
253 /* identifiers */
254 ot->name = "Extend Vertices";
255 ot->idname = "MESH_OT_rip_edge";
256 ot->description = "Extend vertices along the edge closest to the cursor";
257
258 /* API callbacks. */
259 ot->invoke = edbm_rip_edge_invoke;
260 ot->poll = EDBM_view3d_poll;
261
262 /* flags */
264
265 /* to give to transform */
267}
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:61
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
#define BLI_assert(a)
Definition BLI_assert.h:46
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:365
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.
@ OPERATOR_FINISHED
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
#define P_PROPORTIONAL
#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)
#define C
Definition RandGen.cpp:29
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_SELECT_UV
@ 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
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
void BM_loop_edge_uvselect_set(BMesh *bm, BMLoop *l, bool select)
nullptr float
static wmOperatorStatus edbm_rip_edge_invoke(bContext *C, wmOperator *, const wmEvent *event)
void MESH_OT_rip_edge(wmOperatorType *ot)
bool EDBM_view3d_poll(bContext *C)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void properties_register(wmOperatorType *ot, int flags)
MatBase< float, 4, 4 > float4x4
VecBase< float, 2 > float2
#define FLT_MAX
Definition stdcycles.h:14
struct BMLoop * l
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMLoop * next
float co[3]
int mval[2]
Definition WM_types.hh:763
wmOperatorType * ot
Definition wm_files.cc:4237