style cleanup: braces/indentation
[blender-staging.git] / source / blender / editors / mesh / editmesh_loopcut.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  * The Original Code is Copyright (C) 2007 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Joseph Eagar, Joshua Leung
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/editors/mesh/editmesh_loopcut.c
28  *  \ingroup edmesh
29  */
30
31 #include "DNA_object_types.h"
32
33 #include "MEM_guardedalloc.h"
34
35 #include "BLI_array.h"
36 #include "BLI_string.h"
37 #include "BLI_math.h"
38
39 #include "BLF_translation.h"
40
41 #include "BKE_context.h"
42 #include "BKE_modifier.h"
43 #include "BKE_report.h"
44 #include "BKE_editmesh.h"
45 #include "BKE_DerivedMesh.h"
46
47 #include "BIF_gl.h"
48
49 #include "ED_screen.h"
50 #include "ED_space_api.h"
51 #include "ED_view3d.h"
52 #include "ED_mesh.h"
53 #include "ED_numinput.h"
54
55 #include "RNA_access.h"
56 #include "RNA_define.h"
57 #include "RNA_enum_types.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 #include "mesh_intern.h"  /* own include */
63
64 #define SUBD_SMOOTH_MAX 4.0f
65 #define SUBD_CUTS_MAX 500
66
67 /* ringsel operator */
68
69 /* struct for properties used while drawing */
70 typedef struct RingSelOpData {
71         ARegion *ar;        /* region that ringsel was activated in */
72         void *draw_handle;  /* for drawing preview loop */
73         
74         float (*edges)[2][3];
75         int totedge;
76
77         ViewContext vc;
78
79         Object *ob;
80         BMEditMesh *em;
81         BMEdge *eed;
82         NumInput num;
83
84         bool extend;
85         bool do_cut;
86 } RingSelOpData;
87
88 /* modal loop selection drawing callback */
89 static void ringsel_draw(const bContext *C, ARegion *UNUSED(ar), void *arg)
90 {
91         View3D *v3d = CTX_wm_view3d(C);
92         RingSelOpData *lcd = arg;
93         int i;
94         
95         if (lcd->totedge > 0) {
96                 if (v3d && v3d->zbuf)
97                         glDisable(GL_DEPTH_TEST);
98
99                 glPushMatrix();
100                 glMultMatrixf(lcd->ob->obmat);
101
102                 glColor3ub(255, 0, 255);
103                 glBegin(GL_LINES);
104                 for (i = 0; i < lcd->totedge; i++) {
105                         glVertex3fv(lcd->edges[i][0]);
106                         glVertex3fv(lcd->edges[i][1]);
107                 }
108                 glEnd();
109
110                 glPopMatrix();
111                 if (v3d && v3d->zbuf)
112                         glEnable(GL_DEPTH_TEST);
113         }
114 }
115
116 /* given two opposite edges in a face, finds the ordering of their vertices so
117  * that cut preview lines won't cross each other */
118 static void edgering_find_order(BMEdge *lasteed, BMEdge *eed,
119                                 BMVert *lastv1, BMVert *v[2][2])
120 {
121         BMIter liter;
122         BMLoop *l, *l2;
123         int rev;
124
125         l = eed->l;
126
127         /* find correct order for v[1] */
128         if (!(BM_edge_in_face(l->f, eed) && BM_edge_in_face(l->f, lasteed))) {
129                 BM_ITER_ELEM (l, &liter, l, BM_LOOPS_OF_LOOP) {
130                         if (BM_edge_in_face(l->f, eed) && BM_edge_in_face(l->f, lasteed))
131                                 break;
132                 }
133         }
134         
135         /* this should never happen */
136         if (!l) {
137                 v[0][0] = eed->v1;
138                 v[0][1] = eed->v2;
139                 v[1][0] = lasteed->v1;
140                 v[1][1] = lasteed->v2;
141                 return;
142         }
143         
144         l2 = BM_loop_other_edge_loop(l, eed->v1);
145         rev = (l2 == l->prev);
146         while (l2->v != lasteed->v1 && l2->v != lasteed->v2) {
147                 l2 = rev ? l2->prev : l2->next;
148         }
149
150         if (l2->v == lastv1) {
151                 v[0][0] = eed->v1;
152                 v[0][1] = eed->v2;
153         }
154         else {
155                 v[0][0] = eed->v2;
156                 v[0][1] = eed->v1;
157         }
158 }
159
160 static void edgering_vcos_get(DerivedMesh *dm, BMVert *v[2][2], float r_cos[2][2][3])
161 {
162         if (dm) {
163                 int j, k;
164                 for (j = 0; j < 2; j++) {
165                         for (k = 0; k < 2; k++) {
166                                 dm->getVertCo(dm, BM_elem_index_get(v[j][k]), r_cos[j][k]);
167                         }
168                 }
169         }
170         else {
171                 int j, k;
172                 for (j = 0; j < 2; j++) {
173                         for (k = 0; k < 2; k++) {
174                                 copy_v3_v3(r_cos[j][k], v[j][k]->co);
175                         }
176                 }
177         }
178 }
179
180 static void edgering_sel(RingSelOpData *lcd, int previewlines, bool select)
181 {
182         BMEditMesh *em = lcd->em;
183         DerivedMesh *dm = EDBM_mesh_deform_dm_get(em);
184         BMEdge *eed_start = lcd->eed;
185         BMEdge *eed, *eed_last;
186         BMVert *v[2][2], *v_last;
187         BMWalker walker;
188         float (*edges)[2][3] = NULL;
189         BLI_array_declare(edges);
190         int i, tot = 0;
191         
192         memset(v, 0, sizeof(v));
193         
194         if (!eed_start)
195                 return;
196
197         if (lcd->edges) {
198                 MEM_freeN(lcd->edges);
199                 lcd->edges = NULL;
200                 lcd->totedge = 0;
201         }
202
203         if (!lcd->extend) {
204                 EDBM_flag_disable_all(lcd->em, BM_ELEM_SELECT);
205         }
206
207         if (select) {
208                 BMW_init(&walker, em->bm, BMW_EDGERING,
209                          BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
210                          BMW_FLAG_TEST_HIDDEN,
211                          BMW_NIL_LAY);
212
213                 for (eed = BMW_begin(&walker, eed_start); eed; eed = BMW_step(&walker)) {
214                         BM_edge_select_set(em->bm, eed, true);
215                 }
216                 BMW_end(&walker);
217
218                 return;
219         }
220
221         if (dm) {
222                 EDBM_index_arrays_ensure(lcd->em, BM_VERT);
223         }
224
225         BMW_init(&walker, em->bm, BMW_EDGERING,
226                  BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP,
227                  BMW_FLAG_TEST_HIDDEN,
228                  BMW_NIL_LAY);
229
230         v_last   = NULL;
231         eed_last = NULL;
232
233         for (eed = eed_start = BMW_begin(&walker, eed_start); eed; eed = BMW_step(&walker)) {
234                 if (eed_last) {
235                         if (v_last) {
236                                 v[1][0] = v[0][0];
237                                 v[1][1] = v[0][1];
238                         }
239                         else {
240                                 v[1][0] = eed_last->v1;
241                                 v[1][1] = eed_last->v2;
242                                 v_last  = eed_last->v1;
243                         }
244
245                         edgering_find_order(eed_last, eed, v_last, v);
246                         v_last = v[0][0];
247
248                         BLI_array_grow_items(edges, previewlines);
249
250                         for (i = 1; i <= previewlines; i++) {
251                                 const float fac = (i / ((float)previewlines + 1));
252                                 float v_cos[2][2][3];
253
254                                 edgering_vcos_get(dm, v, v_cos);
255
256                                 interp_v3_v3v3(edges[tot][0], v_cos[0][0], v_cos[0][1], fac);
257                                 interp_v3_v3v3(edges[tot][1], v_cos[1][0], v_cos[1][1], fac);
258                                 tot++;
259                         }
260                 }
261                 eed_last = eed;
262         }
263         
264         if ((eed_last != eed_start) &&
265 #ifdef BMW_EDGERING_NGON
266             BM_edge_share_face_check(eed_last, eed_start)
267 #else
268             BM_edge_share_quad_check(eed_last, eed_start)
269 #endif
270             )
271         {
272                 v[1][0] = v[0][0];
273                 v[1][1] = v[0][1];
274
275                 edgering_find_order(eed_last, eed_start, v_last, v);
276                 
277                 BLI_array_grow_items(edges, previewlines);
278
279                 for (i = 1; i <= previewlines; i++) {
280                         const float fac = (i / ((float)previewlines + 1));
281                         float v_cos[2][2][3];
282
283                         if (!v[0][0] || !v[0][1] || !v[1][0] || !v[1][1]) {
284                                 continue;
285                         }
286
287                         edgering_vcos_get(dm, v, v_cos);
288
289                         interp_v3_v3v3(edges[tot][0], v_cos[0][0], v_cos[0][1], fac);
290                         interp_v3_v3v3(edges[tot][1], v_cos[1][0], v_cos[1][1], fac);
291                         tot++;
292                 }
293         }
294
295         BMW_end(&walker);
296         lcd->edges = edges;
297         lcd->totedge = tot;
298 }
299
300 static void ringsel_find_edge(RingSelOpData *lcd, const int previewlines)
301 {
302         if (lcd->eed) {
303                 edgering_sel(lcd, previewlines, false);
304         }
305         else if (lcd->edges) {
306                 MEM_freeN(lcd->edges);
307                 lcd->edges = NULL;
308                 lcd->totedge = 0;
309         }
310 }
311
312 static void ringsel_finish(bContext *C, wmOperator *op)
313 {
314         RingSelOpData *lcd = op->customdata;
315         const int cuts = RNA_int_get(op->ptr, "number_cuts");
316         const float smoothness = 0.292f * RNA_float_get(op->ptr, "smoothness");
317         const int smooth_falloff = RNA_enum_get(op->ptr, "falloff");
318 #ifdef BMW_EDGERING_NGON
319         const bool use_only_quads = false;
320 #else
321         const bool use_only_quads = false;
322 #endif
323
324         if (lcd->eed) {
325                 BMEditMesh *em = lcd->em;
326
327                 edgering_sel(lcd, cuts, true);
328                 
329                 if (lcd->do_cut) {
330                         const bool is_macro = (op->opm != NULL);
331                         /* Enable gridfill, so that intersecting loopcut works as one would expect.
332                          * Note though that it will break edgeslide in this specific case.
333                          * See [#31939]. */
334                         BM_mesh_esubdivide(em->bm, BM_ELEM_SELECT,
335                                            smoothness, smooth_falloff, true,
336                                            0.0f, 0.0f,
337                                            cuts,
338                                            SUBDIV_SELECT_LOOPCUT, SUBD_PATH, 0, true,
339                                            use_only_quads, 0);
340
341                         /* when used in a macro tessface is already re-recalculated */
342                         EDBM_update_generic(em, (is_macro == false), true);
343
344                         /* we cant slide multiple edges in vertex select mode */
345                         if (is_macro && (cuts > 1) && (em->selectmode & SCE_SELECT_VERTEX)) {
346                                 EDBM_selectmode_disable(lcd->vc.scene, em, SCE_SELECT_VERTEX, SCE_SELECT_EDGE);
347                         }
348                         /* force edge slide to edge select mode in in face select mode */
349                         else if (EDBM_selectmode_disable(lcd->vc.scene, em, SCE_SELECT_FACE, SCE_SELECT_EDGE)) {
350                                 /* pass, the change will flush selection */
351                         }
352                         else {
353                                 /* else flush explicitly */
354                                 EDBM_selectmode_flush(lcd->em);
355                         }
356                 }
357                 else {
358                         /* XXX Is this piece of code ever used now? Simple loop select is now
359                          *     in editmesh_select.c (around line 1000)... */
360                         /* sets as active, useful for other tools */
361                         if (em->selectmode & SCE_SELECT_VERTEX)
362                                 BM_select_history_store(em->bm, lcd->eed->v1);  /* low priority TODO, get vertrex close to mouse */
363                         if (em->selectmode & SCE_SELECT_EDGE)
364                                 BM_select_history_store(em->bm, lcd->eed);
365                         
366                         EDBM_selectmode_flush(lcd->em);
367                         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, lcd->ob->data);
368                 }
369         }
370 }
371
372 /* called when modal loop selection is done... */
373 static void ringsel_exit(bContext *UNUSED(C), wmOperator *op)
374 {
375         RingSelOpData *lcd = op->customdata;
376
377         /* deactivate the extra drawing stuff in 3D-View */
378         ED_region_draw_cb_exit(lcd->ar->type, lcd->draw_handle);
379         
380         if (lcd->edges)
381                 MEM_freeN(lcd->edges);
382
383         ED_region_tag_redraw(lcd->ar);
384
385         /* free the custom data */
386         MEM_freeN(lcd);
387         op->customdata = NULL;
388 }
389
390
391 /* called when modal loop selection gets set up... */
392 static int ringsel_init(bContext *C, wmOperator *op, bool do_cut)
393 {
394         RingSelOpData *lcd;
395
396         /* alloc new customdata */
397         lcd = op->customdata = MEM_callocN(sizeof(RingSelOpData), "ringsel Modal Op Data");
398         
399         /* assign the drawing handle for drawing preview line... */
400         lcd->ar = CTX_wm_region(C);
401         lcd->draw_handle = ED_region_draw_cb_activate(lcd->ar->type, ringsel_draw, lcd, REGION_DRAW_POST_VIEW);
402         lcd->ob = CTX_data_edit_object(C);
403         lcd->em = BKE_editmesh_from_object(lcd->ob);
404         lcd->extend = do_cut ? false : RNA_boolean_get(op->ptr, "extend");
405         lcd->do_cut = do_cut;
406         
407         initNumInput(&lcd->num);
408         lcd->num.idx_max = 0;
409         lcd->num.flag |= NUM_NO_NEGATIVE | NUM_NO_FRACTION;
410
411         /* XXX, temp, workaround for [# ] */
412         EDBM_mesh_ensure_valid_dm_hack(CTX_data_scene(C), lcd->em);
413
414         em_setup_viewcontext(C, &lcd->vc);
415
416         ED_region_tag_redraw(lcd->ar);
417
418         return 1;
419 }
420
421 static int ringcut_cancel(bContext *C, wmOperator *op)
422 {
423         /* this is just a wrapper around exit() */
424         ringsel_exit(C, op);
425         return OPERATOR_CANCELLED;
426 }
427
428 static void loopcut_update_edge(RingSelOpData *lcd, BMEdge *e, const int previewlines)
429 {
430         if (e != lcd->eed) {
431                 lcd->eed = e;
432                 ringsel_find_edge(lcd, previewlines);
433         }
434 }
435
436 static void loopcut_mouse_move(RingSelOpData *lcd, const int previewlines)
437 {
438         float dist = 75.0f;
439         BMEdge *e = EDBM_edge_find_nearest(&lcd->vc, &dist);
440         loopcut_update_edge(lcd, e, previewlines);
441 }
442
443 /* called by both init() and exec() */
444 static int loopcut_init(bContext *C, wmOperator *op, const wmEvent *event)
445 {
446         const bool is_interactive = (event != NULL);
447         Object *obedit = CTX_data_edit_object(C);
448         RingSelOpData *lcd;
449
450         if (modifiers_isDeformedByLattice(obedit) || modifiers_isDeformedByArmature(obedit))
451                 BKE_report(op->reports, RPT_WARNING, "Loop cut does not work well on deformed edit mesh display");
452
453         view3d_operator_needs_opengl(C);
454
455         /* for re-execution, check edge index is in range before we setup ringsel */
456         if (is_interactive == false) {
457                 const int e_index = RNA_int_get(op->ptr, "edge_index");
458                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
459                 if (UNLIKELY((e_index == -1) || (e_index >= em->bm->totedge))) {
460                         return OPERATOR_CANCELLED;
461                 }
462         }
463
464         if (!ringsel_init(C, op, true))
465                 return OPERATOR_CANCELLED;
466
467         /* add a modal handler for this operator - handles loop selection */
468         if (is_interactive) {
469                 WM_event_add_modal_handler(C, op);
470         }
471
472         lcd = op->customdata;
473
474         if (is_interactive) {
475                 copy_v2_v2_int(lcd->vc.mval, event->mval);
476                 loopcut_mouse_move(lcd, is_interactive ? 1 : 0);
477         }
478         else {
479                 const int e_index = RNA_int_get(op->ptr, "edge_index");
480                 BMEdge *e;
481                 EDBM_index_arrays_ensure(lcd->em, BM_EDGE);
482                 e = EDBM_edge_at_index(lcd->em, e_index);
483                 loopcut_update_edge(lcd, e, 0);
484         }
485
486 #ifdef USE_LOOPSLIDE_HACK
487         /* for use in macro so we can restore, HACK */
488         {
489                 Scene *scene = CTX_data_scene(C);
490                 ToolSettings *settings = scene->toolsettings;
491                 int mesh_select_mode[3] = {
492                     (settings->selectmode & SCE_SELECT_VERTEX) != 0,
493                     (settings->selectmode & SCE_SELECT_EDGE)   != 0,
494                     (settings->selectmode & SCE_SELECT_FACE)   != 0,
495                 };
496
497                 RNA_boolean_set_array(op->ptr, "mesh_select_mode_init", mesh_select_mode);
498         }
499 #endif
500
501         if (is_interactive) {
502                 ScrArea *sa = CTX_wm_area(C);
503                 ED_area_headerprint(sa, IFACE_("Select a ring to be cut, use mouse-wheel or page-up/down for number of cuts, "
504                                                "hold Alt for smooth"));
505                 return OPERATOR_RUNNING_MODAL;
506         }
507         else {
508                 ringsel_finish(C, op);
509                 ringsel_exit(C, op);
510                 return OPERATOR_FINISHED;
511         }
512 }
513
514 static int ringcut_invoke(bContext *C, wmOperator *op, const wmEvent *event)
515 {
516         return loopcut_init(C, op, event);
517 }
518
519 static int loopcut_exec(bContext *C, wmOperator *op)
520 {
521         return loopcut_init(C, op, NULL);
522 }
523
524 static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event)
525 {
526         float smoothness = RNA_float_get(op->ptr, "smoothness");
527         int cuts = RNA_int_get(op->ptr, "number_cuts");
528         RingSelOpData *lcd = op->customdata;
529         bool show_cuts = false;
530
531         view3d_operator_needs_opengl(C);
532
533         switch (event->type) {
534                 case RETKEY:
535                 case PADENTER:
536                 case LEFTMOUSE: /* confirm */ // XXX hardcoded
537                         if (event->val == KM_PRESS) {
538                                 /* finish */
539                                 ED_region_tag_redraw(lcd->ar);
540                                 
541                                 if (lcd->eed) {
542                                         /* set for redo */
543                                         BM_mesh_elem_index_ensure(lcd->em->bm, BM_EDGE);
544                                         RNA_int_set(op->ptr, "edge_index", BM_elem_index_get(lcd->eed));
545
546                                         /* execute */
547                                         ringsel_finish(C, op);
548                                         ringsel_exit(C, op);
549                                 }
550                                 else {
551                                         return ringcut_cancel(C, op);
552                                 }
553                                 
554                                 ED_area_headerprint(CTX_wm_area(C), NULL);
555                                 
556                                 return OPERATOR_FINISHED;
557                         }
558                         
559                         ED_region_tag_redraw(lcd->ar);
560                         break;
561                 case RIGHTMOUSE: /* abort */ // XXX hardcoded
562                         ED_region_tag_redraw(lcd->ar);
563                         ringsel_exit(C, op);
564                         ED_area_headerprint(CTX_wm_area(C), NULL);
565
566                         return OPERATOR_FINISHED;
567                 case ESCKEY:
568                         if (event->val == KM_RELEASE) {
569                                 /* cancel */
570                                 ED_region_tag_redraw(lcd->ar);
571                                 ED_area_headerprint(CTX_wm_area(C), NULL);
572                                 
573                                 return ringcut_cancel(C, op);
574                         }
575                         
576                         ED_region_tag_redraw(lcd->ar);
577                         break;
578                 case PADPLUSKEY:
579                 case PAGEUPKEY:
580                 case WHEELUPMOUSE:  /* change number of cuts */
581                         if (event->val == KM_RELEASE)
582                                 break;
583                         if (event->alt == 0) {
584                                 cuts++;
585                                 cuts = CLAMPIS(cuts, 0, SUBD_CUTS_MAX);
586                                 RNA_int_set(op->ptr, "number_cuts", cuts);
587                                 ringsel_find_edge(lcd, cuts);
588                                 show_cuts = true;
589                         }
590                         else {
591                                 smoothness = min_ff(smoothness + 0.05f, SUBD_SMOOTH_MAX);
592                                 RNA_float_set(op->ptr, "smoothness", smoothness);
593                                 show_cuts = true;
594                         }
595                         
596                         ED_region_tag_redraw(lcd->ar);
597                         break;
598                 case PADMINUS:
599                 case PAGEDOWNKEY:
600                 case WHEELDOWNMOUSE:  /* change number of cuts */
601                         if (event->val == KM_RELEASE)
602                                 break;
603
604                         if (event->alt == 0) {
605                                 cuts = max_ii(cuts - 1, 1);
606                                 RNA_int_set(op->ptr, "number_cuts", cuts);
607                                 ringsel_find_edge(lcd, cuts);
608                                 show_cuts = true;
609                         }
610                         else {
611                                 smoothness = max_ff(smoothness - 0.05f, -SUBD_SMOOTH_MAX);
612                                 RNA_float_set(op->ptr, "smoothness", smoothness);
613                                 show_cuts = true;
614                         }
615                         
616                         ED_region_tag_redraw(lcd->ar);
617                         break;
618                 case MOUSEMOVE:  /* mouse moved somewhere to select another loop */
619                 {
620                         lcd->vc.mval[0] = event->mval[0];
621                         lcd->vc.mval[1] = event->mval[1];
622                         loopcut_mouse_move(lcd, cuts);
623
624                         ED_region_tag_redraw(lcd->ar);
625                         break;
626                 }
627         }
628         
629         /* using the keyboard to input the number of cuts */
630         if (event->val == KM_PRESS) {
631                 /* init as zero so backspace clears */
632                 
633                 if (handleNumInput(&lcd->num, event)) {
634                         float value = RNA_int_get(op->ptr, "number_cuts");
635                         applyNumInput(&lcd->num, &value);
636                         
637                         /* allow zero so you can backspace and type in a value
638                          * otherwise 1 as minimum would make more sense */
639                         cuts = CLAMPIS(value, 0, SUBD_CUTS_MAX);
640                         
641                         RNA_int_set(op->ptr, "number_cuts", cuts);
642                         ringsel_find_edge(lcd, cuts);
643                         show_cuts = true;
644                         
645                         ED_region_tag_redraw(lcd->ar);
646                 }
647         }
648         
649         if (show_cuts) {
650                 char buf[64];
651                 BLI_snprintf(buf, sizeof(buf), IFACE_("Number of Cuts: %d, Smooth: %.2f (Alt)"), cuts, smoothness);
652                 ED_area_headerprint(CTX_wm_area(C), buf);
653         }
654         
655         /* keep going until the user confirms */
656         return OPERATOR_RUNNING_MODAL;
657 }
658
659 /* for bmesh this tool is in bmesh_select.c */
660 #if 0
661
662 void MESH_OT_edgering_select(wmOperatorType *ot)
663 {
664         /* description */
665         ot->name = "Edge Ring Select";
666         ot->idname = "MESH_OT_edgering_select";
667         ot->description = "Select an edge ring";
668         
669         /* callbacks */
670         ot->invoke = ringsel_invoke;
671         ot->poll = ED_operator_editmesh_region_view3d; 
672         
673         /* flags */
674         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
675
676         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
677 }
678
679 #endif
680
681 void MESH_OT_loopcut(wmOperatorType *ot)
682 {
683         PropertyRNA *prop;
684
685         /* description */
686         ot->name = "Loop Cut";
687         ot->idname = "MESH_OT_loopcut";
688         ot->description = "Add a new loop between existing loops";
689         
690         /* callbacks */
691         ot->invoke = ringcut_invoke;
692         ot->exec = loopcut_exec;
693         ot->modal = loopcut_modal;
694         ot->cancel = ringcut_cancel;
695         ot->poll = ED_operator_editmesh_region_view3d;
696         
697         /* flags */
698         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
699
700         /* properties */
701         prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 100);
702         /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */
703         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
704
705         prop = RNA_def_float(ot->srna, "smoothness", 0.0f, -FLT_MAX, FLT_MAX,
706                              "Smoothness", "Smoothness factor", -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX);
707         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
708
709         prop = RNA_def_property(ot->srna, "falloff", PROP_ENUM, PROP_NONE);
710         RNA_def_property_enum_items(prop, proportional_falloff_curve_only_items);
711         RNA_def_property_enum_default(prop, PROP_ROOT);
712         RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather");
713         RNA_def_property_translation_context(prop, BLF_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */
714
715         prop = RNA_def_int(ot->srna, "edge_index", -1, -1, INT_MAX, "Number of Cuts", "", 0, INT_MAX);
716         RNA_def_property_flag(prop, PROP_HIDDEN);
717
718 #ifdef USE_LOOPSLIDE_HACK
719         prop = RNA_def_boolean_array(ot->srna, "mesh_select_mode_init", 3, NULL, "", "");
720         RNA_def_property_flag(prop, PROP_HIDDEN);
721 #endif
722 }