style cleanup: pep8 some C/style
[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 0.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_vert_slide_exec(bContext *C, wmOperator *op);
103 static void vtx_slide_exit(const bContext *C, wmOperator *op);
104 static void 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         /* Add handler for the vertex sliding */
162         WM_event_add_modal_handler(C, op);
163
164         /* Notify the viewport */
165         view3d_operator_needs_opengl(C);
166
167         /* Set the drawing region */
168         vso->active_region = CTX_wm_region(C);
169
170         /* Set the draw callback */
171         vso->draw_handle = ED_region_draw_cb_activate(vso->active_region->type, vtx_slide_draw, vso, REGION_DRAW_POST_VIEW);
172
173         ED_area_headerprint(CTX_wm_area(C), header_str);
174         
175         em_setup_viewcontext(C, vso->view_context);
176
177         /* Set the object */
178         vso->obj = obedit;
179
180         /* Init frame */
181         vtx_slide_set_frame(vso);
182
183         /* Tag for redraw */
184         ED_region_tag_redraw(vso->active_region);
185
186         return TRUE;
187 }
188
189 static void vtx_slide_confirm(bContext *C, wmOperator *op)
190 {
191         VertexSlideOp *vso = op->customdata;
192         BMEditMesh *em = BMEdit_FromObject(vso->obj);
193         BMesh* bm = em->bm;
194
195         /* Select new edge */
196         BM_edge_select_set(bm, vso->sel_edge, TRUE);
197
198         /* Invoke operator */
199         edbm_vert_slide_exec(C, op);
200
201         if (vso->snap_n_merge) {
202                 float other_d;
203                 BMVert* other = BM_edge_other_vert(vso->sel_edge, vso->start_vtx);
204                 other_d = len_v3v3(vso->interp, other->co);
205
206                 /* Only snap if within threshold */
207                 if (other_d < vso->snap_threshold) {
208                         BM_vert_select_set(bm, other, TRUE);
209                         BM_vert_select_set(bm, vso->start_vtx, TRUE);
210                         EDBM_op_callf(em, op, "pointmerge verts=%hv mergeco=%v", BM_ELEM_SELECT, other->co);
211                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
212                 }
213                 else {
214                         /* Store in historty if not merging */
215                         EDBM_editselection_store(em, &vso->start_vtx->head);
216                 }
217         }
218         else {
219                 /* Store edit selection of the active vertex, allows other
220                  *  ops to run without reselecting */
221                 EDBM_editselection_store(em, &vso->start_vtx->head);
222         }
223
224         EDBM_selectmode_flush(em);
225         
226         /* NC_GEOM | ND_DATA & Retess */
227         EDBM_update_generic(C, em, TRUE);
228         
229         ED_region_tag_redraw(vso->active_region);
230 }
231
232 static void vtx_slide_exit(const bContext *C, wmOperator *op)
233 {
234         /* Fetch custom data */
235         VertexSlideOp *vso = op->customdata;
236
237         /* Clean-up the custom data */
238         ED_region_draw_cb_exit(vso->active_region->type, vso->draw_handle);
239
240         /* Free Custom Data
241          *
242          */
243         MEM_freeN(vso->view_context);
244
245         vso->view_context = NULL;
246
247         if (vso->edge_frame) {
248                 MEM_freeN(vso->edge_frame);
249         }
250
251         if (vso->vtx_frame) {
252                 MEM_freeN(vso->vtx_frame);
253         }
254
255         vso->edge_frame = NULL;
256
257         vso->vtx_frame = NULL;
258
259         vso->slide_mode = FALSE;
260
261         MEM_freeN(vso);
262         vso = NULL;
263
264         /* Clear the header */
265         ED_area_headerprint(CTX_wm_area(C), NULL);
266 }
267
268 static void vtx_slide_draw(const bContext *C, ARegion *UNUSED(ar), void *arg)
269 {
270         VertexSlideOp *vso = arg;
271
272         /* Have an edge to draw */
273         if (vso && vso->sel_edge) {
274                 /* Get 3d view */
275                 View3D *view3d = CTX_wm_view3d(C);
276                 const int outline_w = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 1;
277                 int i = 0;
278
279                 if (view3d && view3d->zbuf)
280                         glDisable(GL_DEPTH_TEST);
281
282                 glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT | GL_POINT_BIT);
283
284                 glPushMatrix();
285                 glMultMatrixf(vso->obj->obmat);
286
287                 glEnable(GL_BLEND);
288                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
289
290                 /* Draw selected edge
291                  * Add color offset and reduce alpha */
292                 UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 50, -50);
293
294                 glLineWidth(outline_w);
295
296                 glBegin(GL_LINES);
297                 bglVertex3fv(vso->sel_edge->v1->co);
298                 bglVertex3fv(vso->sel_edge->v2->co);
299                 glEnd();
300
301                 if (vso->slide_mode && vso->disk_edges > 0) {
302                         /* Draw intermediate edge frame */
303                         UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 50, -50);
304
305                         for (i = 0; i < vso->disk_edges; i++) {
306                                 glBegin(GL_LINES);
307                                 glVertex3fv(vso->vtx_frame[i]);
308                                 glVertex3fv(vso->interp);
309                                 glEnd();
310                         }
311                 }
312
313                 if (vso->slide_mode) {
314                         /* Draw interpolated vertex */
315                         int pt_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 2;
316                         UI_ThemeColorShadeAlpha(TH_FACE_DOT, -90, -50);
317
318                         glPointSize(pt_size);
319
320                         bglBegin(GL_POINTS);
321                         bglVertex3fv(vso->interp);
322                         bglEnd();
323                 }
324
325                 glDisable(GL_BLEND);
326                 glPopMatrix();
327                 glPopAttrib();
328
329                 if (view3d && view3d->zbuf)
330                         glEnable(GL_DEPTH_TEST);
331         }
332 }
333
334 static BMEdge* vtx_slide_nrst_in_frame(VertexSlideOp *vso, const float mval[2])
335 {
336         BMEdge* cl_edge = NULL;
337         if (vso->disk_edges > 0) {
338                 int i = 0;
339                 BMEdge* edge = NULL;
340                 
341                 float v1_proj[3], v2_proj[3];
342                 float dist = 0;
343                 float min_dist = FLT_MAX;
344
345                 for (i = 0; i < vso->disk_edges; i++) {
346                         edge = vso->edge_frame[i];
347
348                         mul_v3_m4v3(v1_proj, vso->obj->obmat, edge->v1->co);
349                         project_float_noclip(vso->active_region, v1_proj, v1_proj);
350
351                         mul_v3_m4v3(v2_proj, vso->obj->obmat, edge->v2->co);
352                         project_float_noclip(vso->active_region, v2_proj, v2_proj);
353
354                         dist = dist_to_line_segment_v2(mval, v1_proj, v2_proj);
355                         if (dist < min_dist) {
356                                 min_dist = dist;
357                                 cl_edge = edge;
358                         }
359                 }
360         }
361         return cl_edge;
362 }
363
364 static void vtx_slide_find_edge(VertexSlideOp *vso, wmEvent *event)
365 {
366         /* Nearest edge */
367         BMEdge *nst_edge = NULL;
368
369         const float mval_float[] = { (float)event->mval[0], (float)event->mval[1]};
370
371         /* Set mouse coords */
372         copy_v2_v2_int(vso->view_context->mval, event->mval);
373
374         /* Find nearest edge */
375         nst_edge = vtx_slide_nrst_in_frame(vso, mval_float);
376
377         if (nst_edge) {
378                 /* Find a connected edge */
379                 if (BM_vert_in_edge(nst_edge, vso->start_vtx)) {
380                         float edge_len;
381
382                         /* Save mouse coords */
383                         copy_v2_v2_int(vso->m_co, event->mval);
384
385                         /* Set edge */
386                         vso->sel_edge = nst_edge;
387                         
388                         /* Set snap threshold to be proportional to edge length */
389                         edge_len = len_v3v3(nst_edge->v1->co, nst_edge->v2->co);
390                         vso->snap_threshold = edge_len * VTX_SLIDE_SNAP_THRSH;
391                 }
392         }
393 }
394
395 /* Updates the status of the operator - Invoked on mouse movement */
396 static void vtx_slide_update(VertexSlideOp *vso, wmEvent *event)
397 {
398         BMEdge *edge;
399
400         /* Find nearest edge */
401         edge = vso->sel_edge;
402
403         if (edge) {
404                 float edge_other_proj[3];
405                 float start_vtx_proj[3];
406                 BMVert *other;
407
408                 float interp[3];
409
410                 /* Calculate interpolation value for preview */
411                 float t_val;
412
413                 float mval_float[] = { (float)event->mval[0], (float)event->mval[1]};
414                 float closest_2d[2];
415
416                 other = BM_edge_other_vert(edge, vso->start_vtx);
417
418                 /* Project points onto screen and do interpolation in 2D */
419                 mul_v3_m4v3(start_vtx_proj, vso->obj->obmat, vso->start_vtx->co);
420                 project_float_noclip(vso->active_region, start_vtx_proj, start_vtx_proj);
421
422                 mul_v3_m4v3(edge_other_proj, vso->obj->obmat, other->co);
423                 project_float_noclip(vso->active_region, edge_other_proj, edge_other_proj);
424
425                 closest_to_line_v2(closest_2d, mval_float, start_vtx_proj, edge_other_proj);
426
427                 t_val = line_point_factor_v2(closest_2d, start_vtx_proj, edge_other_proj);
428
429                 /* Snap to mid */
430                 if (vso->snap_to_mid) {
431                         t_val = 0.5f;
432                 }
433
434                 /* Interpolate preview vertex 3D */
435                 interp_v3_v3v3(interp, vso->start_vtx->co, other->co, t_val);
436                 copy_v3_v3(vso->interp, interp);
437
438                 vso->distance = t_val;
439
440                 /* If snapping */
441                 if (vso->snap_to_end_vtx) {
442                         int start_at_v1 = edge->v1 == vso->start_vtx;
443                         float v1_d = len_v3v3(vso->interp, edge->v1->co);
444                         float v2_d = len_v3v3(vso->interp, edge->v2->co);
445
446                         if (v1_d > v2_d && v2_d < vso->snap_threshold) {
447                                 copy_v3_v3(vso->interp, edge->v2->co);
448
449                                 if (start_at_v1)
450                                         vso->distance = 1.0f;
451                                 else
452                                         vso->distance = 0.0f;
453                         }
454                         if (v2_d > v1_d && v1_d < vso->snap_threshold) {
455                                 copy_v3_v3(vso->interp, edge->v1->co);
456                                 if (start_at_v1)
457                                         vso->distance = 0.0f;
458                                 else
459                                         vso->distance = 1.0f;
460                         }
461                 }
462         }
463 }
464
465 /* Sets the outline frame */
466 static void vtx_slide_set_frame(VertexSlideOp *vso)
467 {
468         BMEdge *edge;
469         float (*vtx_frame)[3] = NULL;
470         BMEdge** edge_frame = NULL;
471         BLI_array_declare(vtx_frame);
472         BLI_array_declare(edge_frame);
473         BMIter iter;
474         BMEditMesh *em = BMEdit_FromObject(vso->obj);
475         BMesh *bm = em->bm;
476         BMVert *sel_vtx = vso->start_vtx;
477         int idx = 0;
478
479         vso->disk_edges = 0;
480
481         if (vso->edge_frame) {
482                 MEM_freeN(vso->edge_frame);
483                 vso->edge_frame = NULL;
484         }
485
486         if (vso->vtx_frame) {
487                 MEM_freeN(vso->vtx_frame);
488                 vso->vtx_frame = NULL;
489         }
490
491         /* Iterate over edges of vertex and copy them */
492         BM_ITER_INDEX(edge, &iter, bm, BM_EDGES_OF_VERT, sel_vtx, idx)
493         {
494                 BLI_array_growone(vtx_frame);
495
496                 copy_v3_v3(vtx_frame[idx], BM_edge_other_vert(edge, sel_vtx)->co);
497
498                 BLI_array_append(edge_frame, edge);
499                 vso->disk_edges++;
500         }
501
502         vso->edge_frame = edge_frame;
503         vso->vtx_frame = vtx_frame;
504
505         /* Set the interp at starting vtx */
506         copy_v3_v3(vso->interp, sel_vtx->co);
507 }
508
509 static int edbm_vert_slide_modal(bContext *C, wmOperator *op, wmEvent *event)
510 {
511         VertexSlideOp *vso = op->customdata;
512
513         /* Notify the viewport */
514         view3d_operator_needs_opengl(C);
515
516         switch (event->type) {
517                 case LEFTSHIFTKEY:
518                 {
519                         switch (event->val) {
520                                 case KM_PRESS:
521                                         vso->snap_to_mid = TRUE;
522                                         break;
523                                 case KM_RELEASE:
524                                         vso->snap_to_mid = FALSE;
525                                         break;
526                         }
527
528                         break;
529                 }
530                 case LEFTCTRLKEY:
531                 {
532                         switch (event->val) {
533                                 case KM_PRESS:
534                                         vso->snap_n_merge = TRUE;
535                                         vso->snap_to_end_vtx = TRUE;
536                                         break;
537                                 case KM_RELEASE:
538                                         vso->snap_n_merge = FALSE;
539                                         vso->snap_to_end_vtx = FALSE;
540                                         break;
541                         }
542
543                         break;
544                 }
545                 case LEFTALTKEY:
546                 {
547                         switch (event->val) {
548                                 case KM_PRESS:
549                                         vso->snap_to_end_vtx = TRUE;
550                                         break;
551                                 case KM_RELEASE:
552                                         vso->snap_to_end_vtx = FALSE;
553                                         break;
554                         }
555
556                         break;
557                 }
558                 case RIGHTMOUSE:
559                 {
560                         /* Enforce redraw */
561                         ED_region_tag_redraw(vso->active_region);
562
563                         /* Clean-up */
564                         vtx_slide_exit(C, op);
565
566                         return OPERATOR_CANCELLED;
567                 }
568                 case LEFTMOUSE:
569                 {
570                         if (event->val == KM_PRESS) {
571                                 /* Update mouse coords */
572                                 copy_v2_v2_int(vso->m_co, event->mval);
573
574                                 if (vso->slide_mode) {
575                                         vtx_slide_confirm(C, op);
576                                         /* Clean-up */
577                                         vtx_slide_exit(C, op);
578                                         return OPERATOR_FINISHED;
579                                 }
580                                 else if (vso->sel_edge) {
581                                         vso->slide_mode = TRUE;
582                                 }
583                         }
584
585                         ED_region_tag_redraw(vso->active_region);
586                         break;
587
588                 }
589                 case MOUSEMOVE:
590                 {
591                         if (!vso->slide_mode) {
592                                 vtx_slide_find_edge(vso, event);
593                         }
594                         else {
595                                 vtx_slide_update(vso, event);
596                         }
597
598                         ED_region_tag_redraw(vso->active_region);
599                         break;
600                 }
601         }
602
603         return OPERATOR_RUNNING_MODAL;
604 }
605
606 static int edbm_vert_slide_cancel(bContext *C, wmOperator *op)
607 {
608         /* Exit the modal */
609         vtx_slide_exit(C, op);
610
611         return OPERATOR_CANCELLED;
612 }
613
614 static int edbm_vert_slide_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
615 {
616         /* Initialize the operator */
617         if (vtx_slide_init(C, op))
618                 return OPERATOR_RUNNING_MODAL;
619         else
620                 return OPERATOR_CANCELLED;
621 }
622
623 /* Vertex Slide */
624 static int edbm_vert_slide_exec(bContext *C, wmOperator *op)
625 {
626         Object *obedit = CTX_data_edit_object(C);
627         BMEditMesh *em = BMEdit_FromObject(obedit);
628         BMesh *bm = em->bm;
629         BMVert *start_vert;
630         BMOperator bmop;
631         BMEditSelection *ese = em->bm->selected.last;
632
633         float distance_t = 0.0f;
634
635         /* Invoked modally? */
636         if (op->type->modal == edbm_vert_slide_modal && op->customdata) {
637                 VertexSlideOp *vso = op->customdata;
638
639                 if (bm->totedgesel > 1) {
640                         /* Reset selections */
641                         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
642                         BM_edge_select_set(bm, vso->sel_edge, TRUE);
643                         BM_vert_select_set(bm, vso->start_vtx, TRUE);
644
645                         EDBM_editselection_store(em, &vso->sel_edge->head);
646                         EDBM_editselection_store(em, &vso->start_vtx->head);                    
647                         ese = em->bm->selected.last;
648                 }
649                 distance_t = vso->distance;
650                 RNA_float_set(op->ptr, "distance_t", distance_t);
651         }
652         else {
653                 /* Get Properties */
654                 distance_t = RNA_float_get(op->ptr, "distance_t");
655         }
656
657         /* Is there a starting vertex  ? */
658         if ((ese == NULL) || (ese->htype != BM_VERT && ese->htype != BM_EDGE)) {
659                 BKE_report(op->reports, RPT_ERROR_INVALID_INPUT, "Vertex Slide Error: Select a (single) vertex");
660                 return OPERATOR_CANCELLED;
661         }
662
663         start_vert = (BMVert *)ese->ele;
664
665         /* Prepare operator */
666         if (!EDBM_op_init(em, &bmop, op, "vertslide vert=%e edge=%hev distance_t=%f", start_vert, BM_ELEM_SELECT, distance_t))  {
667                 return OPERATOR_CANCELLED;
668         }
669         /* Execute operator */
670         BMO_op_exec(bm, &bmop);
671
672         /* Select the edge */
673         BMO_slot_buffer_hflag_enable(bm, &bmop, "vertout", BM_ALL, BM_ELEM_SELECT, TRUE);
674
675         /* Flush the select buffers */
676         EDBM_selectmode_flush(em);
677
678         if (!EDBM_op_finish(em, &bmop, op, TRUE)) {
679                 return OPERATOR_CANCELLED;
680         }
681
682         /* Update Geometry */
683         EDBM_update_generic(C, em, TRUE);
684
685         return OPERATOR_FINISHED;
686 }
687
688 void MESH_OT_vert_slide(wmOperatorType *ot)
689 {
690         PropertyRNA *prop;
691
692         /* identifiers */
693         ot->name = "Vertex Slide";
694         ot->idname = "MESH_OT_vert_slide";
695         ot->description = "Vertex slide";
696
697         /* api callback */
698         ot->invoke = edbm_vert_slide_invoke;
699         ot->modal = edbm_vert_slide_modal;
700         ot->cancel = edbm_vert_slide_cancel;
701         ot->poll = ED_operator_editmesh_region_view3d;
702
703         /* ot->exec = edbm_vert_slide_exec;
704          * ot->poll = ED_operator_editmesh; */
705
706         /* flags */
707         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
708
709         /* Properties for vertex slide */
710         prop = RNA_def_float(ot->srna, "distance_t", 0.0f, -FLT_MAX, FLT_MAX, "Distance", "Distance", -5.0f, 5.0f);
711         RNA_def_property_ui_range(prop, -5.0f, 5.0f, 0.1, 4);
712         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
713 }