ef300fa9db69c8f561d202364ecb1cc2e006dcee
[blender.git] / source / blender / editors / mesh / editmesh_extrude.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 /** \file blender/editors/mesh/editmesh_extrude.c
29  *  \ingroup edmesh
30  */
31
32 #include "DNA_modifier_types.h"
33 #include "DNA_object_types.h"
34
35 #include "BLI_math.h"
36
37 #include "BKE_context.h"
38 #include "BKE_object.h"
39 #include "BKE_report.h"
40 #include "BKE_editmesh.h"
41
42 #include "RNA_define.h"
43 #include "RNA_access.h"
44
45 #include "WM_types.h"
46
47 #include "ED_mesh.h"
48 #include "ED_screen.h"
49 #include "ED_transform.h"
50 #include "ED_view3d.h"
51
52 #include "mesh_intern.h"  /* own include */
53
54 /* allow accumulated normals to form a new direction but don't
55  * accept direct opposite directions else they will cancel each other out */
56 static void add_normal_aligned(float nor[3], const float add[3])
57 {
58         if (dot_v3v3(nor, add) < -0.9999f) {
59                 sub_v3_v3(nor, add);
60         }
61         else {
62                 add_v3_v3(nor, add);
63         }
64 }
65
66 /* individual face extrude */
67 /* will use vertex normals for extrusion directions, so *nor is unaffected */
68 static short edbm_extrude_discrete_faces(BMEditMesh *em, wmOperator *op, const char hflag, float *UNUSED(nor))
69 {
70         BMOIter siter;
71         BMIter liter;
72         BMFace *f;
73         BMLoop *l;
74         BMOperator bmop;
75
76         EDBM_op_init(em, &bmop, op, "extrude_discrete_faces faces=%hf", hflag);
77
78         /* deselect original verts */
79         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
80
81         BMO_op_exec(em->bm, &bmop);
82         
83         BMO_ITER (f, &siter, bmop.slots_out, "faces.out", BM_FACE) {
84                 BM_face_select_set(em->bm, f, true);
85
86                 /* set face vertex normals to face normal */
87                 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
88                         copy_v3_v3(l->v->no, f->no);
89                 }
90         }
91
92         if (!EDBM_op_finish(em, &bmop, op, true)) {
93                 return 0;
94         }
95
96         return 's';  /* s is shrink/fatten */
97 }
98
99 /* extrudes individual edges */
100 static short edbm_extrude_edges_indiv(BMEditMesh *em, wmOperator *op, const char hflag, float *UNUSED(nor))
101 {
102         BMOperator bmop;
103
104         EDBM_op_init(em, &bmop, op, "extrude_edge_only edges=%he", hflag);
105
106         /* deselect original verts */
107         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
108
109         BMO_op_exec(em->bm, &bmop);
110         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_VERT | BM_EDGE, BM_ELEM_SELECT, true);
111
112         if (!EDBM_op_finish(em, &bmop, op, true)) {
113                 return 0;
114         }
115
116         return 'n';  /* n is normal grab */
117 }
118
119 /* extrudes individual vertices */
120 static short edbm_extrude_verts_indiv(BMEditMesh *em, wmOperator *op, const char hflag, float *UNUSED(nor))
121 {
122         BMOperator bmop;
123
124         EDBM_op_init(em, &bmop, op, "extrude_vert_indiv verts=%hv", hflag);
125
126         /* deselect original verts */
127         BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_in, "verts", BM_VERT, BM_ELEM_SELECT, true);
128
129         BMO_op_exec(em->bm, &bmop);
130         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true);
131
132         if (!EDBM_op_finish(em, &bmop, op, true)) {
133                 return 0;
134         }
135
136         return 'g';  /* g is grab */
137 }
138
139 static short edbm_extrude_edge(Object *obedit, BMEditMesh *em, const char hflag, float nor[3])
140 {
141         BMesh *bm = em->bm;
142         BMIter iter;
143         BMOIter siter;
144         BMOperator extop;
145         BMEdge *edge;
146         BMFace *f;
147         ModifierData *md;
148         BMElem *ele;
149         BMOpSlot *slot_edges_exclude;
150         
151         BMO_op_init(bm, &extop, BMO_FLAG_DEFAULTS, "extrude_face_region");
152         BMO_slot_buffer_from_enabled_hflag(bm, &extop, extop.slots_in, "geom", BM_VERT | BM_EDGE | BM_FACE, hflag);
153
154         slot_edges_exclude = BMO_slot_get(extop.slots_in, "edges_exclude");
155
156         /* If a mirror modifier with clipping is on, we need to adjust some 
157          * of the cases above to handle edges on the line of symmetry.
158          */
159         md = obedit->modifiers.first;
160         for (; md; md = md->next) {
161                 if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
162                         MirrorModifierData *mmd = (MirrorModifierData *) md;
163                 
164                         if (mmd->flag & MOD_MIR_CLIPPING) {
165                                 float mtx[4][4];
166                                 if (mmd->mirror_ob) {
167                                         float imtx[4][4];
168                                         invert_m4_m4(imtx, mmd->mirror_ob->obmat);
169                                         mul_m4_m4m4(mtx, imtx, obedit->obmat);
170                                 }
171
172                                 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
173                                         if (BM_elem_flag_test(edge, hflag) &&
174                                             BM_edge_is_boundary(edge) &&
175                                             BM_elem_flag_test(edge->l->f, hflag))
176                                         {
177                                                 float co1[3], co2[3];
178
179                                                 copy_v3_v3(co1, edge->v1->co);
180                                                 copy_v3_v3(co2, edge->v2->co);
181
182                                                 if (mmd->mirror_ob) {
183                                                         mul_v3_m4v3(co1, mtx, co1);
184                                                         mul_v3_m4v3(co2, mtx, co2);
185                                                 }
186
187                                                 if (mmd->flag & MOD_MIR_AXIS_X) {
188                                                         if ((fabsf(co1[0]) < mmd->tolerance) &&
189                                                             (fabsf(co2[0]) < mmd->tolerance))
190                                                         {
191                                                                 BMO_slot_map_empty_insert(&extop, slot_edges_exclude, edge);
192                                                         }
193                                                 }
194                                                 if (mmd->flag & MOD_MIR_AXIS_Y) {
195                                                         if ((fabsf(co1[1]) < mmd->tolerance) &&
196                                                             (fabsf(co2[1]) < mmd->tolerance))
197                                                         {
198                                                                 BMO_slot_map_empty_insert(&extop, slot_edges_exclude, edge);
199                                                         }
200                                                 }
201                                                 if (mmd->flag & MOD_MIR_AXIS_Z) {
202                                                         if ((fabsf(co1[2]) < mmd->tolerance) &&
203                                                             (fabsf(co2[2]) < mmd->tolerance))
204                                                         {
205                                                                 BMO_slot_map_empty_insert(&extop, slot_edges_exclude, edge);
206                                                         }
207                                                 }
208                                         }
209                                 }
210                         }
211                 }
212         }
213
214         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
215
216         BMO_op_exec(bm, &extop);
217
218         zero_v3(nor);
219         
220         BMO_ITER (ele, &siter, extop.slots_out, "geom.out", BM_ALL) {
221                 BM_elem_select_set(bm, ele, true);
222
223                 if (ele->head.htype == BM_FACE) {
224                         f = (BMFace *)ele;
225                         add_normal_aligned(nor, f->no);
226                 }
227         }
228
229         normalize_v3(nor);
230
231         BMO_op_finish(bm, &extop);
232
233         /* grab / normal constraint */
234         return is_zero_v3(nor) ? 'g' : 'n';
235 }
236
237 static short edbm_extrude_vert(Object *obedit, BMEditMesh *em, const char hflag, float nor[3])
238 {
239         BMIter iter;
240         BMEdge *eed;
241                 
242         /* ensure vert flags are consistent for edge selections */
243         BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
244                 if (BM_elem_flag_test(eed, hflag)) {
245                         if (hflag & BM_ELEM_SELECT) {
246                                 BM_vert_select_set(em->bm, eed->v1, true);
247                                 BM_vert_select_set(em->bm, eed->v2, true);
248                         }
249
250                         BM_elem_flag_enable(eed->v1, hflag & ~BM_ELEM_SELECT);
251                         BM_elem_flag_enable(eed->v2, hflag & ~BM_ELEM_SELECT);
252                 }
253                 else {
254                         if (BM_elem_flag_test(eed->v1, hflag) && BM_elem_flag_test(eed->v2, hflag)) {
255                                 if (hflag & BM_ELEM_SELECT) {
256                                         BM_edge_select_set(em->bm, eed, true);
257                                 }
258
259                                 BM_elem_flag_enable(eed, hflag & ~BM_ELEM_SELECT);
260                         }
261                 }
262         }
263
264         return edbm_extrude_edge(obedit, em, hflag, nor);
265 }
266
267 static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op)
268 {
269         Object *obedit = CTX_data_edit_object(C);
270         BMEditMesh *em = BKE_editmesh_from_object(obedit);
271         RegionView3D *rv3d = CTX_wm_region_view3d(C);
272                 
273         const int steps = RNA_int_get(op->ptr, "steps");
274         
275         const float offs = RNA_float_get(op->ptr, "offset");
276         float dvec[3], tmat[3][3], bmat[3][3], nor[3] = {0.0, 0.0, 0.0};
277         short a;
278
279         /* dvec */
280         normalize_v3_v3(dvec, rv3d->persinv[2]);
281         mul_v3_fl(dvec, offs);
282
283         /* base correction */
284         copy_m3_m4(bmat, obedit->obmat);
285         invert_m3_m3(tmat, bmat);
286         mul_m3_v3(tmat, dvec);
287
288         for (a = 0; a < steps; a++) {
289                 edbm_extrude_edge(obedit, em, BM_ELEM_SELECT, nor);
290                 //BMO_op_callf(em->bm, BMO_FLAG_DEFAULTS, "extrude_face_region geom=%hef", BM_ELEM_SELECT);
291                 BMO_op_callf(em->bm, BMO_FLAG_DEFAULTS,
292                              "translate vec=%v verts=%hv",
293                              dvec, BM_ELEM_SELECT);
294                 //extrudeflag(obedit, em, SELECT, nor);
295                 //translateflag(em, SELECT, dvec);
296         }
297         
298         EDBM_mesh_normals_update(em);
299
300         EDBM_update_generic(em, true, true);
301
302         return OPERATOR_FINISHED;
303 }
304
305 void MESH_OT_extrude_repeat(wmOperatorType *ot)
306 {
307         /* identifiers */
308         ot->name = "Extrude Repeat Mesh";
309         ot->description = "Extrude selected vertices, edges or faces repeatedly";
310         ot->idname = "MESH_OT_extrude_repeat";
311         
312         /* api callbacks */
313         ot->exec = edbm_extrude_repeat_exec;
314         ot->poll = ED_operator_editmesh_view3d;
315         
316         /* flags */
317         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
318         
319         /* props */
320         RNA_def_float(ot->srna, "offset", 2.0f, 0.0f, FLT_MAX, "Offset", "", 0.0f, 100.0f);
321         RNA_def_int(ot->srna, "steps", 10, 0, INT_MAX, "Steps", "", 0, 180);
322 }
323
324 /* generic extern called extruder */
325 static int edbm_extrude_mesh(Scene *scene, Object *obedit, BMEditMesh *em, wmOperator *op, float *norin)
326 {
327         short nr, transmode = 0;
328         float stacknor[3] = {0.0f, 0.0f, 0.0f};
329         float *nor = norin ? norin : stacknor;
330
331         zero_v3(nor);
332
333         if (em->selectmode & SCE_SELECT_VERTEX) {
334                 if (em->bm->totvertsel == 0) nr = 0;
335                 else if (em->bm->totvertsel == 1) nr = 4;
336                 else if (em->bm->totedgesel == 0) nr = 4;
337                 else if (em->bm->totfacesel == 0)
338                         nr = 3;
339                 else if (em->bm->totfacesel == 1)
340                         nr = 1;
341                 else
342                         nr = 1;
343         }
344         else if (em->selectmode & SCE_SELECT_EDGE) {
345                 if (em->bm->totedgesel == 0) nr = 0;
346                 
347                 nr = 1;
348         }
349         else {
350                 if (em->bm->totfacesel == 0) nr = 0;
351                 else if (em->bm->totfacesel == 1) nr = 1;
352                 else
353                         nr = 1;
354         }
355
356         if (nr < 1) return 'g';
357
358         if (nr == 1 && (em->selectmode & SCE_SELECT_VERTEX))
359                 transmode = edbm_extrude_vert(obedit, em, BM_ELEM_SELECT, nor);
360         else if (nr == 1) transmode = edbm_extrude_edge(obedit, em, BM_ELEM_SELECT, nor);
361         else if (nr == 4) transmode = edbm_extrude_verts_indiv(em, op, BM_ELEM_SELECT, nor);
362         else if (nr == 3) transmode = edbm_extrude_edges_indiv(em, op, BM_ELEM_SELECT, nor);
363         else transmode = edbm_extrude_discrete_faces(em, op, BM_ELEM_SELECT, nor);
364         
365         if (transmode == 0) {
366                 BKE_report(op->reports, RPT_ERROR, "Not a valid selection for extrude");
367         }
368         else {
369                 
370                 /* We need to force immediate calculation here because
371                  * transform may use derived objects (which are now stale).
372                  *
373                  * This shouldn't be necessary, derived queries should be
374                  * automatically building this data if invalid. Or something.
375                  */
376 //              DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
377                 BKE_object_handle_update(scene, obedit);
378
379                 /* individual faces? */
380                 if (nr == 2) {
381 //                      initTransform(TFM_SHRINKFATTEN, CTX_NO_PET|CTX_NO_MIRROR);
382 //                      Transform();
383                 }
384                 else {
385 //                      initTransform(TFM_TRANSLATION, CTX_NO_PET|CTX_NO_MIRROR);
386                         if (transmode == 'n') {
387                                 mul_m4_v3(obedit->obmat, nor);
388                                 sub_v3_v3v3(nor, nor, obedit->obmat[3]);
389 //                              BIF_setSingleAxisConstraint(nor, "along normal");
390                         }
391 //                      Transform();
392                 }
393         }
394         
395         return transmode;
396 }
397
398 /* extrude without transform */
399 static int edbm_extrude_region_exec(bContext *C, wmOperator *op)
400 {
401         Scene *scene = CTX_data_scene(C);
402         Object *obedit = CTX_data_edit_object(C);
403         BMEditMesh *em = BKE_editmesh_from_object(obedit);
404         
405         edbm_extrude_mesh(scene, obedit, em, op, NULL);
406
407         /* This normally happens when pushing undo but modal operators
408          * like this one don't push undo data until after modal mode is
409          * done.*/
410         EDBM_mesh_normals_update(em);
411
412         EDBM_update_generic(em, true, true);
413         
414         return OPERATOR_FINISHED;
415 }
416
417 void MESH_OT_extrude_region(wmOperatorType *ot)
418 {
419         /* identifiers */
420         ot->name = "Extrude Region";
421         ot->idname = "MESH_OT_extrude_region";
422         ot->description = "Extrude region of faces";
423         
424         /* api callbacks */
425         //ot->invoke = mesh_extrude_region_invoke;
426         ot->exec = edbm_extrude_region_exec;
427         ot->poll = ED_operator_editmesh;
428         
429         /* flags */
430         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
431
432         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
433 }
434
435 static int edbm_extrude_verts_exec(bContext *C, wmOperator *op)
436 {
437         Object *obedit = CTX_data_edit_object(C);
438         BMEditMesh *em = BKE_editmesh_from_object(obedit);
439         float nor[3];
440
441         edbm_extrude_verts_indiv(em, op, BM_ELEM_SELECT, nor);
442         
443         EDBM_update_generic(em, true, true);
444         
445         return OPERATOR_FINISHED;
446 }
447
448 void MESH_OT_extrude_verts_indiv(wmOperatorType *ot)
449 {
450         /* identifiers */
451         ot->name = "Extrude Only Vertices";
452         ot->idname = "MESH_OT_extrude_verts_indiv";
453         ot->description = "Extrude individual vertices only";
454         
455         /* api callbacks */
456         ot->exec = edbm_extrude_verts_exec;
457         ot->poll = ED_operator_editmesh;
458         
459         /* flags */
460         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
461
462         /* to give to transform */
463         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
464 }
465
466 static int edbm_extrude_edges_exec(bContext *C, wmOperator *op)
467 {
468         Object *obedit = CTX_data_edit_object(C);
469         BMEditMesh *em = BKE_editmesh_from_object(obedit);
470         float nor[3];
471
472         edbm_extrude_edges_indiv(em, op, BM_ELEM_SELECT, nor);
473         
474         EDBM_update_generic(em, true, true);
475         
476         return OPERATOR_FINISHED;
477 }
478
479 void MESH_OT_extrude_edges_indiv(wmOperatorType *ot)
480 {
481         /* identifiers */
482         ot->name = "Extrude Only Edges";
483         ot->idname = "MESH_OT_extrude_edges_indiv";
484         ot->description = "Extrude individual edges only";
485         
486         /* api callbacks */
487         ot->exec = edbm_extrude_edges_exec;
488         ot->poll = ED_operator_editmesh;
489         
490         /* flags */
491         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
492
493         /* to give to transform */
494         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
495 }
496
497 static int edbm_extrude_faces_exec(bContext *C, wmOperator *op)
498 {
499         Object *obedit = CTX_data_edit_object(C);
500         BMEditMesh *em = BKE_editmesh_from_object(obedit);
501         float nor[3];
502
503         edbm_extrude_discrete_faces(em, op, BM_ELEM_SELECT, nor);
504         
505         EDBM_update_generic(em, true, true);
506         
507         return OPERATOR_FINISHED;
508 }
509
510 void MESH_OT_extrude_faces_indiv(wmOperatorType *ot)
511 {
512         /* identifiers */
513         ot->name = "Extrude Individual Faces";
514         ot->idname = "MESH_OT_extrude_faces_indiv";
515         ot->description = "Extrude individual faces only";
516         
517         /* api callbacks */
518         ot->exec = edbm_extrude_faces_exec;
519         ot->poll = ED_operator_editmesh;
520         
521         /* flags */
522         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
523
524         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
525 }
526
527 /* *************** add-click-mesh (extrude) operator ************** */
528 static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
529 {
530         ViewContext vc;
531         BMVert *v1;
532         BMIter iter;
533         float min[3], max[3];
534         bool done = false;
535         bool use_proj;
536         
537         em_setup_viewcontext(C, &vc);
538
539         ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
540
541
542         use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
543                     (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
544
545         INIT_MINMAX(min, max);
546         
547         BM_ITER_MESH (v1, &iter, vc.em->bm, BM_VERTS_OF_MESH) {
548                 if (BM_elem_flag_test(v1, BM_ELEM_SELECT)) {
549                         minmax_v3v3_v3(min, max, v1->co);
550                         done = true;
551                 }
552         }
553
554         /* call extrude? */
555         if (done) {
556                 const bool rot_src = RNA_boolean_get(op->ptr, "rotate_source");
557                 BMEdge *eed;
558                 float vec[3], cent[3], mat[3][3];
559                 float nor[3] = {0.0, 0.0, 0.0};
560
561                 /* 2D normal calc */
562                 const float mval_f[2] = {(float)event->mval[0],
563                                          (float)event->mval[1]};
564
565                 /* check for edges that are half selected, use for rotation */
566                 done = false;
567                 BM_ITER_MESH (eed, &iter, vc.em->bm, BM_EDGES_OF_MESH) {
568                         if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
569                                 float co1[2], co2[2];
570
571                                 if ((ED_view3d_project_float_object(vc.ar, eed->v1->co, co1, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) &&
572                                     (ED_view3d_project_float_object(vc.ar, eed->v2->co, co2, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK))
573                                 {
574                                         /* 2D rotate by 90d while adding.
575                                          *  (x, y) = (y, -x)
576                                          *
577                                          * accumulate the screenspace normal in 2D,
578                                          * with screenspace edge length weighting the result. */
579                                         if (line_point_side_v2(co1, co2, mval_f) >= 0.0f) {
580                                                 nor[0] +=  (co1[1] - co2[1]);
581                                                 nor[1] += -(co1[0] - co2[0]);
582                                         }
583                                         else {
584                                                 nor[0] +=  (co2[1] - co1[1]);
585                                                 nor[1] += -(co2[0] - co1[0]);
586                                         }
587                                         done = true;
588                                 }
589                         }
590                 }
591
592                 if (done) {
593                         float view_vec[3], cross[3];
594
595                         /* convert the 2D nomal into 3D */
596                         mul_mat3_m4_v3(vc.rv3d->viewinv, nor); /* worldspace */
597                         mul_mat3_m4_v3(vc.obedit->imat, nor); /* local space */
598
599                         /* correct the normal to be aligned on the view plane */
600                         copy_v3_v3(view_vec, vc.rv3d->viewinv[2]);
601                         mul_mat3_m4_v3(vc.obedit->imat, view_vec);
602                         cross_v3_v3v3(cross, nor, view_vec);
603                         cross_v3_v3v3(nor, view_vec, cross);
604                         normalize_v3(nor);
605                 }
606                 
607                 /* center */
608                 mid_v3_v3v3(cent, min, max);
609                 copy_v3_v3(min, cent);
610
611                 mul_m4_v3(vc.obedit->obmat, min);  /* view space */
612                 ED_view3d_win_to_3d_int(vc.ar, min, event->mval, min);
613                 mul_m4_v3(vc.obedit->imat, min); // back in object space
614
615                 sub_v3_v3(min, cent);
616                 
617                 /* calculate rotation */
618                 unit_m3(mat);
619                 if (done) {
620                         float angle;
621
622                         normalize_v3_v3(vec, min);
623
624                         angle = angle_normalized_v3v3(vec, nor);
625
626                         if (angle != 0.0f) {
627                                 float axis[3];
628
629                                 cross_v3_v3v3(axis, nor, vec);
630
631                                 /* halve the rotation if its applied twice */
632                                 if (rot_src) {
633                                         angle *= 0.5f;
634                                 }
635
636                                 axis_angle_to_mat3(mat, axis, angle);
637                         }
638                 }
639                 
640                 if (rot_src) {
641                         EDBM_op_callf(vc.em, op, "rotate verts=%hv cent=%v matrix=%m3",
642                                       BM_ELEM_SELECT, cent, mat);
643
644                         /* also project the source, for retopo workflow */
645                         if (use_proj)
646                                 EMBM_project_snap_verts(C, vc.ar, vc.em);
647                 }
648
649                 edbm_extrude_edge(vc.obedit, vc.em, BM_ELEM_SELECT, nor);
650                 EDBM_op_callf(vc.em, op, "rotate verts=%hv cent=%v matrix=%m3",
651                               BM_ELEM_SELECT, cent, mat);
652                 EDBM_op_callf(vc.em, op, "translate verts=%hv vec=%v",
653                               BM_ELEM_SELECT, min);
654         }
655         else {
656                 const float *curs = ED_view3d_cursor3d_get(vc.scene, vc.v3d);
657                 BMOperator bmop;
658                 BMOIter oiter;
659                 
660                 copy_v3_v3(min, curs);
661                 ED_view3d_win_to_3d_int(vc.ar, min, event->mval, min);
662
663                 invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
664                 mul_m4_v3(vc.obedit->imat, min); // back in object space
665                 
666                 EDBM_op_init(vc.em, &bmop, op, "create_vert co=%v", min);
667                 BMO_op_exec(vc.em->bm, &bmop);
668
669                 BMO_ITER (v1, &oiter, bmop.slots_out, "vert.out", BM_VERT) {
670                         BM_vert_select_set(vc.em->bm, v1, true);
671                 }
672
673                 if (!EDBM_op_finish(vc.em, &bmop, op, true)) {
674                         return OPERATOR_CANCELLED;
675                 }
676         }
677
678         if (use_proj)
679                 EMBM_project_snap_verts(C, vc.ar, vc.em);
680
681         /* This normally happens when pushing undo but modal operators
682          * like this one don't push undo data until after modal mode is
683          * done. */
684         EDBM_mesh_normals_update(vc.em);
685
686         EDBM_update_generic(vc.em, true, true);
687
688         return OPERATOR_FINISHED;
689 }
690
691 void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot)
692 {
693         /* identifiers */
694         ot->name = "Duplicate or Extrude at 3D Cursor";
695         ot->idname = "MESH_OT_dupli_extrude_cursor";
696         ot->description = "Duplicate and extrude selected vertices, edges or faces towards the mouse cursor";
697         
698         /* api callbacks */
699         ot->invoke = edbm_dupli_extrude_cursor_invoke;
700         ot->poll = ED_operator_editmesh;
701         
702         /* flags */
703         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
704
705         RNA_def_boolean(ot->srna, "rotate_source", 1, "Rotate Source", "Rotate initial selection giving better shape");
706 }
707
708
709 static int edbm_spin_exec(bContext *C, wmOperator *op)
710 {
711         Object *obedit = CTX_data_edit_object(C);
712         BMEditMesh *em = BKE_editmesh_from_object(obedit);
713         BMesh *bm = em->bm;
714         BMOperator spinop;
715         float cent[3], axis[3];
716         float d[3] = {0.0f, 0.0f, 0.0f};
717         int steps, dupli;
718         float angle;
719
720         RNA_float_get_array(op->ptr, "center", cent);
721         RNA_float_get_array(op->ptr, "axis", axis);
722         steps = RNA_int_get(op->ptr, "steps");
723         angle = RNA_float_get(op->ptr, "angle");
724         //if (ts->editbutflag & B_CLOCKWISE)
725         angle = -angle;
726         dupli = RNA_boolean_get(op->ptr, "dupli");
727
728         /* keep the values in worldspace since we're passing the obmat */
729         if (!EDBM_op_init(em, &spinop, op,
730                           "spin geom=%hvef cent=%v axis=%v dvec=%v steps=%i angle=%f space=%m4 use_duplicate=%b",
731                           BM_ELEM_SELECT, cent, axis, d, steps, angle, obedit->obmat, dupli))
732         {
733                 return OPERATOR_CANCELLED;
734         }
735         BMO_op_exec(bm, &spinop);
736         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
737         BMO_slot_buffer_hflag_enable(bm, spinop.slots_out, "geom_last.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
738         if (!EDBM_op_finish(em, &spinop, op, true)) {
739                 return OPERATOR_CANCELLED;
740         }
741
742         EDBM_update_generic(em, true, true);
743
744         return OPERATOR_FINISHED;
745 }
746
747 /* get center and axis, in global coords */
748 static int edbm_spin_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
749 {
750         Scene *scene = CTX_data_scene(C);
751         View3D *v3d = CTX_wm_view3d(C);
752         RegionView3D *rv3d = ED_view3d_context_rv3d(C);
753
754         RNA_float_set_array(op->ptr, "center", ED_view3d_cursor3d_get(scene, v3d));
755         RNA_float_set_array(op->ptr, "axis", rv3d->viewinv[2]);
756
757         return edbm_spin_exec(C, op);
758 }
759
760 void MESH_OT_spin(wmOperatorType *ot)
761 {
762         PropertyRNA *prop;
763
764         /* identifiers */
765         ot->name = "Spin";
766         ot->description = "Extrude selected vertices in a circle around the cursor in indicated viewport";
767         ot->idname = "MESH_OT_spin";
768
769         /* api callbacks */
770         ot->invoke = edbm_spin_invoke;
771         ot->exec = edbm_spin_exec;
772         ot->poll = EDBM_view3d_poll;
773
774         /* flags */
775         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
776
777         /* props */
778         RNA_def_int(ot->srna, "steps", 9, 0, INT_MAX, "Steps", "Steps", 0, INT_MAX);
779         RNA_def_boolean(ot->srna, "dupli", 0, "Dupli", "Make Duplicates");
780         prop = RNA_def_float(ot->srna, "angle", DEG2RADF(90.0f), -FLT_MAX, FLT_MAX, "Angle", "Angle", DEG2RADF(-360.0f), DEG2RADF(360.0f));
781         RNA_def_property_subtype(prop, PROP_ANGLE);
782
783         RNA_def_float_vector(ot->srna, "center", 3, NULL, -FLT_MAX, FLT_MAX, "Center", "Center in global view space", -FLT_MAX, FLT_MAX);
784         RNA_def_float_vector(ot->srna, "axis", 3, NULL, -FLT_MAX, FLT_MAX, "Axis", "Axis in global view space", -1.0f, 1.0f);
785
786 }
787
788 static int edbm_screw_exec(bContext *C, wmOperator *op)
789 {
790         Object *obedit = CTX_data_edit_object(C);
791         BMEditMesh *em = BKE_editmesh_from_object(obedit);
792         BMesh *bm = em->bm;
793         BMEdge *eed;
794         BMVert *eve, *v1, *v2;
795         BMIter iter, eiter;
796         BMOperator spinop;
797         float dvec[3], nor[3], cent[3], axis[3], v1_co_global[3], v2_co_global[3];
798         int steps, turns;
799         int valence;
800
801
802         turns = RNA_int_get(op->ptr, "turns");
803         steps = RNA_int_get(op->ptr, "steps");
804         RNA_float_get_array(op->ptr, "center", cent);
805         RNA_float_get_array(op->ptr, "axis", axis);
806
807         /* find two vertices with valence count == 1, more or less is wrong */
808         v1 = NULL;
809         v2 = NULL;
810
811         BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
812                 valence = 0;
813                 BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
814                         if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
815                                 valence++;
816                         }
817                 }
818
819                 if (valence == 1) {
820                         if (v1 == NULL) {
821                                 v1 = eve;
822                         }
823                         else if (v2 == NULL) {
824                                 v2 = eve;
825                         }
826                         else {
827                                 v1 = NULL;
828                                 break;
829                         }
830                 }
831         }
832
833         if (v1 == NULL || v2 == NULL) {
834                 BKE_report(op->reports, RPT_ERROR, "You have to select a string of connected vertices too");
835                 return OPERATOR_CANCELLED;
836         }
837
838         copy_v3_v3(nor, obedit->obmat[2]);
839
840         /* calculate dvec */
841         mul_v3_m4v3(v1_co_global, obedit->obmat, v1->co);
842         mul_v3_m4v3(v2_co_global, obedit->obmat, v2->co);
843         sub_v3_v3v3(dvec, v1_co_global, v2_co_global);
844         mul_v3_fl(dvec, 1.0f / steps);
845
846         if (dot_v3v3(nor, dvec) > 0.0f)
847                 negate_v3(dvec);
848
849         if (!EDBM_op_init(em, &spinop, op,
850                           "spin geom=%hvef cent=%v axis=%v dvec=%v steps=%i angle=%f space=%m4 use_duplicate=%b",
851                           BM_ELEM_SELECT, cent, axis, dvec, turns * steps, DEG2RADF(360.0f * turns), obedit->obmat, false))
852         {
853                 return OPERATOR_CANCELLED;
854         }
855         BMO_op_exec(bm, &spinop);
856         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
857         BMO_slot_buffer_hflag_enable(bm, spinop.slots_out, "geom_last.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
858         if (!EDBM_op_finish(em, &spinop, op, true)) {
859                 return OPERATOR_CANCELLED;
860         }
861
862         EDBM_update_generic(em, true, true);
863
864         return OPERATOR_FINISHED;
865 }
866
867 /* get center and axis, in global coords */
868 static int edbm_screw_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
869 {
870         Scene *scene = CTX_data_scene(C);
871         View3D *v3d = CTX_wm_view3d(C);
872         RegionView3D *rv3d = ED_view3d_context_rv3d(C);
873
874         RNA_float_set_array(op->ptr, "center", ED_view3d_cursor3d_get(scene, v3d));
875         RNA_float_set_array(op->ptr, "axis", rv3d->viewinv[1]);
876
877         return edbm_screw_exec(C, op);
878 }
879
880 void MESH_OT_screw(wmOperatorType *ot)
881 {
882         /* identifiers */
883         ot->name = "Screw";
884         ot->description = "Extrude selected vertices in screw-shaped rotation around the cursor in indicated viewport";
885         ot->idname = "MESH_OT_screw";
886
887         /* api callbacks */
888         ot->invoke = edbm_screw_invoke;
889         ot->exec = edbm_screw_exec;
890         ot->poll = EDBM_view3d_poll;
891
892         /* flags */
893         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
894
895         /* props */
896         RNA_def_int(ot->srna, "steps", 9, 1, INT_MAX, "Steps", "Steps", 3, 256);
897         RNA_def_int(ot->srna, "turns", 1, 1, INT_MAX, "Turns", "Turns", 1, 256);
898
899         RNA_def_float_vector(ot->srna, "center", 3, NULL, -FLT_MAX, FLT_MAX,
900                              "Center", "Center in global view space", -FLT_MAX, FLT_MAX);
901         RNA_def_float_vector(ot->srna, "axis", 3, NULL, -FLT_MAX, FLT_MAX,
902                              "Axis", "Axis in global view space", -1.0f, 1.0f);
903 }