bugfix [#24179] Button "Loop Cut and Slide" on Mesh Tools not work
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 #include <string.h>
31 #include <ctype.h>
32 #include <stdio.h>
33
34 #include "DNA_ID.h"
35 #include "DNA_screen_types.h"
36 #include "DNA_scene_types.h"
37 #include "DNA_userdef_types.h"
38 #include "DNA_windowmanager_types.h"
39 #include "DNA_object_types.h"
40
41 #include "MEM_guardedalloc.h"
42
43 #include "PIL_time.h"
44
45 #include "BLI_blenlib.h"
46 #include "BLI_dynstr.h" /*for WM_operator_pystring */
47 #include "BLI_editVert.h"
48 #include "BLI_math.h"
49
50 #include "BKE_blender.h"
51 #include "BKE_context.h"
52 #include "BKE_depsgraph.h"
53 #include "BKE_scene.h"
54 #include "BKE_mesh.h"
55
56 #include "BIF_gl.h"
57 #include "BIF_glutil.h" /* for paint cursor */
58
59 #include "IMB_imbuf_types.h"
60
61 #include "ED_screen.h"
62 #include "ED_space_api.h"
63 #include "ED_view3d.h"
64 #include "ED_mesh.h"
65
66 #include "RNA_access.h"
67 #include "RNA_define.h"
68
69 #include "UI_interface.h"
70
71 #include "WM_api.h"
72 #include "WM_types.h"
73
74 #include "mesh_intern.h"
75
76 /* ringsel operator */
77
78 /* struct for properties used while drawing */
79 typedef struct tringselOpData {
80         ARegion *ar;            /* region that ringsel was activated in */
81         void *draw_handle;      /* for drawing preview loop */
82         
83         float (*edges)[2][3];
84         int totedge;
85
86         ViewContext vc;
87
88         Object *ob;
89         EditMesh *em;
90         EditEdge *eed;
91
92         int extend;
93         int do_cut;
94 } tringselOpData;
95
96 /* modal loop selection drawing callback */
97 static void ringsel_draw(const bContext *C, ARegion *ar, void *arg)
98 {
99         int i;
100         tringselOpData *lcd = arg;
101         
102         if (lcd->totedge > 0) {
103                 glDisable(GL_DEPTH_TEST);
104
105                 glPushMatrix();
106                 glMultMatrixf(lcd->ob->obmat);
107
108                 glColor3ub(255, 0, 255);
109                 glBegin(GL_LINES);
110                 for (i=0; i<lcd->totedge; i++) {
111                         glVertex3fv(lcd->edges[i][0]);
112                         glVertex3fv(lcd->edges[i][1]);
113                 }
114                 glEnd();
115
116                 glPopMatrix();
117                 glEnable(GL_DEPTH_TEST);
118         }
119 }
120
121 static void edgering_sel(tringselOpData *lcd, int previewlines, int select)
122 {
123         EditMesh *em = lcd->em;
124         EditEdge *startedge = lcd->eed;
125         EditEdge *eed;
126         EditFace *efa;
127         EditVert *v[2][2];
128         float (*edges)[2][3] = NULL;
129         V_DYNDECLARE(edges);
130         float co[2][3];
131         int looking=1, i, tot=0;
132         
133         if (!startedge)
134                 return;
135
136         if (lcd->edges) {
137                 MEM_freeN(lcd->edges);
138                 lcd->edges = NULL;
139                 lcd->totedge = 0;
140         }
141
142         if (!lcd->extend) {
143                 EM_clear_flag_all(lcd->em, SELECT);
144         }
145
146         /* in eed->f1 we put the valence (amount of faces in edge) */
147         /* in eed->f2 we put tagged flag as correct loop */
148         /* in efa->f1 we put tagged flag as correct to select */
149
150         for(eed= em->edges.first; eed; eed= eed->next) {
151                 eed->f1= 0;
152                 eed->f2= 0;
153         }
154
155         for(efa= em->faces.first; efa; efa= efa->next) {
156                 efa->f1= 0;
157                 if(efa->h==0) {
158                         efa->e1->f1++;
159                         efa->e2->f1++;
160                         efa->e3->f1++;
161                         if(efa->e4) efa->e4->f1++;
162                 }
163         }
164         
165         // tag startedge OK
166         startedge->f2= 1;
167         
168         while(looking) {
169                 looking= 0;
170                 
171                 for(efa= em->faces.first; efa; efa= efa->next) {
172                         if(efa->e4 && efa->f1==0 && efa->h == 0) {      // not done quad
173                                 if(efa->e1->f1<=2 && efa->e2->f1<=2 && efa->e3->f1<=2 && efa->e4->f1<=2) { // valence ok
174
175                                         // if edge tagged, select opposing edge and mark face ok
176                                         if(efa->e1->f2) {
177                                                 efa->e3->f2= 1;
178                                                 efa->f1= 1;
179                                                 looking= 1;
180                                         }
181                                         else if(efa->e2->f2) {
182                                                 efa->e4->f2= 1;
183                                                 efa->f1= 1;
184                                                 looking= 1;
185                                         }
186                                         if(efa->e3->f2) {
187                                                 efa->e1->f2= 1;
188                                                 efa->f1= 1;
189                                                 looking= 1;
190                                         }
191                                         if(efa->e4->f2) {
192                                                 efa->e2->f2= 1;
193                                                 efa->f1= 1;
194                                                 looking= 1;
195                                         }
196                                 }
197                         }
198                 }
199         }
200         
201         if(previewlines > 0 && !select){
202                         for(efa= em->faces.first; efa; efa= efa->next) {
203                                 if(efa->v4 == NULL) {  continue; }
204                                 if(efa->h == 0){
205                                         if(efa->e1->f2 == 1){
206                                                 if(efa->e1->h == 1 || efa->e3->h == 1 )
207                                                         continue;
208                                                 
209                                                 v[0][0] = efa->v1;
210                                                 v[0][1] = efa->v2;
211                                                 v[1][0] = efa->v4;
212                                                 v[1][1] = efa->v3;
213                                         } else if(efa->e2->f2 == 1){
214                                                 if(efa->e2->h == 1 || efa->e4->h == 1)
215                                                         continue;
216                                                 v[0][0] = efa->v2;
217                                                 v[0][1] = efa->v3;
218                                                 v[1][0] = efa->v1;
219                                                 v[1][1] = efa->v4;                                      
220                                         } else { continue; }
221                                                                                   
222                                         for(i=1;i<=previewlines;i++){
223                                                 co[0][0] = (v[0][1]->co[0] - v[0][0]->co[0])*(i/((float)previewlines+1))+v[0][0]->co[0];
224                                                 co[0][1] = (v[0][1]->co[1] - v[0][0]->co[1])*(i/((float)previewlines+1))+v[0][0]->co[1];
225                                                 co[0][2] = (v[0][1]->co[2] - v[0][0]->co[2])*(i/((float)previewlines+1))+v[0][0]->co[2];
226
227                                                 co[1][0] = (v[1][1]->co[0] - v[1][0]->co[0])*(i/((float)previewlines+1))+v[1][0]->co[0];
228                                                 co[1][1] = (v[1][1]->co[1] - v[1][0]->co[1])*(i/((float)previewlines+1))+v[1][0]->co[1];
229                                                 co[1][2] = (v[1][1]->co[2] - v[1][0]->co[2])*(i/((float)previewlines+1))+v[1][0]->co[2];                                        
230                                                 
231                                                 V_GROW(edges);
232                                                 VECCOPY(edges[tot][0], co[0]);
233                                                 VECCOPY(edges[tot][1], co[1]);
234                                                 tot++;
235                                         }
236                                 }
237                         }
238         } else {
239                 select = (startedge->f & SELECT) == 0;
240
241                 /* select the edges */
242                 for(eed= em->edges.first; eed; eed= eed->next) {
243                         if(eed->f2) EM_select_edge(eed, select);
244                 }
245         }
246
247         lcd->edges = edges;
248         lcd->totedge = tot;
249 }
250
251 static void ringsel_find_edge(tringselOpData *lcd, const bContext *C, ARegion *ar, int cuts)
252 {
253         if (lcd->eed) {
254                 edgering_sel(lcd, cuts, 0);
255         } else if(lcd->edges) {
256                 MEM_freeN(lcd->edges);
257                 lcd->edges = NULL;
258                 lcd->totedge = 0;
259         }
260 }
261
262 static void ringsel_finish(bContext *C, wmOperator *op)
263 {
264         tringselOpData *lcd= op->customdata;
265         int cuts= (lcd->do_cut)? RNA_int_get(op->ptr,"number_cuts"): 0;
266
267         if (lcd->eed) {
268                 edgering_sel(lcd, cuts, 1);
269                 if (lcd->do_cut) {
270                         EditMesh *em = BKE_mesh_get_editmesh(lcd->ob->data);
271                         esubdivideflag(lcd->ob, em, SELECT, 0.0f, 0.0f, 0, cuts, 0, SUBDIV_SELECT_LOOPCUT);
272
273                         /* force edge slide to edge select mode in in face select mode */
274                         if (em->selectmode & SCE_SELECT_FACE) {
275                                 if (em->selectmode == SCE_SELECT_FACE)
276                                         em->selectmode = SCE_SELECT_EDGE;
277                                 else
278                                         em->selectmode &= ~SCE_SELECT_FACE;
279                                 CTX_data_tool_settings(C)->selectmode= em->selectmode;
280                                 EM_selectmode_set(em);
281
282                                 WM_event_add_notifier(C, NC_SCENE|ND_TOOLSETTINGS, CTX_data_scene(C));
283                         }
284                         
285                         DAG_id_flush_update(lcd->ob->data, OB_RECALC_DATA);
286                         WM_event_add_notifier(C, NC_GEOM|ND_DATA, lcd->ob->data);
287                 }
288                 else {
289                         EM_selectmode_flush(lcd->em);
290                         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, lcd->ob->data);
291                 }
292         }
293 }
294
295 /* called when modal loop selection is done... */
296 static void ringsel_exit (bContext *C, wmOperator *op)
297 {
298         tringselOpData *lcd= op->customdata;
299
300         /* deactivate the extra drawing stuff in 3D-View */
301         ED_region_draw_cb_exit(lcd->ar->type, lcd->draw_handle);
302         
303         if (lcd->edges)
304                 MEM_freeN(lcd->edges);
305
306         ED_region_tag_redraw(lcd->ar);
307
308         /* free the custom data */
309         MEM_freeN(lcd);
310         op->customdata= NULL;
311 }
312
313 /* called when modal loop selection gets set up... */
314 static int ringsel_init (bContext *C, wmOperator *op, int do_cut)
315 {
316         tringselOpData *lcd;
317         
318         /* alloc new customdata */
319         lcd= op->customdata= MEM_callocN(sizeof(tringselOpData), "ringsel Modal Op Data");
320         
321         /* assign the drawing handle for drawing preview line... */
322         lcd->ar= CTX_wm_region(C);
323         lcd->draw_handle= ED_region_draw_cb_activate(lcd->ar->type, ringsel_draw, lcd, REGION_DRAW_POST_VIEW);
324         lcd->ob = CTX_data_edit_object(C);
325         lcd->em= BKE_mesh_get_editmesh((Mesh *)lcd->ob->data);
326         lcd->extend = do_cut ? 0 : RNA_boolean_get(op->ptr, "extend");
327         lcd->do_cut = do_cut;
328         em_setup_viewcontext(C, &lcd->vc);
329
330         ED_region_tag_redraw(lcd->ar);
331
332         return 1;
333 }
334
335 static int ringcut_cancel (bContext *C, wmOperator *op)
336 {
337         /* this is just a wrapper around exit() */
338         ringsel_exit(C, op);
339         return OPERATOR_CANCELLED;
340 }
341
342 static int ringsel_invoke (bContext *C, wmOperator *op, wmEvent *evt)
343 {
344         tringselOpData *lcd;
345         EditEdge *edge;
346         int dist = 75;
347         
348         view3d_operator_needs_opengl(C);
349
350         if (!ringsel_init(C, op, 0))
351                 return OPERATOR_CANCELLED;
352         
353         lcd = op->customdata;
354         
355         if (lcd->em->selectmode == SCE_SELECT_FACE) {
356                 ringsel_exit(C, op);
357                 WM_operator_name_call(C, "MESH_OT_loop_select", WM_OP_INVOKE_REGION_WIN, NULL);
358                 return OPERATOR_CANCELLED;
359         }
360
361         lcd->vc.mval[0] = evt->mval[0];
362         lcd->vc.mval[1] = evt->mval[1];
363         
364         edge = findnearestedge(&lcd->vc, &dist);
365         if(!edge) {
366                 ringsel_exit(C, op);
367                 return OPERATOR_CANCELLED;
368         }
369
370         lcd->eed = edge;
371         ringsel_find_edge(lcd, C, lcd->ar, 1);
372
373         ringsel_finish(C, op);
374         ringsel_exit(C, op);
375
376         return OPERATOR_FINISHED;
377 }
378
379 static int ringcut_invoke (bContext *C, wmOperator *op, wmEvent *evt)
380 {
381         tringselOpData *lcd;
382         EditEdge *edge;
383         int dist = 75;
384
385         view3d_operator_needs_opengl(C);
386
387         if (!ringsel_init(C, op, 1))
388                 return OPERATOR_CANCELLED;
389         
390         /* add a modal handler for this operator - handles loop selection */
391         WM_event_add_modal_handler(C, op);
392
393         lcd = op->customdata;
394         lcd->vc.mval[0] = evt->mval[0];
395         lcd->vc.mval[1] = evt->mval[1];
396         
397         edge = findnearestedge(&lcd->vc, &dist);
398         if (edge != lcd->eed) {
399                 lcd->eed = edge;
400                 ringsel_find_edge(lcd, C, lcd->ar, 1);
401         }
402
403         return OPERATOR_RUNNING_MODAL;
404 }
405
406 static int ringcut_modal (bContext *C, wmOperator *op, wmEvent *event)
407 {
408         int cuts= RNA_int_get(op->ptr,"number_cuts");
409         tringselOpData *lcd= op->customdata;
410
411         view3d_operator_needs_opengl(C);
412
413
414         switch (event->type) {
415                 case LEFTMOUSE: /* confirm */ // XXX hardcoded
416                         if (event->val == KM_PRESS) {
417                                 /* finish */
418                                 ED_region_tag_redraw(lcd->ar);
419                                 
420                                 ringsel_finish(C, op);
421                                 ringsel_exit(C, op);
422                                 
423                                 return OPERATOR_FINISHED;
424                         }
425                         
426                         ED_region_tag_redraw(lcd->ar);
427                         break;
428                 case RIGHTMOUSE: /* abort */ // XXX hardcoded
429                 case ESCKEY:
430                         if (event->val == KM_RELEASE) {
431                                 /* cancel */
432                                 ED_region_tag_redraw(lcd->ar);
433                                 
434                                 return ringcut_cancel(C, op);
435                         }
436                         
437                         ED_region_tag_redraw(lcd->ar);
438                         break;
439                 case WHEELUPMOUSE:  /* change number of cuts */
440                 case PAGEUPKEY:
441                         if (event->val == KM_PRESS) {
442                                 cuts++;
443                                 RNA_int_set(op->ptr, "number_cuts",cuts);
444                                 ringsel_find_edge(lcd, C, lcd->ar, cuts);
445                                 
446                                 ED_region_tag_redraw(lcd->ar);
447                         }
448                         break;
449                 case WHEELDOWNMOUSE:  /* change number of cuts */
450                 case PAGEDOWNKEY:
451                         if (event->val == KM_PRESS) {
452                                 cuts=MAX2(cuts-1,1);
453                                 RNA_int_set(op->ptr,"number_cuts",cuts);
454                                 ringsel_find_edge(lcd, C, lcd->ar,cuts);
455                                 
456                                 ED_region_tag_redraw(lcd->ar);
457                         }
458                         break;
459                 case MOUSEMOVE: { /* mouse moved somewhere to select another loop */
460                         int dist = 75;
461                         EditEdge *edge;
462
463                         lcd->vc.mval[0] = event->mval[0];
464                         lcd->vc.mval[1] = event->mval[1];
465                         edge = findnearestedge(&lcd->vc, &dist);
466
467                         if (edge != lcd->eed) {
468                                 lcd->eed = edge;
469                                 ringsel_find_edge(lcd, C, lcd->ar, cuts);
470                         }
471
472                         ED_region_tag_redraw(lcd->ar);
473                         break;
474                 }                       
475         }
476         
477         /* keep going until the user confirms */
478         return OPERATOR_RUNNING_MODAL;
479 }
480
481 void MESH_OT_edgering_select (wmOperatorType *ot)
482 {
483         /* description */
484         ot->name= "Edge Ring Select";
485         ot->idname= "MESH_OT_edgering_select";
486         ot->description= "Select an edge ring";
487         
488         /* callbacks */
489         ot->invoke= ringsel_invoke;
490         ot->poll= ED_operator_editmesh_view3d;
491         
492         /* flags */
493         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
494
495         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
496 }
497
498 void MESH_OT_loopcut (wmOperatorType *ot)
499 {
500         /* description */
501         ot->name= "Loop Cut";
502         ot->idname= "MESH_OT_loopcut";
503         ot->description= "Add a new loop between existing loops";
504         
505         /* callbacks */
506         ot->invoke= ringcut_invoke;
507         ot->modal= ringcut_modal;
508         ot->cancel= ringcut_cancel;
509         ot->poll= ED_operator_editmesh_view3d;
510         
511         /* flags */
512         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
513
514         /* properties */
515         RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10);
516 }