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