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