replace most uses of ED_view3d_project_float_noclip() with ED_view3d_project_float_gl...
[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 min_dist = FLT_MAX;
385
386                 for (i = 0; i < vso->disk_edges; i++) {
387                         edge = vso->edge_frame[i];
388
389                         mul_v3_m4v3(v1_proj, vso->obj->obmat, edge->v1->co);
390                         mul_v3_m4v3(v2_proj, vso->obj->obmat, edge->v2->co);
391
392                         /* we could use ED_view3d_project_float_object here, but for now dont since we dont have the context */
393                         if ((ED_view3d_project_float_global(vso->active_region, v1_proj, v1_proj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_SUCCESS) &&
394                             (ED_view3d_project_float_global(vso->active_region, v2_proj, v2_proj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_SUCCESS))
395                         {
396                                 const float 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         }
404         return cl_edge;
405 }
406
407 static void vtx_slide_find_edge(VertexSlideOp *vso, wmEvent *event)
408 {
409         /* Nearest edge */
410         BMEdge *nst_edge = NULL;
411
412         const float mval_float[] = { (float)event->mval[0], (float)event->mval[1]};
413
414         /* Set mouse coords */
415         copy_v2_v2_int(vso->view_context->mval, event->mval);
416
417         /* Find nearest edge */
418         nst_edge = vtx_slide_nrst_in_frame(vso, mval_float);
419
420         if (nst_edge) {
421                 /* Find a connected edge */
422                 if (BM_vert_in_edge(nst_edge, vso->start_vtx)) {
423
424                         /* Save mouse coords */
425                         copy_v2_v2_int(vso->m_co, event->mval);
426
427                         /* Set edge */
428                         vso->sel_edge = nst_edge;
429                 }
430         }
431 }
432
433 /* Updates the status of the operator - Invoked on mouse movement */
434 static void vtx_slide_update(VertexSlideOp *vso, wmEvent *event)
435 {
436         BMEdge *edge;
437
438         /* Find nearest edge */
439         edge = vso->sel_edge;
440
441         if (edge) {
442                 float edge_other_proj[3];
443                 float start_vtx_proj[3];
444                 float edge_len;
445                 BMVert *other;
446
447                 float interp[3];
448
449                 /* Calculate interpolation value for preview */
450                 float t_val;
451
452                 float mval_float[2] = { (float)event->mval[0], (float)event->mval[1]};
453                 float closest_2d[2];
454
455                 other = BM_edge_other_vert(edge, vso->start_vtx);
456
457                 /* Project points onto screen and do interpolation in 2D */
458                 mul_v3_m4v3(start_vtx_proj, vso->obj->obmat, vso->start_vtx->co);
459                 mul_v3_m4v3(edge_other_proj, vso->obj->obmat, other->co);
460
461                 if ((ED_view3d_project_float_global(vso->active_region, edge_other_proj, edge_other_proj, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_SUCCESS) ||
462                     (ED_view3d_project_float_global(vso->active_region, start_vtx_proj, start_vtx_proj, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_SUCCESS))
463                 {
464                         /* not much we can do here */
465                         return;
466                 }
467
468                 closest_to_line_v2(closest_2d, mval_float, start_vtx_proj, edge_other_proj);
469
470                 t_val = line_point_factor_v2(closest_2d, start_vtx_proj, edge_other_proj);
471
472                 /* Set snap threshold to be proportional to edge length */
473                 edge_len = len_v3v3(start_vtx_proj, edge_other_proj);
474                 
475                 if (edge_len <= 0.0f)
476                         edge_len = VTX_SLIDE_SNAP_THRSH;
477
478                 edge_len =  (BM_edge_calc_length(edge) * VTX_SLIDE_SNAP_THRSH) / edge_len;
479
480                 vso->snap_threshold =  edge_len;
481
482                 /* Snap to mid */
483                 if (vso->snap_to_mid) {
484                         t_val = 0.5f;
485                 }
486
487                 /* Interpolate preview vertex 3D */
488                 interp_v3_v3v3(interp, vso->start_vtx->co, other->co, t_val);
489                 copy_v3_v3(vso->interp, interp);
490
491                 vso->distance = t_val;
492
493                 /* If snapping */
494                 if (vso->snap_to_end_vtx) {
495                         int start_at_v1 = edge->v1 == vso->start_vtx;
496                         float v1_d = len_v3v3(vso->interp, edge->v1->co);
497                         float v2_d = len_v3v3(vso->interp, edge->v2->co);
498
499                         if (v1_d > v2_d && v2_d < vso->snap_threshold) {
500                                 copy_v3_v3(vso->interp, edge->v2->co);
501
502                                 if (start_at_v1)
503                                         vso->distance = 1.0f;
504                                 else
505                                         vso->distance = 0.0f;
506                         }
507                         if (v2_d > v1_d && v1_d < vso->snap_threshold) {
508                                 copy_v3_v3(vso->interp, edge->v1->co);
509                                 if (start_at_v1)
510                                         vso->distance = 0.0f;
511                                 else
512                                         vso->distance = 1.0f;
513                         }
514                 }
515         }
516 }
517
518 /* Sets the outline frame */
519 static int vtx_slide_set_frame(VertexSlideOp *vso)
520 {
521         BMEdge *edge;
522         float (*vtx_frame)[3] = NULL;
523         BMEdge **edge_frame = NULL;
524         BMVert *curr_vert = NULL;
525         BLI_array_declare(vtx_frame);
526         BLI_array_declare(edge_frame);
527         BMIter iter;
528         BMVert *sel_vtx = vso->start_vtx;
529         int idx = 0;
530
531         vso->disk_edges = 0;
532
533         if (vso->edge_frame) {
534                 MEM_freeN(vso->edge_frame);
535                 vso->edge_frame = NULL;
536         }
537
538         if (vso->vtx_frame) {
539                 MEM_freeN(vso->vtx_frame);
540                 vso->vtx_frame = NULL;
541         }
542
543         /* Iterate over edges of vertex and copy them */
544         BM_ITER_ELEM_INDEX (edge, &iter, sel_vtx, BM_EDGES_OF_VERT, idx) {
545                 curr_vert = BM_edge_other_vert(edge, sel_vtx);
546                 if (curr_vert) {
547                         BLI_array_grow_one(vtx_frame);
548
549                         copy_v3_v3(vtx_frame[idx], curr_vert->co);
550
551                         BLI_array_append(edge_frame, edge);
552                         vso->disk_edges++;
553                 }
554         }
555
556         vso->edge_frame = edge_frame;
557         vso->vtx_frame = vtx_frame;
558
559         /* Set the interp at starting vtx */
560         copy_v3_v3(vso->interp, sel_vtx->co);
561
562         return vso->disk_edges > 0;
563 }
564
565 static int edbm_vertex_slide_modal(bContext *C, wmOperator *op, wmEvent *event)
566 {
567         VertexSlideOp *vso = op->customdata;
568         char buff[128];
569
570         if (!vso)
571                 return OPERATOR_CANCELLED;
572
573         /* Notify the viewport */
574         view3d_operator_needs_opengl(C);
575
576         switch (event->type) {
577                 case LEFTSHIFTKEY:
578                 {
579                         switch (event->val) {
580                                 case KM_PRESS:
581                                         vso->snap_to_mid = TRUE;
582                                         break;
583                                 case KM_RELEASE:
584                                         vso->snap_to_mid = FALSE;
585                                         break;
586                         }
587
588                         break;
589                 }
590                 case LEFTCTRLKEY:
591                 {
592                         switch (event->val) {
593                                 case KM_PRESS:
594                                         vso->snap_n_merge = TRUE;
595                                         vso->snap_to_end_vtx = TRUE;
596                                         break;
597                                 case KM_RELEASE:
598                                         vso->snap_n_merge = FALSE;
599                                         vso->snap_to_end_vtx = FALSE;
600                                         break;
601                         }
602
603                         break;
604                 }
605                 case LEFTALTKEY:
606                 {
607                         switch (event->val) {
608                                 case KM_PRESS:
609                                         vso->snap_to_end_vtx = TRUE;
610                                         break;
611                                 case KM_RELEASE:
612                                         vso->snap_to_end_vtx = FALSE;
613                                         break;
614                         }
615
616                         break;
617                 }
618                 case RIGHTMOUSE:
619                 case ESCKEY:
620                 {
621                         /* Enforce redraw */
622                         ED_region_tag_redraw(vso->active_region);
623
624                         /* Clean-up */
625                         vtx_slide_exit(C, op);
626
627                         return OPERATOR_CANCELLED;
628                 }
629                 case LEFTMOUSE:
630                 {
631                         if (event->val == KM_PRESS) {
632                                 /* Update mouse coords */
633                                 copy_v2_v2_int(vso->m_co, event->mval);
634
635                                 if (vso->slide_mode) {
636                                         vtx_slide_confirm(C, op);
637                                         /* Clean-up */
638                                         vtx_slide_exit(C, op);
639                                         return OPERATOR_FINISHED;
640                                 }
641                                 else if (vso->sel_edge) {
642                                         vso->slide_mode = TRUE;
643                                 }
644                         }
645
646                         ED_region_tag_redraw(vso->active_region);
647                         break;
648
649                 }
650                 case MOUSEMOVE:
651                 {
652                         sprintf(buff, "Vertex Slide: %f", vso->distance);
653                         if (!vso->slide_mode) {
654                                 vtx_slide_find_edge(vso, event);
655                         }
656                         else {
657                                 vtx_slide_update(vso, event);
658                         }
659                         ED_area_headerprint(CTX_wm_area(C), buff);
660                         ED_region_tag_redraw(vso->active_region);
661                         break;
662                 }
663         }
664
665         return OPERATOR_RUNNING_MODAL;
666 }
667
668 static int edbm_vertex_slide_cancel(bContext *C, wmOperator *op)
669 {
670         /* Exit the modal */
671         vtx_slide_exit(C, op);
672
673         return OPERATOR_CANCELLED;
674 }
675
676 static int edbm_vertex_slide_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
677 {
678         /* Initialize the operator */
679         if (vtx_slide_init(C, op))
680                 return OPERATOR_RUNNING_MODAL;
681         else
682                 return OPERATOR_CANCELLED;
683 }
684
685 /* Vertex Slide */
686 static int edbm_vertex_slide_exec_ex(bContext *C, wmOperator *op, const int do_update)
687 {
688         Object *obedit = CTX_data_edit_object(C);
689         BMEditMesh *em = BMEdit_FromObject(obedit);
690         BMesh *bm = em->bm;
691         BMVert *start_vert;
692         BMOperator bmop;
693         BMEditSelection *ese = (BMEditSelection *)em->bm->selected.last;
694
695         float distance_t = 0.0f;
696
697         /* Invoked modally? */
698         if (op->type->modal == edbm_vertex_slide_modal && op->customdata) {
699                 VertexSlideOp *vso = (VertexSlideOp *)op->customdata;
700
701                 if (bm->totedgesel > 1) {
702                         /* Reset selections */
703                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
704                         BM_edge_select_set(bm, vso->sel_edge, TRUE);
705                         BM_vert_select_set(bm, vso->start_vtx, TRUE);
706
707                         BM_select_history_store(em->bm, vso->sel_edge);
708                         BM_select_history_store(em->bm, vso->start_vtx);
709                         ese = (BMEditSelection *)em->bm->selected.last;
710                 }
711                 distance_t = vso->distance;
712                 RNA_float_set(op->ptr, "distance_t", distance_t);
713         }
714         else {
715                 /* Get Properties */
716                 distance_t = RNA_float_get(op->ptr, "distance_t");
717         }
718
719         /* Is there a starting vertex  ? */
720         if ((ese == NULL) || (ese->htype != BM_VERT && ese->htype != BM_EDGE)) {
721                 BKE_report(op->reports, RPT_ERROR_INVALID_INPUT, "Vertex Slide Error: Select a (single) vertex");
722                 return OPERATOR_CANCELLED;
723         }
724
725         start_vert = (BMVert *)ese->ele;
726
727         /* Prepare operator */
728         if (!EDBM_op_init(em, &bmop, op,
729                           "slide_vert vert=%e edge=%hev distance_t=%f",
730                           start_vert, BM_ELEM_SELECT, distance_t))
731         {
732                 return OPERATOR_CANCELLED;
733         }
734         /* Execute operator */
735         BMO_op_exec(bm, &bmop);
736
737         /* Deselect the input edges */
738         BMO_slot_buffer_hflag_disable(bm, &bmop, "edge", BM_ALL, BM_ELEM_SELECT, TRUE);
739
740         /* Select the output vert */
741         BMO_slot_buffer_hflag_enable(bm, &bmop, "vertout", BM_ALL, BM_ELEM_SELECT, TRUE);
742
743         /* Flush the select buffers */
744         EDBM_selectmode_flush(em);
745
746         if (!EDBM_op_finish(em, &bmop, op, TRUE)) {
747                 return OPERATOR_CANCELLED;
748         }
749
750         if (do_update) {
751                 /* Update Geometry */
752                 EDBM_update_generic(C, em, TRUE);
753         }
754
755         return OPERATOR_FINISHED;
756 }
757
758 #if 0
759 static int edbm_vertex_slide_exec(bContext *C, wmOperator *op)
760 {
761         return edbm_vertex_slide_exec_ex(C, op, TRUE);
762 }
763 #endif
764
765 void MESH_OT_vert_slide(wmOperatorType *ot)
766 {
767         PropertyRNA *prop;
768
769         /* identifiers */
770         ot->name = "Vertex Slide";
771         ot->idname = "MESH_OT_vert_slide";
772         ot->description = "Vertex slide";
773
774         /* api callback */
775         ot->invoke = edbm_vertex_slide_invoke;
776         ot->modal = edbm_vertex_slide_modal;
777         ot->cancel = edbm_vertex_slide_cancel;
778         ot->poll = ED_operator_editmesh_region_view3d;
779
780         /* ot->exec = edbm_vertex_slide_exec;
781          * ot->poll = ED_operator_editmesh; */
782
783         /* flags */
784         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
785
786         /* Properties for vertex slide */
787         prop = RNA_def_float(ot->srna, "distance_t", 0.0f, -FLT_MAX, FLT_MAX, "Distance", "Distance", -5.0f, 5.0f);
788         RNA_def_property_ui_range(prop, -5.0f, 5.0f, 0.1, 4);
789         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
790 }