bd1d13f3528a4eca9f1e54fab55258e7c52b067d
[blender.git] / source / blender / editors / mesh / editmesh_slide.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
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.
8  *
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.
13  *
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.
17  *
18  * Contributor(s): Francisco De La Cruz
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/editors/mesh/editmesh_slide.c
24  *  \ingroup edmesh
25  */
26
27 /* Takes heavily from editmesh_loopcut.c */
28
29 #include "DNA_object_types.h"
30 #include "DNA_mesh_types.h"
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_array.h"
35 #include "BLI_math.h"
36
37 #include "BKE_context.h"
38 #include "BKE_report.h"
39 #include "BKE_tessmesh.h"
40
41 #include "BIF_gl.h"
42 #include "BIF_glutil.h"
43
44 #include "ED_screen.h"
45 #include "ED_view3d.h"
46 #include "ED_mesh.h"
47 #include "ED_space_api.h"
48
49 #include "UI_resources.h"
50
51 #include "RNA_access.h"
52 #include "RNA_define.h"
53
54 #include "WM_api.h"
55 #include "WM_types.h"
56
57 #include "mesh_intern.h"
58
59 #define VTX_SLIDE_SNAP_THRSH 15
60
61 /* Cusom VertexSlide Operator data */
62 typedef struct VertexSlideOp {
63         /* Starting Vertex */
64         BMVert *start_vtx;
65         BMEdge *sel_edge;
66
67         ViewContext *view_context;
68         ARegion *active_region;
69
70         /* Draw callback handle */
71         void *draw_handle;
72
73         /* Active Object */
74         Object *obj;
75
76         /* Are we in slide mode */
77         int slide_mode;
78         int snap_n_merge;
79         int snap_to_end_vtx;
80         int snap_to_mid;
81
82         /* Snap threshold */
83         float snap_threshold;
84
85         float distance;
86         float interp[3];
87
88         /* Edge Frame Count */
89         int disk_edges;
90
91         /* Edges */
92         BMEdge **edge_frame;
93
94         /* Slide Frame Endpoints */
95         float (*vtx_frame)[3];
96
97         /* Mouse Click 2d pos */
98         int m_co[2];
99
100 } VertexSlideOp;
101
102 static void vtx_slide_draw(const bContext *C, ARegion *ar, void *arg);
103 static int edbm_vertex_slide_exec_ex(bContext *C, wmOperator *op, const int do_update);
104 static void vtx_slide_exit(const bContext *C, wmOperator *op);
105 static int vtx_slide_set_frame(VertexSlideOp *vso);
106
107 static int vtx_slide_init(bContext *C, wmOperator *op)
108 {
109         Object *obedit = CTX_data_edit_object(C);
110         BMEditMesh *em = BMEdit_FromObject(obedit);
111         BMEditSelection *ese;
112
113         /* Custom data */
114         VertexSlideOp *vso;
115
116         const char *header_str = "Vertex Slide: Hover over an edge and left-click to select slide edge. "
117                                  "Left-Shift: Midpoint Snap, Left-Alt: Snap, Left-Ctrl: Snap&Merge";
118
119         if (!obedit) {
120                 BKE_report(op->reports, RPT_ERROR, "Vertex Slide Error: Not object in context");
121                 return FALSE;
122         }
123
124         EDBM_selectmode_flush(em);
125         ese = em->bm->selected.last;
126
127         /* Is there a starting vertex  ? */
128         if (ese == NULL || (ese->htype != BM_VERT && ese->htype != BM_EDGE)) {
129                 BKE_report(op->reports, RPT_ERROR_INVALID_INPUT, "Vertex Slide Error: Select a (single) vertex");
130                 return FALSE;
131         }
132
133         vso = MEM_callocN(sizeof(VertexSlideOp), "Vertex Slide Operator");
134         vso->view_context = MEM_callocN(sizeof(ViewContext), "Vertex Slide View Context");
135
136         op->customdata = vso;
137
138         /* Set the start vertex */
139         vso->start_vtx = (BMVert *)ese->ele;
140
141         vso->sel_edge = NULL;
142
143         /* Edges */
144         vso->edge_frame = NULL;
145
146         vso->vtx_frame = NULL;
147
148         vso->disk_edges = 0;
149
150         vso->slide_mode = FALSE;
151
152         vso->snap_n_merge = FALSE;
153
154         vso->snap_to_end_vtx = FALSE;
155
156         vso->snap_to_mid = FALSE;
157
158         vso->distance = 0.0f;
159
160         vso->snap_threshold = 0.2f;
161
162         /* Notify the viewport */
163         view3d_operator_needs_opengl(C);
164
165         /* Set the drawing region */
166         vso->active_region = CTX_wm_region(C);
167
168         /* Set the draw callback */
169         vso->draw_handle = ED_region_draw_cb_activate(vso->active_region->type, vtx_slide_draw, vso, REGION_DRAW_POST_VIEW);
170
171         ED_area_headerprint(CTX_wm_area(C), header_str);
172         
173         em_setup_viewcontext(C, vso->view_context);
174
175         /* Set the object */
176         vso->obj = obedit;
177
178         /* Init frame */
179         if (!vtx_slide_set_frame(vso)) {
180                 BKE_report(op->reports, RPT_ERROR_INVALID_INPUT, "Vertex Slide: Can't find starting vertex!");
181                 vtx_slide_exit(C, op);
182                 return FALSE;
183         }
184
185         /* Add handler for the vertex sliding */
186         WM_event_add_modal_handler(C, op);
187
188         /* Tag for redraw */
189         ED_region_tag_redraw(vso->active_region);
190
191         return TRUE;
192 }
193
194 static void vtx_slide_confirm(bContext *C, wmOperator *op)
195 {
196         VertexSlideOp *vso = op->customdata;
197         BMEditMesh *em = BMEdit_FromObject(vso->obj);
198         BMesh *bm = em->bm;
199         BMVert *other = NULL;
200
201         BMVert *mirr_vtx = NULL;
202         BMVert *mirr_vtx_other = NULL;
203
204         /* Select new edge */
205         BM_edge_select_set(bm, vso->sel_edge, TRUE);
206
207         if (vso->snap_n_merge) {
208                 other = BM_edge_other_vert(vso->sel_edge, vso->start_vtx);
209         }
210
211         if (((Mesh *)em->ob->data)->editflag & ME_EDIT_MIRROR_X) {
212                 EDBM_verts_mirror_cache_begin(em, TRUE);
213
214                 mirr_vtx = EDBM_verts_mirror_get(em, vso->start_vtx);
215                 if (vso->snap_n_merge) {
216                         mirr_vtx_other = EDBM_verts_mirror_get(em, other);
217                 }
218         }
219
220         /* Invoke operator - warning */
221         edbm_vertex_slide_exec_ex(C, op, FALSE);
222
223         if (mirr_vtx) {
224                 mirr_vtx->co[0] = -vso->start_vtx->co[0];
225                 mirr_vtx->co[1] =  vso->start_vtx->co[1];
226                 mirr_vtx->co[2] =  vso->start_vtx->co[2];
227         }
228
229         if (vso->snap_n_merge) {
230                 float other_d;
231                 other_d = len_v3v3(vso->interp, other->co);
232
233                 /* Only snap if within threshold */
234                 if (other_d < vso->snap_threshold) {
235                         BM_vert_select_set(bm, other, TRUE);
236                         BM_vert_select_set(bm, vso->start_vtx, TRUE);
237                         EDBM_op_callf(em, op, "pointmerge verts=%hv merge_co=%v", BM_ELEM_SELECT, other->co);
238                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
239
240                         if (mirr_vtx_other) {
241                                 BM_vert_select_set(bm, mirr_vtx, TRUE);
242                                 BM_vert_select_set(bm, mirr_vtx_other, TRUE);
243                                 EDBM_op_callf(em, op, "pointmerge verts=%hv merge_co=%v", BM_ELEM_SELECT, mirr_vtx_other->co);
244                                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
245                         }
246                 }
247                 else {
248                         /* Store in historty if not merging */
249                         BM_select_history_store(em->bm, vso->start_vtx);
250                 }
251         }
252         else {
253                 /* Store edit selection of the active vertex, allows other
254                  *  ops to run without reselecting */
255                 BM_select_history_store(em->bm, vso->start_vtx);
256         }
257
258         if (((Mesh *)em->ob->data)->editflag & ME_EDIT_MIRROR_X) {
259                 EDBM_verts_mirror_cache_end(em);
260         }
261
262         EDBM_selectmode_flush(em);
263         
264         /* NC_GEOM | ND_DATA & Retess */
265         EDBM_update_generic(C, em, TRUE);
266         
267         ED_region_tag_redraw(vso->active_region);
268 }
269
270 static void vtx_slide_exit(const bContext *C, wmOperator *op)
271 {
272         /* Fetch custom data */
273         VertexSlideOp *vso = op->customdata;
274
275         /* Clean-up the custom data */
276         ED_region_draw_cb_exit(vso->active_region->type, vso->draw_handle);
277
278         /* Free Custom Data
279          *
280          */
281         MEM_freeN(vso->view_context);
282
283         vso->view_context = NULL;
284
285         if (vso->edge_frame) {
286                 MEM_freeN(vso->edge_frame);
287         }
288
289         if (vso->vtx_frame) {
290                 MEM_freeN(vso->vtx_frame);
291         }
292
293         vso->edge_frame = NULL;
294
295         vso->vtx_frame = NULL;
296
297         vso->slide_mode = FALSE;
298
299         MEM_freeN(vso);
300         vso = NULL;
301         op->customdata = NULL;
302
303         /* Clear the header */
304         ED_area_headerprint(CTX_wm_area(C), NULL);
305 }
306
307 static void vtx_slide_draw(const bContext *C, ARegion *UNUSED(ar), void *arg)
308 {
309         VertexSlideOp *vso = arg;
310
311         /* Have an edge to draw */
312         if (vso && vso->sel_edge) {
313                 /* Get 3d view */
314                 View3D *view3d = CTX_wm_view3d(C);
315                 const float outline_w = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.8f;
316                 const float pt_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f;
317
318                 int i = 0;
319
320                 if (view3d && view3d->zbuf)
321                         glDisable(GL_DEPTH_TEST);
322
323                 glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT | GL_POINT_BIT);
324
325                 glPushMatrix();
326                 glMultMatrixf(vso->obj->obmat);
327
328                 glEnable(GL_BLEND);
329                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
330                         
331
332                 if (vso->slide_mode && vso->disk_edges > 0) {
333                         /* Draw intermediate edge frame */
334                         UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 50, -50);
335
336                         for (i = 0; i < vso->disk_edges; i++) {
337                                 glBegin(GL_LINES);
338                                 glVertex3fv(vso->vtx_frame[i]);
339                                 glVertex3fv(vso->interp);
340                                 glEnd();
341                         }
342                 }
343
344                 /* Draw selected edge
345                  * Add color offset and reduce alpha */
346                 UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 40, -50);
347
348                 glLineWidth(outline_w);
349
350                 glBegin(GL_LINES);
351                 bglVertex3fv(vso->sel_edge->v1->co);
352                 bglVertex3fv(vso->sel_edge->v2->co);
353                 glEnd();
354
355                 if (vso->slide_mode) {
356                         /* Draw interpolated vertex */
357                         
358                         UI_ThemeColorShadeAlpha(TH_FACE_DOT, -80, -50);
359
360                         glPointSize(pt_size);
361
362                         bglBegin(GL_POINTS);
363                         bglVertex3fv(vso->interp);
364                         bglEnd();
365                 }
366
367                 glDisable(GL_BLEND);
368                 glPopMatrix();
369                 glPopAttrib();
370
371                 if (view3d && view3d->zbuf)
372                         glEnable(GL_DEPTH_TEST);
373         }
374 }
375
376 static BMEdge *vtx_slide_nrst_in_frame(VertexSlideOp *vso, const float mval[2])
377 {
378         BMEdge *cl_edge = NULL;
379         if (vso->disk_edges > 0) {
380                 int i = 0;
381                 BMEdge *edge = NULL;
382                 
383                 float v1_proj[3], v2_proj[3];
384                 float dist = 0;
385                 float min_dist = FLT_MAX;
386
387                 for (i = 0; i < vso->disk_edges; i++) {
388                         edge = vso->edge_frame[i];
389
390                         mul_v3_m4v3(v1_proj, vso->obj->obmat, edge->v1->co);
391                         ED_view3d_project_float_noclip(vso->active_region, v1_proj, v1_proj);
392
393                         mul_v3_m4v3(v2_proj, vso->obj->obmat, edge->v2->co);
394                         ED_view3d_project_float_noclip(vso->active_region, v2_proj, v2_proj);
395
396                         dist = dist_to_line_segment_v2(mval, v1_proj, v2_proj);
397                         if (dist < min_dist) {
398                                 min_dist = dist;
399                                 cl_edge = edge;
400                         }
401                 }
402         }
403         return cl_edge;
404 }
405
406 static void vtx_slide_find_edge(VertexSlideOp *vso, wmEvent *event)
407 {
408         /* Nearest edge */
409         BMEdge *nst_edge = NULL;
410
411         const float mval_float[] = { (float)event->mval[0], (float)event->mval[1]};
412
413         /* Set mouse coords */
414         copy_v2_v2_int(vso->view_context->mval, event->mval);
415
416         /* Find nearest edge */
417         nst_edge = vtx_slide_nrst_in_frame(vso, mval_float);
418
419         if (nst_edge) {
420                 /* Find a connected edge */
421                 if (BM_vert_in_edge(nst_edge, vso->start_vtx)) {
422
423                         /* Save mouse coords */
424                         copy_v2_v2_int(vso->m_co, event->mval);
425
426                         /* Set edge */
427                         vso->sel_edge = nst_edge;
428                 }
429         }
430 }
431
432 /* Updates the status of the operator - Invoked on mouse movement */
433 static void vtx_slide_update(VertexSlideOp *vso, wmEvent *event)
434 {
435         BMEdge *edge;
436
437         /* Find nearest edge */
438         edge = vso->sel_edge;
439
440         if (edge) {
441                 float edge_other_proj[3];
442                 float start_vtx_proj[3];
443                 float edge_len;
444                 BMVert *other;
445
446                 float interp[3];
447
448                 /* Calculate interpolation value for preview */
449                 float t_val;
450
451                 float mval_float[] = { (float)event->mval[0], (float)event->mval[1]};
452                 float closest_2d[2];
453
454                 other = BM_edge_other_vert(edge, vso->start_vtx);
455
456                 /* Project points onto screen and do interpolation in 2D */
457                 mul_v3_m4v3(start_vtx_proj, vso->obj->obmat, vso->start_vtx->co);
458                 ED_view3d_project_float_noclip(vso->active_region, start_vtx_proj, start_vtx_proj);
459
460                 mul_v3_m4v3(edge_other_proj, vso->obj->obmat, other->co);
461                 ED_view3d_project_float_noclip(vso->active_region, edge_other_proj, edge_other_proj);
462
463                 closest_to_line_v2(closest_2d, mval_float, start_vtx_proj, edge_other_proj);
464
465                 t_val = line_point_factor_v2(closest_2d, start_vtx_proj, edge_other_proj);
466
467                 /* Set snap threshold to be proportional to edge length */
468                 edge_len = len_v3v3(start_vtx_proj, edge_other_proj);
469                 
470                 if (edge_len <= 0.0f)
471                         edge_len = VTX_SLIDE_SNAP_THRSH;
472
473                 edge_len =  (len_v3v3(edge->v1->co, edge->v2->co) * VTX_SLIDE_SNAP_THRSH) / edge_len;
474
475                 vso->snap_threshold =  edge_len;
476
477                 /* Snap to mid */
478                 if (vso->snap_to_mid) {
479                         t_val = 0.5f;
480                 }
481
482                 /* Interpolate preview vertex 3D */
483                 interp_v3_v3v3(interp, vso->start_vtx->co, other->co, t_val);
484                 copy_v3_v3(vso->interp, interp);
485
486                 vso->distance = t_val;
487
488                 /* If snapping */
489                 if (vso->snap_to_end_vtx) {
490                         int start_at_v1 = edge->v1 == vso->start_vtx;
491                         float v1_d = len_v3v3(vso->interp, edge->v1->co);
492                         float v2_d = len_v3v3(vso->interp, edge->v2->co);
493
494                         if (v1_d > v2_d && v2_d < vso->snap_threshold) {
495                                 copy_v3_v3(vso->interp, edge->v2->co);
496
497                                 if (start_at_v1)
498                                         vso->distance = 1.0f;
499                                 else
500                                         vso->distance = 0.0f;
501                         }
502                         if (v2_d > v1_d && v1_d < vso->snap_threshold) {
503                                 copy_v3_v3(vso->interp, edge->v1->co);
504                                 if (start_at_v1)
505                                         vso->distance = 0.0f;
506                                 else
507                                         vso->distance = 1.0f;
508                         }
509                 }
510         }
511 }
512
513 /* Sets the outline frame */
514 static int vtx_slide_set_frame(VertexSlideOp *vso)
515 {
516         BMEdge *edge;
517         float (*vtx_frame)[3] = NULL;
518         BMEdge **edge_frame = NULL;
519         BMVert *curr_vert = NULL;
520         BLI_array_declare(vtx_frame);
521         BLI_array_declare(edge_frame);
522         BMIter iter;
523         BMVert *sel_vtx = vso->start_vtx;
524         int idx = 0;
525
526         vso->disk_edges = 0;
527
528         if (vso->edge_frame) {
529                 MEM_freeN(vso->edge_frame);
530                 vso->edge_frame = NULL;
531         }
532
533         if (vso->vtx_frame) {
534                 MEM_freeN(vso->vtx_frame);
535                 vso->vtx_frame = NULL;
536         }
537
538         /* Iterate over edges of vertex and copy them */
539         BM_ITER_ELEM_INDEX (edge, &iter, sel_vtx, BM_EDGES_OF_VERT, idx) {
540                 curr_vert = BM_edge_other_vert(edge, sel_vtx);
541                 if (curr_vert) {
542                         BLI_array_grow_one(vtx_frame);
543
544                         copy_v3_v3(vtx_frame[idx], curr_vert->co);
545
546                         BLI_array_append(edge_frame, edge);
547                         vso->disk_edges++;
548                 }
549         }
550
551         vso->edge_frame = edge_frame;
552         vso->vtx_frame = vtx_frame;
553
554         /* Set the interp at starting vtx */
555         copy_v3_v3(vso->interp, sel_vtx->co);
556
557         return vso->disk_edges > 0;
558 }
559
560 static int edbm_vertex_slide_modal(bContext *C, wmOperator *op, wmEvent *event)
561 {
562         VertexSlideOp *vso = op->customdata;
563         char buff[128];
564
565         if (!vso)
566                 return OPERATOR_CANCELLED;
567
568         /* Notify the viewport */
569         view3d_operator_needs_opengl(C);
570
571         switch (event->type) {
572                 case LEFTSHIFTKEY:
573                 {
574                         switch (event->val) {
575                                 case KM_PRESS:
576                                         vso->snap_to_mid = TRUE;
577                                         break;
578                                 case KM_RELEASE:
579                                         vso->snap_to_mid = FALSE;
580                                         break;
581                         }
582
583                         break;
584                 }
585                 case LEFTCTRLKEY:
586                 {
587                         switch (event->val) {
588                                 case KM_PRESS:
589                                         vso->snap_n_merge = TRUE;
590                                         vso->snap_to_end_vtx = TRUE;
591                                         break;
592                                 case KM_RELEASE:
593                                         vso->snap_n_merge = FALSE;
594                                         vso->snap_to_end_vtx = FALSE;
595                                         break;
596                         }
597
598                         break;
599                 }
600                 case LEFTALTKEY:
601                 {
602                         switch (event->val) {
603                                 case KM_PRESS:
604                                         vso->snap_to_end_vtx = TRUE;
605                                         break;
606                                 case KM_RELEASE:
607                                         vso->snap_to_end_vtx = FALSE;
608                                         break;
609                         }
610
611                         break;
612                 }
613                 case RIGHTMOUSE:
614                 case ESCKEY:
615                 {
616                         /* Enforce redraw */
617                         ED_region_tag_redraw(vso->active_region);
618
619                         /* Clean-up */
620                         vtx_slide_exit(C, op);
621
622                         return OPERATOR_CANCELLED;
623                 }
624                 case LEFTMOUSE:
625                 {
626                         if (event->val == KM_PRESS) {
627                                 /* Update mouse coords */
628                                 copy_v2_v2_int(vso->m_co, event->mval);
629
630                                 if (vso->slide_mode) {
631                                         vtx_slide_confirm(C, op);
632                                         /* Clean-up */
633                                         vtx_slide_exit(C, op);
634                                         return OPERATOR_FINISHED;
635                                 }
636                                 else if (vso->sel_edge) {
637                                         vso->slide_mode = TRUE;
638                                 }
639                         }
640
641                         ED_region_tag_redraw(vso->active_region);
642                         break;
643
644                 }
645                 case MOUSEMOVE:
646                 {
647                         sprintf(buff, "Vertex Slide: %f", vso->distance);
648                         if (!vso->slide_mode) {
649                                 vtx_slide_find_edge(vso, event);
650                         }
651                         else {
652                                 vtx_slide_update(vso, event);
653                         }
654                         ED_area_headerprint(CTX_wm_area(C), buff);
655                         ED_region_tag_redraw(vso->active_region);
656                         break;
657                 }
658         }
659
660         return OPERATOR_RUNNING_MODAL;
661 }
662
663 static int edbm_vertex_slide_cancel(bContext *C, wmOperator *op)
664 {
665         /* Exit the modal */
666         vtx_slide_exit(C, op);
667
668         return OPERATOR_CANCELLED;
669 }
670
671 static int edbm_vertex_slide_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
672 {
673         /* Initialize the operator */
674         if (vtx_slide_init(C, op))
675                 return OPERATOR_RUNNING_MODAL;
676         else
677                 return OPERATOR_CANCELLED;
678 }
679
680 /* Vertex Slide */
681 static int edbm_vertex_slide_exec_ex(bContext *C, wmOperator *op, const int do_update)
682 {
683         Object *obedit = CTX_data_edit_object(C);
684         BMEditMesh *em = BMEdit_FromObject(obedit);
685         BMesh *bm = em->bm;
686         BMVert *start_vert;
687         BMOperator bmop;
688         BMEditSelection *ese = (BMEditSelection *)em->bm->selected.last;
689
690         float distance_t = 0.0f;
691
692         /* Invoked modally? */
693         if (op->type->modal == edbm_vertex_slide_modal && op->customdata) {
694                 VertexSlideOp *vso = (VertexSlideOp *)op->customdata;
695
696                 if (bm->totedgesel > 1) {
697                         /* Reset selections */
698                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
699                         BM_edge_select_set(bm, vso->sel_edge, TRUE);
700                         BM_vert_select_set(bm, vso->start_vtx, TRUE);
701
702                         BM_select_history_store(em->bm, vso->sel_edge);
703                         BM_select_history_store(em->bm, vso->start_vtx);
704                         ese = (BMEditSelection *)em->bm->selected.last;
705                 }
706                 distance_t = vso->distance;
707                 RNA_float_set(op->ptr, "distance_t", distance_t);
708         }
709         else {
710                 /* Get Properties */
711                 distance_t = RNA_float_get(op->ptr, "distance_t");
712         }
713
714         /* Is there a starting vertex  ? */
715         if ((ese == NULL) || (ese->htype != BM_VERT && ese->htype != BM_EDGE)) {
716                 BKE_report(op->reports, RPT_ERROR_INVALID_INPUT, "Vertex Slide Error: Select a (single) vertex");
717                 return OPERATOR_CANCELLED;
718         }
719
720         start_vert = (BMVert *)ese->ele;
721
722         /* Prepare operator */
723         if (!EDBM_op_init(em, &bmop, op,
724                           "slide_vert vert=%e edge=%hev distance_t=%f",
725                           start_vert, BM_ELEM_SELECT, distance_t))
726         {
727                 return OPERATOR_CANCELLED;
728         }
729         /* Execute operator */
730         BMO_op_exec(bm, &bmop);
731
732         /* Deselect the input edges */
733         BMO_slot_buffer_hflag_disable(bm, &bmop, "edge", BM_ALL, BM_ELEM_SELECT, TRUE);
734
735         /* Select the output vert */
736         BMO_slot_buffer_hflag_enable(bm, &bmop, "vertout", BM_ALL, BM_ELEM_SELECT, TRUE);
737
738         /* Flush the select buffers */
739         EDBM_selectmode_flush(em);
740
741         if (!EDBM_op_finish(em, &bmop, op, TRUE)) {
742                 return OPERATOR_CANCELLED;
743         }
744
745         if (do_update) {
746                 /* Update Geometry */
747                 EDBM_update_generic(C, em, TRUE);
748         }
749
750         return OPERATOR_FINISHED;
751 }
752
753 #if 0
754 static int edbm_vertex_slide_exec(bContext *C, wmOperator *op)
755 {
756         return edbm_vertex_slide_exec_ex(C, op, TRUE);
757 }
758 #endif
759
760 void MESH_OT_vert_slide(wmOperatorType *ot)
761 {
762         PropertyRNA *prop;
763
764         /* identifiers */
765         ot->name = "Vertex Slide";
766         ot->idname = "MESH_OT_vert_slide";
767         ot->description = "Vertex slide";
768
769         /* api callback */
770         ot->invoke = edbm_vertex_slide_invoke;
771         ot->modal = edbm_vertex_slide_modal;
772         ot->cancel = edbm_vertex_slide_cancel;
773         ot->poll = ED_operator_editmesh_region_view3d;
774
775         /* ot->exec = edbm_vertex_slide_exec;
776          * ot->poll = ED_operator_editmesh; */
777
778         /* flags */
779         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
780
781         /* Properties for vertex slide */
782         prop = RNA_def_float(ot->srna, "distance_t", 0.0f, -FLT_MAX, FLT_MAX, "Distance", "Distance", -5.0f, 5.0f);
783         RNA_def_property_ui_range(prop, -5.0f, 5.0f, 0.1, 4);
784         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
785 }