Cleanup: style
[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 #include "BLI_listbase.h"
37
38 #include "BKE_layer.h"
39 #include "BKE_context.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_api.h"
48 #include "WM_types.h"
49
50 #include "ED_mesh.h"
51 #include "ED_screen.h"
52 #include "ED_transform.h"
53 #include "ED_view3d.h"
54
55 #include "UI_resources.h"
56
57 #include "MEM_guardedalloc.h"
58
59 #include "mesh_intern.h"  /* own include */
60
61 #define USE_MANIPULATOR
62
63 #ifdef USE_MANIPULATOR
64 #include "ED_manipulator_library.h"
65 #include "ED_undo.h"
66 #endif
67
68 /* -------------------------------------------------------------------- */
69 /** \name Extrude Internal Utilities
70  * \{ */
71
72 static void edbm_extrude_edge_exclude_mirror(
73         Object *obedit, BMEditMesh *em,
74         const char hflag,
75         BMOperator *op, BMOpSlot *slot_edges_exclude)
76 {
77         BMesh *bm = em->bm;
78         ModifierData *md;
79
80         /* If a mirror modifier with clipping is on, we need to adjust some
81          * of the cases above to handle edges on the line of symmetry.
82          */
83         for (md = obedit->modifiers.first; md; md = md->next) {
84                 if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
85                         MirrorModifierData *mmd = (MirrorModifierData *) md;
86
87                         if (mmd->flag & MOD_MIR_CLIPPING) {
88                                 BMIter iter;
89                                 BMEdge *edge;
90
91                                 float mtx[4][4];
92                                 if (mmd->mirror_ob) {
93                                         float imtx[4][4];
94                                         invert_m4_m4(imtx, mmd->mirror_ob->obmat);
95                                         mul_m4_m4m4(mtx, imtx, obedit->obmat);
96                                 }
97
98                                 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
99                                         if (BM_elem_flag_test(edge, hflag) &&
100                                             BM_edge_is_boundary(edge) &&
101                                             BM_elem_flag_test(edge->l->f, hflag))
102                                         {
103                                                 float co1[3], co2[3];
104
105                                                 copy_v3_v3(co1, edge->v1->co);
106                                                 copy_v3_v3(co2, edge->v2->co);
107
108                                                 if (mmd->mirror_ob) {
109                                                         mul_v3_m4v3(co1, mtx, co1);
110                                                         mul_v3_m4v3(co2, mtx, co2);
111                                                 }
112
113                                                 if (mmd->flag & MOD_MIR_AXIS_X) {
114                                                         if ((fabsf(co1[0]) < mmd->tolerance) &&
115                                                             (fabsf(co2[0]) < mmd->tolerance))
116                                                         {
117                                                                 BMO_slot_map_empty_insert(op, slot_edges_exclude, edge);
118                                                         }
119                                                 }
120                                                 if (mmd->flag & MOD_MIR_AXIS_Y) {
121                                                         if ((fabsf(co1[1]) < mmd->tolerance) &&
122                                                             (fabsf(co2[1]) < mmd->tolerance))
123                                                         {
124                                                                 BMO_slot_map_empty_insert(op, slot_edges_exclude, edge);
125                                                         }
126                                                 }
127                                                 if (mmd->flag & MOD_MIR_AXIS_Z) {
128                                                         if ((fabsf(co1[2]) < mmd->tolerance) &&
129                                                             (fabsf(co2[2]) < mmd->tolerance))
130                                                         {
131                                                                 BMO_slot_map_empty_insert(op, slot_edges_exclude, edge);
132                                                         }
133                                                 }
134                                         }
135                                 }
136                         }
137                 }
138         }
139 }
140
141 /* individual face extrude */
142 /* will use vertex normals for extrusion directions, so *nor is unaffected */
143 static bool edbm_extrude_discrete_faces(BMEditMesh *em, wmOperator *op, const char hflag)
144 {
145         BMOIter siter;
146         BMIter liter;
147         BMFace *f;
148         BMLoop *l;
149         BMOperator bmop;
150
151         EDBM_op_init(
152                 em, &bmop, op,
153                 "extrude_discrete_faces faces=%hf use_select_history=%b",
154                 hflag, true);
155
156         /* deselect original verts */
157         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
158
159         BMO_op_exec(em->bm, &bmop);
160
161         BMO_ITER (f, &siter, bmop.slots_out, "faces.out", BM_FACE) {
162                 BM_face_select_set(em->bm, f, true);
163
164                 /* set face vertex normals to face normal */
165                 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
166                         copy_v3_v3(l->v->no, f->no);
167                 }
168         }
169
170         if (!EDBM_op_finish(em, &bmop, op, true)) {
171                 return false;
172         }
173
174         return true;
175 }
176
177 /* extrudes individual edges */
178 static bool edbm_extrude_edges_indiv(BMEditMesh *em, wmOperator *op, const char hflag)
179 {
180         BMesh *bm = em->bm;
181         BMOperator bmop;
182
183         EDBM_op_init(
184                 em, &bmop, op,
185                 "extrude_edge_only edges=%he use_select_history=%b",
186                 hflag, true);
187
188         /* deselect original verts */
189         BM_SELECT_HISTORY_BACKUP(bm);
190         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
191         BM_SELECT_HISTORY_RESTORE(bm);
192
193         BMO_op_exec(em->bm, &bmop);
194         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_VERT | BM_EDGE, BM_ELEM_SELECT, true);
195
196         if (!EDBM_op_finish(em, &bmop, op, true)) {
197                 return false;
198         }
199
200         return true;
201 }
202
203 /* extrudes individual vertices */
204 static bool edbm_extrude_verts_indiv(BMEditMesh *em, wmOperator *op, const char hflag)
205 {
206         BMOperator bmop;
207
208         EDBM_op_init(
209                 em, &bmop, op,
210                 "extrude_vert_indiv verts=%hv use_select_history=%b",
211                 hflag, true);
212
213         /* deselect original verts */
214         BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_in, "verts", BM_VERT, BM_ELEM_SELECT, true);
215
216         BMO_op_exec(em->bm, &bmop);
217         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true);
218
219         if (!EDBM_op_finish(em, &bmop, op, true)) {
220                 return false;
221         }
222
223         return true;
224 }
225
226 static char edbm_extrude_htype_from_em_select(BMEditMesh *em)
227 {
228         char htype = BM_ALL_NOLOOP;
229
230         if (em->selectmode & SCE_SELECT_VERTEX) {
231                 /* pass */
232         }
233         else if (em->selectmode & SCE_SELECT_EDGE) {
234                 htype &= ~BM_VERT;
235         }
236         else {
237                 htype &= ~(BM_VERT | BM_EDGE);
238         }
239
240         if (em->bm->totedgesel == 0) {
241                 htype &= ~(BM_EDGE | BM_FACE);
242         }
243         else if (em->bm->totfacesel == 0) {
244                 htype &= ~BM_FACE;
245         }
246
247         return htype;
248 }
249
250 static bool edbm_extrude_ex(
251         Object *obedit, BMEditMesh *em,
252         char htype, const char hflag,
253         const bool use_mirror,
254         const bool use_select_history)
255 {
256         BMesh *bm = em->bm;
257         BMOIter siter;
258         BMOperator extop;
259         BMElem *ele;
260
261         /* needed to remove the faces left behind */
262         if (htype & BM_FACE) {
263                 htype |= BM_EDGE;
264         }
265
266         BMO_op_init(bm, &extop, BMO_FLAG_DEFAULTS, "extrude_face_region");
267         BMO_slot_bool_set(extop.slots_in, "use_select_history", use_select_history);
268         BMO_slot_buffer_from_enabled_hflag(bm, &extop, extop.slots_in, "geom", htype, hflag);
269
270         if (use_mirror) {
271                 BMOpSlot *slot_edges_exclude;
272                 slot_edges_exclude = BMO_slot_get(extop.slots_in, "edges_exclude");
273
274                 edbm_extrude_edge_exclude_mirror(obedit, em, hflag, &extop, slot_edges_exclude);
275         }
276
277         BM_SELECT_HISTORY_BACKUP(bm);
278         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
279         BM_SELECT_HISTORY_RESTORE(bm);
280
281         BMO_op_exec(bm, &extop);
282
283         BMO_ITER (ele, &siter, extop.slots_out, "geom.out", BM_ALL_NOLOOP) {
284                 BM_elem_select_set(bm, ele, true);
285         }
286
287         BMO_op_finish(bm, &extop);
288
289         return true;
290 }
291
292 /** \} */
293
294 /* -------------------------------------------------------------------- */
295 /** \name Extrude Repeat Operator
296  * \{ */
297
298 static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op)
299 {
300         Object *obedit = CTX_data_edit_object(C);
301         BMEditMesh *em = BKE_editmesh_from_object(obedit);
302         RegionView3D *rv3d = CTX_wm_region_view3d(C);
303
304         const int steps = RNA_int_get(op->ptr, "steps");
305
306         const float offs = RNA_float_get(op->ptr, "offset");
307         float dvec[3], tmat[3][3], bmat[3][3];
308         short a;
309
310         /* dvec */
311         normalize_v3_v3_length(dvec, rv3d->persinv[2], offs);
312
313         /* base correction */
314         copy_m3_m4(bmat, obedit->obmat);
315         invert_m3_m3(tmat, bmat);
316         mul_m3_v3(tmat, dvec);
317
318         for (a = 0; a < steps; a++) {
319                 edbm_extrude_ex(obedit, em, BM_ALL_NOLOOP, BM_ELEM_SELECT, false, false);
320
321                 BMO_op_callf(
322                         em->bm, BMO_FLAG_DEFAULTS,
323                         "translate vec=%v verts=%hv",
324                         dvec, BM_ELEM_SELECT);
325         }
326
327         EDBM_mesh_normals_update(em);
328
329         EDBM_update_generic(em, true, true);
330
331         return OPERATOR_FINISHED;
332 }
333
334 void MESH_OT_extrude_repeat(wmOperatorType *ot)
335 {
336         /* identifiers */
337         ot->name = "Extrude Repeat Mesh";
338         ot->description = "Extrude selected vertices, edges or faces repeatedly";
339         ot->idname = "MESH_OT_extrude_repeat";
340
341         /* api callbacks */
342         ot->exec = edbm_extrude_repeat_exec;
343         ot->poll = ED_operator_editmesh_view3d;
344
345         /* flags */
346         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
347
348         /* props */
349         RNA_def_float_distance(ot->srna, "offset", 2.0f, 0.0f, 1e12f, "Offset", "", 0.0f, 100.0f);
350         RNA_def_int(ot->srna, "steps", 10, 0, 1000000, "Steps", "", 0, 180);
351 }
352
353 /** \} */
354
355 /* -------------------------------------------------------------------- */
356 /** \name Extrude Operator
357  * \{ */
358
359 /* generic extern called extruder */
360 static bool edbm_extrude_mesh(Object *obedit, BMEditMesh *em, wmOperator *op)
361 {
362         bool changed = false;
363         const char htype = edbm_extrude_htype_from_em_select(em);
364         enum {NONE = 0, ELEM_FLAG, VERT_ONLY, EDGE_ONLY} nr;
365
366         if (em->selectmode & SCE_SELECT_VERTEX) {
367                 if      (em->bm->totvertsel == 0) nr = NONE;
368                 else if (em->bm->totvertsel == 1) nr = VERT_ONLY;
369                 else if (em->bm->totedgesel == 0) nr = VERT_ONLY;
370                 else                              nr = ELEM_FLAG;
371         }
372         else if (em->selectmode & SCE_SELECT_EDGE) {
373                 if      (em->bm->totedgesel == 0) nr = NONE;
374                 else if (em->bm->totfacesel == 0) nr = EDGE_ONLY;
375                 else                              nr = ELEM_FLAG;
376         }
377         else {
378                 if      (em->bm->totfacesel == 0) nr = NONE;
379                 else                              nr = ELEM_FLAG;
380         }
381
382         switch (nr) {
383                 case NONE:
384                         return false;
385                 case ELEM_FLAG:
386                         changed = edbm_extrude_ex(obedit, em, htype, BM_ELEM_SELECT, true, true);
387                         break;
388                 case VERT_ONLY:
389                         changed = edbm_extrude_verts_indiv(em, op, BM_ELEM_SELECT);
390                         break;
391                 case EDGE_ONLY:
392                         changed = edbm_extrude_edges_indiv(em, op, BM_ELEM_SELECT);
393                         break;
394         }
395
396         if (changed) {
397                 return true;
398         }
399         else {
400                 BKE_report(op->reports, RPT_ERROR, "Not a valid selection for extrude");
401                 return false;
402         }
403 }
404
405 /* extrude without transform */
406 static int edbm_extrude_region_exec(bContext *C, wmOperator *op)
407 {
408         Object *obedit = CTX_data_edit_object(C);
409         BMEditMesh *em = BKE_editmesh_from_object(obedit);
410
411         edbm_extrude_mesh(obedit, em, op);
412
413         /* This normally happens when pushing undo but modal operators
414          * like this one don't push undo data until after modal mode is
415          * done.*/
416         EDBM_mesh_normals_update(em);
417
418         EDBM_update_generic(em, true, true);
419
420         return OPERATOR_FINISHED;
421 }
422
423 void MESH_OT_extrude_region(wmOperatorType *ot)
424 {
425         /* identifiers */
426         ot->name = "Extrude Region";
427         ot->idname = "MESH_OT_extrude_region";
428         ot->description = "Extrude region of faces";
429
430         /* api callbacks */
431         //ot->invoke = mesh_extrude_region_invoke;
432         ot->exec = edbm_extrude_region_exec;
433         ot->poll = ED_operator_editmesh;
434
435         /* flags */
436         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
437
438         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
439 }
440
441 /** \} */
442
443 /* -------------------------------------------------------------------- */
444 /** \name Extrude Verts Operator
445  * \{ */
446
447 static int edbm_extrude_verts_exec(bContext *C, wmOperator *op)
448 {
449         ViewLayer *view_layer = CTX_data_view_layer(C);
450         uint objects_len = 0;
451         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
452
453         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
454                 Object *obedit = objects[ob_index];
455                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
456                 if (em->bm->totvertsel == 0) {
457                         continue;
458                 }
459
460                 edbm_extrude_verts_indiv(em, op, BM_ELEM_SELECT);
461
462                 EDBM_update_generic(em, true, true);
463         }
464         MEM_freeN(objects);
465
466         return OPERATOR_FINISHED;
467 }
468
469 void MESH_OT_extrude_verts_indiv(wmOperatorType *ot)
470 {
471         /* identifiers */
472         ot->name = "Extrude Only Vertices";
473         ot->idname = "MESH_OT_extrude_verts_indiv";
474         ot->description = "Extrude individual vertices only";
475
476         /* api callbacks */
477         ot->exec = edbm_extrude_verts_exec;
478         ot->poll = ED_operator_editmesh;
479
480         /* flags */
481         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
482
483         /* to give to transform */
484         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
485 }
486
487 /** \} */
488
489 /* -------------------------------------------------------------------- */
490 /** \name Extrude Edges Operator
491  * \{ */
492
493 static int edbm_extrude_edges_exec(bContext *C, wmOperator *op)
494 {
495         ViewLayer *view_layer = CTX_data_view_layer(C);
496         uint objects_len = 0;
497         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
498
499         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
500                 Object *obedit = objects[ob_index];
501                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
502                 if (em->bm->totedgesel == 0) {
503                         continue;
504                 }
505
506                 edbm_extrude_edges_indiv(em, op, BM_ELEM_SELECT);
507
508                 EDBM_update_generic(em, true, true);
509         }
510         MEM_freeN(objects);
511
512         return OPERATOR_FINISHED;
513 }
514
515 void MESH_OT_extrude_edges_indiv(wmOperatorType *ot)
516 {
517         /* identifiers */
518         ot->name = "Extrude Only Edges";
519         ot->idname = "MESH_OT_extrude_edges_indiv";
520         ot->description = "Extrude individual edges only";
521
522         /* api callbacks */
523         ot->exec = edbm_extrude_edges_exec;
524         ot->poll = ED_operator_editmesh;
525
526         /* flags */
527         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
528
529         /* to give to transform */
530         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
531 }
532
533 /** \} */
534
535 /* -------------------------------------------------------------------- */
536 /** \name Extrude Faces Operator
537  * \{ */
538
539 static int edbm_extrude_faces_exec(bContext *C, wmOperator *op)
540 {
541         ViewLayer *view_layer = CTX_data_view_layer(C);
542         uint objects_len = 0;
543         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
544
545         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
546                 Object *obedit = objects[ob_index];
547                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
548                 if (em->bm->totfacesel == 0) {
549                         continue;
550                 }
551
552                 edbm_extrude_discrete_faces(em, op, BM_ELEM_SELECT);
553
554                 EDBM_update_generic(em, true, true);
555         }
556         MEM_freeN(objects);
557
558         return OPERATOR_FINISHED;
559 }
560
561 void MESH_OT_extrude_faces_indiv(wmOperatorType *ot)
562 {
563         /* identifiers */
564         ot->name = "Extrude Individual Faces";
565         ot->idname = "MESH_OT_extrude_faces_indiv";
566         ot->description = "Extrude individual faces only";
567
568         /* api callbacks */
569         ot->exec = edbm_extrude_faces_exec;
570         ot->poll = ED_operator_editmesh;
571
572         /* flags */
573         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
574
575         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
576 }
577
578 /** \} */
579
580 /* -------------------------------------------------------------------- */
581 /** \name Dupli-Extrude Operator
582  *
583  * Add-click-mesh (extrude) operator.
584  * \{ */
585
586 static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
587 {
588         ViewContext vc;
589         BMVert *v1;
590         BMIter iter;
591         float center[3];
592         uint verts_len;
593         bool use_proj;
594
595         em_setup_viewcontext(C, &vc);
596
597         invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
598
599         ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
600
601         use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
602                     (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
603
604         zero_v3(center);
605         verts_len = 0;
606
607         BM_ITER_MESH (v1, &iter, vc.em->bm, BM_VERTS_OF_MESH) {
608                 if (BM_elem_flag_test(v1, BM_ELEM_SELECT)) {
609                         add_v3_v3(center, v1->co);
610                         verts_len += 1;
611                 }
612         }
613
614         /* call extrude? */
615         if (verts_len != 0) {
616                 const char extrude_htype = edbm_extrude_htype_from_em_select(vc.em);
617                 const bool rot_src = RNA_boolean_get(op->ptr, "rotate_source");
618                 BMEdge *eed;
619                 float mat[3][3];
620                 float vec[3], ofs[3];
621                 float nor[3] = {0.0, 0.0, 0.0};
622
623                 /* 2D normal calc */
624                 const float mval_f[2] = {(float)event->mval[0],
625                                          (float)event->mval[1]};
626
627                 mul_v3_fl(center, 1.0f / (float)verts_len);
628
629                 /* check for edges that are half selected, use for rotation */
630                 bool done = false;
631                 BM_ITER_MESH (eed, &iter, vc.em->bm, BM_EDGES_OF_MESH) {
632                         if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
633                                 float co1[2], co2[2];
634
635                                 if ((ED_view3d_project_float_object(vc.ar, eed->v1->co, co1, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) &&
636                                     (ED_view3d_project_float_object(vc.ar, eed->v2->co, co2, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK))
637                                 {
638                                         /* 2D rotate by 90d while adding.
639                                          *  (x, y) = (y, -x)
640                                          *
641                                          * accumulate the screenspace normal in 2D,
642                                          * with screenspace edge length weighting the result. */
643                                         if (line_point_side_v2(co1, co2, mval_f) >= 0.0f) {
644                                                 nor[0] +=  (co1[1] - co2[1]);
645                                                 nor[1] += -(co1[0] - co2[0]);
646                                         }
647                                         else {
648                                                 nor[0] +=  (co2[1] - co1[1]);
649                                                 nor[1] += -(co2[0] - co1[0]);
650                                         }
651                                         done = true;
652                                 }
653                         }
654                 }
655
656                 if (done) {
657                         float view_vec[3], cross[3];
658
659                         /* convert the 2D nomal into 3D */
660                         mul_mat3_m4_v3(vc.rv3d->viewinv, nor); /* worldspace */
661                         mul_mat3_m4_v3(vc.obedit->imat, nor); /* local space */
662
663                         /* correct the normal to be aligned on the view plane */
664                         mul_v3_mat3_m4v3(view_vec, vc.obedit->imat, vc.rv3d->viewinv[2]);
665                         cross_v3_v3v3(cross, nor, view_vec);
666                         cross_v3_v3v3(nor, view_vec, cross);
667                         normalize_v3(nor);
668                 }
669
670                 /* center */
671                 copy_v3_v3(ofs, center);
672
673                 mul_m4_v3(vc.obedit->obmat, ofs);  /* view space */
674                 ED_view3d_win_to_3d_int(vc.v3d, vc.ar, ofs, event->mval, ofs);
675                 mul_m4_v3(vc.obedit->imat, ofs); // back in object space
676
677                 sub_v3_v3(ofs, center);
678
679                 /* calculate rotation */
680                 unit_m3(mat);
681                 if (done) {
682                         float angle;
683
684                         normalize_v3_v3(vec, ofs);
685
686                         angle = angle_normalized_v3v3(vec, nor);
687
688                         if (angle != 0.0f) {
689                                 float axis[3];
690
691                                 cross_v3_v3v3(axis, nor, vec);
692
693                                 /* halve the rotation if its applied twice */
694                                 if (rot_src) {
695                                         angle *= 0.5f;
696                                 }
697
698                                 axis_angle_to_mat3(mat, axis, angle);
699                         }
700                 }
701
702                 if (rot_src) {
703                         EDBM_op_callf(vc.em, op, "rotate verts=%hv cent=%v matrix=%m3",
704                                       BM_ELEM_SELECT, center, mat);
705
706                         /* also project the source, for retopo workflow */
707                         if (use_proj)
708                                 EMBM_project_snap_verts(C, vc.ar, vc.em);
709                 }
710
711                 edbm_extrude_ex(vc.obedit, vc.em, extrude_htype, BM_ELEM_SELECT, true, true);
712                 EDBM_op_callf(vc.em, op, "rotate verts=%hv cent=%v matrix=%m3",
713                               BM_ELEM_SELECT, center, mat);
714                 EDBM_op_callf(vc.em, op, "translate verts=%hv vec=%v",
715                               BM_ELEM_SELECT, ofs);
716         }
717         else {
718                 const float *cursor = ED_view3d_cursor3d_get(vc.scene, vc.v3d);
719                 BMOperator bmop;
720                 BMOIter oiter;
721
722                 copy_v3_v3(center, cursor);
723                 ED_view3d_win_to_3d_int(vc.v3d, vc.ar, center, event->mval, center);
724
725                 mul_m4_v3(vc.obedit->imat, center); // back in object space
726
727                 EDBM_op_init(vc.em, &bmop, op, "create_vert co=%v", center);
728                 BMO_op_exec(vc.em->bm, &bmop);
729
730                 BMO_ITER (v1, &oiter, bmop.slots_out, "vert.out", BM_VERT) {
731                         BM_vert_select_set(vc.em->bm, v1, true);
732                 }
733
734                 if (!EDBM_op_finish(vc.em, &bmop, op, true)) {
735                         return OPERATOR_CANCELLED;
736                 }
737         }
738
739         if (use_proj)
740                 EMBM_project_snap_verts(C, vc.ar, vc.em);
741
742         /* This normally happens when pushing undo but modal operators
743          * like this one don't push undo data until after modal mode is
744          * done. */
745         EDBM_mesh_normals_update(vc.em);
746
747         EDBM_update_generic(vc.em, true, true);
748
749         return OPERATOR_FINISHED;
750 }
751
752 void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot)
753 {
754         /* identifiers */
755         ot->name = "Duplicate or Extrude to Cursor";
756         ot->idname = "MESH_OT_dupli_extrude_cursor";
757         ot->description = "Duplicate and extrude selected vertices, edges or faces towards the mouse cursor";
758
759         /* api callbacks */
760         ot->invoke = edbm_dupli_extrude_cursor_invoke;
761         ot->poll = ED_operator_editmesh_region_view3d;
762
763         /* flags */
764         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
765
766         RNA_def_boolean(ot->srna, "rotate_source", true, "Rotate Source", "Rotate initial selection giving better shape");
767 }
768
769 /** \} */
770
771 /* -------------------------------------------------------------------- */
772 /** \name Spin Operator
773  * \{ */
774
775 static int edbm_spin_exec(bContext *C, wmOperator *op)
776 {
777         Object *obedit = CTX_data_edit_object(C);
778         BMEditMesh *em = BKE_editmesh_from_object(obedit);
779         BMesh *bm = em->bm;
780         BMOperator spinop;
781         float cent[3], axis[3];
782         float d[3] = {0.0f, 0.0f, 0.0f};
783         int steps, dupli;
784         float angle;
785
786         RNA_float_get_array(op->ptr, "center", cent);
787         RNA_float_get_array(op->ptr, "axis", axis);
788         steps = RNA_int_get(op->ptr, "steps");
789         angle = RNA_float_get(op->ptr, "angle");
790         //if (ts->editbutflag & B_CLOCKWISE)
791         angle = -angle;
792         dupli = RNA_boolean_get(op->ptr, "dupli");
793
794         if (is_zero_v3(axis)) {
795                 BKE_report(op->reports, RPT_ERROR, "Invalid/unset axis");
796                 return OPERATOR_CANCELLED;
797         }
798
799         /* keep the values in worldspace since we're passing the obmat */
800         if (!EDBM_op_init(em, &spinop, op,
801                           "spin geom=%hvef cent=%v axis=%v dvec=%v steps=%i angle=%f space=%m4 use_duplicate=%b",
802                           BM_ELEM_SELECT, cent, axis, d, steps, angle, obedit->obmat, dupli))
803         {
804                 return OPERATOR_CANCELLED;
805         }
806         BMO_op_exec(bm, &spinop);
807         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
808         BMO_slot_buffer_hflag_enable(bm, spinop.slots_out, "geom_last.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
809         if (!EDBM_op_finish(em, &spinop, op, true)) {
810                 return OPERATOR_CANCELLED;
811         }
812
813         EDBM_update_generic(em, true, true);
814
815         return OPERATOR_FINISHED;
816 }
817
818 /* get center and axis, in global coords */
819 static int edbm_spin_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
820 {
821         Scene *scene = CTX_data_scene(C);
822         View3D *v3d = CTX_wm_view3d(C);
823         RegionView3D *rv3d = ED_view3d_context_rv3d(C);
824
825         PropertyRNA *prop;
826         prop = RNA_struct_find_property(op->ptr, "center");
827         if (!RNA_property_is_set(op->ptr, prop)) {
828                 RNA_property_float_set_array(op->ptr, prop, ED_view3d_cursor3d_get(scene, v3d));
829         }
830         if (rv3d) {
831                 prop = RNA_struct_find_property(op->ptr, "axis");
832                 if (!RNA_property_is_set(op->ptr, prop)) {
833                         RNA_property_float_set_array(op->ptr, prop, rv3d->viewinv[2]);
834                 }
835         }
836
837         int ret = edbm_spin_exec(C, op);
838
839 #ifdef USE_MANIPULATOR
840         if (ret & OPERATOR_FINISHED) {
841                 /* Setup manipulators */
842                 if (v3d && (v3d->twtype & V3D_MANIPULATOR_DRAW)) {
843                         WM_manipulator_group_type_ensure("MESH_WGT_spin");
844                 }
845         }
846 #endif
847
848         return ret;
849
850 }
851
852 #ifdef USE_MANIPULATOR
853 static void MESH_WGT_spin(struct wmManipulatorGroupType *wgt);
854 #endif
855
856 void MESH_OT_spin(wmOperatorType *ot)
857 {
858         PropertyRNA *prop;
859
860         /* identifiers */
861         ot->name = "Spin";
862         ot->description = "Extrude selected vertices in a circle around the cursor in indicated viewport";
863         ot->idname = "MESH_OT_spin";
864
865         /* api callbacks */
866         ot->invoke = edbm_spin_invoke;
867         ot->exec = edbm_spin_exec;
868         ot->poll = ED_operator_editmesh;
869
870         /* flags */
871         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
872
873         /* props */
874         RNA_def_int(ot->srna, "steps", 9, 0, 1000000, "Steps", "Steps", 0, 1000);
875         RNA_def_boolean(ot->srna, "dupli", 0, "Dupli", "Make Duplicates");
876         prop = RNA_def_float(ot->srna, "angle", DEG2RADF(90.0f), -1e12f, 1e12f, "Angle", "Rotation for each step",
877                              DEG2RADF(-360.0f), DEG2RADF(360.0f));
878         RNA_def_property_subtype(prop, PROP_ANGLE);
879
880         RNA_def_float_vector(ot->srna, "center", 3, NULL, -1e12f, 1e12f,
881                              "Center", "Center in global view space", -1e4f, 1e4f);
882         RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f, "Axis", "Axis in global view space", -1.0f, 1.0f);
883
884 #ifdef USE_MANIPULATOR
885         WM_manipulatorgrouptype_append(MESH_WGT_spin);
886 #endif
887 }
888
889
890 #ifdef USE_MANIPULATOR
891
892 /* -------------------------------------------------------------------- */
893 /** \name Screw Operator
894  * \{ */
895
896 typedef struct ManipulatorSpinGroup {
897         /* Arrow to change plane depth. */
898         struct wmManipulator *translate_z;
899         /* Translate XYZ */
900         struct wmManipulator *translate_c;
901         /* For grabbing the manipulator and moving freely. */
902         struct wmManipulator *rotate_c;
903         /* Spin angle */
904         struct wmManipulator *angle_z;
905
906         /* We could store more vars here! */
907         struct {
908                 bContext *context;
909                 wmOperator *op;
910                 PropertyRNA *prop_axis_co;
911                 PropertyRNA *prop_axis_no;
912                 PropertyRNA *prop_angle;
913
914                 float rotate_axis[3];
915                 float rotate_up[3];
916         } data;
917 } ManipulatorSpinGroup;
918
919 /**
920  * XXX. calling redo from property updates is not great.
921  * This is needed because changing the RNA doesn't cause a redo
922  * and we're not using operator UI which does just this.
923  */
924 static void manipulator_spin_exec(ManipulatorSpinGroup *man)
925 {
926         wmOperator *op = man->data.op;
927         if (op == WM_operator_last_redo((bContext *)man->data.context)) {
928                 ED_undo_operator_repeat((bContext *)man->data.context, op);
929         }
930 }
931
932 static void manipulator_mesh_spin_update_from_op(ManipulatorSpinGroup *man)
933 {
934         wmOperator *op = man->data.op;
935
936         float plane_co[3], plane_no[3];
937
938         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, plane_co);
939         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
940
941         WM_manipulator_set_matrix_location(man->translate_z, plane_co);
942         WM_manipulator_set_matrix_location(man->rotate_c, plane_co);
943         WM_manipulator_set_matrix_location(man->angle_z, plane_co);
944         /* translate_c location comes from the property. */
945
946         WM_manipulator_set_matrix_rotation_from_z_axis(man->translate_z, plane_no);
947         WM_manipulator_set_matrix_rotation_from_z_axis(man->angle_z, plane_no);
948
949         WM_manipulator_set_scale(man->translate_c, 0.2);
950
951         RegionView3D *rv3d = ED_view3d_context_rv3d(man->data.context);
952         if (rv3d) {
953                 normalize_v3_v3(man->data.rotate_axis, rv3d->viewinv[2]);
954                 normalize_v3_v3(man->data.rotate_up, rv3d->viewinv[1]);
955
956                 /* ensure its orthogonal */
957                 project_plane_normalized_v3_v3v3(man->data.rotate_up, man->data.rotate_up, man->data.rotate_axis);
958                 normalize_v3(man->data.rotate_up);
959
960                 WM_manipulator_set_matrix_rotation_from_z_axis(man->translate_c, plane_no);
961                 WM_manipulator_set_matrix_rotation_from_yz_axis(man->rotate_c, plane_no, man->data.rotate_axis);
962
963                 /* show the axis instead of mouse cursor */
964                 RNA_enum_set(man->rotate_c->ptr, "draw_options",
965                              ED_MANIPULATOR_DIAL_DRAW_FLAG_ANGLE_MIRROR |
966                              ED_MANIPULATOR_DIAL_DRAW_FLAG_ANGLE_START_Y);
967
968         }
969 }
970
971 /* depth callbacks */
972 static void manipulator_spin_prop_depth_get(
973         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
974         void *value_p)
975 {
976         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
977         wmOperator *op = man->data.op;
978         float *value = value_p;
979
980         BLI_assert(mpr_prop->type->array_length == 1);
981         UNUSED_VARS_NDEBUG(mpr_prop);
982
983         float plane_co[3], plane_no[3];
984         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, plane_co);
985         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
986
987         value[0] = dot_v3v3(plane_no, plane_co) - dot_v3v3(plane_no, mpr->matrix_basis[3]);
988 }
989
990 static void manipulator_spin_prop_depth_set(
991         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
992         const void *value_p)
993 {
994         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
995         wmOperator *op = man->data.op;
996         const float *value = value_p;
997
998         BLI_assert(mpr_prop->type->array_length == 1);
999         UNUSED_VARS_NDEBUG(mpr_prop);
1000
1001         float plane_co[3], plane[4];
1002         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, plane_co);
1003         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane);
1004         normalize_v3(plane);
1005
1006         plane[3] = -value[0] - dot_v3v3(plane, mpr->matrix_basis[3]);
1007
1008         /* Keep our location, may be offset simply to be inside the viewport. */
1009         closest_to_plane_normalized_v3(plane_co, plane, plane_co);
1010
1011         RNA_property_float_set_array(op->ptr, man->data.prop_axis_co, plane_co);
1012
1013         manipulator_spin_exec(man);
1014 }
1015
1016 /* translate callbacks */
1017 static void manipulator_spin_prop_translate_get(
1018         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1019         void *value_p)
1020 {
1021         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1022         wmOperator *op = man->data.op;
1023         float *value = value_p;
1024
1025         BLI_assert(mpr_prop->type->array_length == 3);
1026         UNUSED_VARS_NDEBUG(mpr_prop);
1027
1028         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, value);
1029 }
1030
1031 static void manipulator_spin_prop_translate_set(
1032         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1033         const void *value)
1034 {
1035         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1036         wmOperator *op = man->data.op;
1037
1038         BLI_assert(mpr_prop->type->array_length == 3);
1039         UNUSED_VARS_NDEBUG(mpr_prop);
1040
1041         RNA_property_float_set_array(op->ptr, man->data.prop_axis_co, value);
1042
1043         manipulator_spin_exec(man);
1044 }
1045
1046 /* angle callbacks */
1047 static void manipulator_spin_prop_axis_angle_get(
1048         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1049         void *value_p)
1050 {
1051         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1052         wmOperator *op = man->data.op;
1053         float *value = value_p;
1054
1055         BLI_assert(mpr_prop->type->array_length == 1);
1056         UNUSED_VARS_NDEBUG(mpr_prop);
1057
1058         float plane_no[4];
1059         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
1060         normalize_v3(plane_no);
1061
1062         float plane_no_proj[3];
1063         project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, man->data.rotate_axis);
1064
1065         if (!is_zero_v3(plane_no_proj)) {
1066                 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, man->data.rotate_up, man->data.rotate_axis);
1067                 value[0] = angle;
1068         }
1069         else {
1070                 value[0] = 0.0f;
1071         }
1072 }
1073
1074 static void manipulator_spin_prop_axis_angle_set(
1075         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1076         const void *value_p)
1077 {
1078         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1079         wmOperator *op = man->data.op;
1080         const float *value = value_p;
1081
1082         BLI_assert(mpr_prop->type->array_length == 1);
1083         UNUSED_VARS_NDEBUG(mpr_prop);
1084
1085         float plane_no[4];
1086         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
1087         normalize_v3(plane_no);
1088
1089         float plane_no_proj[3];
1090         project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, man->data.rotate_axis);
1091
1092         if (!is_zero_v3(plane_no_proj)) {
1093                 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, man->data.rotate_up, man->data.rotate_axis);
1094                 const float angle_delta = angle - angle_compat_rad(value[0], angle);
1095                 if (angle_delta != 0.0f) {
1096                         float mat[3][3];
1097                         axis_angle_normalized_to_mat3(mat, man->data.rotate_axis, angle_delta);
1098                         mul_m3_v3(mat, plane_no);
1099
1100                         /* re-normalize - seems acceptable */
1101                         RNA_property_float_set_array(op->ptr, man->data.prop_axis_no, plane_no);
1102
1103                         manipulator_spin_exec(man);
1104                 }
1105         }
1106 }
1107
1108 /* angle callbacks */
1109 static void manipulator_spin_prop_angle_get(
1110         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1111         void *value_p)
1112 {
1113         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1114         wmOperator *op = man->data.op;
1115         float *value = value_p;
1116
1117         BLI_assert(mpr_prop->type->array_length == 1);
1118         UNUSED_VARS_NDEBUG(mpr_prop);
1119         value[0] = RNA_property_float_get(op->ptr, man->data.prop_angle);
1120 }
1121
1122 static void manipulator_spin_prop_angle_set(
1123         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1124         const void *value_p)
1125 {
1126         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1127         wmOperator *op = man->data.op;
1128         BLI_assert(mpr_prop->type->array_length == 1);
1129         UNUSED_VARS_NDEBUG(mpr_prop);
1130         const float *value = value_p;
1131         RNA_property_float_set(op->ptr, man->data.prop_angle, value[0]);
1132
1133         manipulator_spin_exec(man);
1134 }
1135
1136 static bool manipulator_mesh_spin_poll(const bContext *C, wmManipulatorGroupType *wgt)
1137 {
1138         wmOperator *op = WM_operator_last_redo(C);
1139         if (op == NULL || !STREQ(op->type->idname, "MESH_OT_spin")) {
1140                 WM_manipulator_group_type_unlink_delayed_ptr(wgt);
1141                 return false;
1142         }
1143         return true;
1144 }
1145
1146 static void manipulator_mesh_spin_setup(const bContext *C, wmManipulatorGroup *mgroup)
1147 {
1148         wmOperator *op = WM_operator_last_redo(C);
1149
1150         if (op == NULL || !STREQ(op->type->idname, "MESH_OT_spin")) {
1151                 return;
1152         }
1153
1154         struct ManipulatorSpinGroup *man = MEM_callocN(sizeof(ManipulatorSpinGroup), __func__);
1155         mgroup->customdata = man;
1156
1157         const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_3d", true);
1158         const wmManipulatorType *wt_grab = WM_manipulatortype_find("MANIPULATOR_WT_grab_3d", true);
1159         const wmManipulatorType *wt_dial = WM_manipulatortype_find("MANIPULATOR_WT_dial_3d", true);
1160
1161         man->translate_z = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL);
1162         man->translate_c = WM_manipulator_new_ptr(wt_grab, mgroup, NULL);
1163         man->rotate_c = WM_manipulator_new_ptr(wt_dial, mgroup, NULL);
1164         man->angle_z = WM_manipulator_new_ptr(wt_dial, mgroup, NULL);
1165
1166         UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, man->translate_z->color);
1167         UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, man->translate_c->color);
1168         UI_GetThemeColor3fv(TH_MANIPULATOR_SECONDARY, man->rotate_c->color);
1169         UI_GetThemeColor3fv(TH_AXIS_Z, man->angle_z->color);
1170
1171
1172         RNA_enum_set(man->translate_z->ptr, "draw_style", ED_MANIPULATOR_ARROW_STYLE_NORMAL);
1173         RNA_enum_set(man->translate_c->ptr, "draw_style", ED_MANIPULATOR_GRAB_STYLE_RING_2D);
1174
1175         WM_manipulator_set_flag(man->translate_c, WM_MANIPULATOR_DRAW_VALUE, true);
1176         WM_manipulator_set_flag(man->rotate_c, WM_MANIPULATOR_DRAW_VALUE, true);
1177         WM_manipulator_set_flag(man->angle_z, WM_MANIPULATOR_DRAW_VALUE, true);
1178
1179         WM_manipulator_set_scale(man->angle_z, 0.5f);
1180
1181         {
1182                 man->data.context = (bContext *)C;
1183                 man->data.op = op;
1184                 man->data.prop_axis_co = RNA_struct_find_property(op->ptr, "center");
1185                 man->data.prop_axis_no = RNA_struct_find_property(op->ptr, "axis");
1186                 man->data.prop_angle = RNA_struct_find_property(op->ptr, "angle");
1187         }
1188
1189         manipulator_mesh_spin_update_from_op(man);
1190
1191         /* Setup property callbacks */
1192         {
1193                 WM_manipulator_target_property_def_func(
1194                         man->translate_z, "offset",
1195                         &(const struct wmManipulatorPropertyFnParams) {
1196                             .value_get_fn = manipulator_spin_prop_depth_get,
1197                             .value_set_fn = manipulator_spin_prop_depth_set,
1198                             .range_get_fn = NULL,
1199                             .user_data = NULL,
1200                         });
1201
1202                 WM_manipulator_target_property_def_func(
1203                         man->translate_c, "offset",
1204                         &(const struct wmManipulatorPropertyFnParams) {
1205                             .value_get_fn = manipulator_spin_prop_translate_get,
1206                             .value_set_fn = manipulator_spin_prop_translate_set,
1207                             .range_get_fn = NULL,
1208                             .user_data = NULL,
1209                         });
1210
1211                 WM_manipulator_target_property_def_func(
1212                         man->rotate_c, "offset",
1213                         &(const struct wmManipulatorPropertyFnParams) {
1214                             .value_get_fn = manipulator_spin_prop_axis_angle_get,
1215                             .value_set_fn = manipulator_spin_prop_axis_angle_set,
1216                             .range_get_fn = NULL,
1217                             .user_data = NULL,
1218                         });
1219
1220                 WM_manipulator_target_property_def_func(
1221                         man->angle_z, "offset",
1222                         &(const struct wmManipulatorPropertyFnParams) {
1223                             .value_get_fn = manipulator_spin_prop_angle_get,
1224                             .value_set_fn = manipulator_spin_prop_angle_set,
1225                             .range_get_fn = NULL,
1226                             .user_data = NULL,
1227                         });
1228
1229         }
1230 }
1231
1232 static void manipulator_mesh_spin_draw_prepare(
1233         const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
1234 {
1235         ManipulatorSpinGroup *man = mgroup->customdata;
1236         if (man->data.op->next) {
1237                 man->data.op = WM_operator_last_redo((bContext *)man->data.context);
1238         }
1239         manipulator_mesh_spin_update_from_op(man);
1240 }
1241
1242 static void MESH_WGT_spin(struct wmManipulatorGroupType *wgt)
1243 {
1244         wgt->name = "Mesh Spin";
1245         wgt->idname = "MESH_WGT_spin";
1246
1247         wgt->flag = WM_MANIPULATORGROUPTYPE_3D;
1248
1249         wgt->mmap_params.spaceid = SPACE_VIEW3D;
1250         wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
1251
1252         wgt->poll = manipulator_mesh_spin_poll;
1253         wgt->setup = manipulator_mesh_spin_setup;
1254         wgt->draw_prepare = manipulator_mesh_spin_draw_prepare;
1255 }
1256
1257 /** \} */
1258
1259 #endif  /* USE_MANIPULATOR */
1260
1261
1262 static int edbm_screw_exec(bContext *C, wmOperator *op)
1263 {
1264         Object *obedit = CTX_data_edit_object(C);
1265         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1266         BMesh *bm = em->bm;
1267         BMEdge *eed;
1268         BMVert *eve, *v1, *v2;
1269         BMIter iter, eiter;
1270         BMOperator spinop;
1271         float dvec[3], nor[3], cent[3], axis[3], v1_co_global[3], v2_co_global[3];
1272         int steps, turns;
1273         int valence;
1274
1275
1276         turns = RNA_int_get(op->ptr, "turns");
1277         steps = RNA_int_get(op->ptr, "steps");
1278         RNA_float_get_array(op->ptr, "center", cent);
1279         RNA_float_get_array(op->ptr, "axis", axis);
1280
1281         if (is_zero_v3(axis)) {
1282                 BKE_report(op->reports, RPT_ERROR, "Invalid/unset axis");
1283                 return OPERATOR_CANCELLED;
1284         }
1285
1286         /* find two vertices with valence count == 1, more or less is wrong */
1287         v1 = NULL;
1288         v2 = NULL;
1289
1290         BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1291                 valence = 0;
1292                 BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
1293                         if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
1294                                 valence++;
1295                         }
1296                 }
1297
1298                 if (valence == 1) {
1299                         if (v1 == NULL) {
1300                                 v1 = eve;
1301                         }
1302                         else if (v2 == NULL) {
1303                                 v2 = eve;
1304                         }
1305                         else {
1306                                 v1 = NULL;
1307                                 break;
1308                         }
1309                 }
1310         }
1311
1312         if (v1 == NULL || v2 == NULL) {
1313                 BKE_report(op->reports, RPT_ERROR, "You have to select a string of connected vertices too");
1314                 return OPERATOR_CANCELLED;
1315         }
1316
1317         copy_v3_v3(nor, obedit->obmat[2]);
1318
1319         /* calculate dvec */
1320         mul_v3_m4v3(v1_co_global, obedit->obmat, v1->co);
1321         mul_v3_m4v3(v2_co_global, obedit->obmat, v2->co);
1322         sub_v3_v3v3(dvec, v1_co_global, v2_co_global);
1323         mul_v3_fl(dvec, 1.0f / steps);
1324
1325         if (dot_v3v3(nor, dvec) > 0.0f)
1326                 negate_v3(dvec);
1327
1328         if (!EDBM_op_init(em, &spinop, op,
1329                           "spin geom=%hvef cent=%v axis=%v dvec=%v steps=%i angle=%f space=%m4 use_duplicate=%b",
1330                           BM_ELEM_SELECT, cent, axis, dvec, turns * steps, DEG2RADF(360.0f * turns), obedit->obmat, false))
1331         {
1332                 return OPERATOR_CANCELLED;
1333         }
1334         BMO_op_exec(bm, &spinop);
1335         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1336         BMO_slot_buffer_hflag_enable(bm, spinop.slots_out, "geom_last.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
1337         if (!EDBM_op_finish(em, &spinop, op, true)) {
1338                 return OPERATOR_CANCELLED;
1339         }
1340
1341         EDBM_update_generic(em, true, true);
1342
1343         return OPERATOR_FINISHED;
1344 }
1345
1346 /* get center and axis, in global coords */
1347 static int edbm_screw_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1348 {
1349         Scene *scene = CTX_data_scene(C);
1350         View3D *v3d = CTX_wm_view3d(C);
1351         RegionView3D *rv3d = ED_view3d_context_rv3d(C);
1352
1353         PropertyRNA *prop;
1354         prop = RNA_struct_find_property(op->ptr, "center");
1355         if (!RNA_property_is_set(op->ptr, prop)) {
1356                 RNA_property_float_set_array(op->ptr, prop, ED_view3d_cursor3d_get(scene, v3d));
1357         }
1358         if (rv3d) {
1359                 prop = RNA_struct_find_property(op->ptr, "axis");
1360                 if (!RNA_property_is_set(op->ptr, prop)) {
1361                         RNA_property_float_set_array(op->ptr, prop, rv3d->viewinv[1]);
1362                 }
1363         }
1364
1365         return edbm_screw_exec(C, op);
1366 }
1367
1368 void MESH_OT_screw(wmOperatorType *ot)
1369 {
1370         /* identifiers */
1371         ot->name = "Screw";
1372         ot->description = "Extrude selected vertices in screw-shaped rotation around the cursor in indicated viewport";
1373         ot->idname = "MESH_OT_screw";
1374
1375         /* api callbacks */
1376         ot->invoke = edbm_screw_invoke;
1377         ot->exec = edbm_screw_exec;
1378         ot->poll = ED_operator_editmesh;
1379
1380         /* flags */
1381         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1382
1383         /* props */
1384         RNA_def_int(ot->srna, "steps", 9, 1, 100000, "Steps", "Steps", 3, 256);
1385         RNA_def_int(ot->srna, "turns", 1, 1, 100000, "Turns", "Turns", 1, 256);
1386
1387         RNA_def_float_vector(ot->srna, "center", 3, NULL, -1e12f, 1e12f,
1388                              "Center", "Center in global view space", -1e4f, 1e4f);
1389         RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f,
1390                              "Axis", "Axis in global view space", -1.0f, 1.0f);
1391 }
1392
1393 /** \} */