2
* ***** BEGIN GPL LICENSE BLOCK *****
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License
6
* as published by the Free Software Foundation; either version 2
7
* of the License, or (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software Foundation,
16
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
* Contributor(s): Francisco De La Cruz
20
* ***** END GPL LICENSE BLOCK *****
23
/** \file blender/editors/mesh/editmesh_slide.c
27
/* Takes heavily from editmesh_loopcut.c */
29
#include "DNA_object_types.h"
31
#include "MEM_guardedalloc.h"
33
#include "BLI_array.h"
36
#include "BKE_context.h"
37
#include "BKE_report.h"
38
#include "BKE_tessmesh.h"
41
#include "BIF_glutil.h"
43
#include "ED_screen.h"
44
#include "ED_view3d.h"
46
#include "ED_space_api.h"
48
#include "UI_resources.h"
50
#include "RNA_access.h"
51
#include "RNA_define.h"
56
#include "mesh_intern.h"
58
#define VTX_SLIDE_SNAP_THRSH 15
60
/* Cusom VertexSlide Operator data */
61
typedef struct VertexSlideOp {
66
ViewContext *view_context;
67
ARegion *active_region;
69
/* Draw callback handle */
75
/* Are we in slide mode */
87
/* Edge Frame Count */
93
/* Slide Frame Endpoints */
94
float (*vtx_frame)[3];
96
/* Mouse Click 2d pos */
101
static void vtx_slide_draw(const bContext *C, ARegion *ar, void *arg);
102
static int edbm_vertex_slide_exec(bContext *C, wmOperator *op);
103
static void vtx_slide_exit(const bContext *C, wmOperator *op);
104
static int vtx_slide_set_frame(VertexSlideOp *vso);
106
static int vtx_slide_init(bContext *C, wmOperator *op)
108
Object *obedit = CTX_data_edit_object(C);
109
BMEditMesh *em = BMEdit_FromObject(obedit);
110
BMEditSelection *ese;
115
const char *header_str = "Vertex Slide: Hover over an edge and left-click to select slide edge. "
116
"Left-Shift: Midpoint Snap, Left-Alt: Snap, Left-Ctrl: Snap&Merge";
119
BKE_report(op->reports, RPT_ERROR, "Vertex Slide Error: Not object in context");
123
EDBM_selectmode_flush(em);
124
ese = em->bm->selected.last;
126
/* Is there a starting vertex ? */
127
if (ese == NULL || (ese->htype != BM_VERT && ese->htype != BM_EDGE)) {
128
BKE_report(op->reports, RPT_ERROR_INVALID_INPUT, "Vertex Slide Error: Select a (single) vertex");
132
vso = MEM_callocN(sizeof(VertexSlideOp), "Vertex Slide Operator");
133
vso->view_context = MEM_callocN(sizeof(ViewContext), "Vertex Slide View Context");
135
op->customdata = vso;
137
/* Set the start vertex */
138
vso->start_vtx = (BMVert *)ese->ele;
140
vso->sel_edge = NULL;
143
vso->edge_frame = NULL;
145
vso->vtx_frame = NULL;
149
vso->slide_mode = FALSE;
151
vso->snap_n_merge = FALSE;
153
vso->snap_to_end_vtx = FALSE;
155
vso->snap_to_mid = FALSE;
157
vso->distance = 0.0f;
159
vso->snap_threshold = 0.2f;
161
/* Notify the viewport */
162
view3d_operator_needs_opengl(C);
164
/* Set the drawing region */
165
vso->active_region = CTX_wm_region(C);
167
/* Set the draw callback */
168
vso->draw_handle = ED_region_draw_cb_activate(vso->active_region->type, vtx_slide_draw, vso, REGION_DRAW_POST_VIEW);
170
ED_area_headerprint(CTX_wm_area(C), header_str);
172
em_setup_viewcontext(C, vso->view_context);
178
if (!vtx_slide_set_frame(vso)) {
179
BKE_report(op->reports, RPT_ERROR_INVALID_INPUT, "Vertex Slide: Can't find starting vertex!");
180
vtx_slide_exit(C, op);
184
/* Add handler for the vertex sliding */
185
WM_event_add_modal_handler(C, op);
188
ED_region_tag_redraw(vso->active_region);
193
static void vtx_slide_confirm(bContext *C, wmOperator *op)
195
VertexSlideOp *vso = op->customdata;
196
BMEditMesh *em = BMEdit_FromObject(vso->obj);
199
/* Select new edge */
200
BM_edge_select_set(bm, vso->sel_edge, TRUE);
202
/* Invoke operator */
203
edbm_vertex_slide_exec(C, op);
205
if (vso->snap_n_merge) {
207
BMVert* other = BM_edge_other_vert(vso->sel_edge, vso->start_vtx);
208
other_d = len_v3v3(vso->interp, other->co);
210
/* Only snap if within threshold */
211
if (other_d < vso->snap_threshold) {
212
BM_vert_select_set(bm, other, TRUE);
213
BM_vert_select_set(bm, vso->start_vtx, TRUE);
214
EDBM_op_callf(em, op, "pointmerge verts=%hv mergeco=%v", BM_ELEM_SELECT, other->co);
215
EDBM_flag_disable_all(em, BM_ELEM_SELECT);
218
/* Store in historty if not merging */
219
BM_select_history_store(em->bm, vso->start_vtx);
223
/* Store edit selection of the active vertex, allows other
224
* ops to run without reselecting */
225
BM_select_history_store(em->bm, vso->start_vtx);
228
EDBM_selectmode_flush(em);
230
/* NC_GEOM | ND_DATA & Retess */
231
EDBM_update_generic(C, em, TRUE);
233
ED_region_tag_redraw(vso->active_region);
236
static void vtx_slide_exit(const bContext *C, wmOperator *op)
238
/* Fetch custom data */
239
VertexSlideOp *vso = op->customdata;
241
/* Clean-up the custom data */
242
ED_region_draw_cb_exit(vso->active_region->type, vso->draw_handle);
247
MEM_freeN(vso->view_context);
249
vso->view_context = NULL;
251
if (vso->edge_frame) {
252
MEM_freeN(vso->edge_frame);
255
if (vso->vtx_frame) {
256
MEM_freeN(vso->vtx_frame);
259
vso->edge_frame = NULL;
261
vso->vtx_frame = NULL;
263
vso->slide_mode = FALSE;
267
op->customdata = NULL;
269
/* Clear the header */
270
ED_area_headerprint(CTX_wm_area(C), NULL);
273
static void vtx_slide_draw(const bContext *C, ARegion *UNUSED(ar), void *arg)
275
VertexSlideOp *vso = arg;
277
/* Have an edge to draw */
278
if (vso && vso->sel_edge) {
280
View3D *view3d = CTX_wm_view3d(C);
281
const float outline_w = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.8f;
282
const float pt_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5;
286
if (view3d && view3d->zbuf)
287
glDisable(GL_DEPTH_TEST);
289
glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT | GL_POINT_BIT);
292
glMultMatrixf(vso->obj->obmat);
295
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
298
if (vso->slide_mode && vso->disk_edges > 0) {
299
/* Draw intermediate edge frame */
300
UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 50, -50);
302
for (i = 0; i < vso->disk_edges; i++) {
304
glVertex3fv(vso->vtx_frame[i]);
305
glVertex3fv(vso->interp);
310
/* Draw selected edge
311
* Add color offset and reduce alpha */
312
UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 40, -50);
314
glLineWidth(outline_w);
317
bglVertex3fv(vso->sel_edge->v1->co);
318
bglVertex3fv(vso->sel_edge->v2->co);
321
if (vso->slide_mode) {
322
/* Draw interpolated vertex */
324
UI_ThemeColorShadeAlpha(TH_FACE_DOT, -80, -50);
326
glPointSize(pt_size);
329
bglVertex3fv(vso->interp);
337
if (view3d && view3d->zbuf)
338
glEnable(GL_DEPTH_TEST);
342
static BMEdge* vtx_slide_nrst_in_frame(VertexSlideOp *vso, const float mval[2])
344
BMEdge* cl_edge = NULL;
345
if (vso->disk_edges > 0) {
349
float v1_proj[3], v2_proj[3];
351
float min_dist = FLT_MAX;
353
for (i = 0; i < vso->disk_edges; i++) {
354
edge = vso->edge_frame[i];
356
mul_v3_m4v3(v1_proj, vso->obj->obmat, edge->v1->co);
357
project_float_noclip(vso->active_region, v1_proj, v1_proj);
359
mul_v3_m4v3(v2_proj, vso->obj->obmat, edge->v2->co);
360
project_float_noclip(vso->active_region, v2_proj, v2_proj);
362
dist = dist_to_line_segment_v2(mval, v1_proj, v2_proj);
363
if (dist < min_dist) {
372
static void vtx_slide_find_edge(VertexSlideOp *vso, wmEvent *event)
375
BMEdge *nst_edge = NULL;
377
const float mval_float[] = { (float)event->mval[0], (float)event->mval[1]};
379
/* Set mouse coords */
380
copy_v2_v2_int(vso->view_context->mval, event->mval);
382
/* Find nearest edge */
383
nst_edge = vtx_slide_nrst_in_frame(vso, mval_float);
386
/* Find a connected edge */
387
if (BM_vert_in_edge(nst_edge, vso->start_vtx)) {
389
/* Save mouse coords */
390
copy_v2_v2_int(vso->m_co, event->mval);
393
vso->sel_edge = nst_edge;
398
/* Updates the status of the operator - Invoked on mouse movement */
399
static void vtx_slide_update(VertexSlideOp *vso, wmEvent *event)
403
/* Find nearest edge */
404
edge = vso->sel_edge;
407
float edge_other_proj[3];
408
float start_vtx_proj[3];
414
/* Calculate interpolation value for preview */
417
float mval_float[] = { (float)event->mval[0], (float)event->mval[1]};
420
other = BM_edge_other_vert(edge, vso->start_vtx);
422
/* Project points onto screen and do interpolation in 2D */
423
mul_v3_m4v3(start_vtx_proj, vso->obj->obmat, vso->start_vtx->co);
424
project_float_noclip(vso->active_region, start_vtx_proj, start_vtx_proj);
426
mul_v3_m4v3(edge_other_proj, vso->obj->obmat, other->co);
427
project_float_noclip(vso->active_region, edge_other_proj, edge_other_proj);
429
closest_to_line_v2(closest_2d, mval_float, start_vtx_proj, edge_other_proj);
431
t_val = line_point_factor_v2(closest_2d, start_vtx_proj, edge_other_proj);
433
/* Set snap threshold to be proportional to edge length */
434
edge_len = len_v3v3(start_vtx_proj, edge_other_proj);
436
if (edge_len <= 0.0f)
437
edge_len = VTX_SLIDE_SNAP_THRSH;
439
edge_len = (len_v3v3(edge->v1->co, edge->v2->co) * VTX_SLIDE_SNAP_THRSH) / edge_len;
441
vso->snap_threshold = edge_len;
444
if (vso->snap_to_mid) {
448
/* Interpolate preview vertex 3D */
449
interp_v3_v3v3(interp, vso->start_vtx->co, other->co, t_val);
450
copy_v3_v3(vso->interp, interp);
452
vso->distance = t_val;
455
if (vso->snap_to_end_vtx) {
456
int start_at_v1 = edge->v1 == vso->start_vtx;
457
float v1_d = len_v3v3(vso->interp, edge->v1->co);
458
float v2_d = len_v3v3(vso->interp, edge->v2->co);
460
if (v1_d > v2_d && v2_d < vso->snap_threshold) {
461
copy_v3_v3(vso->interp, edge->v2->co);
464
vso->distance = 1.0f;
466
vso->distance = 0.0f;
468
if (v2_d > v1_d && v1_d < vso->snap_threshold) {
469
copy_v3_v3(vso->interp, edge->v1->co);
471
vso->distance = 0.0f;
473
vso->distance = 1.0f;
479
/* Sets the outline frame */
480
static int vtx_slide_set_frame(VertexSlideOp *vso)
483
float (*vtx_frame)[3] = NULL;
484
BMEdge** edge_frame = NULL;
485
BMVert *curr_vert = NULL;
486
BLI_array_declare(vtx_frame);
487
BLI_array_declare(edge_frame);
489
BMVert *sel_vtx = vso->start_vtx;
494
if (vso->edge_frame) {
495
MEM_freeN(vso->edge_frame);
496
vso->edge_frame = NULL;
499
if (vso->vtx_frame) {
500
MEM_freeN(vso->vtx_frame);
501
vso->vtx_frame = NULL;
504
/* Iterate over edges of vertex and copy them */
505
BM_ITER_ELEM_INDEX (edge, &iter, sel_vtx, BM_EDGES_OF_VERT, idx) {
506
curr_vert = BM_edge_other_vert(edge, sel_vtx);
508
BLI_array_growone(vtx_frame);
510
copy_v3_v3(vtx_frame[idx], curr_vert->co);
512
BLI_array_append(edge_frame, edge);
517
vso->edge_frame = edge_frame;
518
vso->vtx_frame = vtx_frame;
520
/* Set the interp at starting vtx */
521
copy_v3_v3(vso->interp, sel_vtx->co);
523
return vso->disk_edges > 0;
526
static int edbm_vertex_slide_modal(bContext *C, wmOperator *op, wmEvent *event)
528
VertexSlideOp *vso = op->customdata;
532
return OPERATOR_CANCELLED;
534
/* Notify the viewport */
535
view3d_operator_needs_opengl(C);
537
switch (event->type) {
540
switch (event->val) {
542
vso->snap_to_mid = TRUE;
545
vso->snap_to_mid = FALSE;
553
switch (event->val) {
555
vso->snap_n_merge = TRUE;
556
vso->snap_to_end_vtx = TRUE;
559
vso->snap_n_merge = FALSE;
560
vso->snap_to_end_vtx = FALSE;
568
switch (event->val) {
570
vso->snap_to_end_vtx = TRUE;
573
vso->snap_to_end_vtx = FALSE;
582
ED_region_tag_redraw(vso->active_region);
585
vtx_slide_exit(C, op);
587
return OPERATOR_CANCELLED;
591
if (event->val == KM_PRESS) {
592
/* Update mouse coords */
593
copy_v2_v2_int(vso->m_co, event->mval);
595
if (vso->slide_mode) {
596
vtx_slide_confirm(C, op);
598
vtx_slide_exit(C, op);
599
return OPERATOR_FINISHED;
601
else if (vso->sel_edge) {
602
vso->slide_mode = TRUE;
606
ED_region_tag_redraw(vso->active_region);
612
sprintf(buff, "Vertex Slide: %f", vso->distance);
613
if (!vso->slide_mode) {
614
vtx_slide_find_edge(vso, event);
617
vtx_slide_update(vso, event);
619
ED_area_headerprint(CTX_wm_area(C), buff);
620
ED_region_tag_redraw(vso->active_region);
625
return OPERATOR_RUNNING_MODAL;
628
static int edbm_vertex_slide_cancel(bContext *C, wmOperator *op)
631
vtx_slide_exit(C, op);
633
return OPERATOR_CANCELLED;
636
static int edbm_vertex_slide_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
638
/* Initialize the operator */
639
if (vtx_slide_init(C, op))
640
return OPERATOR_RUNNING_MODAL;
642
return OPERATOR_CANCELLED;
646
static int edbm_vertex_slide_exec(bContext *C, wmOperator *op)
648
Object *obedit = CTX_data_edit_object(C);
649
BMEditMesh *em = BMEdit_FromObject(obedit);
653
BMEditSelection *ese = (BMEditSelection *)em->bm->selected.last;
655
float distance_t = 0.0f;
657
/* Invoked modally? */
658
if (op->type->modal == edbm_vertex_slide_modal && op->customdata) {
659
VertexSlideOp *vso = (VertexSlideOp *)op->customdata;
661
if (bm->totedgesel > 1) {
662
/* Reset selections */
663
EDBM_flag_disable_all(em, BM_ELEM_SELECT);
664
BM_edge_select_set(bm, vso->sel_edge, TRUE);
665
BM_vert_select_set(bm, vso->start_vtx, TRUE);
667
BM_select_history_store(em->bm, vso->sel_edge);
668
BM_select_history_store(em->bm, vso->start_vtx);
669
ese = (BMEditSelection *)em->bm->selected.last;
671
distance_t = vso->distance;
672
RNA_float_set(op->ptr, "distance_t", distance_t);
676
distance_t = RNA_float_get(op->ptr, "distance_t");
679
/* Is there a starting vertex ? */
680
if ((ese == NULL) || (ese->htype != BM_VERT && ese->htype != BM_EDGE)) {
681
BKE_report(op->reports, RPT_ERROR_INVALID_INPUT, "Vertex Slide Error: Select a (single) vertex");
682
return OPERATOR_CANCELLED;
685
start_vert = (BMVert *)ese->ele;
687
/* Prepare operator */
688
if (!EDBM_op_init(em, &bmop, op,
689
"vertex_slide vert=%e edge=%hev distance_t=%f",
690
start_vert, BM_ELEM_SELECT, distance_t))
692
return OPERATOR_CANCELLED;
694
/* Execute operator */
695
BMO_op_exec(bm, &bmop);
697
/* Deselect the input edges */
698
BMO_slot_buffer_hflag_disable(bm, &bmop, "edge", BM_ALL, BM_ELEM_SELECT, TRUE);
700
/* Select the output vert */
701
BMO_slot_buffer_hflag_enable(bm, &bmop, "vertout", BM_ALL, BM_ELEM_SELECT, TRUE);
703
/* Flush the select buffers */
704
EDBM_selectmode_flush(em);
706
if (!EDBM_op_finish(em, &bmop, op, TRUE)) {
707
return OPERATOR_CANCELLED;
710
/* Update Geometry */
711
EDBM_update_generic(C, em, TRUE);
713
return OPERATOR_FINISHED;
716
void MESH_OT_vert_slide(wmOperatorType *ot)
721
ot->name = "Vertex Slide";
722
ot->idname = "MESH_OT_vert_slide";
723
ot->description = "Vertex slide";
726
ot->invoke = edbm_vertex_slide_invoke;
727
ot->modal = edbm_vertex_slide_modal;
728
ot->cancel = edbm_vertex_slide_cancel;
729
ot->poll = ED_operator_editmesh_region_view3d;
731
/* ot->exec = edbm_vertex_slide_exec;
732
* ot->poll = ED_operator_editmesh; */
735
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
737
/* Properties for vertex slide */
738
prop = RNA_def_float(ot->srna, "distance_t", 0.0f, -FLT_MAX, FLT_MAX, "Distance", "Distance", -5.0f, 5.0f);
739
RNA_def_property_ui_range(prop, -5.0f, 5.0f, 0.1, 4);
740
RNA_def_property_flag(prop, PROP_SKIP_SAVE);