91aaa36c77d5a935b8ae50435046c7a0a4972ae7
[blender-staging.git] / source / blender / editors / mesh / bmesh_tools.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) 2004 by Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Joseph Eagar
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 #include "MEM_guardedalloc.h"
29
30 #include "DNA_material_types.h"
31 #include "DNA_mesh_types.h"
32 #include "DNA_modifier_types.h"
33 #include "DNA_object_types.h"
34 #include "DNA_scene_types.h"
35
36 #include "RNA_define.h"
37 #include "RNA_access.h"
38
39 #include "BLI_blenlib.h"
40 #include "BLI_math.h"
41 #include "BLI_rand.h"
42
43 #include "BKE_material.h"
44 #include "BKE_context.h"
45 #include "BKE_cdderivedmesh.h"
46 #include "BKE_depsgraph.h"
47 #include "BKE_object.h"
48 #include "BKE_report.h"
49 #include "BKE_texture.h"
50 #include "BKE_main.h"
51 #include "BKE_tessmesh.h"
52
53 #include "WM_api.h"
54 #include "WM_types.h"
55
56 #include "ED_mesh.h"
57 #include "ED_view3d.h"
58 #include "ED_screen.h"
59 #include "ED_transform.h"
60 #include "ED_object.h"
61
62 #include "RE_render_ext.h"
63
64 #include "mesh_intern.h"
65
66
67 static void add_normal_aligned(float nor[3], const float add[3])
68 {
69         if (dot_v3v3(nor, add) < -0.9999f)
70                 sub_v3_v3(nor, add);
71         else
72                 sub_v3_v3(nor, add);
73 }
74
75
76 static int subdivide_exec(bContext *C, wmOperator *op)
77 {
78         ToolSettings *ts = CTX_data_tool_settings(C);
79         Object *obedit = CTX_data_edit_object(C);
80         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
81         int cuts = RNA_int_get(op->ptr,"number_cuts");
82         float smooth = 0.292f * RNA_float_get(op->ptr, "smoothness");
83         float fractal = RNA_float_get(op->ptr, "fractal")/2.5;
84         int flag = 0;
85
86         if (smooth != 0.0f)
87                 flag |= B_SMOOTH;
88         if (fractal != 0.0f)
89                 flag |= B_FRACTAL;
90         
91         if (RNA_boolean_get(op->ptr, "quadtri") && 
92             RNA_enum_get(op->ptr, "quadcorner") == SUBD_STRAIGHT_CUT)
93         {
94                 RNA_enum_set(op->ptr, "quadcorner", SUBD_INNERVERT);
95         }
96         
97         BM_mesh_esubdivideflag(obedit, em->bm, BM_ELEM_SELECT,
98                           smooth, fractal,
99                           ts->editbutflag|flag, 
100                           cuts, 0, RNA_enum_get(op->ptr, "quadcorner"), 
101                           RNA_boolean_get(op->ptr, "quadtri"),
102                           TRUE, RNA_int_get(op->ptr, "seed"));
103
104         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
105         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
106
107         return OPERATOR_FINISHED;
108 }
109
110 /* Note, these values must match delete_mesh() event values */
111 static EnumPropertyItem prop_mesh_cornervert_types[] = {
112         {SUBD_INNERVERT,     "INNERVERT", 0,      "Inner Vert", ""},
113         {SUBD_PATH,          "PATH", 0,           "Path", ""},
114         {SUBD_STRAIGHT_CUT,  "STRAIGHT_CUT", 0,   "Straight Cut", ""},
115         {SUBD_FAN,           "FAN", 0,            "Fan", ""},
116         {0, NULL, 0, NULL, NULL}
117 };
118
119 void MESH_OT_subdivide(wmOperatorType *ot)
120 {
121         /* identifiers */
122         ot->name = "Subdivide";
123         ot->description = "Subdivide selected edges";
124         ot->idname = "MESH_OT_subdivide";
125
126         /* api callbacks */
127         ot->exec = subdivide_exec;
128         ot->poll = ED_operator_editmesh;
129
130         /* flags */
131         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
132
133         /* properties */
134         RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10);
135         /* BMESH_TODO, this currently does nothing, just add to stop UI from erroring out! */
136         RNA_def_float(ot->srna, "smoothness", 0.0f, 0.0f, FLT_MAX, "Smoothness", "Smoothness factor (BMESH TODO)", 0.0f, 1.0f);
137
138         RNA_def_boolean(ot->srna, "quadtri", 0, "Quad/Tri Mode", "Tries to prevent ngons");
139         RNA_def_enum(ot->srna, "quadcorner", prop_mesh_cornervert_types, SUBD_STRAIGHT_CUT,
140                      "Quad Corner Type", "How to subdivide quad corners (anything other then Straight Cut will prevent ngons)");
141
142         RNA_def_float(ot->srna, "fractal", 0.0f, 0.0f, FLT_MAX, "Fractal", "Fractal randomness factor", 0.0f, 1000.0f);
143         RNA_def_int(ot->srna, "seed", 0, 0, 10000, "Random Seed", "Seed for the random number generator", 0, 50);
144 }
145
146
147 void EMBM_project_snap_verts(bContext *C, ARegion *ar, Object *obedit, BMEditMesh *em)
148 {
149         BMIter iter;
150         BMVert *eve;
151
152         BM_ITER(eve, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
153                 if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
154                         float mval[2], vec[3], no_dummy[3];
155                         int dist_dummy;
156                         mul_v3_m4v3(vec, obedit->obmat, eve->co);
157                         project_float_noclip(ar, vec, mval);
158                         if (snapObjectsContext(C, mval, &dist_dummy, vec, no_dummy, SNAP_NOT_OBEDIT)) {
159                                 mul_v3_m4v3(eve->co, obedit->imat, vec);
160                         }
161                 }
162         }
163 }
164
165
166 /* individual face extrude */
167 /* will use vertex normals for extrusion directions, so *nor is unaffected */
168 static short EDBM_Extrude_face_indiv(BMEditMesh *em, wmOperator *op, const char hflag, float *UNUSED(nor))
169 {
170         BMOIter siter;
171         BMIter liter;
172         BMFace *f;
173         BMLoop *l;
174         BMOperator bmop;
175
176         EDBM_InitOpf(em, &bmop, op, "extrude_face_indiv faces=%hf", hflag);
177
178         /* deselect original verts */
179         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
180
181         BMO_op_exec(em->bm, &bmop);
182         
183         BMO_ITER(f, &siter, em->bm, &bmop, "faceout", BM_FACE) {
184                 BM_elem_select_set(em->bm, f, TRUE);
185
186                 /* set face vertex normals to face normal */
187                 BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) {
188                         copy_v3_v3(l->v->no, f->no);
189                 }
190         }
191
192         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
193                 return 0;
194         }
195
196         return 's'; // s is shrink/fatten
197 }
198
199 /* extrudes individual edges */
200 static short EDBM_Extrude_edges_indiv(BMEditMesh *em, wmOperator *op, const char hflag, float *UNUSED(nor))
201 {
202         BMOperator bmop;
203
204         EDBM_InitOpf(em, &bmop, op, "extrude_edge_only edges=%he", hflag);
205
206         /* deselect original verts */
207         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
208
209         BMO_op_exec(em->bm, &bmop);
210         BMO_slot_buffer_hflag_enable(em->bm, &bmop, "geomout", BM_ELEM_SELECT, BM_VERT|BM_EDGE, TRUE);
211
212         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
213                 return 0;
214         }
215
216         return 'n'; // n is normal grab
217 }
218
219 /* extrudes individual vertices */
220 static short EDBM_Extrude_verts_indiv(BMEditMesh *em, wmOperator *op, const char hflag, float *UNUSED(nor))
221 {
222         BMOperator bmop;
223
224         EDBM_InitOpf(em, &bmop, op, "extrude_vert_indiv verts=%hv", hflag);
225
226         /* deselect original verts */
227         BMO_slot_buffer_hflag_disable(em->bm, &bmop, "verts", BM_ELEM_SELECT, BM_VERT, TRUE);
228
229         BMO_op_exec(em->bm, &bmop);
230         BMO_slot_buffer_hflag_enable(em->bm, &bmop, "vertout", BM_ELEM_SELECT, BM_VERT, TRUE);
231
232         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
233                 return 0;
234         }
235
236         return 'g'; // g is grab
237 }
238
239 static short EDBM_Extrude_edge(Object *obedit, BMEditMesh *em, const char hflag, float nor[3])
240 {
241         BMesh *bm = em->bm;
242         BMIter iter;
243         BMOIter siter;
244         BMOperator extop;
245         BMEdge *edge;
246         BMFace *f;
247         ModifierData *md;
248         BMHeader *el;
249         
250         BMO_op_init(bm, &extop, "extrudefaceregion");
251         BMO_slot_from_hflag(bm, &extop, "edgefacein",
252                                hflag, BM_VERT|BM_EDGE|BM_FACE);
253
254         /* If a mirror modifier with clipping is on, we need to adjust some 
255          * of the cases above to handle edges on the line of symmetry.
256          */
257         md = obedit->modifiers.first;
258         for (; md; md = md->next) {
259                 if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
260                         MirrorModifierData *mmd = (MirrorModifierData *) md;
261                 
262                         if (mmd->flag & MOD_MIR_CLIPPING) {
263                                 float mtx[4][4];
264                                 if (mmd->mirror_ob) {
265                                         float imtx[4][4];
266                                         invert_m4_m4(imtx, mmd->mirror_ob->obmat);
267                                         mult_m4_m4m4(mtx, imtx, obedit->obmat);
268                                 }
269
270                                 for (edge = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL);
271                                      edge;
272                                      edge = BM_iter_step(&iter))
273                                 {
274                                         if (BM_elem_flag_test(edge, hflag)) {
275                                                 float co1[3], co2[3];
276
277                                                 copy_v3_v3(co1, edge->v1->co);
278                                                 copy_v3_v3(co2, edge->v2->co);
279
280                                                 if (mmd->mirror_ob) {
281                                                         mul_v3_m4v3(co1, mtx, co1);
282                                                         mul_v3_m4v3(co2, mtx, co2);
283                                                 }
284
285                                                 if (mmd->flag & MOD_MIR_AXIS_X) {
286                                                         if ( (fabs(co1[0]) < mmd->tolerance) &&
287                                                                  (fabs(co2[0]) < mmd->tolerance) )
288                                                         {
289                                                                 BMO_slot_map_ptr_insert(bm, &extop, "exclude", edge, NULL);
290                                                         }
291                                                 }
292                                                 if (mmd->flag & MOD_MIR_AXIS_Y) {
293                                                         if ( (fabs(co1[1]) < mmd->tolerance) &&
294                                                                  (fabs(co2[1]) < mmd->tolerance) )
295                                                         {
296                                                                 BMO_slot_map_ptr_insert(bm, &extop, "exclude", edge, NULL);
297                                                         }
298                                                 }
299                                                 if (mmd->flag & MOD_MIR_AXIS_Z) {
300                                                         if ( (fabs(co1[2]) < mmd->tolerance) &&
301                                                                  (fabs(co2[2]) < mmd->tolerance) )
302                                                         {
303                                                                 BMO_slot_map_ptr_insert(bm, &extop, "exclude", edge, NULL);
304                                                         }
305                                                 }
306                                         }
307                                 }
308                         }
309                 }
310         }
311
312         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
313
314         BMO_op_exec(bm, &extop);
315
316         nor[0] = nor[1] = nor[2] = 0.0f;
317         
318         BMO_ITER(el, &siter, bm, &extop, "geomout", BM_ALL) {
319                 BM_elem_select_set(bm, el, TRUE);
320
321                 if (el->htype == BM_FACE) {
322                         f = (BMFace *)el;
323                         add_normal_aligned(nor, f->no);
324                 };
325         }
326
327         normalize_v3(nor);
328
329         BMO_op_finish(bm, &extop);
330
331         if (nor[0] == 0.0f && nor[1] == 0.0f && nor[2] == 0.0f) return 'g'; // grab
332         return 'n'; // normal constraint 
333
334 }
335 static short EDBM_Extrude_vert(Object *obedit, BMEditMesh *em, const char hflag, float *nor)
336 {
337         BMIter iter;
338         BMEdge *eed;
339                 
340         /* ensure vert flags are consistent for edge selections */
341         eed = BM_iter_new(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
342         for ( ; eed; eed = BM_iter_step(&iter)) {
343                 if (BM_elem_flag_test(eed, hflag)) {
344                         if (hflag & BM_ELEM_SELECT) {
345                                 BM_elem_select_set(em->bm, eed->v1, TRUE);
346                                 BM_elem_select_set(em->bm, eed->v2, TRUE);
347                         }
348
349                         BM_elem_flag_enable(eed->v1, hflag & ~BM_ELEM_SELECT);
350                         BM_elem_flag_enable(eed->v2, hflag & ~BM_ELEM_SELECT);
351                 }
352                 else {
353                         if (BM_elem_flag_test(eed->v1, hflag) && BM_elem_flag_test(eed->v2, hflag)) {
354                                 if (hflag & BM_ELEM_SELECT) {
355                                         BM_elem_select_set(em->bm, eed, TRUE);
356                                 }
357
358                                 BM_elem_flag_enable(eed, hflag & ~BM_ELEM_SELECT);
359                         }
360                 }
361         }
362
363         return EDBM_Extrude_edge(obedit, em, hflag, nor);
364 }
365
366 static int extrude_repeat_mesh(bContext *C, wmOperator *op)
367 {
368         Object *obedit = CTX_data_edit_object(C);
369         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
370         RegionView3D *rv3d = CTX_wm_region_view3d(C);
371                 
372         int steps = RNA_int_get(op->ptr,"steps");
373         
374         float offs = RNA_float_get(op->ptr,"offset");
375         float dvec[3], tmat[3][3], bmat[3][3], nor[3] = {0.0, 0.0, 0.0};
376         short a;
377
378         /* dvec */
379         normalize_v3_v3(dvec, rv3d->persinv[2]);
380         mul_v3_fl(dvec, offs);
381
382         /* base correction */
383         copy_m3_m4(bmat, obedit->obmat);
384         invert_m3_m3(tmat, bmat);
385         mul_m3_v3(tmat, dvec);
386
387         for (a = 0; a < steps; a++) {
388                 EDBM_Extrude_edge(obedit, em, BM_ELEM_SELECT, nor);
389                 //BMO_op_callf(em->bm, "extrudefaceregion edgefacein=%hef", BM_ELEM_SELECT);
390                 BMO_op_callf(em->bm, "translate vec=%v verts=%hv", (float *)dvec, BM_ELEM_SELECT);
391                 //extrudeflag(obedit, em, SELECT, nor);
392                 //translateflag(em, SELECT, dvec);
393         }
394         
395         EDBM_RecalcNormals(em);
396
397         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
398         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
399
400         return OPERATOR_FINISHED;
401 }
402
403 void MESH_OT_extrude_repeat(wmOperatorType *ot)
404 {
405         /* identifiers */
406         ot->name = "Extrude Repeat Mesh";
407         ot->description = "Extrude selected vertices, edges or faces repeatedly";
408         ot->idname = "MESH_OT_extrude_repeat";
409         
410         /* api callbacks */
411         ot->exec = extrude_repeat_mesh;
412         ot->poll = ED_operator_editmesh;
413         
414         /* flags */
415         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
416         
417         /* props */
418         RNA_def_float(ot->srna, "offset", 2.0f, 0.0f, 100.0f, "Offset", "", 0.0f, FLT_MAX);
419         RNA_def_int(ot->srna, "steps", 10, 0, 180, "Steps", "", 0, INT_MAX);
420 }
421
422 /* generic extern called extruder */
423 static int EDBM_Extrude_Mesh(Scene *scene, Object *obedit, BMEditMesh *em, wmOperator *op, float *norin)
424 {
425         short nr, transmode = 0;
426         float stacknor[3] = {0.0f, 0.0f, 0.0f};
427         float *nor = norin ? norin : stacknor;
428
429         nor[0] = nor[1] = nor[2] = 0.0f;
430
431         if (em->selectmode & SCE_SELECT_VERTEX) {
432                 if (em->bm->totvertsel == 0) nr = 0;
433                 else if (em->bm->totvertsel == 1) nr = 4;
434                 else if (em->bm->totedgesel == 0) nr = 4;
435                 else if (em->bm->totfacesel == 0)
436                         nr = 3; // pupmenu("Extrude %t|Only Edges%x3|Only Vertices%x4");
437                 else if (em->bm->totfacesel == 1)
438                         nr = 1; // pupmenu("Extrude %t|Region %x1|Only Edges%x3|Only Vertices%x4");
439                 else 
440                         nr = 1; // pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3|Only Vertices%x4");
441         }
442         else if (em->selectmode & SCE_SELECT_EDGE) {
443                 if (em->bm->totedgesel == 0) nr = 0;
444                 
445                 nr = 1;
446                 /* else if (em->totedgesel == 1) nr = 3;
447                 else if (em->totfacesel == 0) nr = 3;
448                 else if (em->totfacesel == 1)
449                         nr = 1; // pupmenu("Extrude %t|Region %x1|Only Edges%x3");
450                 else
451                         nr = 1; // pupmenu("Extrude %t|Region %x1||Individual Faces %x2|Only Edges%x3");
452                 */
453         }
454         else {
455                 if (em->bm->totfacesel == 0) nr = 0;
456                 else if (em->bm->totfacesel == 1) nr = 1;
457                 else
458                         nr = 1; // pupmenu("Extrude %t|Region %x1||Individual Faces %x2");
459         }
460
461         if (nr < 1) return 'g';
462
463         if (nr == 1 && em->selectmode & SCE_SELECT_VERTEX)
464                 transmode = EDBM_Extrude_vert(obedit, em, BM_ELEM_SELECT, nor);
465         else if (nr == 1) transmode = EDBM_Extrude_edge(obedit, em, BM_ELEM_SELECT, nor);
466         else if (nr == 4) transmode = EDBM_Extrude_verts_indiv(em, op, BM_ELEM_SELECT, nor);
467         else if (nr == 3) transmode = EDBM_Extrude_edges_indiv(em, op, BM_ELEM_SELECT, nor);
468         else transmode = EDBM_Extrude_face_indiv(em, op, BM_ELEM_SELECT, nor);
469         
470         if (transmode == 0) {
471                 BKE_report(op->reports, RPT_ERROR, "Not a valid selection for extrude");
472         }
473         else {
474                 
475                         /* We need to force immediate calculation here because 
476                         * transform may use derived objects (which are now stale).
477                         *
478                         * This shouldn't be necessary, derived queries should be
479                         * automatically building this data if invalid. Or something.
480                         */
481 //              DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
482                 object_handle_update(scene, obedit);
483
484                 /* individual faces? */
485 //              BIF_TransformSetUndo("Extrude");
486                 if (nr == 2) {
487 //                      initTransform(TFM_SHRINKFATTEN, CTX_NO_PET|CTX_NO_MIRROR);
488 //                      Transform();
489                 }
490                 else {
491 //                      initTransform(TFM_TRANSLATION, CTX_NO_PET|CTX_NO_MIRROR);
492                         if (transmode == 'n') {
493                                 mul_m4_v3(obedit->obmat, nor);
494                                 sub_v3_v3v3(nor, nor, obedit->obmat[3]);
495 //                              BIF_setSingleAxisConstraint(nor, "along normal");
496                         }
497 //                      Transform();
498                 }
499         }
500         
501         return transmode;
502 }
503
504 /* extrude without transform */
505 static int mesh_extrude_region_exec(bContext *C, wmOperator *op)
506 {
507         Scene *scene = CTX_data_scene(C);
508         Object *obedit = CTX_data_edit_object(C);
509         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
510         
511         EDBM_Extrude_Mesh(scene, obedit, em, op, NULL);
512
513         /* This normally happens when pushing undo but modal operators
514          * like this one don't push undo data until after modal mode is
515          * done.*/
516         EDBM_RecalcNormals(em);
517         BMEdit_RecalcTesselation(em);
518
519         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
520         
521         return OPERATOR_FINISHED;
522 }
523
524 void MESH_OT_extrude_region(wmOperatorType *ot)
525 {
526         /* identifiers */
527         ot->name = "Extrude Region";
528         ot->idname = "MESH_OT_extrude_region";
529         
530         /* api callbacks */
531         //ot->invoke = mesh_extrude_region_invoke;
532         ot->exec = mesh_extrude_region_exec;
533         ot->poll = ED_operator_editmesh;
534         
535         /* flags */
536         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
537
538         RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
539 }
540
541 static int mesh_extrude_verts_exec(bContext *C, wmOperator *op)
542 {
543         Object *obedit = CTX_data_edit_object(C);
544         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
545         float nor[3];
546
547         EDBM_Extrude_verts_indiv(em, op, BM_ELEM_SELECT, nor);
548         
549         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
550         
551         return OPERATOR_FINISHED;
552 }
553
554 void MESH_OT_extrude_verts_indiv(wmOperatorType *ot)
555 {
556         /* identifiers */
557         ot->name = "Extrude Only Vertices";
558         ot->idname = "MESH_OT_extrude_verts_indiv";
559         
560         /* api callbacks */
561         ot->exec = mesh_extrude_verts_exec;
562         ot->poll = ED_operator_editmesh;
563         
564         /* flags */
565         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
566
567         /* to give to transform */
568         RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
569 }
570
571 static int mesh_extrude_edges_exec(bContext *C, wmOperator *op)
572 {
573         Object *obedit = CTX_data_edit_object(C);
574         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
575         float nor[3];
576
577         EDBM_Extrude_edges_indiv(em, op, BM_ELEM_SELECT, nor);
578         
579         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
580         
581         return OPERATOR_FINISHED;
582 }
583
584 void MESH_OT_extrude_edges_indiv(wmOperatorType *ot)
585 {
586         /* identifiers */
587         ot->name = "Extrude Only Edges";
588         ot->idname = "MESH_OT_extrude_edges_indiv";
589         
590         /* api callbacks */
591         ot->exec = mesh_extrude_edges_exec;
592         ot->poll = ED_operator_editmesh;
593         
594         /* flags */
595         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
596
597         /* to give to transform */
598         RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
599 }
600
601 static int mesh_extrude_faces_exec(bContext *C, wmOperator *op)
602 {
603         Object *obedit = CTX_data_edit_object(C);
604         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
605         float nor[3];
606
607         EDBM_Extrude_face_indiv(em, op, BM_ELEM_SELECT, nor);
608         
609         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
610         
611         return OPERATOR_FINISHED;
612 }
613
614 void MESH_OT_extrude_faces_indiv(wmOperatorType *ot)
615 {
616         /* identifiers */
617         ot->name = "Extrude Individual Faces";
618         ot->idname = "MESH_OT_extrude_faces_indiv";
619         
620         /* api callbacks */
621         ot->exec = mesh_extrude_faces_exec;
622         ot->poll = ED_operator_editmesh;
623         
624         /* flags */
625         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
626
627         RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
628 }
629
630 /* ******************** (de)select all operator **************** */
631
632 void EDBM_toggle_select_all(BMEditMesh *em) /* exported for UV */
633 {
634         if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel)
635                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
636         else 
637                 EDBM_flag_enable_all(em, BM_ELEM_SELECT);
638 }
639
640 static int mesh_select_all_exec(bContext *C, wmOperator *op)
641 {
642         Object *obedit = CTX_data_edit_object(C);
643         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
644         int action = RNA_enum_get(op->ptr, "action");
645         
646         switch (action) {
647         case SEL_TOGGLE:
648                 EDBM_toggle_select_all(em);
649                 break;
650         case SEL_SELECT:
651                 EDBM_flag_enable_all(em, BM_ELEM_SELECT);
652                 break;
653         case SEL_DESELECT:
654                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
655                 break;
656         case SEL_INVERT:
657                 EDBM_select_swap(em);
658                 break;
659         }
660         
661         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
662
663         return OPERATOR_FINISHED;
664 }
665
666 void MESH_OT_select_all(wmOperatorType *ot)
667 {
668         /* identifiers */
669         ot->name = "Select/Deselect All";
670         ot->idname = "MESH_OT_select_all";
671         ot->description = "(de)select all vertices, edges or faces";
672         
673         /* api callbacks */
674         ot->exec = mesh_select_all_exec;
675         ot->poll = ED_operator_editmesh;
676         
677         /* flags */
678         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
679
680         WM_operator_properties_select_all(ot);
681 }
682
683 static int mesh_faces_select_interior_exec(bContext *C, wmOperator *UNUSED(op))
684 {
685         Object *obedit = CTX_data_edit_object(C);
686         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
687
688         if (EDBM_select_interior_faces(em)) {
689                 WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
690
691                 return OPERATOR_FINISHED;
692         }
693         else {
694                 return OPERATOR_CANCELLED;
695         }
696
697 }
698
699 void MESH_OT_select_interior_faces(wmOperatorType *ot)
700 {
701         /* identifiers */
702         ot->name = "Select Interior Faces";
703         ot->idname = "MESH_OT_select_interior_faces";
704         ot->description = "Select faces where all edges have more than 2 face users";
705
706         /* api callbacks */
707         ot->exec = mesh_faces_select_interior_exec;
708         ot->poll = ED_operator_editmesh;
709
710         /* flags */
711         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
712 }
713
714 /* *************** add-click-mesh (extrude) operator ************** */
715 /* in trunk see: 'editmesh_add.c' */
716 static int dupli_extrude_cursor(bContext *C, wmOperator *op, wmEvent *event)
717 {
718         ViewContext vc;
719         BMVert *v1;
720         BMIter iter;
721         float min[3], max[3];
722         int done = 0;
723         short use_proj;
724         
725         em_setup_viewcontext(C, &vc);
726         
727         use_proj = (vc.scene->toolsettings->snap_flag & SCE_SNAP) &&    (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE);
728
729         INIT_MINMAX(min, max);
730         
731         BM_ITER(v1, &iter, vc.em->bm, BM_VERTS_OF_MESH, NULL) {
732                 if (BM_elem_flag_test(v1, BM_ELEM_SELECT)) {
733                         DO_MINMAX(v1->co, min, max);
734                         done = 1;
735                 }
736         }
737
738         /* call extrude? */
739         if (done) {
740                 const short rot_src = RNA_boolean_get(op->ptr, "rotate_source");
741                 BMEdge *eed;
742                 float vec[3], cent[3], mat[3][3];
743                 float nor[3] = {0.0, 0.0, 0.0};
744
745                 /* 2D normal calc */
746                 float mval_f[2];
747
748                 mval_f[0] = (float)event->mval[0];
749                 mval_f[1] = (float)event->mval[1];
750
751                 /* check for edges that are half selected, use for rotation */
752                 done = 0;
753                 BM_ITER(eed, &iter, vc.em->bm, BM_EDGES_OF_MESH, NULL) {
754                         if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
755                                 float co1[3], co2[3];
756                                 mul_v3_m4v3(co1, vc.obedit->obmat, eed->v1->co);
757                                 mul_v3_m4v3(co2, vc.obedit->obmat, eed->v2->co);
758                                 project_float_noclip(vc.ar, co1, co1);
759                                 project_float_noclip(vc.ar, co2, co2);
760
761                                 /* 2D rotate by 90d while adding.
762                                  *  (x, y) = (y, -x)
763                                  *
764                                  * accumulate the screenspace normal in 2D,
765                                  * with screenspace edge length weighting the result. */
766                                 if (line_point_side_v2(co1, co2, mval_f) >= 0.0f) {
767                                         nor[0] +=  (co1[1] - co2[1]);
768                                         nor[1] += -(co1[0] - co2[0]);
769                                 }
770                                 else {
771                                         nor[0] +=  (co2[1] - co1[1]);
772                                         nor[1] += -(co2[0] - co1[0]);
773                                 }
774                         }
775                         done = 1;
776                 }
777
778                 if (done) {
779                         float view_vec[3], cross[3];
780
781                         /* convert the 2D nomal into 3D */
782                         mul_mat3_m4_v3(vc.rv3d->viewinv, nor); /* worldspace */
783                         mul_mat3_m4_v3(vc.obedit->imat, nor); /* local space */
784
785                         /* correct the normal to be aligned on the view plane */
786                         copy_v3_v3(view_vec, vc.rv3d->viewinv[2]);
787                         mul_mat3_m4_v3(vc.obedit->imat, view_vec);
788                         cross_v3_v3v3(cross, nor, view_vec);
789                         cross_v3_v3v3(nor, view_vec, cross);
790                         normalize_v3(nor);
791                 }
792                 
793                 /* center */
794                 mid_v3_v3v3(cent, min, max);
795                 copy_v3_v3(min, cent);
796
797                 mul_m4_v3(vc.obedit->obmat, min);       // view space
798                 view3d_get_view_aligned_coordinate(&vc, min, event->mval, TRUE);
799                 mul_m4_v3(vc.obedit->imat, min); // back in object space
800
801                 sub_v3_v3(min, cent);
802                 
803                 /* calculate rotation */
804                 unit_m3(mat);
805                 if (done) {
806                         float dot;
807
808                         copy_v3_v3(vec, min);
809                         normalize_v3(vec);
810                         dot = dot_v3v3(vec, nor);
811
812                         if (fabsf(dot) < 0.999f) {
813                                 float cross[3], si, q1[4];
814
815                                 cross_v3_v3v3(cross, nor, vec);
816                                 normalize_v3(cross);
817                                 dot = 0.5f * saacos(dot);
818
819                                 /* halve the rotation if its applied twice */
820                                 if (rot_src) dot *= 0.5f;
821
822                                 si = sinf(dot);
823                                 q1[0] = cosf(dot);
824                                 q1[1] = cross[0] * si;
825                                 q1[2] = cross[1] * si;
826                                 q1[3] = cross[2] * si;
827                                 normalize_qt(q1);
828                                 quat_to_mat3(mat, q1);
829                         }
830                 }
831                 
832                 if (rot_src) {
833                         EDBM_CallOpf(vc.em, op, "rotate verts=%hv cent=%v mat=%m3",
834                                 BM_ELEM_SELECT, cent, mat);
835
836                         /* also project the source, for retopo workflow */
837                         if (use_proj)
838                                 EMBM_project_snap_verts(C, vc.ar, vc.obedit, vc.em);
839                 }
840
841                 EDBM_Extrude_edge(vc.obedit, vc.em, BM_ELEM_SELECT, nor);
842                 EDBM_CallOpf(vc.em, op, "rotate verts=%hv cent=%v mat=%m3",
843                         BM_ELEM_SELECT, cent, mat);
844                 EDBM_CallOpf(vc.em, op, "translate verts=%hv vec=%v",
845                         BM_ELEM_SELECT, min);
846         }
847         else {
848                 float *curs = give_cursor(vc.scene, vc.v3d);
849                 BMOperator bmop;
850                 BMOIter oiter;
851                 
852                 copy_v3_v3(min, curs);
853                 view3d_get_view_aligned_coordinate(&vc, min, event->mval, 0);
854
855                 invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
856                 mul_m4_v3(vc.obedit->imat, min); // back in object space
857                 
858                 EDBM_InitOpf(vc.em, &bmop, op, "makevert co=%v", min);
859                 BMO_op_exec(vc.em->bm, &bmop);
860
861                 BMO_ITER(v1, &oiter, vc.em->bm, &bmop, "newvertout", BM_VERT) {
862                         BM_elem_select_set(vc.em->bm, v1, TRUE);
863                 }
864
865                 if (!EDBM_FinishOp(vc.em, &bmop, op, TRUE)) {
866                         return OPERATOR_CANCELLED;
867                 }
868         }
869
870         if (use_proj)
871                 EMBM_project_snap_verts(C, vc.ar, vc.obedit, vc.em);
872
873         /* This normally happens when pushing undo but modal operators
874          * like this one don't push undo data until after modal mode is
875          * done. */
876         EDBM_RecalcNormals(vc.em);
877         BMEdit_RecalcTesselation(vc.em);
878
879         WM_event_add_notifier(C, NC_GEOM|ND_DATA, vc.obedit->data);
880         DAG_id_tag_update(vc.obedit->data, OB_RECALC_DATA);
881         
882         return OPERATOR_FINISHED;
883 }
884
885 void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot)
886 {
887         /* identifiers */
888         ot->name = "Duplicate or Extrude at 3D Cursor";
889         ot->idname = "MESH_OT_dupli_extrude_cursor";
890         
891         /* api callbacks */
892         ot->invoke = dupli_extrude_cursor;
893         ot->description = "Duplicate and extrude selected vertices, edges or faces towards the mouse cursor";
894         ot->poll = ED_operator_editmesh;
895         
896         /* flags */
897         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
898
899         RNA_def_boolean(ot->srna, "rotate_source", 1, "Rotate Source", "Rotate initial selection giving better shape");
900 }
901
902 static int delete_mesh(bContext *C, Object *obedit, wmOperator *op, int event, Scene *UNUSED(scene))
903 {
904         BMEditMesh *bem = ((Mesh *)obedit->data)->edit_btmesh;
905         
906         if (event < 1) return OPERATOR_CANCELLED;
907
908         if (event == 10) {
909                 //"Erase Vertices";
910
911                 if (!EDBM_CallOpf(bem, op, "del geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS))
912                         return OPERATOR_CANCELLED;
913         } 
914         else if (event == 11) {
915                 //"Edge Loop"
916                 if (!EDBM_CallOpf(bem, op, "dissolveedgeloop edges=%he", BM_ELEM_SELECT))
917                         return OPERATOR_CANCELLED;
918         }
919         else if (event == 7) {
920                 int use_verts = RNA_boolean_get(op->ptr, "use_verts");
921                 //"Dissolve"
922                 if (bem->selectmode & SCE_SELECT_FACE) {
923                         if (!EDBM_CallOpf(bem, op, "dissolvefaces faces=%hf use_verts=%b", BM_ELEM_SELECT, use_verts))
924                                 return OPERATOR_CANCELLED;
925                 }
926                 else if (bem->selectmode & SCE_SELECT_EDGE) {
927                         if (!EDBM_CallOpf(bem, op, "dissolveedges edges=%he use_verts=%b", BM_ELEM_SELECT, use_verts))
928                                 return OPERATOR_CANCELLED;
929                 }
930                 else if (bem->selectmode & SCE_SELECT_VERTEX) {
931                         if (!EDBM_CallOpf(bem, op, "dissolveverts verts=%hv", BM_ELEM_SELECT))
932                                 return OPERATOR_CANCELLED;
933                 }
934         }
935         else if (event == 4) {
936                 //Edges and Faces
937                 if (!EDBM_CallOpf(bem, op, "del geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES))
938                         return OPERATOR_CANCELLED;
939         } 
940         else if (event == 1) {
941                 //"Erase Edges"
942                 if (!EDBM_CallOpf(bem, op, "del geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES))
943                         return OPERATOR_CANCELLED;
944         }
945         else if (event == 2) {
946                 //"Erase Faces";
947                 if (!EDBM_CallOpf(bem, op, "del geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES))
948                         return OPERATOR_CANCELLED;
949         }
950         else if (event == 5) {
951                 //"Erase Only Faces";
952                 if (!EDBM_CallOpf(bem, op, "del geom=%hf context=%i",
953                                   BM_ELEM_SELECT, DEL_ONLYFACES))
954                         return OPERATOR_CANCELLED;
955         }
956         
957         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
958         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
959
960         return OPERATOR_FINISHED;
961 }
962
963 /* Note, these values must match delete_mesh() event values */
964 static EnumPropertyItem prop_mesh_delete_types[] = {
965         {7, "DISSOLVE",         0, "Dissolve", ""},
966         {12, "COLLAPSE", 0, "Collapse", ""},
967         {10,"VERT",             0, "Vertices", ""},
968         {1, "EDGE",             0, "Edges", ""},
969         {2, "FACE",             0, "Faces", ""},
970         {11, "EDGE_LOOP", 0, "Edge Loop", ""},
971         {4, "EDGE_FACE", 0, "Edges & Faces", ""},
972         {5, "ONLY_FACE", 0, "Only Faces", ""},
973         {0, NULL, 0, NULL, NULL}
974 };
975
976 static int delete_mesh_exec(bContext *C, wmOperator *op)
977 {
978         Object *obedit = CTX_data_edit_object(C);
979         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
980         Scene *scene = CTX_data_scene(C);
981         int type = RNA_enum_get(op->ptr, "type");
982         
983         if (type != 12) {
984                 if (delete_mesh(C, obedit, op, type, scene) == OPERATOR_CANCELLED)
985                         return OPERATOR_CANCELLED;
986                 EDBM_flag_disable_all(em, BM_ELEM_SELECT);
987         }
988         else {
989                 if (!EDBM_CallOpf(em, op, "collapse edges=%he", BM_ELEM_SELECT))
990                         return OPERATOR_CANCELLED;
991                 DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
992         }
993
994         WM_event_add_notifier(C, NC_GEOM|ND_DATA|ND_SELECT, obedit);
995         
996         return OPERATOR_FINISHED;
997 }
998
999 void MESH_OT_delete(wmOperatorType *ot)
1000 {
1001         /* identifiers */
1002         ot->name = "Delete";
1003         ot->description = "Delete selected vertices, edges or faces";
1004         ot->idname = "MESH_OT_delete";
1005         
1006         /* api callbacks */
1007         ot->invoke = WM_menu_invoke;
1008         ot->exec = delete_mesh_exec;
1009         
1010         ot->poll = ED_operator_editmesh;
1011         
1012         /* flags */
1013         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1014
1015         /* props */
1016         ot->prop = RNA_def_enum(ot->srna, "type", prop_mesh_delete_types, 10, "Type", "Method used for deleting mesh data");
1017
1018         /* TODO, move dissolve into its own operator so this doesnt confuse non-dissolve options */
1019         RNA_def_boolean(ot->srna, "use_verts", 0, "Dissolve Verts",
1020                         "When dissolving faaces/edges, also dissolve remaining vertices");
1021 }
1022
1023
1024 static int addedgeface_mesh_exec(bContext *C, wmOperator *op)
1025 {
1026         BMOperator bmop;
1027         Object *obedit = CTX_data_edit_object(C);
1028         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
1029         
1030         if (!EDBM_InitOpf(em, &bmop, op, "contextual_create geom=%hfev", BM_ELEM_SELECT))
1031                 return OPERATOR_CANCELLED;
1032         
1033         BMO_op_exec(em->bm, &bmop);
1034         BMO_slot_buffer_hflag_enable(em->bm, &bmop, "faceout", BM_ELEM_SELECT, BM_FACE, TRUE);
1035
1036         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
1037                 return OPERATOR_CANCELLED;
1038         }
1039
1040         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit);
1041         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1042         
1043         return OPERATOR_FINISHED;
1044 }
1045
1046 void MESH_OT_edge_face_add(wmOperatorType *ot)
1047 {
1048         /* identifiers */
1049         ot->name = "Make Edge/Face";
1050         ot->description = "Add an edge or face to selected";
1051         ot->idname = "MESH_OT_edge_face_add";
1052         
1053         /* api callbacks */
1054         ot->exec = addedgeface_mesh_exec;
1055         ot->poll = ED_operator_editmesh;
1056         
1057         /* flags */
1058         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1059 }
1060
1061 /* ************************* SEAMS AND EDGES **************** */
1062
1063 static int editbmesh_mark_seam(bContext *C, wmOperator *op)
1064 {
1065         Object *obedit = CTX_data_edit_object(C);
1066         Mesh *me = ((Mesh *)obedit->data);
1067         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
1068         BMesh *bm = em->bm;
1069         BMEdge *eed;
1070         BMIter iter;
1071         int clear = RNA_boolean_get(op->ptr, "clear");
1072         
1073         /* auto-enable seams drawing */
1074         if (clear == 0) {
1075                 me->drawflag |= ME_DRAWSEAMS;
1076         }
1077
1078         if (clear) {
1079                 BM_ITER(eed, &iter, bm, BM_EDGES_OF_MESH, NULL) {
1080                         if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN))
1081                                 continue;
1082                         
1083                         BM_elem_flag_disable(eed, BM_ELEM_SEAM);
1084                 }
1085         }
1086         else {
1087                 BM_ITER(eed, &iter, bm, BM_EDGES_OF_MESH, NULL) {
1088                         if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN))
1089                                 continue;
1090                         BM_elem_flag_enable(eed, BM_ELEM_SEAM);
1091                 }
1092         }
1093
1094         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1095         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1096
1097         return OPERATOR_FINISHED;
1098 }
1099
1100 void MESH_OT_mark_seam(wmOperatorType *ot)
1101 {
1102         /* identifiers */
1103         ot->name = "Mark Seam";
1104         ot->idname = "MESH_OT_mark_seam";
1105         ot->description = "(un)mark selected edges as a seam";
1106         
1107         /* api callbacks */
1108         ot->exec = editbmesh_mark_seam;
1109         ot->poll = ED_operator_editmesh;
1110         
1111         /* flags */
1112         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1113         
1114         RNA_def_boolean(ot->srna, "clear", 0, "Clear", "");
1115 }
1116
1117 static int editbmesh_mark_sharp(bContext *C, wmOperator *op)
1118 {
1119         Object *obedit = CTX_data_edit_object(C);
1120         Mesh *me = ((Mesh *)obedit->data);
1121         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
1122         BMesh *bm = em->bm;
1123         BMEdge *eed;
1124         BMIter iter;
1125         int clear = RNA_boolean_get(op->ptr, "clear");
1126
1127         /* auto-enable sharp edge drawing */
1128         if (clear == 0) {
1129                 me->drawflag |= ME_DRAWSHARP;
1130         }
1131
1132         if (!clear) {
1133                 BM_ITER(eed, &iter, bm, BM_EDGES_OF_MESH, NULL) {
1134                         if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN))
1135                                 continue;
1136                         
1137                         BM_elem_flag_disable(eed, BM_ELEM_SMOOTH);
1138                 }
1139         }
1140         else {
1141                 BM_ITER(eed, &iter, bm, BM_EDGES_OF_MESH, NULL) {
1142                         if (!BM_elem_flag_test(eed, BM_ELEM_SELECT) || BM_elem_flag_test(eed, BM_ELEM_HIDDEN))
1143                                 continue;
1144                         
1145                         BM_elem_flag_enable(eed, BM_ELEM_SMOOTH);
1146                 }
1147         }
1148
1149
1150         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1151         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1152
1153         return OPERATOR_FINISHED;
1154 }
1155
1156 void MESH_OT_mark_sharp(wmOperatorType *ot)
1157 {
1158         /* identifiers */
1159         ot->name = "Mark Sharp";
1160         ot->idname = "MESH_OT_mark_sharp";
1161         ot->description = "(un)mark selected edges as sharp";
1162         
1163         /* api callbacks */
1164         ot->exec = editbmesh_mark_sharp;
1165         ot->poll = ED_operator_editmesh;
1166         
1167         /* flags */
1168         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1169         
1170         RNA_def_boolean(ot->srna, "clear", 0, "Clear", "");
1171 }
1172
1173
1174 static int editbmesh_vert_connect(bContext *C, wmOperator *op)
1175 {
1176         Object *obedit = CTX_data_edit_object(C);
1177         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
1178         BMesh *bm = em->bm;
1179         BMOperator bmop;
1180         int len = 0;
1181         
1182         if (!EDBM_InitOpf(em, &bmop, op, "connectverts verts=%hv", BM_ELEM_SELECT)) {
1183                 return OPERATOR_CANCELLED;
1184         }
1185         BMO_op_exec(bm, &bmop);
1186         len = BMO_slot_get(&bmop, "edgeout")->len;
1187         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
1188                 return OPERATOR_CANCELLED;
1189         }
1190         
1191         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1192         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1193
1194         return len ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1195 }
1196
1197 void MESH_OT_vert_connect(wmOperatorType *ot)
1198 {
1199         /* identifiers */
1200         ot->name = "Vertex Connect";
1201         ot->idname = "MESH_OT_vert_connect";
1202         
1203         /* api callbacks */
1204         ot->exec = editbmesh_vert_connect;
1205         ot->poll = ED_operator_editmesh;
1206         
1207         /* flags */
1208         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1209 }
1210
1211 static int editbmesh_edge_split(bContext *C, wmOperator *op)
1212 {
1213         Object *obedit = CTX_data_edit_object(C);
1214         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
1215         BMesh *bm = em->bm;
1216         BMOperator bmop;
1217         int len = 0;
1218         
1219         if (!EDBM_InitOpf(em, &bmop, op, "edgesplit edges=%he numcuts=%i",
1220                           BM_ELEM_SELECT, RNA_int_get(op->ptr,"number_cuts")))
1221         {
1222                 return OPERATOR_CANCELLED;
1223         }
1224         BMO_op_exec(bm, &bmop);
1225         len = BMO_slot_get(&bmop, "outsplit")->len;
1226         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
1227                 return OPERATOR_CANCELLED;
1228         }
1229         
1230         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1231         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1232
1233         return len ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1234 }
1235
1236 void MESH_OT_edge_split(wmOperatorType *ot)
1237 {
1238         /* identifiers */
1239         ot->name = "Edge Split";
1240         ot->idname = "MESH_OT_edge_split";
1241         
1242         /* api callbacks */
1243         ot->exec = editbmesh_edge_split;
1244         ot->poll = ED_operator_editmesh;
1245         
1246         /* flags */
1247         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1248
1249         RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, INT_MAX);
1250 }
1251
1252 /****************** add duplicate operator ***************/
1253
1254 static int mesh_duplicate_exec(bContext *C, wmOperator *op)
1255 {
1256         Object *ob = CTX_data_edit_object(C);
1257         BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
1258         BMOperator bmop;
1259
1260         EDBM_InitOpf(em, &bmop, op, "dupe geom=%hvef", BM_ELEM_SELECT);
1261         
1262         BMO_op_exec(em->bm, &bmop);
1263         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1264
1265         BMO_slot_buffer_hflag_enable(em->bm, &bmop, "newout", BM_ELEM_SELECT, BM_ALL, TRUE);
1266
1267         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
1268                 return OPERATOR_CANCELLED;
1269         }
1270
1271         DAG_id_tag_update(ob->data, OB_RECALC_DATA);
1272         WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
1273         
1274         return OPERATOR_FINISHED;
1275 }
1276
1277 static int mesh_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1278 {
1279         WM_cursor_wait(1);
1280         mesh_duplicate_exec(C, op);
1281         WM_cursor_wait(0);
1282         
1283         return OPERATOR_FINISHED;
1284 }
1285
1286 void MESH_OT_duplicate(wmOperatorType *ot)
1287 {
1288         /* identifiers */
1289         ot->name = "Duplicate";
1290         ot->description = "Duplicate selected vertices, edges or faces";
1291         ot->idname = "MESH_OT_duplicate";
1292         
1293         /* api callbacks */
1294         ot->invoke = mesh_duplicate_invoke;
1295         ot->exec = mesh_duplicate_exec;
1296         
1297         ot->poll = ED_operator_editmesh;
1298         
1299         /* to give to transform */
1300         RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX);
1301 }
1302
1303 static int flip_normals(bContext *C, wmOperator *op)
1304 {
1305         Object *obedit = CTX_data_edit_object(C);
1306         BMEditMesh *em = (((Mesh *)obedit->data))->edit_btmesh;
1307         
1308         if (!EDBM_CallOpf(em, op, "reversefaces faces=%hf", BM_ELEM_SELECT))
1309                 return OPERATOR_CANCELLED;
1310         
1311         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1312         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1313
1314         return OPERATOR_FINISHED;
1315 }
1316
1317 void MESH_OT_flip_normals(wmOperatorType *ot)
1318 {
1319         /* identifiers */
1320         ot->name = "Flip Normals";
1321         ot->description = "Flip the direction of selected face's vertex and face normals";
1322         ot->idname = "MESH_OT_flip_normals";
1323         
1324         /* api callbacks */
1325         ot->exec = flip_normals;
1326         ot->poll = ED_operator_editmesh;
1327         
1328         /* flags */
1329         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1330 }
1331
1332 static const EnumPropertyItem direction_items[] = {
1333         {DIRECTION_CW, "CW", 0, "Clockwise", ""},
1334         {DIRECTION_CCW, "CCW", 0, "Counter Clockwise", ""},
1335         {0, NULL, 0, NULL, NULL}};
1336
1337 /* only accepts 1 selected edge, or 2 selected faces */
1338 static int edge_rotate_selected(bContext *C, wmOperator *op)
1339 {
1340         Object *obedit = CTX_data_edit_object(C);
1341         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
1342         BMOperator bmop;
1343         BMEdge *eed;
1344         BMIter iter;
1345         const int do_ccw = RNA_enum_get(op->ptr, "direction") == 1;
1346         int do_deselect = FALSE; /* do we deselect */
1347         
1348         if (!(em->bm->totfacesel == 2 || em->bm->totedgesel == 1)) {
1349                 BKE_report(op->reports, RPT_ERROR, "Select one edge or two adjacent faces");
1350                 return OPERATOR_CANCELLED;
1351         }
1352
1353         /* first see if we have two adjacent faces */
1354         BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
1355                 if (BM_edge_face_count(eed) == 2) {
1356                         if ((BM_elem_flag_test(eed->l->f, BM_ELEM_SELECT) && BM_elem_flag_test(eed->l->radial_next->f, BM_ELEM_SELECT))
1357                                  && !(BM_elem_flag_test(eed->l->f, BM_ELEM_HIDDEN) || BM_elem_flag_test(eed->l->radial_next->f, BM_ELEM_HIDDEN)))
1358                         {
1359                                 break;
1360                         }
1361                 }
1362         }
1363         
1364         /* ok, we don't have two adjacent faces, but we do have two selected ones.
1365          * that's an error condition.*/
1366         if (!eed && em->bm->totfacesel == 2) {
1367                 BKE_report(op->reports, RPT_ERROR, "Select one edge or two adjacent faces");
1368                 return OPERATOR_CANCELLED;
1369         }
1370
1371         if (!eed) {
1372                 BM_ITER(eed, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
1373                         if (BM_elem_flag_test(eed, BM_ELEM_SELECT) && !BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
1374                                 /* de-select the edge before */
1375                                 do_deselect = TRUE;
1376                                 break;
1377                         }
1378                 }
1379         }
1380
1381         /* this should never happen */
1382         if (!eed)
1383                 return OPERATOR_CANCELLED;
1384         
1385         EDBM_InitOpf(em, &bmop, op, "edgerotate edges=%e ccw=%b", eed, do_ccw);
1386
1387         /* avoid adding to the selection if we start off with only a selected edge,
1388          * we could also just deselect the single edge easily but use the BMO api
1389          * since it seems this is more 'correct' */
1390         if (do_deselect) BMO_slot_buffer_hflag_disable(em->bm, &bmop, "edges", BM_ELEM_SELECT, BM_EDGE, TRUE);
1391
1392         BMO_op_exec(em->bm, &bmop);
1393         BMO_slot_buffer_hflag_enable(em->bm, &bmop, "edgeout", BM_ELEM_SELECT, BM_EDGE, TRUE);
1394
1395         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
1396                 return OPERATOR_CANCELLED;
1397         }
1398
1399         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1400         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1401
1402         return OPERATOR_FINISHED;
1403 }
1404
1405 void MESH_OT_edge_rotate(wmOperatorType *ot)
1406 {
1407         /* identifiers */
1408         ot->name = "Rotate Selected Edge";
1409         ot->description = "Rotate selected edge or adjoining faces";
1410         ot->idname = "MESH_OT_edge_rotate";
1411
1412         /* api callbacks */
1413         ot->exec = edge_rotate_selected;
1414         ot->poll = ED_operator_editmesh;
1415
1416         /* flags */
1417         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1418
1419         /* props */
1420         RNA_def_enum(ot->srna, "direction", direction_items, DIRECTION_CW, "direction", "direction to rotate edge around");
1421 }
1422
1423 /* swap is 0 or 1, if 1 it hides not selected */
1424 void EDBM_hide_mesh(BMEditMesh *em, int swap)
1425 {
1426         BMIter iter;
1427         BMHeader *h;
1428         int itermode;
1429
1430         if (em == NULL) return;
1431         
1432         if (em->selectmode & SCE_SELECT_VERTEX)
1433                 itermode = BM_VERTS_OF_MESH;
1434         else if (em->selectmode & SCE_SELECT_EDGE)
1435                 itermode = BM_EDGES_OF_MESH;
1436         else
1437                 itermode = BM_FACES_OF_MESH;
1438
1439         BM_ITER(h, &iter, em->bm, itermode, NULL) {
1440                 if (BM_elem_flag_test(h, BM_ELEM_SELECT) ^ swap)
1441                         BM_elem_hide_set(em->bm, h, TRUE);
1442         }
1443
1444         EDBM_selectmode_flush(em);
1445
1446         /* original hide flushing comment (OUTDATED):
1447          * hide happens on least dominant select mode, and flushes up, not down! (helps preventing errors in subsurf) */
1448         /*  - vertex hidden, always means edge is hidden too
1449                 - edge hidden, always means face is hidden too
1450                 - face hidden, only set face hide
1451                 - then only flush back down what's absolute hidden
1452         */
1453
1454 }
1455
1456 static int hide_mesh_exec(bContext *C, wmOperator *op)
1457 {
1458         Object *obedit = CTX_data_edit_object(C);
1459         BMEditMesh *em = (((Mesh *)obedit->data))->edit_btmesh;
1460         
1461         EDBM_hide_mesh(em, RNA_boolean_get(op->ptr, "unselected"));
1462                 
1463         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1464         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1465
1466         return OPERATOR_FINISHED;
1467 }
1468
1469 void MESH_OT_hide(wmOperatorType *ot)
1470 {
1471         /* identifiers */
1472         ot->name = "Hide Selection";
1473         ot->idname = "MESH_OT_hide";
1474         
1475         /* api callbacks */
1476         ot->exec = hide_mesh_exec;
1477         ot->poll = ED_operator_editmesh;
1478          ot->description = "Hide (un)selected vertices, edges or faces";
1479
1480         /* flags */
1481         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1482         
1483         /* props */
1484         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
1485 }
1486
1487
1488 void EDBM_reveal_mesh(BMEditMesh *em)
1489 {
1490         const char iter_types[3] = {BM_VERTS_OF_MESH,
1491                                     BM_EDGES_OF_MESH,
1492                                     BM_FACES_OF_MESH};
1493
1494         int sels[3] = {(em->selectmode & SCE_SELECT_VERTEX),
1495                        (em->selectmode & SCE_SELECT_EDGE),
1496                        (em->selectmode & SCE_SELECT_FACE),
1497                       };
1498
1499         BMIter iter;
1500     BMHeader *ele;
1501         int i;
1502
1503         /* Use tag flag to remember what was hidden before all is revealed.
1504          * BM_ELEM_HIDDEN --> BM_ELEM_TAG */
1505         for (i = 0; i < 3; i++) {
1506                 BM_ITER(ele, &iter, em->bm, iter_types[i], NULL) {
1507                         BM_elem_flag_set(ele, BM_ELEM_TAG, BM_elem_flag_test(ele, BM_ELEM_HIDDEN));
1508                 }
1509         }
1510
1511         /* Reveal everything */
1512         EDBM_flag_disable_all(em, BM_ELEM_HIDDEN);
1513
1514         /* Select relevant just-revealed elements */
1515         for (i = 0; i < 3; i++) {
1516                 if (!sels[i]) {
1517                         continue;
1518                 }
1519
1520                 BM_ITER(ele, &iter, em->bm, iter_types[i], NULL) {
1521                         if (BM_elem_flag_test(ele, BM_ELEM_TAG)) {
1522                                 BM_elem_select_set(em->bm, ele, TRUE);
1523                         }
1524                 }
1525         }
1526
1527         EDBM_selectmode_flush(em);
1528 }
1529
1530 static int reveal_mesh_exec(bContext *C, wmOperator *UNUSED(op))
1531 {
1532         Object *obedit = CTX_data_edit_object(C);
1533         BMEditMesh *em = (((Mesh *)obedit->data))->edit_btmesh;
1534         
1535         EDBM_reveal_mesh(em);
1536
1537         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1538         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1539
1540         return OPERATOR_FINISHED;
1541 }
1542
1543 void MESH_OT_reveal(wmOperatorType *ot)
1544 {
1545         /* identifiers */
1546         ot->name = "Reveal Hidden";
1547         ot->idname = "MESH_OT_reveal";
1548         ot->description = "Reveal all hidden vertices, edges and faces";
1549         
1550         /* api callbacks */
1551         ot->exec = reveal_mesh_exec;
1552         ot->poll = ED_operator_editmesh;
1553         
1554         /* flags */
1555         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1556 }
1557
1558 static int normals_make_consistent_exec(bContext *C, wmOperator *op)
1559 {
1560         Object *obedit = CTX_data_edit_object(C);
1561         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
1562         
1563         /* doflip has to do with bmesh_rationalize_normals, it's an internal
1564          * thing */
1565         if (!EDBM_CallOpf(em, op, "righthandfaces faces=%hf do_flip=%b", BM_ELEM_SELECT, TRUE))
1566                 return OPERATOR_CANCELLED;
1567
1568         if (RNA_boolean_get(op->ptr, "inside"))
1569                 EDBM_CallOpf(em, op, "reversefaces faces=%hf", BM_ELEM_SELECT);
1570
1571         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1572         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1573
1574         return OPERATOR_FINISHED;
1575 }
1576
1577 void MESH_OT_normals_make_consistent(wmOperatorType *ot)
1578 {
1579         /* identifiers */
1580         ot->name = "Make Normals Consistent";
1581         ot->description = "Make face and vertex normals point either outside or inside the mesh";
1582         ot->idname = "MESH_OT_normals_make_consistent";
1583         
1584         /* api callbacks */
1585         ot->exec = normals_make_consistent_exec;
1586         ot->poll = ED_operator_editmesh;
1587         
1588         /* flags */
1589         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1590         
1591         RNA_def_boolean(ot->srna, "inside", 0, "Inside", "");
1592 }
1593
1594
1595
1596 static int do_smooth_vertex(bContext *C, wmOperator *op)
1597 {
1598         Object *obedit = CTX_data_edit_object(C);
1599         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
1600         ModifierData *md;
1601         int mirrx = FALSE, mirry = FALSE, mirrz = FALSE;
1602         int i, repeat;
1603         float clipdist = 0.0f;
1604
1605         /* mirror before smooth */
1606         if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) {
1607                 EDBM_CacheMirrorVerts(em, TRUE);
1608         }
1609
1610         /* if there is a mirror modifier with clipping, flag the verts that
1611          * are within tolerance of the plane(s) of reflection 
1612          */
1613         for (md = obedit->modifiers.first; md; md = md->next) {
1614                 if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) {
1615                         MirrorModifierData *mmd = (MirrorModifierData *)md;
1616                 
1617                         if (mmd->flag & MOD_MIR_CLIPPING) {
1618                                 if (mmd->flag & MOD_MIR_AXIS_X)
1619                                         mirrx = TRUE;
1620                                 if (mmd->flag & MOD_MIR_AXIS_Y)
1621                                         mirry = TRUE;
1622                                 if (mmd->flag & MOD_MIR_AXIS_Z)
1623                                         mirrz = TRUE;
1624
1625                                 clipdist = mmd->tolerance;
1626                         }
1627                 }
1628         }
1629
1630         repeat = RNA_int_get(op->ptr,"repeat");
1631         if (!repeat)
1632                 repeat = 1;
1633         
1634         for (i = 0; i < repeat; i++) {
1635                 if (!EDBM_CallOpf(em, op,
1636                                   "vertexsmooth verts=%hv mirror_clip_x=%b mirror_clip_y=%b mirror_clip_z=%b clipdist=%f",
1637                                   BM_ELEM_SELECT, mirrx, mirry, mirrz, clipdist))
1638                 {
1639                         return OPERATOR_CANCELLED;
1640                 }
1641         }
1642
1643         /* apply mirror */
1644         if (((Mesh *)obedit->data)->editflag & ME_EDIT_MIRROR_X) {
1645                 EDBM_ApplyMirrorCache(em, BM_ELEM_SELECT, 0);
1646                 EDBM_EndMirrorCache(em);
1647         }
1648
1649         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1650         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1651
1652         return OPERATOR_FINISHED;
1653 }       
1654         
1655 void MESH_OT_vertices_smooth(wmOperatorType *ot)
1656 {
1657         /* identifiers */
1658         ot->name = "Smooth Vertex";
1659         ot->description = "Flatten angles of selected vertices";
1660         ot->idname = "MESH_OT_vertices_smooth";
1661         
1662         /* api callbacks */
1663         ot->exec = do_smooth_vertex;
1664         ot->poll = ED_operator_editmesh;
1665         
1666         /* flags */
1667         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1668
1669         RNA_def_int(ot->srna, "repeat", 1, 1, 100, "Number of times to smooth the mesh", "", 1, INT_MAX);
1670 }
1671
1672
1673 static int bm_test_exec(bContext *C, wmOperator *UNUSED(op))
1674 {
1675         Object *obedit = CTX_data_edit_object(C);
1676         ARegion *ar = CTX_wm_region(C);
1677         View3D *v3d = CTX_wm_view3d(C);
1678         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
1679         BMBVHTree *tree = BMBVH_NewBVH(em, 0, NULL, NULL);
1680         BMIter iter;
1681         BMEdge *e;
1682
1683         /* hide all back edges */
1684         BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
1685                 if (!BM_elem_flag_test(e, BM_ELEM_SELECT))
1686                         continue;
1687
1688                 if (!BMBVH_EdgeVisible(tree, e, ar, v3d, obedit))
1689                         BM_elem_select_set(em->bm, e, FALSE);
1690         }
1691
1692         BMBVH_FreeBVH(tree);
1693         
1694 #if 0 //uv island walker test
1695         BMIter iter, liter;
1696         BMFace *f;
1697         BMLoop *l, *l2;
1698         MLoopUV *luv;
1699         BMWalker walker;
1700         int i = 0;
1701
1702         BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
1703                 BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) {
1704                         luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1705                 }
1706         }
1707
1708         BMW_init(&walker, em->bm, BMW_UVISLAND, BMW_NIL_LAY);
1709
1710         BM_ITER(f, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
1711                 BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) {
1712                         luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV);
1713                         if (luv->flag & MLOOPUV_VERTSEL) {
1714                                 l2 = BMW_begin(&walker, l);
1715                                 for (; l2; l2 = BMW_step(&walker)) {
1716                                         luv = CustomData_bmesh_get(&em->bm->ldata, l2->head.data, CD_MLOOPUV);
1717                                         luv->flag |= MLOOPUV_VERTSEL;
1718                                 }                               
1719                         }
1720                 }
1721         }
1722
1723         BMW_end(&walker);
1724 #endif
1725         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1726         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1727
1728         return OPERATOR_FINISHED;
1729 }       
1730         
1731 void MESH_OT_bm_test(wmOperatorType *ot)
1732 {
1733         /* identifiers */
1734         ot->name = "BMesh Test Operator";
1735         ot->idname = "MESH_OT_bm_test";
1736         
1737         /* api callbacks */
1738         ot->exec = bm_test_exec;
1739         ot->poll = ED_operator_editmesh;
1740         
1741         /* flags */
1742         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1743
1744         //RNA_def_int(ot->srna, "repeat", 1, 1, 100, "Number of times to smooth the mesh", "", 1, INT_MAX);
1745 }
1746
1747 /********************** Smooth/Solid Operators *************************/
1748
1749 static void mesh_set_smooth_faces(BMEditMesh *em, short smooth)
1750 {
1751         BMIter iter;
1752         BMFace *efa;
1753
1754         if (em == NULL) return;
1755         
1756         BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) {
1757                 if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1758                         BM_elem_flag_set(efa, BM_ELEM_SMOOTH, smooth);
1759                 }
1760         }
1761 }
1762
1763 static int mesh_faces_shade_smooth_exec(bContext *C, wmOperator *UNUSED(op))
1764 {
1765         Object *obedit = CTX_data_edit_object(C);
1766         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
1767
1768         mesh_set_smooth_faces(em, 1);
1769
1770         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1771         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1772
1773         return OPERATOR_FINISHED;
1774 }
1775
1776 void MESH_OT_faces_shade_smooth(wmOperatorType *ot)
1777 {
1778         /* identifiers */
1779         ot->name = "Shade Smooth";
1780          ot->description = "Display faces smooth (using vertex normals)";
1781         ot->idname = "MESH_OT_faces_shade_smooth";
1782
1783         /* api callbacks */
1784         ot->exec = mesh_faces_shade_smooth_exec;
1785         ot->poll = ED_operator_editmesh;
1786
1787         /* flags */
1788         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1789 }
1790
1791 static int mesh_faces_shade_flat_exec(bContext *C, wmOperator *UNUSED(op))
1792 {
1793         Object *obedit = CTX_data_edit_object(C);
1794         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
1795
1796         mesh_set_smooth_faces(em, 0);
1797
1798         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
1799         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1800
1801         return OPERATOR_FINISHED;
1802 }
1803
1804 void MESH_OT_faces_shade_flat(wmOperatorType *ot)
1805 {
1806         /* identifiers */
1807         ot->name = "Shade Flat";
1808         ot->description = "Display faces flat";
1809         ot->idname = "MESH_OT_faces_shade_flat";
1810
1811         /* api callbacks */
1812         ot->exec = mesh_faces_shade_flat_exec;
1813         ot->poll = ED_operator_editmesh;
1814
1815         /* flags */
1816         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1817 }
1818
1819
1820 /********************** UV/Color Operators *************************/
1821
1822
1823 static const EnumPropertyItem axis_items[] = {
1824         {OPUVC_AXIS_X, "X", 0, "X", ""},
1825         {OPUVC_AXIS_Y, "Y", 0, "Y", ""},
1826         {0, NULL, 0, NULL, NULL}};
1827
1828 static int mesh_rotate_uvs(bContext *C, wmOperator *op)
1829 {
1830         Object *ob = CTX_data_edit_object(C);
1831         BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
1832         BMOperator bmop;
1833
1834         /* get the direction from RNA */
1835         int dir = RNA_enum_get(op->ptr, "direction");
1836
1837         /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
1838         EDBM_InitOpf(em, &bmop, op, "meshrotateuvs faces=%hf dir=%i", BM_ELEM_SELECT, dir);
1839
1840         /* execute the operator */
1841         BMO_op_exec(em->bm, &bmop);
1842
1843         /* finish the operator */
1844         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
1845                 return OPERATOR_CANCELLED;
1846         }
1847
1848         /* dependencies graph and notification stuff */
1849         DAG_id_tag_update(ob->data, OB_RECALC_DATA);
1850         WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
1851
1852         /* we succeeded */
1853         return OPERATOR_FINISHED;
1854 }
1855
1856 static int mesh_reverse_uvs(bContext *C, wmOperator *op)
1857 {
1858         Object *ob = CTX_data_edit_object(C);
1859         BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
1860         BMOperator bmop;
1861
1862         /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
1863         EDBM_InitOpf(em, &bmop, op, "meshreverseuvs faces=%hf", BM_ELEM_SELECT);
1864
1865         /* execute the operator */
1866         BMO_op_exec(em->bm, &bmop);
1867
1868         /* finish the operator */
1869         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
1870                 return OPERATOR_CANCELLED;
1871         }
1872
1873         /* dependencies graph and notification stuff */
1874         DAG_id_tag_update(ob->data, OB_RECALC_DATA);
1875         WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
1876
1877         /* we succeeded */
1878         return OPERATOR_FINISHED;
1879 }
1880
1881 static int mesh_rotate_colors(bContext *C, wmOperator *op)
1882 {
1883         Object *ob = CTX_data_edit_object(C);
1884         BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
1885         BMOperator bmop;
1886
1887         /* get the direction from RNA */
1888         int dir = RNA_enum_get(op->ptr, "direction");
1889
1890         /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
1891         EDBM_InitOpf(em, &bmop, op, "meshrotatecolors faces=%hf dir=%i", BM_ELEM_SELECT, dir);
1892
1893         /* execute the operator */
1894         BMO_op_exec(em->bm, &bmop);
1895
1896         /* finish the operator */
1897         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
1898                 return OPERATOR_CANCELLED;
1899         }
1900
1901         /* dependencies graph and notification stuff */
1902         DAG_id_tag_update(ob->data, OB_RECALC_DATA);
1903         WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
1904 /*      DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
1905         WM_event_add_notifier(C, NC_OBJECT | ND_GEOM_SELECT, ob);
1906 */
1907         /* we succeeded */
1908         return OPERATOR_FINISHED;
1909 }
1910
1911
1912 static int mesh_reverse_colors(bContext *C, wmOperator *op)
1913 {
1914         Object *ob = CTX_data_edit_object(C);
1915         BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
1916         BMOperator bmop;
1917
1918         /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
1919         EDBM_InitOpf(em, &bmop, op, "meshreversecolors faces=%hf", BM_ELEM_SELECT);
1920
1921         /* execute the operator */
1922         BMO_op_exec(em->bm, &bmop);
1923
1924         /* finish the operator */
1925         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
1926                 return OPERATOR_CANCELLED;
1927         }
1928
1929         DAG_id_tag_update(ob->data, OB_RECALC_DATA);
1930         WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
1931
1932         /* we succeeded */
1933         return OPERATOR_FINISHED;
1934 }
1935
1936 void MESH_OT_uvs_rotate(wmOperatorType *ot)
1937 {
1938         /* identifiers */
1939         ot->name = "Rotate UVs";
1940         ot->idname = "MESH_OT_uvs_rotate";
1941
1942         /* api callbacks */
1943         ot->exec = mesh_rotate_uvs;
1944         ot->poll = ED_operator_editmesh;
1945
1946         /* flags */
1947         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1948
1949         /* props */
1950         RNA_def_enum(ot->srna, "direction", direction_items, DIRECTION_CW, "Direction", "Direction to rotate UVs around");
1951 }
1952
1953 //void MESH_OT_uvs_mirror(wmOperatorType *ot)
1954 void MESH_OT_uvs_reverse(wmOperatorType *ot)
1955 {
1956         /* identifiers */
1957         ot->name = "Reverse UVs";
1958         ot->idname = "MESH_OT_uvs_reverse";
1959
1960         /* api callbacks */
1961         ot->exec = mesh_reverse_uvs;
1962         ot->poll = ED_operator_editmesh;
1963
1964         /* flags */
1965         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1966
1967         /* props */
1968         //RNA_def_enum(ot->srna, "axis", axis_items, DIRECTION_CW, "Axis", "Axis to mirror UVs around");
1969 }
1970
1971 void MESH_OT_colors_rotate(wmOperatorType *ot)
1972 {
1973         /* identifiers */
1974         ot->name = "Rotate Colors";
1975         ot->idname = "MESH_OT_colors_rotate";
1976
1977         /* api callbacks */
1978         ot->exec = mesh_rotate_colors;
1979         ot->poll = ED_operator_editmesh;
1980
1981         /* flags */
1982         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1983
1984         /* props */
1985         RNA_def_enum(ot->srna, "direction", direction_items, DIRECTION_CCW, "Direction", "Direction to rotate edge around");
1986 }
1987
1988 void MESH_OT_colors_reverse(wmOperatorType *ot)
1989 {
1990         /* identifiers */
1991         ot->name = "Reverse Colors";
1992         ot->idname = "MESH_OT_colors_reverse";
1993
1994         /* api callbacks */
1995         ot->exec = mesh_reverse_colors;
1996         ot->poll = ED_operator_editmesh;
1997
1998         /* flags */
1999         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2000
2001         /* props */
2002         //RNA_def_enum(ot->srna, "axis", axis_items, DIRECTION_CW, "Axis", "Axis to mirror colors around");
2003 }
2004
2005
2006 static int merge_firstlast(BMEditMesh *em, int first, int uvmerge, wmOperator *wmop)
2007 {
2008         BMVert *mergevert;
2009         BMEditSelection *ese;
2010
2011         /* do sanity check in mergemenu in edit.c ?*/
2012         if (first == 0) {
2013                 ese = em->bm->selected.last;
2014                 mergevert = (BMVert *)ese->data;
2015         }
2016         else{
2017                 ese = em->bm->selected.first;
2018                 mergevert = (BMVert *)ese->data;
2019         }
2020
2021         if (!BM_elem_flag_test(mergevert, BM_ELEM_SELECT))
2022                 return OPERATOR_CANCELLED;
2023         
2024         if (uvmerge) {
2025                 if (!EDBM_CallOpf(em, wmop, "pointmerge_facedata verts=%hv snapv=%e", BM_ELEM_SELECT, mergevert))
2026                         return OPERATOR_CANCELLED;
2027         }
2028
2029         if (!EDBM_CallOpf(em, wmop, "pointmerge verts=%hv mergeco=%v", BM_ELEM_SELECT, mergevert->co))
2030                 return OPERATOR_CANCELLED;
2031
2032         return OPERATOR_FINISHED;
2033 }
2034
2035 static int merge_target(BMEditMesh *em, Scene *scene, View3D *v3d, Object *ob, 
2036                         int target, int uvmerge, wmOperator *wmop)
2037 {
2038         BMIter iter;
2039         BMVert *v;
2040         float *vco = NULL, co[3], cent[3] = {0.0f, 0.0f, 0.0f};
2041
2042         if (target) {
2043                 vco = give_cursor(scene, v3d);
2044                 copy_v3_v3(co, vco);
2045                 mul_m4_v3(ob->imat, co);
2046         }
2047         else {
2048                 float fac;
2049                 int i = 0;
2050                 BM_ITER(v, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
2051                         if (!BM_elem_flag_test(v, BM_ELEM_SELECT))
2052                                 continue;
2053                         add_v3_v3(cent, v->co);
2054                         i++;
2055                 }
2056                 
2057                 if (!i)
2058                         return OPERATOR_CANCELLED;
2059
2060                 fac = 1.0f / (float)i;
2061                 mul_v3_fl(cent, fac);
2062                 copy_v3_v3(co, cent);
2063                 vco = co;
2064         }
2065
2066         if (!vco)
2067                 return OPERATOR_CANCELLED;
2068         
2069         if (uvmerge) {
2070                 if (!EDBM_CallOpf(em, wmop, "vert_average_facedata verts=%hv", BM_ELEM_SELECT))
2071                         return OPERATOR_CANCELLED;
2072         }
2073
2074         if (!EDBM_CallOpf(em, wmop, "pointmerge verts=%hv mergeco=%v", BM_ELEM_SELECT, co))
2075                 return OPERATOR_CANCELLED;
2076
2077         return OPERATOR_FINISHED;
2078 }
2079
2080 static int merge_exec(bContext *C, wmOperator *op)
2081 {
2082         Scene *scene = CTX_data_scene(C);
2083         View3D *v3d = CTX_wm_view3d(C);
2084         Object *obedit = CTX_data_edit_object(C);
2085         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
2086         int status = 0, uvs = RNA_boolean_get(op->ptr, "uvs");
2087
2088         switch(RNA_enum_get(op->ptr, "type")) {
2089                 case 3:
2090                         status = merge_target(em, scene, v3d, obedit, 0, uvs, op);
2091                         break;
2092                 case 4:
2093                         status = merge_target(em, scene, v3d, obedit, 1, uvs, op);
2094                         break;
2095                 case 1:
2096                         status = merge_firstlast(em, 0, uvs, op);
2097                         break;
2098                 case 6:
2099                         status = merge_firstlast(em, 1, uvs, op);
2100                         break;
2101                 case 5:
2102                         status = 1;
2103                         if (!EDBM_CallOpf(em, op, "collapse edges=%he", BM_ELEM_SELECT))
2104                                 status = 0;
2105                         break;
2106         }
2107
2108         if (!status)
2109                 return OPERATOR_CANCELLED;
2110
2111         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
2112         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
2113
2114         return OPERATOR_FINISHED;
2115 }
2116
2117 static EnumPropertyItem merge_type_items[] = {
2118         {6, "FIRST", 0, "At First", ""},
2119         {1, "LAST", 0, "At Last", ""},
2120         {3, "CENTER", 0, "At Center", ""},
2121         {4, "CURSOR", 0, "At Cursor", ""},
2122         {5, "COLLAPSE", 0, "Collapse", ""},
2123         {0, NULL, 0, NULL, NULL}};
2124
2125 static EnumPropertyItem *merge_type_itemf(bContext *C, PointerRNA *UNUSED(ptr),  PropertyRNA *UNUSED(prop), int *free)
2126 {       
2127         Object *obedit;
2128         EnumPropertyItem *item = NULL;
2129         int totitem = 0;
2130         
2131         if (!C) /* needed for docs */
2132                 return merge_type_items;
2133         
2134         obedit = CTX_data_edit_object(C);
2135         if (obedit && obedit->type == OB_MESH) {
2136                 BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
2137
2138                 if (em->selectmode & SCE_SELECT_VERTEX) {
2139                         if ( em->bm->selected.first && em->bm->selected.last &&
2140                              ((BMEditSelection *)em->bm->selected.first)->htype == BM_VERT &&
2141                              ((BMEditSelection *)em->bm->selected.last)->htype == BM_VERT)
2142                         {
2143                                 RNA_enum_items_add_value(&item, &totitem, merge_type_items, 6);
2144                                 RNA_enum_items_add_value(&item, &totitem, merge_type_items, 1);
2145                         }
2146                         else if (em->bm->selected.first && ((BMEditSelection *)em->bm->selected.first)->htype == BM_VERT) {
2147                                 RNA_enum_items_add_value(&item, &totitem, merge_type_items, 1);
2148                         }
2149                         else if (em->bm->selected.last && ((BMEditSelection *)em->bm->selected.last)->htype == BM_VERT) {
2150                                 RNA_enum_items_add_value(&item, &totitem, merge_type_items, 6);
2151                         }
2152                 }
2153
2154                 RNA_enum_items_add_value(&item, &totitem, merge_type_items, 3);
2155                 RNA_enum_items_add_value(&item, &totitem, merge_type_items, 4);
2156                 RNA_enum_items_add_value(&item, &totitem, merge_type_items, 5);
2157                 RNA_enum_item_end(&item, &totitem);
2158
2159                 *free = 1;
2160
2161                 return item;
2162         }
2163         
2164         return NULL;
2165 }
2166
2167 void MESH_OT_merge(wmOperatorType *ot)
2168 {
2169         /* identifiers */
2170         ot->name = "Merge";
2171         ot->idname = "MESH_OT_merge";
2172
2173         /* api callbacks */
2174         ot->exec = merge_exec;
2175         ot->invoke = WM_menu_invoke;
2176         ot->poll = ED_operator_editmesh;
2177
2178         /* flags */
2179         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2180
2181         /* properties */
2182         ot->prop = RNA_def_enum(ot->srna, "type", merge_type_items, 3, "Type", "Merge method to use");
2183         RNA_def_enum_funcs(ot->prop, merge_type_itemf);
2184         RNA_def_boolean(ot->srna, "uvs", 1, "UVs", "Move UVs according to merge");
2185 }
2186
2187
2188 static int removedoublesflag_exec(bContext *C, wmOperator *op)
2189 {
2190         Object *obedit = CTX_data_edit_object(C);
2191         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
2192         BMOperator bmop;
2193         int count;
2194
2195         EDBM_InitOpf(em, &bmop, op, "finddoubles verts=%hv dist=%f", BM_ELEM_SELECT, RNA_float_get(op->ptr, "mergedist"));
2196         BMO_op_exec(em->bm, &bmop);
2197
2198         count = BMO_slot_map_count(em->bm, &bmop, "targetmapout");
2199
2200         if (!EDBM_CallOpf(em, op, "weldverts targetmap=%s", &bmop, "targetmapout")) {
2201                 BMO_op_finish(em->bm, &bmop);
2202                 return OPERATOR_CANCELLED;
2203         }
2204
2205         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
2206                 return OPERATOR_CANCELLED;
2207         }
2208         
2209         BKE_reportf(op->reports, RPT_INFO, "Removed %d vert%s", count, (count==1)?"ex":"ices");
2210
2211
2212         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
2213         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
2214
2215         return OPERATOR_FINISHED;
2216 }
2217
2218 void MESH_OT_remove_doubles(wmOperatorType *ot)
2219 {
2220         /* identifiers */
2221         ot->name = "Remove Doubles";
2222         ot->idname = "MESH_OT_remove_doubles";
2223
2224         /* api callbacks */
2225         ot->exec = removedoublesflag_exec;
2226         ot->poll = ED_operator_editmesh;
2227
2228         /* flags */
2229         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2230
2231         RNA_def_float(ot->srna, "mergedist", 0.0001f, 0.000001f, 50.0f, 
2232                 "Merge Distance", 
2233                 "Minimum distance between elements to merge", 0.00001, 10.0);
2234 }
2235
2236 /************************ Vertex Path Operator *************************/
2237
2238 typedef struct PathNode {
2239         int u;
2240         int visited;
2241         ListBase edges;
2242 } PathNode;
2243
2244 typedef struct PathEdge {
2245         struct PathEdge *next, *prev;
2246         int v;
2247         float w;
2248 } PathEdge;
2249
2250
2251
2252 static int select_vertex_path_exec(bContext *C, wmOperator *op)
2253 {
2254         Object *ob = CTX_data_edit_object(C);
2255         BMEditMesh *em = ((Mesh *)ob->data)->edit_btmesh;
2256         BMOperator bmop;
2257         BMEditSelection *sv, *ev;
2258
2259         /* get the type from RNA */
2260         int type = RNA_enum_get(op->ptr, "type");
2261
2262         sv = em->bm->selected.last;
2263         if (sv != NULL)
2264                 ev = sv->prev;
2265         else return OPERATOR_CANCELLED;
2266         if (ev == NULL)
2267                 return OPERATOR_CANCELLED;
2268
2269         if ((sv->htype != BM_VERT) || (ev->htype != BM_VERT))
2270                 return OPERATOR_CANCELLED;
2271
2272         /* initialize the bmop using EDBM api, which does various ui error reporting and other stuff */
2273         EDBM_InitOpf(em, &bmop, op, "vertexshortestpath startv=%e endv=%e type=%i", sv->data, ev->data, type);
2274
2275         /* execute the operator */
2276         BMO_op_exec(em->bm, &bmop);
2277
2278         /* DO NOT clear the existing selection */
2279         /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */
2280
2281         /* select the output */
2282         BMO_slot_buffer_hflag_enable(em->bm, &bmop, "vertout", BM_ELEM_SELECT, BM_ALL, TRUE);
2283
2284         /* finish the operator */
2285         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
2286                 return OPERATOR_CANCELLED;
2287         }
2288
2289         EDBM_selectmode_flush(em);
2290
2291         /* dependencies graph and notification stuff */
2292 /*      DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
2293         WM_event_add_notifier(C, NC_OBJECT | ND_GEOM_SELECT, ob);
2294 */
2295         DAG_id_tag_update(ob->data, OB_RECALC_DATA);
2296         WM_event_add_notifier(C, NC_GEOM|ND_DATA, ob->data);
2297
2298
2299         /* we succeeded */
2300         return OPERATOR_FINISHED;
2301 }
2302
2303 void MESH_OT_select_vertex_path(wmOperatorType *ot)
2304 {
2305         static const EnumPropertyItem type_items[] = {
2306                 {VPATH_SELECT_EDGE_LENGTH, "EDGE_LENGTH", 0, "Edge Length", NULL},
2307                 {VPATH_SELECT_TOPOLOGICAL, "TOPOLOGICAL", 0, "Topological", NULL},
2308                 {0, NULL, 0, NULL, NULL}};
2309
2310         /* identifiers */
2311         ot->name = "Select Vertex Path";
2312         ot->idname = "MESH_OT_select_vertex_path";
2313
2314         /* api callbacks */
2315         ot->exec = select_vertex_path_exec;
2316         ot->poll = ED_operator_editmesh;
2317
2318         /* flags */
2319         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2320
2321         /* properties */
2322         RNA_def_enum(ot->srna, "type", type_items, VPATH_SELECT_EDGE_LENGTH, "Type", "Method to compute distance");
2323 }
2324 /********************** Rip Operator *************************/
2325
2326 /* helper to find edge for edge_rip */
2327 static float mesh_rip_edgedist(ARegion *ar, float mat[][4], float *co1, float *co2, const int mval[2])
2328 {
2329         float vec1[3], vec2[3], mvalf[2];
2330
2331         ED_view3d_project_float(ar, co1, vec1, mat);
2332         ED_view3d_project_float(ar, co2, vec2, mat);
2333         mvalf[0] = (float)mval[0];
2334         mvalf[1] = (float)mval[1];
2335
2336         return dist_to_line_segment_v2(mvalf, vec1, vec2);
2337 }
2338
2339 /* based on mouse cursor position, it defines how is being ripped */
2340 static int mesh_rip_invoke(bContext *C, wmOperator *op, wmEvent *event)
2341 {
2342         Object *obedit = CTX_data_edit_object(C);
2343         ARegion *ar = CTX_wm_region(C);
2344         View3D *v3d = CTX_wm_view3d(C);
2345         RegionView3D *rv3d = CTX_wm_region_view3d(C);
2346         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
2347         BMesh *bm = em->bm;
2348         BMOperator bmop;
2349         BMBVHTree *bvhtree;
2350         BMOIter siter;
2351         BMIter iter, eiter, liter;
2352         BMLoop *l;
2353         BMEdge *e, *e2, *closest = NULL;
2354         BMVert *v, *ripvert = NULL;
2355         int side = 0, i, singlesel = 0;
2356         float projectMat[4][4], fmval[3] = {event->mval[0], event->mval[1]};
2357         float dist = FLT_MAX, d;
2358
2359         ED_view3d_ob_project_mat_get(rv3d, obedit, projectMat);
2360
2361         /* BM_ELEM_SELECT --> BM_ELEM_TAG */
2362         BM_ITER(e, &iter, em->bm, BM_EDGES_OF_MESH, NULL) {
2363                 BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT));
2364         }
2365
2366         /* handle case of one vert selected.  identify
2367          * closest edge around that vert to mouse cursor,
2368          * then rip two adjacent edges in the vert fan. */
2369         if (bm->totvertsel == 1 && bm->totedgesel == 0 && bm->totfacesel == 0) {
2370                 singlesel = 1;
2371
2372                 /* find selected vert */
2373                 BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
2374                         if (BM_elem_flag_test(v, BM_ELEM_SELECT))
2375                                 break;
2376                 }
2377
2378                 /* this should be impossible, but sanity checks are a good thing */
2379                 if (!v)
2380                         return OPERATOR_CANCELLED;
2381
2382                 if (!v->e || !v->e->l) {
2383                         BKE_report(op->reports, RPT_ERROR, "Selected vertex has no faces");
2384                         return OPERATOR_CANCELLED;
2385                 }
2386
2387                 /* find closest edge to mouse cursor */
2388                 e2 = NULL;
2389                 BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) {
2390                         d = mesh_rip_edgedist(ar, projectMat, e->v1->co, e->v2->co, event->mval);
2391                         if (d < dist) {
2392                                 dist = d;
2393                                 e2 = e;
2394                         }
2395                 }
2396
2397                 if (!e2)
2398                         return OPERATOR_CANCELLED;
2399
2400                 /* rip two adjacent edges */
2401                 if (BM_edge_face_count(e2) == 1 || BM_vert_face_count(v) == 2) {
2402                         l = e2->l;
2403                         ripvert = BM_vert_rip(bm, l->f, v);
2404
2405                         BLI_assert(ripvert);
2406                         if (!ripvert) {
2407                                 return OPERATOR_CANCELLED;
2408                         }
2409                 }
2410                 else if (BM_edge_face_count(e2) == 2) {
2411                         l = e2->l;
2412                         e = BM_face_other_loop(e2, l->f, v)->e;
2413                         BM_elem_flag_enable(e, BM_ELEM_TAG);
2414                         BM_elem_select_set(bm, e, TRUE);
2415                         
2416                         l = e2->l->radial_next;
2417                         e = BM_face_other_loop(e2, l->f, v)->e;
2418                         BM_elem_flag_enable(e, BM_ELEM_TAG);
2419                         BM_elem_select_set(bm, e, TRUE);
2420                 }
2421
2422                 dist = FLT_MAX;
2423         }
2424         else {
2425                 /* expand edge selection */
2426                 BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
2427                         e2 = NULL;
2428                         i = 0;
2429                         BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
2430                                 if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
2431                                         e2 = e;
2432                                         i++;
2433                                 }
2434                         }
2435                         
2436                         if (i == 1 && e2->l) {
2437                                 l = BM_face_other_loop(e2, e2->l->f, v);
2438                                 l = l->radial_next;
2439                                 l = BM_face_other_loop(l->e, l->f, v);
2440
2441                                 if (l) {
2442                                         BM_elem_select_set(bm, l->e, TRUE);
2443                                 }
2444                         }
2445                 }
2446         }
2447
2448         if (!EDBM_InitOpf(em, &bmop, op, "edgesplit edges=%he", BM_ELEM_SELECT)) {
2449                 return OPERATOR_CANCELLED;
2450         }
2451         
2452         BMO_op_exec(bm, &bmop);
2453
2454         /* build bvh tree for edge visibility tests */
2455         bvhtree = BMBVH_NewBVH(em, 0, NULL, NULL);
2456
2457         for (i = 0; i < 2; i++) {
2458                 BMO_ITER(e, &siter, bm, &bmop, i ? "edgeout2":"edgeout1", BM_EDGE) {
2459                         float cent[3] = {0, 0, 0}, mid[3], vec[3];
2460
2461                         if (!BMBVH_EdgeVisible(bvhtree, e, ar, v3d, obedit) || !e->l)
2462                                 continue;
2463
2464                         /* method for calculating distance:
2465                          *
2466                          * for each edge: calculate face center, then made a vector
2467                          * from edge midpoint to face center.  offset edge midpoint
2468                          * by a small amount along this vector. */
2469                         BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, e->l->f) {
2470                                 add_v3_v3(cent, l->v->co);
2471                         }
2472                         mul_v3_fl(cent, 1.0f/(float)e->l->f->len);
2473
2474                         mid_v3_v3v3(mid, e->v1->co, e->v2->co);
2475                         sub_v3_v3v3(vec, cent, mid);
2476                         normalize_v3(vec);
2477                         mul_v3_fl(vec, 0.01f);
2478                         add_v3_v3v3(mid, mid, vec);
2479
2480                         /* yay we have our comparison point, now project it */
2481                         ED_view3d_project_float(ar, mid, mid, projectMat);
2482
2483                         d = len_squared_v2v2(fmval, mid);
2484
2485                         if (d < dist) {
2486                                 side = i;
2487                                 closest = e;
2488                                 dist = d;
2489                         }
2490                 }
2491         }
2492         
2493         BMBVH_FreeBVH(bvhtree);
2494
2495         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
2496         BMO_slot_buffer_hflag_enable(bm, &bmop, side?"edgeout2":"edgeout1", BM_ELEM_SELECT, BM_EDGE, TRUE);
2497
2498         BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
2499                 BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT));
2500         }
2501
2502         /* constrict edge selection again */
2503         BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) {
2504                 e2 = NULL;
2505                 i = 0;
2506                 BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) {
2507                         if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
2508                                 e2 = e;
2509                                 i++;
2510                         }
2511                 }
2512                 
2513                 if (i == 1)  {
2514                         if (singlesel)
2515                                 BM_elem_select_set(bm, v, FALSE);
2516                         else
2517                                 BM_elem_select_set(bm, e2, FALSE);
2518                 }
2519         }
2520
2521         if (ripvert) {
2522                 BM_elem_select_set(bm, ripvert, TRUE);
2523         }
2524
2525         EDBM_selectmode_flush(em);
2526
2527         BLI_assert(singlesel ? (bm->totvertsel > 0) : (bm->totedgesel > 0));
2528
2529         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
2530                 return OPERATOR_CANCELLED;
2531         }
2532
2533         if (bm->totvertsel == 0) {
2534                 return OPERATOR_CANCELLED;
2535         }
2536         
2537         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
2538         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
2539
2540         return OPERATOR_FINISHED;
2541 }
2542
2543 void MESH_OT_rip(wmOperatorType *ot)
2544 {
2545         /* identifiers */
2546         ot->name = "Rip";
2547         ot->idname = "MESH_OT_rip";
2548
2549         /* api callbacks */
2550         ot->invoke = mesh_rip_invoke;
2551         ot->poll = EM_view3d_poll;
2552
2553         /* flags */
2554         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2555
2556         /* to give to transform */
2557         Transform_Properties(ot, P_PROPORTIONAL);
2558         RNA_def_boolean(ot->srna, "mirror", 0, "Mirror Editing", "");
2559 }
2560
2561 /************************ Shape Operators *************************/
2562
2563 /* BMESH_TODO this should be properly encapsulated in a bmop.  but later.*/
2564 static void shape_propagate(Object *obedit, BMEditMesh *em, wmOperator *op)
2565 {
2566         BMIter iter;
2567         BMVert *eve = NULL;
2568         float *co;
2569         int i, totshape = CustomData_number_of_layers(&em->bm->vdata, CD_SHAPEKEY);
2570
2571         if (!CustomData_has_layer(&em->bm->vdata, CD_SHAPEKEY)) {
2572                 BKE_report(op->reports, RPT_ERROR, "Mesh does not have shape keys");
2573                 return;
2574         }
2575         
2576         BM_ITER(eve, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
2577                 if (!BM_elem_flag_test(eve, BM_ELEM_SELECT) || BM_elem_flag_test(eve, BM_ELEM_HIDDEN))
2578                         continue;
2579
2580                 for (i = 0; i < totshape; i++) {
2581                         co = CustomData_bmesh_get_n(&em->bm->vdata, eve->head.data, CD_SHAPEKEY, i);
2582                         copy_v3_v3(co, eve->co);
2583                 }
2584         }
2585
2586 #if 0
2587         //TAG Mesh Objects that share this data
2588         for (base = scene->base.first; base; base = base->next) {
2589                 if (base->object && base->object->data == me) {
2590                         base->object->recalc = OB_RECALC_DATA;
2591                 }
2592         }
2593 #endif
2594
2595         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
2596 }
2597
2598
2599 static int shape_propagate_to_all_exec(bContext *C, wmOperator *op)
2600 {
2601         Object *obedit = CTX_data_edit_object(C);
2602         Mesh *me = obedit->data;
2603         BMEditMesh *em = me->edit_btmesh;
2604
2605         shape_propagate(obedit, em, op);
2606
2607         DAG_id_tag_update(&me->id, OB_RECALC_DATA);
2608         WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
2609
2610         return OPERATOR_FINISHED;
2611 }
2612
2613
2614 void MESH_OT_shape_propagate_to_all(wmOperatorType *ot)
2615 {
2616         /* identifiers */
2617         ot->name = "Shape Propagate";
2618         ot->description = "Apply selected vertex locations to all other shape keys";
2619         ot->idname = "MESH_OT_shape_propagate_to_all";
2620
2621         /* api callbacks */
2622         ot->exec = shape_propagate_to_all_exec;
2623         ot->poll = ED_operator_editmesh;
2624
2625         /* flags */
2626         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2627 }
2628
2629 /* BMESH_TODO this should be properly encapsulated in a bmop.  but later.*/
2630 static int blend_from_shape_exec(bContext *C, wmOperator *op)
2631 {
2632         Object *obedit = CTX_data_edit_object(C);
2633         Mesh *me = obedit->data;
2634         BMEditMesh *em = me->edit_btmesh;
2635         BMVert *eve;
2636         BMIter iter;
2637         float co[3], *sco;
2638         float blend = RNA_float_get(op->ptr, "blend");
2639         int shape = RNA_enum_get(op->ptr, "shape");
2640         int add = RNA_boolean_get(op->ptr, "add");
2641         int totshape;
2642
2643         /* sanity check */
2644         totshape = CustomData_number_of_layers(&em->bm->vdata, CD_SHAPEKEY);
2645         if (totshape == 0 || shape < 0 || shape >= totshape)
2646                 return OPERATOR_CANCELLED;
2647
2648         BM_ITER(eve, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
2649                 if (!BM_elem_flag_test(eve, BM_ELEM_SELECT) || BM_elem_flag_test(eve, BM_ELEM_HIDDEN))
2650                         continue;
2651
2652                 sco = CustomData_bmesh_get_n(&em->bm->vdata, eve->head.data, CD_SHAPEKEY, shape);
2653                 copy_v3_v3(co, sco);
2654
2655
2656                 if (add) {
2657                         mul_v3_fl(co, blend);
2658                         add_v3_v3v3(eve->co, eve->co, co);
2659                 }
2660                 else
2661                         interp_v3_v3v3(eve->co, eve->co, co, blend);
2662                 
2663                 copy_v3_v3(sco, co);
2664         }
2665
2666         DAG_id_tag_update(&me->id, OB_RECALC_DATA);
2667         WM_event_add_notifier(C, NC_GEOM|ND_DATA, me);
2668
2669         return OPERATOR_FINISHED;
2670 }
2671
2672 static EnumPropertyItem *shape_itemf(bContext *C, PointerRNA *UNUSED(ptr),  PropertyRNA *UNUSED(prop), int *free)
2673 {       
2674         Object *obedit = CTX_data_edit_object(C);
2675         Mesh *me = (obedit) ? obedit->data : NULL;
2676         BMEditMesh *em = (me) ? me->edit_btmesh : NULL;
2677         EnumPropertyItem *item = NULL;
2678         int totitem = 0;
2679
2680         if (obedit && obedit->type == OB_MESH && CustomData_has_layer(&em->bm->vdata, CD_SHAPEKEY)) {
2681                 EnumPropertyItem tmp = {0, "", 0, "", ""};
2682                 int a;
2683
2684                 for (a = 0; a < em->bm->vdata.totlayer; a++) {
2685                         if (em->bm->vdata.layers[a].type != CD_SHAPEKEY)
2686                                 continue;
2687
2688                         tmp.value = totitem;
2689                         tmp.identifier = em->bm->vdata.layers[a].name;
2690                         tmp.name = em->bm->vdata.layers[a].name;
2691                         RNA_enum_item_add(&item, &totitem, &tmp);
2692
2693                         totitem++;
2694                 }
2695         }
2696
2697         RNA_enum_item_end(&item, &totitem);
2698         *free = 1;
2699
2700         return item;
2701 }
2702
2703 void MESH_OT_blend_from_shape(wmOperatorType *ot)
2704 {
2705         PropertyRNA *prop;
2706         static EnumPropertyItem shape_items[] = {{0, NULL, 0, NULL, NULL}};
2707
2708         /* identifiers */
2709         ot->name = "Blend From Shape";
2710         ot->description = "Blend in shape from a shape key";
2711         ot->idname = "MESH_OT_blend_from_shape";
2712
2713         /* api callbacks */
2714         ot->exec = blend_from_shape_exec;
2715         ot->invoke = WM_operator_props_popup;
2716         ot->poll = ED_operator_editmesh;
2717
2718         /* flags */
2719         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2720
2721         /* properties */
2722         prop = RNA_def_enum(ot->srna, "shape", shape_items, 0, "Shape", "Shape key to use for blending");
2723         RNA_def_enum_funcs(prop, shape_itemf);
2724         RNA_def_float(ot->srna, "blend", 1.0f, -FLT_MAX, FLT_MAX, "Blend", "Blending factor", -2.0f, 2.0f);
2725         RNA_def_boolean(ot->srna, "add", 1, "Add", "Add rather then blend between shapes");
2726 }
2727
2728 /* BMESH_TODO - some way to select on an arbitrary axis */
2729 static int select_axis_exec(bContext *C, wmOperator *op)
2730 {
2731         Object *obedit = CTX_data_edit_object(C);
2732         BMEditMesh *em = ((Mesh *)obedit->data)->edit_btmesh;
2733         BMEditSelection *ese = em->bm->selected.last;
2734         int axis = RNA_enum_get(op->ptr, "axis");
2735         int mode = RNA_enum_get(op->ptr, "mode"); /* -1 == aligned, 0 == neg, 1 == pos */
2736
2737         if (ese == NULL || ese->htype != BM_VERT) {
2738                 BKE_report(op->reports, RPT_WARNING, "This operator requires an active vertex (last selected)");
2739                 return OPERATOR_CANCELLED;
2740         }
2741         else {
2742                 BMVert *ev, *act_vert = (BMVert *)ese->data;
2743                 BMIter iter;
2744                 float value = act_vert->co[axis];
2745                 float limit =  CTX_data_tool_settings(C)->doublimit; // XXX
2746
2747                 if (mode == 0)
2748                         value -= limit;
2749                 else if (mode == 1)
2750                         value += limit;
2751
2752                 BM_ITER(ev, &iter, em->bm, BM_VERTS_OF_MESH, NULL) {
2753                         if (!BM_elem_flag_test(ev, BM_ELEM_HIDDEN)) {
2754                                 switch(mode) {
2755                                 case -1: /* aligned */
2756                                         if (fabs(ev->co[axis] - value) < limit)
2757                                                 BM_elem_select_set(em->bm, ev, TRUE);
2758                                         break;
2759                                 case 0: /* neg */
2760                                         if (ev->co[axis] > value)
2761                                                 BM_elem_select_set(em->bm, ev, TRUE);
2762                                         break;
2763                                 case 1: /* pos */
2764                                         if (ev->co[axis] < value)
2765                                                 BM_elem_select_set(em->bm, ev, TRUE);
2766                                         break;
2767                                 }
2768                         }
2769                 }
2770         }
2771
2772         EDBM_selectmode_flush(em);
2773         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
2774
2775         return OPERATOR_FINISHED;
2776 }
2777
2778 void MESH_OT_select_axis(wmOperatorType *ot)
2779 {
2780         static EnumPropertyItem axis_mode_items[] = {
2781                 {0,  "POSITIVE", 0, "Positive Axis", ""},
2782                 {1,  "NEGATIVE", 0, "Negative Axis", ""},
2783                 {-1, "ALIGNED",  0, "Aligned Axis", ""},
2784                 {0, NULL, 0, NULL, NULL}};
2785
2786         static EnumPropertyItem axis_items_xyz[] = {
2787                 {0, "X_AXIS", 0, "X Axis", ""},
2788                 {1, "Y_AXIS", 0, "Y Axis", ""},
2789                 {2, "Z_AXIS", 0, "Z Axis", ""},
2790                 {0, NULL, 0, NULL, NULL}};
2791
2792         /* identifiers */
2793         ot->name = "Select Axis";
2794         ot->description = "Select all data in the mesh on a single axis";
2795         ot->idname = "MESH_OT_select_axis";
2796
2797         /* api callbacks */
2798         ot->exec = select_axis_exec;
2799         ot->poll = ED_operator_editmesh;
2800
2801         /* flags */
2802         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2803
2804         /* properties */
2805         RNA_def_enum(ot->srna, "mode", axis_mode_items, 0, "Axis Mode", "Axis side to use when selecting");
2806         RNA_def_enum(ot->srna, "axis", axis_items_xyz, 0, "Axis", "Select the axis to compare each vertex on");
2807 }
2808
2809 static int solidify_exec(bContext *C, wmOperator *op)
2810 {
2811         Object *obedit = CTX_data_edit_object(C);
2812         Mesh *me = obedit->data;
2813         BMEditMesh *em = me->edit_btmesh;
2814         BMesh *bm = em->bm;
2815         BMOperator bmop;
2816
2817         float thickness = RNA_float_get(op->ptr, "thickness");
2818
2819         if (!EDBM_InitOpf(em, &bmop, op, "solidify geom=%hf thickness=%f", BM_ELEM_SELECT, thickness)) {
2820                 return OPERATOR_CANCELLED;
2821         }
2822
2823         /* deselect only the faces in the region to be solidified (leave wire
2824            edges and loose verts selected, as there will be no corresponding
2825            geometry selected below) */
2826         BMO_slot_buffer_hflag_disable(bm, &bmop, "geom", BM_ELEM_SELECT, BM_FACE, TRUE);
2827
2828         /* run the solidify operator */
2829         BMO_op_exec(bm, &bmop);
2830
2831         /* select the newly generated faces */
2832         BMO_slot_buffer_hflag_enable(bm, &bmop, "geomout", BM_ELEM_SELECT, BM_FACE, TRUE);
2833
2834         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
2835                 return OPERATOR_CANCELLED;
2836         }
2837
2838         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
2839         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
2840
2841         return OPERATOR_FINISHED;
2842 }
2843
2844
2845 void MESH_OT_solidify(wmOperatorType *ot)
2846 {
2847         PropertyRNA *prop;
2848         /* identifiers */
2849         ot->name = "Solidify";
2850         ot->description = "Create a solid skin by extruding, compensating for sharp angles";
2851         ot->idname = "MESH_OT_solidify";
2852
2853         /* api callbacks */
2854         ot->exec = solidify_exec;
2855         ot->poll = ED_operator_editmesh;
2856
2857         /* flags */
2858         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
2859
2860         prop = RNA_def_float(ot->srna, "thickness", 0.01f, -FLT_MAX, FLT_MAX, "thickness", "", -10.0f, 10.0f);
2861         RNA_def_property_ui_range(prop, -10, 10, 0.1, 4);
2862 }
2863
2864 #define TRAIL_POLYLINE 1 /* For future use, They don't do anything yet */
2865 #define TRAIL_FREEHAND 2
2866 #define TRAIL_MIXED    3 /* (1|2) */
2867 #define TRAIL_AUTO     4 
2868 #define TRAIL_MIDPOINTS 8
2869
2870 typedef struct CutCurve {
2871         float  x;
2872         float  y;
2873 } CutCurve;
2874
2875 /* ******************************************************************** */
2876 /* Knife Subdivide Tool.  Subdivides edges intersected by a mouse trail
2877         drawn by user.
2878         
2879         Currently mapped to KKey when in MeshEdit mode.
2880         Usage:
2881                 Hit Shift K, Select Centers or Exact
2882                 Hold LMB down to draw path, hit RETKEY.
2883                 ESC cancels as expected.
2884    
2885         Contributed by Robert Wenzlaff (Det. Thorn).
2886
2887     2.5 revamp:
2888     - non modal (no menu before cutting)
2889     - exit on mouse release
2890     - polygon/segment drawing can become handled by WM cb later
2891
2892         bmesh port version
2893 */
2894
2895 #define KNIFE_EXACT             1
2896 #define KNIFE_MIDPOINT  2
2897 #define KNIFE_MULTICUT  3
2898
2899 static EnumPropertyItem knife_items[] = {
2900         {KNIFE_EXACT, "EXACT", 0, "Exact", ""},
2901         {KNIFE_MIDPOINT, "MIDPOINTS", 0, "Midpoints", ""},
2902         {KNIFE_MULTICUT, "MULTICUT", 0, "Multicut", ""},
2903         {0, NULL, 0, NULL, NULL}
2904 };
2905
2906 /* seg_intersect() Determines if and where a mouse trail intersects an EditEdge */
2907
2908 static float bm_edge_seg_isect(BMEdge *e, CutCurve *c, int len, char mode,
2909                                struct GHash *gh, int *isected)
2910 {
2911 #define MAXSLOPE 100000
2912         float  x11, y11, x12 = 0, y12 = 0, x2max, x2min, y2max;
2913         float  y2min, dist, lastdist = 0, xdiff2, xdiff1;
2914         float  m1, b1, m2, b2, x21, x22, y21, y22, xi;
2915         float  yi, x1min, x1max, y1max, y1min, perc = 0;
2916         float  *scr;
2917         float  threshold = 0.0;
2918         int  i;
2919         
2920         //threshold = 0.000001; /* tolerance for vertex intersection */
2921         // XXX  threshold = scene->toolsettings->select_thresh / 100;
2922         
2923         /* Get screen coords of verts */
2924         scr = BLI_ghash_lookup(gh, e->v1);
2925         x21 = scr[0];
2926         y21 = scr[1];
2927         
2928         scr = BLI_ghash_lookup(gh, e->v2);
2929         x22 = scr[0];
2930         y22 = scr[1];
2931         
2932         xdiff2 = (x22 - x21);
2933         if (xdiff2) {
2934                 m2 = (y22 - y21) / xdiff2;
2935                 b2 = ((x22 * y21) - (x21 * y22)) / xdiff2;
2936         }
2937         else {
2938                 m2 = MAXSLOPE;  /* Verticle slope  */
2939                 b2 = x22;
2940         }
2941
2942         *isected = 0;
2943
2944         /* check for *exact* vertex intersection first */
2945         if (mode != KNIFE_MULTICUT) {
2946                 for (i = 0; i < len; i++) {
2947                         if (i > 0) {
2948                                 x11 = x12;
2949                                 y11 = y12;
2950                         }
2951                         else {
2952                                 x11 = c[i].x;
2953                                 y11 = c[i].y;
2954                         }
2955                         x12 = c[i].x;
2956                         y12 = c[i].y;
2957                         
2958                         /* test e->v1*/
2959                         if ((x11 == x21 && y11 == y21) || (x12 == x21 && y12 == y21)) {
2960                                 perc = 0;
2961                                 *isected = 1;
2962                                 return perc;
2963                         }
2964                         /* test e->v2*/
2965                         else if ((x11 == x22 && y11 == y22) || (x12 == x22 && y12 == y22)) {
2966                                 perc = 0;
2967                                 *isected = 2;
2968                                 return perc;
2969                         }
2970                 }
2971         }
2972         
2973         /* now check for edge interesect (may produce vertex intersection as well)*/
2974         for (i = 0; i < len; i++) {
2975                 if (i > 0) {
2976                         x11 = x12;
2977                         y11 = y12;
2978                 }
2979                 else {
2980                         x11 = c[i].x;
2981                         y11 = c[i].y;
2982                 }
2983                 x12 = c[i].x;
2984                 y12 = c[i].y;
2985                 
2986                 /* Perp. Distance from point to line */
2987                 if (m2 != MAXSLOPE) dist = (y12 - m2 * x12 - b2);/* /sqrt(m2 * m2 + 1); Only looking for */
2988                         /* change in sign.  Skip extra math */  
2989                 else dist = x22 - x12;
2990                 
2991                 if (i == 0) lastdist = dist;
2992                 
2993                 /* if dist changes sign, and intersect point in edge's Bound Box */
2994                 if ((lastdist * dist) <= 0) {
2995                         xdiff1 = (x12 - x11); /* Equation of line between last 2 points */
2996                         if (xdiff1) {
2997                                 m1 = (y12 - y11) / xdiff1;
2998                                 b1 = ((x12 * y11) - (x11 * y12)) / xdiff1;
2999                         }
3000                         else{
3001                                 m1 = MAXSLOPE;
3002                                 b1 = x12;
3003                         }
3004                         x2max = MAX2(x21, x22) + 0.001; /* prevent missed edges   */
3005                         x2min = MIN2(x21, x22) - 0.001; /* due to round off error */
3006                         y2max = MAX2(y21, y22) + 0.001;
3007                         y2min = MIN2(y21, y22) - 0.001;
3008                         
3009                         /* Found an intersect,  calc intersect point */
3010                         if (m1 == m2) { /* co-incident lines */
3011                                 /* cut at 50% of overlap area */
3012                                 x1max = MAX2(x11, x12);
3013                                 x1min = MIN2(x11, x12);
3014                                 xi = (MIN2(x2max, x1max) + MAX2(x2min, x1min)) / 2.0;
3015                                 
3016                                 y1max = MAX2(y11, y12);
3017                                 y1min = MIN2(y11, y12);
3018                                 yi = (MIN2(y2max, y1max) + MAX2(y2min, y1min)) / 2.0;
3019                         }
3020                         else if (m2 == MAXSLOPE) {
3021                                 xi = x22;
3022                                 yi = m1 * x22 + b1;
3023                         }
3024                         else if (m1 == MAXSLOPE) {
3025                                 xi = x12;
3026                                 yi = m2 * x12 + b2;
3027                         }
3028                         else {
3029                                 xi = (b1 - b2) / (m2 - m1);
3030                                 yi = (b1 * m2 - m1 * b2) / (m2 - m1);
3031                         }
3032                         
3033                         /* Intersect inside bounding box of edge?*/
3034                         if ((xi >= x2min) && (xi <= x2max) && (yi <= y2max) && (yi >= y2min)) {
3035                                 /* test for vertex intersect that may be 'close enough'*/
3036                                 if (mode != KNIFE_MULTICUT) {
3037                                         if (xi <= (x21 + threshold) && xi >= (x21 - threshold)) {
3038                                                 if (yi <= (y21 + threshold) && yi >= (y21 - threshold)) {
3039                                                         *isected = 1;
3040                                                         perc = 0;
3041                                                         break;
3042                                                 }
3043                                         }
3044                                         if (xi <= (x22 + threshold) && xi >= (x22 - threshold)) {
3045                                                 if (yi <= (y22 + threshold) && yi >= (y22 - threshold)) {
3046                                                         *isected = 2;
3047                                                         perc = 0;
3048                                                         break;
3049                                                 }
3050                                         }
3051                                 }
3052                                 if ((m2 <= 1.0f) && (m2 >= -1.0f)) perc = (xi - x21) / (x22 - x21);
3053                                 else perc = (yi - y21) / (y22 - y21); /* lower slope more accurate */
3054                                 //isect = 32768.0 * (perc + 0.0000153); /* Percentage in 1 / 32768ths */
3055                                 
3056                                 break;
3057                         }
3058                 }       
3059                 lastdist = dist;
3060         }
3061         return(perc);
3062
3063
3064 #define MAX_CUTS 2048
3065
3066 static int knife_cut_exec(bContext *C, wmOperator *op)
3067 {
3068         Object *obedit = CTX_data_edit_object(C);
3069         BMEditMesh *em = (((Mesh *)obedit->data))->edit_btmesh;
3070         BMesh *bm = em->bm;
3071         ARegion *ar = CTX_wm_region(C);
3072         BMVert *bv;
3073         BMIter iter;
3074         BMEdge *be;
3075         BMOperator bmop;
3076         CutCurve curve[MAX_CUTS];
3077         struct GHash *gh;
3078         float isect = 0.0f;
3079         float  *scr, co[4];
3080         int len = 0, isected;
3081         short numcuts = 1, mode = RNA_int_get(op->ptr, "type");
3082         
3083         /* edit-object needed for matrix, and ar->regiondata for projections to work */
3084         if (ELEM3(NULL, obedit, ar, ar->regiondata))
3085                 return OPERATOR_CANCELLED;
3086         
3087         if (bm->totvertsel < 2) {
3088                 //error("No edges are selected to operate on");
3089                 return OPERATOR_CANCELLED;
3090         }
3091
3092         /* get the cut curve */
3093         RNA_BEGIN(op->ptr, itemptr, "path") {
3094                 
3095                 RNA_float_get_array(&itemptr, "loc", (float *)&curve[len]);
3096                 len++;
3097                 if (len >= MAX_CUTS) break;
3098         }
3099         RNA_END;
3100         
3101         if (len < 2) {
3102                 return OPERATOR_CANCELLED;
3103         }
3104
3105         /* the floating point coordinates of verts in screen space will be stored in a hash table according to the vertices pointer */
3106         gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "knife cut exec");
3107         for (bv = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); bv; bv = BM_iter_step(&iter)) {
3108                 scr = MEM_mallocN(sizeof(float)*2, "Vertex Screen Coordinates");
3109                 copy_v3_v3(co, bv->co);
3110                 co[3] = 1.0f;
3111                 mul_m4_v4(obedit->obmat, co);
3112                 project_float(ar, co, scr);
3113                 BLI_ghash_insert(gh, bv, scr);
3114         }
3115
3116         if (!EDBM_InitOpf(em, &bmop, op, "esubd")) {
3117                 return OPERATOR_CANCELLED;
3118         }
3119
3120         /* store percentage of edge cut for KNIFE_EXACT here.*/
3121         for (be = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); be; be = BM_iter_step(&iter)) {
3122                 if (BM_elem_flag_test(be, BM_ELEM_SELECT)) {
3123                         isect = bm_edge_seg_isect(be, curve, len, mode, gh, &isected);
3124                         
3125                         if (isect != 0.0f) {
3126                                 if (mode != KNIFE_MULTICUT && mode != KNIFE_MIDPOINT) {
3127                                         BMO_slot_map_float_insert(bm, &bmop,
3128                                                             "edgepercents",
3129                                                             be, isect);
3130
3131                                 }
3132                                 BMO_elem_flag_enable(bm, be, 1);
3133                         }
3134                         else {
3135                                 BMO_elem_flag_disable(bm, be, 1);
3136                         }
3137                 }
3138                 else {
3139                         BMO_elem_flag_disable(bm, be, 1);
3140                 }
3141         }
3142         
3143         BMO_slot_from_flag(bm, &bmop, "edges", 1, BM_EDGE);
3144
3145         if (mode == KNIFE_MIDPOINT) numcuts = 1;
3146         BMO_slot_int_set(&bmop, "numcuts", numcuts);
3147
3148         BMO_slot_int_set(&bmop, "flag", B_KNIFE);
3149         BMO_slot_int_set(&bmop, "quadcornertype", SUBD_STRAIGHT_CUT);
3150         BMO_slot_bool_set(&bmop, "singleedge", FALSE);
3151         BMO_slot_bool_set(&bmop, "gridfill", FALSE);
3152
3153         BMO_slot_float_set(&bmop, "radius", 0);
3154         
3155         BMO_op_exec(bm, &bmop);
3156         if (!EDBM_FinishOp(em, &bmop, op, TRUE)) {
3157                 return OPERATOR_CANCELLED;
3158         }
3159         
3160         BLI_ghash_free(gh, NULL, (GHashValFreeFP)MEM_freeN);
3161
3162         DAG_id_tag_update(obedit->data, OB_RECALC_DATA);
3163         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
3164
3165         return OPERATOR_FINISHED;
3166 }
3167
3168 void MESH_OT_knife_cut(wmOperatorType *ot)
3169 {
3170         PropertyRNA *prop;
3171         
3172         ot->name = "Knife Cut";
3173         ot->description = "Cut selected edges and faces into parts";
3174         ot->idname = "MESH_OT_knife_cut";
3175         
3176         ot->invoke = WM_gesture_lines_invoke;
3177         ot->modal = WM_gesture_lines_modal;
3178         ot->exec = knife_cut_exec;
3179         
3180         ot->poll = EM_view3d_poll;
3181         
3182         /* flags */
3183         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
3184         
3185         RNA_def_enum(ot->srna, "type", knife_items, KNIFE_EXACT, "Type", "");
3186         prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
3187         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
3188         
3189         /* internal */
3190         RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
3191 }
3192
3193 static int mesh_separate_selected(Main *bmain, Scene *scene, Base *editbase, wmOperator *wmop)
3194 {
3195         Base *basenew;
3196         BMIter iter;
3197         BMVert *v;
3198         BMEdge *e;
3199         Object *obedit = editbase->object;
3200         Mesh *me = obedit->data;
3201         BMEditMesh *em = me->edit_btmesh;
3202         BMesh *bmnew;
3203         
3204         if (!em)
3205                 return OPERATOR_CANCELLED;
3206                 
3207         bmnew = BM_mesh_create(obedit, bm_mesh_allocsize_default);
3208         CustomData_copy(&em->bm->vdata, &bmnew->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
3209         CustomData_copy(&em->bm->e