committing working copy
[blender.git] / source / blender / editors / mesh / loopcut.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2007 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Joseph Eagar, Joshua Leung
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <float.h>
30 #define _USE_MATH_DEFINES
31 #include <math.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <stdio.h>
35
36 #include "DNA_ID.h"
37 #include "DNA_screen_types.h"
38 #include "DNA_scene_types.h"
39 #include "DNA_userdef_types.h"
40 #include "DNA_windowmanager_types.h"
41 #include "DNA_object_types.h"
42
43 #include "MEM_guardedalloc.h"
44
45 #include "PIL_time.h"
46
47 #include "BLI_blenlib.h"
48 #include "BLI_dynstr.h" /*for WM_operator_pystring */
49 #include "BLI_editVert.h"
50 #include "BLI_array.h"
51
52 #include "BKE_blender.h"
53 #include "BKE_context.h"
54 #include "BKE_depsgraph.h"
55 #include "BKE_scene.h"
56 #include "BKE_utildefines.h"
57 #include "BKE_mesh.h"
58 #include "BKE_tessmesh.h"
59 #include "BKE_depsgraph.h"
60
61 #include "BIF_gl.h"
62 #include "BIF_glutil.h" /* for paint cursor */
63
64 #include "IMB_imbuf_types.h"
65
66 #include "ED_screen.h"
67 #include "ED_util.h"
68 #include "ED_space_api.h"
69 #include "ED_view3d.h"
70 #include "ED_mesh.h"
71
72 #include "RNA_access.h"
73 #include "RNA_define.h"
74
75 #include "UI_interface.h"
76
77 #include "WM_api.h"
78 #include "WM_types.h"
79
80 #include "mesh_intern.h"
81
82 /* ringsel operator */
83
84 /* struct for properties used while drawing */
85 typedef struct tringselOpData {
86         ARegion *ar;            /* region that ringsel was activated in */
87         void *draw_handle;      /* for drawing preview loop */
88         
89         float (*edges)[2][3];
90         int totedge;
91
92         ViewContext vc;
93
94         Object *ob;
95         BMEditMesh *em;
96         BMEdge *eed;
97
98         int extend;
99         int do_cut;
100 } tringselOpData;
101
102 /* modal loop selection drawing callback */
103 static void ringsel_draw(const bContext *C, ARegion *ar, void *arg)
104 {
105         int i;
106         tringselOpData *lcd = arg;
107         
108         if (lcd->totedge > 0) {
109                 glDisable(GL_DEPTH_TEST);
110
111                 glPushMatrix();
112                 glMultMatrixf(lcd->ob->obmat);
113
114                 glColor3ub(255, 0, 255);
115                 glBegin(GL_LINES);
116                 for (i=0; i<lcd->totedge; i++) {
117                         glVertex3fv(lcd->edges[i][0]);
118                         glVertex3fv(lcd->edges[i][1]);
119                 }
120                 glEnd();
121
122                 glPopMatrix();
123                 glEnable(GL_DEPTH_TEST);
124         }
125 }
126
127 /*given two opposite edges in a face, finds the ordering of their vertices so
128   that cut preview lines won't cross each other*/
129 static void edgering_find_order(BMEditMesh *em, BMEdge *lasteed, BMEdge *eed, 
130                                 BMVert *lastv1, BMVert *v[2][2])
131 {
132         BMIter liter;
133         BMLoop *l, *l2;
134         int rev;
135
136         l = eed->l;
137
138         /*find correct order for v[1]*/
139         if (!(BM_Edge_In_Face(l->f, eed) && BM_Edge_In_Face(l->f, lasteed))) {
140                 BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_LOOP, l) {
141                         if (BM_Edge_In_Face(l->f, eed) && BM_Edge_In_Face(l->f, lasteed))
142                                 break;
143                 }
144         }
145         
146         /*this should never happen*/
147         if (!l) {
148                 v[0][0] = eed->v1;
149                 v[0][1] = eed->v2;
150                 v[1][0] = lasteed->v1;
151                 v[1][1] = lasteed->v2;
152                 return;
153         }
154         
155         l2 = BM_OtherFaceLoop(l->e, l->f, eed->v1);
156         rev = (l2 == (BMLoop*)l->prev);
157         while (l2->v != lasteed->v1 && l2->v != lasteed->v2) {
158                 l2 = rev ? (BMLoop*)l2->prev : (BMLoop*)l2->next;
159         }
160
161         if (l2->v == lastv1) {
162                 v[0][0] = eed->v1;
163                 v[0][1] = eed->v2;
164         } else {
165                 v[0][0] = eed->v2;
166                 v[0][1] = eed->v1;
167         }
168 }
169
170 static void edgering_sel(tringselOpData *lcd, int previewlines, int select)
171 {
172         BMEditMesh *em = lcd->em;
173         BMEdge *startedge = lcd->eed;
174         BMEdge *eed, *lasteed;
175         BMVert *v[2][2], *lastv1;
176         BMWalker walker;
177         float (*edges)[2][3] = NULL;
178         BLI_array_declare(edges);
179         float co[2][3];
180         int i, tot=0;
181         
182         if (!startedge)
183                 return;
184
185         if (lcd->edges) {
186                 MEM_freeN(lcd->edges);
187                 lcd->edges = NULL;
188                 lcd->totedge = 0;
189         }
190
191         if (!lcd->extend) {
192                 EDBM_clear_flag_all(lcd->em, BM_SELECT);
193         }
194
195         if (select) {
196                 BMW_Init(&walker, em->bm, BMW_EDGERING, 0, 0);
197                 eed = BMW_Begin(&walker, startedge);
198                 for (; eed; eed=BMW_Step(&walker)) {
199                         BM_Select(em->bm, eed, 1);
200                 }
201                 BMW_End(&walker);
202
203                 return;
204         }
205
206         BMW_Init(&walker, em->bm, BMW_EDGERING, 0, 0);
207         eed = startedge = BMW_Begin(&walker, startedge);
208         lastv1 = NULL;
209         for (lasteed=NULL; eed; eed=BMW_Step(&walker)) {
210                 if (lasteed) {
211                         if (lastv1) {
212                                 v[1][0] = v[0][0];
213                                 v[1][1] = v[0][1];
214                         } else {
215                                 v[1][0] = lasteed->v1;
216                                 v[1][1] = lasteed->v2;
217                                 lastv1 = lasteed->v1;
218                         }
219
220                         edgering_find_order(em, lasteed, eed, lastv1, v);
221                         lastv1 = v[0][0];
222
223                         for(i=1;i<=previewlines;i++){
224                                 co[0][0] = (v[0][1]->co[0] - v[0][0]->co[0])*(i/((float)previewlines+1))+v[0][0]->co[0];
225                                 co[0][1] = (v[0][1]->co[1] - v[0][0]->co[1])*(i/((float)previewlines+1))+v[0][0]->co[1];
226                                 co[0][2] = (v[0][1]->co[2] - v[0][0]->co[2])*(i/((float)previewlines+1))+v[0][0]->co[2];
227
228                                 co[1][0] = (v[1][1]->co[0] - v[1][0]->co[0])*(i/((float)previewlines+1))+v[1][0]->co[0];
229                                 co[1][1] = (v[1][1]->co[1] - v[1][0]->co[1])*(i/((float)previewlines+1))+v[1][0]->co[1];
230                                 co[1][2] = (v[1][1]->co[2] - v[1][0]->co[2])*(i/((float)previewlines+1))+v[1][0]->co[2];                                        
231                                 
232                                 BLI_array_growone(edges);
233                                 VECCOPY(edges[tot][0], co[0]);
234                                 VECCOPY(edges[tot][1], co[1]);
235                                 tot++;
236                         }
237                 }
238                 lasteed = eed;
239         }
240         
241         if (BM_Edge_Share_Faces(lasteed, startedge)) {
242                 edgering_find_order(em, lasteed, startedge, lastv1, v);
243
244                 for(i=1;i<=previewlines;i++){
245                         co[0][0] = (v[0][1]->co[0] - v[0][0]->co[0])*(i/((float)previewlines+1))+v[0][0]->co[0];
246                         co[0][1] = (v[0][1]->co[1] - v[0][0]->co[1])*(i/((float)previewlines+1))+v[0][0]->co[1];
247                         co[0][2] = (v[0][1]->co[2] - v[0][0]->co[2])*(i/((float)previewlines+1))+v[0][0]->co[2];
248
249                         co[1][0] = (v[1][1]->co[0] - v[1][0]->co[0])*(i/((float)previewlines+1))+v[1][0]->co[0];
250                         co[1][1] = (v[1][1]->co[1] - v[1][0]->co[1])*(i/((float)previewlines+1))+v[1][0]->co[1];
251                         co[1][2] = (v[1][1]->co[2] - v[1][0]->co[2])*(i/((float)previewlines+1))+v[1][0]->co[2];                                        
252                         
253                         BLI_array_growone(edges);
254                         VECCOPY(edges[tot][0], co[0]);
255                         VECCOPY(edges[tot][1], co[1]);
256                         tot++;
257                 }
258         }
259
260         BMW_End(&walker);
261         lcd->edges = edges;
262         lcd->totedge = tot;
263 }
264
265 static void ringsel_find_edge(tringselOpData *lcd, const bContext *C, ARegion *ar, int cuts)
266 {
267         if (lcd->eed) {
268                 edgering_sel(lcd, cuts, 0);
269         } else {
270                 MEM_freeN(lcd->edges);
271                 lcd->edges = NULL;
272                 lcd->totedge = 0;
273         }
274 }
275
276 static void ringsel_finish(bContext *C, wmOperator *op)
277 {
278         tringselOpData *lcd= op->customdata;
279         int cuts= RNA_int_get(op->ptr, "number_cuts");
280
281         if (lcd->eed) {
282                 edgering_sel(lcd, cuts, 1);
283                 if (lcd->do_cut) {
284                         BMEditMesh *em = lcd->em;
285
286                         BM_esubdivideflag(lcd->ob, em->bm, BM_SELECT, 0.0f, 
287                                           0.0f, 0, cuts, SUBDIV_SELECT_LOOPCUT, 
288                                           SUBD_PATH, 0, 0);
289                         /* force edge slide to edge select mode in in face select mode */
290                         if (em->selectmode & SCE_SELECT_FACE) {
291                                 if (em->selectmode == SCE_SELECT_FACE)
292                                         em->selectmode = SCE_SELECT_EDGE;
293                                 else
294                                         em->selectmode &= ~SCE_SELECT_FACE;
295                                 CTX_data_tool_settings(C)->selectmode= em->selectmode;
296                                 EDBM_selectmode_set(em);
297                         }
298
299                         WM_event_add_notifier(C, NC_GEOM|ND_SELECT|ND_DATA, lcd->ob->data);
300                         DAG_id_flush_update(lcd->ob->data, OB_RECALC_DATA);
301                 }
302                 WM_event_add_notifier(C, NC_GEOM|ND_DATA, lcd->ob->data);
303         }
304 }
305
306 /* called when modal loop selection is done... */
307 static void ringsel_exit (bContext *C, wmOperator *op)
308 {
309         tringselOpData *lcd= op->customdata;
310
311         /* deactivate the extra drawing stuff in 3D-View */
312         ED_region_draw_cb_exit(lcd->ar->type, lcd->draw_handle);
313         
314         if (lcd->edges)
315                 MEM_freeN(lcd->edges);
316
317         ED_region_tag_redraw(lcd->ar);
318
319         /* free the custom data */
320         MEM_freeN(lcd);
321         op->customdata= NULL;
322 }
323
324 /* called when modal loop selection gets set up... */
325 static int ringsel_init (bContext *C, wmOperator *op, int do_cut)
326 {
327         tringselOpData *lcd;
328         
329         /* alloc new customdata */
330         lcd= op->customdata= MEM_callocN(sizeof(tringselOpData), "ringsel Modal Op Data");
331         
332         /* assign the drawing handle for drawing preview line... */
333         lcd->ar= CTX_wm_region(C);
334         lcd->draw_handle= ED_region_draw_cb_activate(lcd->ar->type, ringsel_draw, lcd, REGION_DRAW_POST_VIEW);
335         lcd->ob = CTX_data_edit_object(C);
336         lcd->em= ((Mesh *)lcd->ob->data)->edit_btmesh;
337         lcd->extend = do_cut ? 0 : RNA_boolean_get(op->ptr, "extend");
338         lcd->do_cut = do_cut;
339         em_setup_viewcontext(C, &lcd->vc);
340
341         ED_region_tag_redraw(lcd->ar);
342
343         return 1;
344 }
345
346 static int ringsel_cancel (bContext *C, wmOperator *op)
347 {
348         /* this is just a wrapper around exit() */
349         ringsel_exit(C, op);
350         return OPERATOR_CANCELLED;
351 }
352
353 static int ringsel_invoke (bContext *C, wmOperator *op, wmEvent *evt)
354 {
355         tringselOpData *lcd;
356         BMEdge *edge;
357         int dist = 75;
358
359         view3d_operator_needs_opengl(C);
360
361         if (!ringsel_init(C, op, 0))
362                 return OPERATOR_CANCELLED;
363         
364         /* add a modal handler for this operator - handles loop selection */
365         WM_event_add_modal_handler(C, op);
366
367         lcd = op->customdata;
368         lcd->vc.mval[0] = evt->mval[0];
369         lcd->vc.mval[1] = evt->mval[1];
370         
371         edge = EDBM_findnearestedge(&lcd->vc, &dist);
372         if (edge != lcd->eed) {
373                 lcd->eed = edge;
374                 ringsel_find_edge(lcd, C, lcd->ar, 1);
375         }
376
377         return OPERATOR_RUNNING_MODAL;
378 }
379
380
381 static int ringcut_invoke (bContext *C, wmOperator *op, wmEvent *evt)
382 {
383         tringselOpData *lcd;
384         BMEdge *edge;
385         int dist = 75;
386
387         view3d_operator_needs_opengl(C);
388
389         if (!ringsel_init(C, op, 1))
390                 return OPERATOR_CANCELLED;
391         
392         /* add a modal handler for this operator - handles loop selection */
393         WM_event_add_modal_handler(C, op);
394
395         lcd = op->customdata;
396         lcd->vc.mval[0] = evt->mval[0];
397         lcd->vc.mval[1] = evt->mval[1];
398         
399         edge = EDBM_findnearestedge(&lcd->vc, &dist);
400         if (edge != lcd->eed) {
401                 lcd->eed = edge;
402                 ringsel_find_edge(lcd, C, lcd->ar, 1);
403         }
404
405         return OPERATOR_RUNNING_MODAL;
406 }
407
408 static int ringsel_modal (bContext *C, wmOperator *op, wmEvent *event)
409 {
410         int cuts= RNA_int_get(op->ptr,"number_cuts");
411         tringselOpData *lcd= op->customdata;
412
413         view3d_operator_needs_opengl(C);
414
415
416         switch (event->type) {
417                 case LEFTMOUSE: /* abort */ // XXX hardcoded
418                         ED_region_tag_redraw(lcd->ar);
419                         ringsel_exit(C, op);
420
421                         return OPERATOR_FINISHED;
422                 case RIGHTMOUSE: /* confirm */ // XXX hardcoded
423                         if (event->val == KM_PRESS) {
424                                 /* finish */
425                                 ED_region_tag_redraw(lcd->ar);
426                                 
427                                 ringsel_finish(C, op);
428                                 ringsel_exit(C, op);
429                                 
430                                 return OPERATOR_FINISHED;
431                         }
432                         
433                         ED_region_tag_redraw(lcd->ar);
434                         break;
435                 case ESCKEY:
436                         if (event->val == KM_RELEASE) {
437                                 /* cancel */
438                                 ED_region_tag_redraw(lcd->ar);
439                                 
440                                 return ringsel_cancel(C, op);
441                         }
442                         
443                         ED_region_tag_redraw(lcd->ar);
444                         break;
445                 case MOUSEMOVE: { /* mouse moved somewhere to select another loop */
446                         int dist = 75;
447                         BMEdge *edge;
448
449                         lcd->vc.mval[0] = event->mval[0];
450                         lcd->vc.mval[1] = event->mval[1];
451                         edge = EDBM_findnearestedge(&lcd->vc, &dist);
452
453                         if (edge != lcd->eed) {
454                                 lcd->eed = edge;
455                                 ringsel_find_edge(lcd, C, lcd->ar, cuts);
456                         }
457
458                         ED_region_tag_redraw(lcd->ar);
459                         break;
460                 }                       
461         }
462         
463         /* keep going until the user confirms */
464         return OPERATOR_RUNNING_MODAL;
465 }
466
467 static int loopcut_modal (bContext *C, wmOperator *op, wmEvent *event)
468 {
469         int cuts= RNA_int_get(op->ptr,"number_cuts");
470         tringselOpData *lcd= op->customdata;
471
472         view3d_operator_needs_opengl(C);
473
474
475         switch (event->type) {
476                 case LEFTMOUSE: /* confirm */ // XXX hardcoded
477                         if (event->val == KM_RELEASE) {
478                                 /* finish */
479                                 ED_region_tag_redraw(lcd->ar);
480                                 
481                                 ringsel_finish(C, op);
482                                 ringsel_exit(C, op);
483                                 
484                                 return OPERATOR_FINISHED;
485                         }
486                         
487                         ED_region_tag_redraw(lcd->ar);
488                         break;
489                 case RIGHTMOUSE: /* abort */ // XXX hardcoded
490                         ED_region_tag_redraw(lcd->ar);
491                         ringsel_exit(C, op);
492
493                         return OPERATOR_FINISHED;
494                 case ESCKEY:
495                         if (event->val == KM_RELEASE) {
496                                 /* cancel */
497                                 ED_region_tag_redraw(lcd->ar);
498                                 
499                                 return ringsel_cancel(C, op);
500                         }
501                         
502                         ED_region_tag_redraw(lcd->ar);
503                         break;
504                 case PAGEUPKEY:
505                 case WHEELUPMOUSE:  /* change number of cuts */
506                         if (event->val == KM_RELEASE)
507                                 break;
508
509                         cuts++;
510                         RNA_int_set(op->ptr,"number_cuts",cuts);
511                         ringsel_find_edge(lcd, C, lcd->ar, cuts);
512                         
513                         ED_region_tag_redraw(lcd->ar);
514                         break;
515                 case PAGEDOWNKEY:
516                 case WHEELDOWNMOUSE:  /* change number of cuts */
517                         if (event->val == KM_RELEASE)
518                                 break;
519
520                         cuts=MAX2(cuts-1,1);
521                         RNA_int_set(op->ptr,"number_cuts",cuts);
522                         ringsel_find_edge(lcd, C, lcd->ar,cuts);
523                         
524                         ED_region_tag_redraw(lcd->ar);
525                         break;
526                 case MOUSEMOVE: { /* mouse moved somewhere to select another loop */
527                         int dist = 75;
528                         BMEdge *edge;
529
530                         lcd->vc.mval[0] = event->mval[0];
531                         lcd->vc.mval[1] = event->mval[1];
532                         edge = EDBM_findnearestedge(&lcd->vc, &dist);
533
534                         if (edge != lcd->eed) {
535                                 lcd->eed = edge;
536                                 ringsel_find_edge(lcd, C, lcd->ar, cuts);
537                         }
538
539                         ED_region_tag_redraw(lcd->ar);
540                         break;
541                 }                       
542         }
543         
544         /* keep going until the user confirms */
545         return OPERATOR_RUNNING_MODAL;
546 }
547
548 void MESH_OT_edgering_select (wmOperatorType *ot)
549 {
550         /* description */
551         ot->name= "Edge Ring Select";
552         ot->idname= "MESH_OT_edgering_select";
553         ot->description= "Select an edge ring";
554         
555         /* callbacks */
556         ot->invoke= ringsel_invoke;
557         ot->modal= ringsel_modal;
558         ot->cancel= ringsel_cancel;
559         ot->poll= ED_operator_editmesh_view3d;
560         
561         /* flags */
562         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
563
564         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
565 }
566
567 void MESH_OT_loopcut (wmOperatorType *ot)
568 {
569         /* description */
570         ot->name= "Loop Cut";
571         ot->idname= "MESH_OT_loopcut";
572         ot->description= "Add a new loop between existing loops.";
573         
574         /* callbacks */
575         ot->invoke= ringcut_invoke;
576         ot->modal= loopcut_modal;
577         ot->cancel= ringsel_cancel;
578         ot->poll= ED_operator_editmesh_view3d;
579         
580         /* flags */
581         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
582
583         /* properties */
584         RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10);
585 }