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