Edit Mesh: multi-object support for 'Vertices Only' extrusion (Alt+E)
[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
454         for (uint ob_index = 0; ob_index < objects_len; ob_index++)
455         {
456                 Object *obedit = objects[ob_index];
457                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
458                 if (em->bm->totvertsel == 0) {
459                         continue;
460                 }
461
462                 edbm_extrude_verts_indiv(em, op, BM_ELEM_SELECT);
463
464                 EDBM_update_generic(em, true, true);
465         }
466         MEM_freeN(objects);
467
468         return OPERATOR_FINISHED;
469 }
470
471 void MESH_OT_extrude_verts_indiv(wmOperatorType *ot)
472 {
473         /* identifiers */
474         ot->name = "Extrude Only Vertices";
475         ot->idname = "MESH_OT_extrude_verts_indiv";
476         ot->description = "Extrude individual vertices only";
477
478         /* api callbacks */
479         ot->exec = edbm_extrude_verts_exec;
480         ot->poll = ED_operator_editmesh;
481
482         /* flags */
483         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
484
485         /* to give to transform */
486         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
487 }
488
489 /** \} */
490
491 /* -------------------------------------------------------------------- */
492 /** \name Extrude Edges Operator
493  * \{ */
494
495 static int edbm_extrude_edges_exec(bContext *C, wmOperator *op)
496 {
497         ViewLayer *view_layer = CTX_data_view_layer(C);
498         uint objects_len = 0;
499         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
500
501
502         for (uint ob_index = 0; ob_index < objects_len; ob_index++)
503         {
504                 Object *obedit = objects[ob_index];
505                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
506                 if (em->bm->totedgesel == 0) {
507                         continue;
508                 }
509
510                 edbm_extrude_edges_indiv(em, op, BM_ELEM_SELECT);
511
512                 EDBM_update_generic(em, true, true);
513         }
514         MEM_freeN(objects);
515
516         return OPERATOR_FINISHED;
517 }
518
519 void MESH_OT_extrude_edges_indiv(wmOperatorType *ot)
520 {
521         /* identifiers */
522         ot->name = "Extrude Only Edges";
523         ot->idname = "MESH_OT_extrude_edges_indiv";
524         ot->description = "Extrude individual edges only";
525
526         /* api callbacks */
527         ot->exec = edbm_extrude_edges_exec;
528         ot->poll = ED_operator_editmesh;
529
530         /* flags */
531         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
532
533         /* to give to transform */
534         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
535 }
536
537 /** \} */
538
539 /* -------------------------------------------------------------------- */
540 /** \name Extrude Faces Operator
541  * \{ */
542
543 static int edbm_extrude_faces_exec(bContext *C, wmOperator *op)
544 {
545         ViewLayer *view_layer = CTX_data_view_layer(C);
546         uint objects_len = 0;
547         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
548
549         for (uint ob_index = 0; ob_index < objects_len; ob_index++)
550         {
551                 Object *obedit = objects[ob_index];
552                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
553                 if (em->bm->totfacesel == 0) {
554                         continue;
555                 }
556
557                 edbm_extrude_discrete_faces(em, op, BM_ELEM_SELECT);
558
559                 EDBM_update_generic(em, true, true);
560         }
561         MEM_freeN(objects);
562
563         return OPERATOR_FINISHED;
564 }
565
566 void MESH_OT_extrude_faces_indiv(wmOperatorType *ot)
567 {
568         /* identifiers */
569         ot->name = "Extrude Individual Faces";
570         ot->idname = "MESH_OT_extrude_faces_indiv";
571         ot->description = "Extrude individual faces only";
572
573         /* api callbacks */
574         ot->exec = edbm_extrude_faces_exec;
575         ot->poll = ED_operator_editmesh;
576
577         /* flags */
578         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
579
580         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
581 }
582
583 /** \} */
584
585 /* -------------------------------------------------------------------- */
586 /** \name Dupli-Extrude Operator
587  *
588  * Add-click-mesh (extrude) operator.
589  * \{ */
590
591 static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
592 {
593         ViewContext vc;
594         BMVert *v1;
595         BMIter iter;
596         float center[3];
597         uint verts_len;
598         bool use_proj;
599
600         em_setup_viewcontext(C, &vc);
601
602         invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
603
604         ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
605
606         use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
607                     (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
608
609         zero_v3(center);
610         verts_len = 0;
611
612         BM_ITER_MESH (v1, &iter, vc.em->bm, BM_VERTS_OF_MESH) {
613                 if (BM_elem_flag_test(v1, BM_ELEM_SELECT)) {
614                         add_v3_v3(center, v1->co);
615                         verts_len += 1;
616                 }
617         }
618
619         /* call extrude? */
620         if (verts_len != 0) {
621                 const char extrude_htype = edbm_extrude_htype_from_em_select(vc.em);
622                 const bool rot_src = RNA_boolean_get(op->ptr, "rotate_source");
623                 BMEdge *eed;
624                 float mat[3][3];
625                 float vec[3], ofs[3];
626                 float nor[3] = {0.0, 0.0, 0.0};
627
628                 /* 2D normal calc */
629                 const float mval_f[2] = {(float)event->mval[0],
630                                          (float)event->mval[1]};
631
632                 mul_v3_fl(center, 1.0f / (float)verts_len);
633
634                 /* check for edges that are half selected, use for rotation */
635                 bool done = false;
636                 BM_ITER_MESH (eed, &iter, vc.em->bm, BM_EDGES_OF_MESH) {
637                         if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
638                                 float co1[2], co2[2];
639
640                                 if ((ED_view3d_project_float_object(vc.ar, eed->v1->co, co1, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) &&
641                                     (ED_view3d_project_float_object(vc.ar, eed->v2->co, co2, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK))
642                                 {
643                                         /* 2D rotate by 90d while adding.
644                                          *  (x, y) = (y, -x)
645                                          *
646                                          * accumulate the screenspace normal in 2D,
647                                          * with screenspace edge length weighting the result. */
648                                         if (line_point_side_v2(co1, co2, mval_f) >= 0.0f) {
649                                                 nor[0] +=  (co1[1] - co2[1]);
650                                                 nor[1] += -(co1[0] - co2[0]);
651                                         }
652                                         else {
653                                                 nor[0] +=  (co2[1] - co1[1]);
654                                                 nor[1] += -(co2[0] - co1[0]);
655                                         }
656                                         done = true;
657                                 }
658                         }
659                 }
660
661                 if (done) {
662                         float view_vec[3], cross[3];
663
664                         /* convert the 2D nomal into 3D */
665                         mul_mat3_m4_v3(vc.rv3d->viewinv, nor); /* worldspace */
666                         mul_mat3_m4_v3(vc.obedit->imat, nor); /* local space */
667
668                         /* correct the normal to be aligned on the view plane */
669                         mul_v3_mat3_m4v3(view_vec, vc.obedit->imat, vc.rv3d->viewinv[2]);
670                         cross_v3_v3v3(cross, nor, view_vec);
671                         cross_v3_v3v3(nor, view_vec, cross);
672                         normalize_v3(nor);
673                 }
674
675                 /* center */
676                 copy_v3_v3(ofs, center);
677
678                 mul_m4_v3(vc.obedit->obmat, ofs);  /* view space */
679                 ED_view3d_win_to_3d_int(vc.v3d, vc.ar, ofs, event->mval, ofs);
680                 mul_m4_v3(vc.obedit->imat, ofs); // back in object space
681
682                 sub_v3_v3(ofs, center);
683
684                 /* calculate rotation */
685                 unit_m3(mat);
686                 if (done) {
687                         float angle;
688
689                         normalize_v3_v3(vec, ofs);
690
691                         angle = angle_normalized_v3v3(vec, nor);
692
693                         if (angle != 0.0f) {
694                                 float axis[3];
695
696                                 cross_v3_v3v3(axis, nor, vec);
697
698                                 /* halve the rotation if its applied twice */
699                                 if (rot_src) {
700                                         angle *= 0.5f;
701                                 }
702
703                                 axis_angle_to_mat3(mat, axis, angle);
704                         }
705                 }
706
707                 if (rot_src) {
708                         EDBM_op_callf(vc.em, op, "rotate verts=%hv cent=%v matrix=%m3",
709                                       BM_ELEM_SELECT, center, mat);
710
711                         /* also project the source, for retopo workflow */
712                         if (use_proj)
713                                 EMBM_project_snap_verts(C, vc.ar, vc.em);
714                 }
715
716                 edbm_extrude_ex(vc.obedit, vc.em, extrude_htype, BM_ELEM_SELECT, true, true);
717                 EDBM_op_callf(vc.em, op, "rotate verts=%hv cent=%v matrix=%m3",
718                               BM_ELEM_SELECT, center, mat);
719                 EDBM_op_callf(vc.em, op, "translate verts=%hv vec=%v",
720                               BM_ELEM_SELECT, ofs);
721         }
722         else {
723                 const float *cursor = ED_view3d_cursor3d_get(vc.scene, vc.v3d);
724                 BMOperator bmop;
725                 BMOIter oiter;
726
727                 copy_v3_v3(center, cursor);
728                 ED_view3d_win_to_3d_int(vc.v3d, vc.ar, center, event->mval, center);
729
730                 mul_m4_v3(vc.obedit->imat, center); // back in object space
731
732                 EDBM_op_init(vc.em, &bmop, op, "create_vert co=%v", center);
733                 BMO_op_exec(vc.em->bm, &bmop);
734
735                 BMO_ITER (v1, &oiter, bmop.slots_out, "vert.out", BM_VERT) {
736                         BM_vert_select_set(vc.em->bm, v1, true);
737                 }
738
739                 if (!EDBM_op_finish(vc.em, &bmop, op, true)) {
740                         return OPERATOR_CANCELLED;
741                 }
742         }
743
744         if (use_proj)
745                 EMBM_project_snap_verts(C, vc.ar, vc.em);
746
747         /* This normally happens when pushing undo but modal operators
748          * like this one don't push undo data until after modal mode is
749          * done. */
750         EDBM_mesh_normals_update(vc.em);
751
752         EDBM_update_generic(vc.em, true, true);
753
754         return OPERATOR_FINISHED;
755 }
756
757 void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot)
758 {
759         /* identifiers */
760         ot->name = "Duplicate or Extrude to Cursor";
761         ot->idname = "MESH_OT_dupli_extrude_cursor";
762         ot->description = "Duplicate and extrude selected vertices, edges or faces towards the mouse cursor";
763
764         /* api callbacks */
765         ot->invoke = edbm_dupli_extrude_cursor_invoke;
766         ot->poll = ED_operator_editmesh_region_view3d;
767
768         /* flags */
769         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
770
771         RNA_def_boolean(ot->srna, "rotate_source", true, "Rotate Source", "Rotate initial selection giving better shape");
772 }
773
774 /** \} */
775
776 /* -------------------------------------------------------------------- */
777 /** \name Spin Operator
778  * \{ */
779
780 static int edbm_spin_exec(bContext *C, wmOperator *op)
781 {
782         Object *obedit = CTX_data_edit_object(C);
783         BMEditMesh *em = BKE_editmesh_from_object(obedit);
784         BMesh *bm = em->bm;
785         BMOperator spinop;
786         float cent[3], axis[3];
787         float d[3] = {0.0f, 0.0f, 0.0f};
788         int steps, dupli;
789         float angle;
790
791         RNA_float_get_array(op->ptr, "center", cent);
792         RNA_float_get_array(op->ptr, "axis", axis);
793         steps = RNA_int_get(op->ptr, "steps");
794         angle = RNA_float_get(op->ptr, "angle");
795         //if (ts->editbutflag & B_CLOCKWISE)
796         angle = -angle;
797         dupli = RNA_boolean_get(op->ptr, "dupli");
798
799         if (is_zero_v3(axis)) {
800                 BKE_report(op->reports, RPT_ERROR, "Invalid/unset axis");
801                 return OPERATOR_CANCELLED;
802         }
803
804         /* keep the values in worldspace since we're passing the obmat */
805         if (!EDBM_op_init(em, &spinop, op,
806                           "spin geom=%hvef cent=%v axis=%v dvec=%v steps=%i angle=%f space=%m4 use_duplicate=%b",
807                           BM_ELEM_SELECT, cent, axis, d, steps, angle, obedit->obmat, dupli))
808         {
809                 return OPERATOR_CANCELLED;
810         }
811         BMO_op_exec(bm, &spinop);
812         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
813         BMO_slot_buffer_hflag_enable(bm, spinop.slots_out, "geom_last.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
814         if (!EDBM_op_finish(em, &spinop, op, true)) {
815                 return OPERATOR_CANCELLED;
816         }
817
818         EDBM_update_generic(em, true, true);
819
820         return OPERATOR_FINISHED;
821 }
822
823 /* get center and axis, in global coords */
824 static int edbm_spin_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
825 {
826         Scene *scene = CTX_data_scene(C);
827         View3D *v3d = CTX_wm_view3d(C);
828         RegionView3D *rv3d = ED_view3d_context_rv3d(C);
829
830         PropertyRNA *prop;
831         prop = RNA_struct_find_property(op->ptr, "center");
832         if (!RNA_property_is_set(op->ptr, prop)) {
833                 RNA_property_float_set_array(op->ptr, prop, ED_view3d_cursor3d_get(scene, v3d));
834         }
835         if (rv3d) {
836                 prop = RNA_struct_find_property(op->ptr, "axis");
837                 if (!RNA_property_is_set(op->ptr, prop)) {
838                         RNA_property_float_set_array(op->ptr, prop, rv3d->viewinv[2]);
839                 }
840         }
841
842         int ret = edbm_spin_exec(C, op);
843
844 #ifdef USE_MANIPULATOR
845         if (ret & OPERATOR_FINISHED) {
846                 /* Setup manipulators */
847                 if (v3d && (v3d->twtype & V3D_MANIPULATOR_DRAW)) {
848                         WM_manipulator_group_type_ensure("MESH_WGT_spin");
849                 }
850         }
851 #endif
852
853         return ret;
854
855 }
856
857 #ifdef USE_MANIPULATOR
858 static void MESH_WGT_spin(struct wmManipulatorGroupType *wgt);
859 #endif
860
861 void MESH_OT_spin(wmOperatorType *ot)
862 {
863         PropertyRNA *prop;
864
865         /* identifiers */
866         ot->name = "Spin";
867         ot->description = "Extrude selected vertices in a circle around the cursor in indicated viewport";
868         ot->idname = "MESH_OT_spin";
869
870         /* api callbacks */
871         ot->invoke = edbm_spin_invoke;
872         ot->exec = edbm_spin_exec;
873         ot->poll = ED_operator_editmesh;
874
875         /* flags */
876         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
877
878         /* props */
879         RNA_def_int(ot->srna, "steps", 9, 0, 1000000, "Steps", "Steps", 0, 1000);
880         RNA_def_boolean(ot->srna, "dupli", 0, "Dupli", "Make Duplicates");
881         prop = RNA_def_float(ot->srna, "angle", DEG2RADF(90.0f), -1e12f, 1e12f, "Angle", "Rotation for each step",
882                              DEG2RADF(-360.0f), DEG2RADF(360.0f));
883         RNA_def_property_subtype(prop, PROP_ANGLE);
884
885         RNA_def_float_vector(ot->srna, "center", 3, NULL, -1e12f, 1e12f,
886                              "Center", "Center in global view space", -1e4f, 1e4f);
887         RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f, "Axis", "Axis in global view space", -1.0f, 1.0f);
888
889 #ifdef USE_MANIPULATOR
890         WM_manipulatorgrouptype_append(MESH_WGT_spin);
891 #endif
892 }
893
894
895 #ifdef USE_MANIPULATOR
896
897 /* -------------------------------------------------------------------- */
898 /** \name Screw Operator
899  * \{ */
900
901 typedef struct ManipulatorSpinGroup {
902         /* Arrow to change plane depth. */
903         struct wmManipulator *translate_z;
904         /* Translate XYZ */
905         struct wmManipulator *translate_c;
906         /* For grabbing the manipulator and moving freely. */
907         struct wmManipulator *rotate_c;
908         /* Spin angle */
909         struct wmManipulator *angle_z;
910
911         /* We could store more vars here! */
912         struct {
913                 bContext *context;
914                 wmOperator *op;
915                 PropertyRNA *prop_axis_co;
916                 PropertyRNA *prop_axis_no;
917                 PropertyRNA *prop_angle;
918
919                 float rotate_axis[3];
920                 float rotate_up[3];
921         } data;
922 } ManipulatorSpinGroup;
923
924 /**
925  * XXX. calling redo from property updates is not great.
926  * This is needed because changing the RNA doesn't cause a redo
927  * and we're not using operator UI which does just this.
928  */
929 static void manipulator_spin_exec(ManipulatorSpinGroup *man)
930 {
931         wmOperator *op = man->data.op;
932         if (op == WM_operator_last_redo((bContext *)man->data.context)) {
933                 ED_undo_operator_repeat((bContext *)man->data.context, op);
934         }
935 }
936
937 static void manipulator_mesh_spin_update_from_op(ManipulatorSpinGroup *man)
938 {
939         wmOperator *op = man->data.op;
940
941         float plane_co[3], plane_no[3];
942
943         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, plane_co);
944         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
945
946         WM_manipulator_set_matrix_location(man->translate_z, plane_co);
947         WM_manipulator_set_matrix_location(man->rotate_c, plane_co);
948         WM_manipulator_set_matrix_location(man->angle_z, plane_co);
949         /* translate_c location comes from the property. */
950
951         WM_manipulator_set_matrix_rotation_from_z_axis(man->translate_z, plane_no);
952         WM_manipulator_set_matrix_rotation_from_z_axis(man->angle_z, plane_no);
953
954         WM_manipulator_set_scale(man->translate_c, 0.2);
955
956         RegionView3D *rv3d = ED_view3d_context_rv3d(man->data.context);
957         if (rv3d) {
958                 normalize_v3_v3(man->data.rotate_axis, rv3d->viewinv[2]);
959                 normalize_v3_v3(man->data.rotate_up, rv3d->viewinv[1]);
960
961                 /* ensure its orthogonal */
962                 project_plane_normalized_v3_v3v3(man->data.rotate_up, man->data.rotate_up, man->data.rotate_axis);
963                 normalize_v3(man->data.rotate_up);
964
965                 WM_manipulator_set_matrix_rotation_from_z_axis(man->translate_c, plane_no);
966                 WM_manipulator_set_matrix_rotation_from_yz_axis(man->rotate_c, plane_no, man->data.rotate_axis);
967
968                 /* show the axis instead of mouse cursor */
969                 RNA_enum_set(man->rotate_c->ptr, "draw_options",
970                              ED_MANIPULATOR_DIAL_DRAW_FLAG_ANGLE_MIRROR |
971                              ED_MANIPULATOR_DIAL_DRAW_FLAG_ANGLE_START_Y);
972
973         }
974 }
975
976 /* depth callbacks */
977 static void manipulator_spin_prop_depth_get(
978         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
979         void *value_p)
980 {
981         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
982         wmOperator *op = man->data.op;
983         float *value = value_p;
984
985         BLI_assert(mpr_prop->type->array_length == 1);
986         UNUSED_VARS_NDEBUG(mpr_prop);
987
988         float plane_co[3], plane_no[3];
989         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, plane_co);
990         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
991
992         value[0] = dot_v3v3(plane_no, plane_co) - dot_v3v3(plane_no, mpr->matrix_basis[3]);
993 }
994
995 static void manipulator_spin_prop_depth_set(
996         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
997         const void *value_p)
998 {
999         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1000         wmOperator *op = man->data.op;
1001         const float *value = value_p;
1002
1003         BLI_assert(mpr_prop->type->array_length == 1);
1004         UNUSED_VARS_NDEBUG(mpr_prop);
1005
1006         float plane_co[3], plane[4];
1007         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, plane_co);
1008         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane);
1009         normalize_v3(plane);
1010
1011         plane[3] = -value[0] - dot_v3v3(plane, mpr->matrix_basis[3]);
1012
1013         /* Keep our location, may be offset simply to be inside the viewport. */
1014         closest_to_plane_normalized_v3(plane_co, plane, plane_co);
1015
1016         RNA_property_float_set_array(op->ptr, man->data.prop_axis_co, plane_co);
1017
1018         manipulator_spin_exec(man);
1019 }
1020
1021 /* translate callbacks */
1022 static void manipulator_spin_prop_translate_get(
1023         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1024         void *value_p)
1025 {
1026         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1027         wmOperator *op = man->data.op;
1028         float *value = value_p;
1029
1030         BLI_assert(mpr_prop->type->array_length == 3);
1031         UNUSED_VARS_NDEBUG(mpr_prop);
1032
1033         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, value);
1034 }
1035
1036 static void manipulator_spin_prop_translate_set(
1037         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1038         const void *value)
1039 {
1040         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1041         wmOperator *op = man->data.op;
1042
1043         BLI_assert(mpr_prop->type->array_length == 3);
1044         UNUSED_VARS_NDEBUG(mpr_prop);
1045
1046         RNA_property_float_set_array(op->ptr, man->data.prop_axis_co, value);
1047
1048         manipulator_spin_exec(man);
1049 }
1050
1051 /* angle callbacks */
1052 static void manipulator_spin_prop_axis_angle_get(
1053         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1054         void *value_p)
1055 {
1056         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1057         wmOperator *op = man->data.op;
1058         float *value = value_p;
1059
1060         BLI_assert(mpr_prop->type->array_length == 1);
1061         UNUSED_VARS_NDEBUG(mpr_prop);
1062
1063         float plane_no[4];
1064         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
1065         normalize_v3(plane_no);
1066
1067         float plane_no_proj[3];
1068         project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, man->data.rotate_axis);
1069
1070         if (!is_zero_v3(plane_no_proj)) {
1071                 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, man->data.rotate_up, man->data.rotate_axis);
1072                 value[0] = angle;
1073         }
1074         else {
1075                 value[0] = 0.0f;
1076         }
1077 }
1078
1079 static void manipulator_spin_prop_axis_angle_set(
1080         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1081         const void *value_p)
1082 {
1083         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1084         wmOperator *op = man->data.op;
1085         const float *value = value_p;
1086
1087         BLI_assert(mpr_prop->type->array_length == 1);
1088         UNUSED_VARS_NDEBUG(mpr_prop);
1089
1090         float plane_no[4];
1091         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
1092         normalize_v3(plane_no);
1093
1094         float plane_no_proj[3];
1095         project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, man->data.rotate_axis);
1096
1097         if (!is_zero_v3(plane_no_proj)) {
1098                 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, man->data.rotate_up, man->data.rotate_axis);
1099                 const float angle_delta = angle - angle_compat_rad(value[0], angle);
1100                 if (angle_delta != 0.0f) {
1101                         float mat[3][3];
1102                         axis_angle_normalized_to_mat3(mat, man->data.rotate_axis, angle_delta);
1103                         mul_m3_v3(mat, plane_no);
1104
1105                         /* re-normalize - seems acceptable */
1106                         RNA_property_float_set_array(op->ptr, man->data.prop_axis_no, plane_no);
1107
1108                         manipulator_spin_exec(man);
1109                 }
1110         }
1111 }
1112
1113 /* angle callbacks */
1114 static void manipulator_spin_prop_angle_get(
1115         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1116         void *value_p)
1117 {
1118         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1119         wmOperator *op = man->data.op;
1120         float *value = value_p;
1121
1122         BLI_assert(mpr_prop->type->array_length == 1);
1123         UNUSED_VARS_NDEBUG(mpr_prop);
1124         value[0] = RNA_property_float_get(op->ptr, man->data.prop_angle);
1125 }
1126
1127 static void manipulator_spin_prop_angle_set(
1128         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
1129         const void *value_p)
1130 {
1131         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
1132         wmOperator *op = man->data.op;
1133         BLI_assert(mpr_prop->type->array_length == 1);
1134         UNUSED_VARS_NDEBUG(mpr_prop);
1135         const float *value = value_p;
1136         RNA_property_float_set(op->ptr, man->data.prop_angle, value[0]);
1137
1138         manipulator_spin_exec(man);
1139 }
1140
1141 static bool manipulator_mesh_spin_poll(const bContext *C, wmManipulatorGroupType *wgt)
1142 {
1143         wmOperator *op = WM_operator_last_redo(C);
1144         if (op == NULL || !STREQ(op->type->idname, "MESH_OT_spin")) {
1145                 WM_manipulator_group_type_unlink_delayed_ptr(wgt);
1146                 return false;
1147         }
1148         return true;
1149 }
1150
1151 static void manipulator_mesh_spin_setup(const bContext *C, wmManipulatorGroup *mgroup)
1152 {
1153         wmOperator *op = WM_operator_last_redo(C);
1154
1155         if (op == NULL || !STREQ(op->type->idname, "MESH_OT_spin")) {
1156                 return;
1157         }
1158
1159         struct ManipulatorSpinGroup *man = MEM_callocN(sizeof(ManipulatorSpinGroup), __func__);
1160         mgroup->customdata = man;
1161
1162         const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_3d", true);
1163         const wmManipulatorType *wt_grab = WM_manipulatortype_find("MANIPULATOR_WT_grab_3d", true);
1164         const wmManipulatorType *wt_dial = WM_manipulatortype_find("MANIPULATOR_WT_dial_3d", true);
1165
1166         man->translate_z = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL);
1167         man->translate_c = WM_manipulator_new_ptr(wt_grab, mgroup, NULL);
1168         man->rotate_c = WM_manipulator_new_ptr(wt_dial, mgroup, NULL);
1169         man->angle_z = WM_manipulator_new_ptr(wt_dial, mgroup, NULL);
1170
1171         UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, man->translate_z->color);
1172         UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, man->translate_c->color);
1173         UI_GetThemeColor3fv(TH_MANIPULATOR_SECONDARY, man->rotate_c->color);
1174         UI_GetThemeColor3fv(TH_AXIS_Z, man->angle_z->color);
1175
1176
1177         RNA_enum_set(man->translate_z->ptr, "draw_style", ED_MANIPULATOR_ARROW_STYLE_NORMAL);
1178         RNA_enum_set(man->translate_c->ptr, "draw_style", ED_MANIPULATOR_GRAB_STYLE_RING_2D);
1179
1180         WM_manipulator_set_flag(man->translate_c, WM_MANIPULATOR_DRAW_VALUE, true);
1181         WM_manipulator_set_flag(man->rotate_c, WM_MANIPULATOR_DRAW_VALUE, true);
1182         WM_manipulator_set_flag(man->angle_z, WM_MANIPULATOR_DRAW_VALUE, true);
1183
1184         WM_manipulator_set_scale(man->angle_z, 0.5f);
1185
1186         {
1187                 man->data.context = (bContext *)C;
1188                 man->data.op = op;
1189                 man->data.prop_axis_co = RNA_struct_find_property(op->ptr, "center");
1190                 man->data.prop_axis_no = RNA_struct_find_property(op->ptr, "axis");
1191                 man->data.prop_angle = RNA_struct_find_property(op->ptr, "angle");
1192         }
1193
1194         manipulator_mesh_spin_update_from_op(man);
1195
1196         /* Setup property callbacks */
1197         {
1198                 WM_manipulator_target_property_def_func(
1199                         man->translate_z, "offset",
1200                         &(const struct wmManipulatorPropertyFnParams) {
1201                             .value_get_fn = manipulator_spin_prop_depth_get,
1202                             .value_set_fn = manipulator_spin_prop_depth_set,
1203                             .range_get_fn = NULL,
1204                             .user_data = NULL,
1205                         });
1206
1207                 WM_manipulator_target_property_def_func(
1208                         man->translate_c, "offset",
1209                         &(const struct wmManipulatorPropertyFnParams) {
1210                             .value_get_fn = manipulator_spin_prop_translate_get,
1211                             .value_set_fn = manipulator_spin_prop_translate_set,
1212                             .range_get_fn = NULL,
1213                             .user_data = NULL,
1214                         });
1215
1216                 WM_manipulator_target_property_def_func(
1217                         man->rotate_c, "offset",
1218                         &(const struct wmManipulatorPropertyFnParams) {
1219                             .value_get_fn = manipulator_spin_prop_axis_angle_get,
1220                             .value_set_fn = manipulator_spin_prop_axis_angle_set,
1221                             .range_get_fn = NULL,
1222                             .user_data = NULL,
1223                         });
1224
1225                 WM_manipulator_target_property_def_func(
1226                         man->angle_z, "offset",
1227                         &(const struct wmManipulatorPropertyFnParams) {
1228                             .value_get_fn = manipulator_spin_prop_angle_get,
1229                             .value_set_fn = manipulator_spin_prop_angle_set,
1230                             .range_get_fn = NULL,
1231                             .user_data = NULL,
1232                         });
1233
1234         }
1235 }
1236
1237 static void manipulator_mesh_spin_draw_prepare(
1238         const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
1239 {
1240         ManipulatorSpinGroup *man = mgroup->customdata;
1241         if (man->data.op->next) {
1242                 man->data.op = WM_operator_last_redo((bContext *)man->data.context);
1243         }
1244         manipulator_mesh_spin_update_from_op(man);
1245 }
1246
1247 static void MESH_WGT_spin(struct wmManipulatorGroupType *wgt)
1248 {
1249         wgt->name = "Mesh Spin";
1250         wgt->idname = "MESH_WGT_spin";
1251
1252         wgt->flag = WM_MANIPULATORGROUPTYPE_3D;
1253
1254         wgt->mmap_params.spaceid = SPACE_VIEW3D;
1255         wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
1256
1257         wgt->poll = manipulator_mesh_spin_poll;
1258         wgt->setup = manipulator_mesh_spin_setup;
1259         wgt->draw_prepare = manipulator_mesh_spin_draw_prepare;
1260 }
1261
1262 /** \} */
1263
1264 #endif  /* USE_MANIPULATOR */
1265
1266
1267 static int edbm_screw_exec(bContext *C, wmOperator *op)
1268 {
1269         Object *obedit = CTX_data_edit_object(C);
1270         BMEditMesh *em = BKE_editmesh_from_object(obedit);
1271         BMesh *bm = em->bm;
1272         BMEdge *eed;
1273         BMVert *eve, *v1, *v2;
1274         BMIter iter, eiter;
1275         BMOperator spinop;
1276         float dvec[3], nor[3], cent[3], axis[3], v1_co_global[3], v2_co_global[3];
1277         int steps, turns;
1278         int valence;
1279
1280
1281         turns = RNA_int_get(op->ptr, "turns");
1282         steps = RNA_int_get(op->ptr, "steps");
1283         RNA_float_get_array(op->ptr, "center", cent);
1284         RNA_float_get_array(op->ptr, "axis", axis);
1285
1286         if (is_zero_v3(axis)) {
1287                 BKE_report(op->reports, RPT_ERROR, "Invalid/unset axis");
1288                 return OPERATOR_CANCELLED;
1289         }
1290
1291         /* find two vertices with valence count == 1, more or less is wrong */
1292         v1 = NULL;
1293         v2 = NULL;
1294
1295         BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
1296                 valence = 0;
1297                 BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
1298                         if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
1299                                 valence++;
1300                         }
1301                 }
1302
1303                 if (valence == 1) {
1304                         if (v1 == NULL) {
1305                                 v1 = eve;
1306                         }
1307                         else if (v2 == NULL) {
1308                                 v2 = eve;
1309                         }
1310                         else {
1311                                 v1 = NULL;
1312                                 break;
1313                         }
1314                 }
1315         }
1316
1317         if (v1 == NULL || v2 == NULL) {
1318                 BKE_report(op->reports, RPT_ERROR, "You have to select a string of connected vertices too");
1319                 return OPERATOR_CANCELLED;
1320         }
1321
1322         copy_v3_v3(nor, obedit->obmat[2]);
1323
1324         /* calculate dvec */
1325         mul_v3_m4v3(v1_co_global, obedit->obmat, v1->co);
1326         mul_v3_m4v3(v2_co_global, obedit->obmat, v2->co);
1327         sub_v3_v3v3(dvec, v1_co_global, v2_co_global);
1328         mul_v3_fl(dvec, 1.0f / steps);
1329
1330         if (dot_v3v3(nor, dvec) > 0.0f)
1331                 negate_v3(dvec);
1332
1333         if (!EDBM_op_init(em, &spinop, op,
1334                           "spin geom=%hvef cent=%v axis=%v dvec=%v steps=%i angle=%f space=%m4 use_duplicate=%b",
1335                           BM_ELEM_SELECT, cent, axis, dvec, turns * steps, DEG2RADF(360.0f * turns), obedit->obmat, false))
1336         {
1337                 return OPERATOR_CANCELLED;
1338         }
1339         BMO_op_exec(bm, &spinop);
1340         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1341         BMO_slot_buffer_hflag_enable(bm, spinop.slots_out, "geom_last.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
1342         if (!EDBM_op_finish(em, &spinop, op, true)) {
1343                 return OPERATOR_CANCELLED;
1344         }
1345
1346         EDBM_update_generic(em, true, true);
1347
1348         return OPERATOR_FINISHED;
1349 }
1350
1351 /* get center and axis, in global coords */
1352 static int edbm_screw_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1353 {
1354         Scene *scene = CTX_data_scene(C);
1355         View3D *v3d = CTX_wm_view3d(C);
1356         RegionView3D *rv3d = ED_view3d_context_rv3d(C);
1357
1358         PropertyRNA *prop;
1359         prop = RNA_struct_find_property(op->ptr, "center");
1360         if (!RNA_property_is_set(op->ptr, prop)) {
1361                 RNA_property_float_set_array(op->ptr, prop, ED_view3d_cursor3d_get(scene, v3d));
1362         }
1363         if (rv3d) {
1364                 prop = RNA_struct_find_property(op->ptr, "axis");
1365                 if (!RNA_property_is_set(op->ptr, prop)) {
1366                         RNA_property_float_set_array(op->ptr, prop, rv3d->viewinv[1]);
1367                 }
1368         }
1369
1370         return edbm_screw_exec(C, op);
1371 }
1372
1373 void MESH_OT_screw(wmOperatorType *ot)
1374 {
1375         /* identifiers */
1376         ot->name = "Screw";
1377         ot->description = "Extrude selected vertices in screw-shaped rotation around the cursor in indicated viewport";
1378         ot->idname = "MESH_OT_screw";
1379
1380         /* api callbacks */
1381         ot->invoke = edbm_screw_invoke;
1382         ot->exec = edbm_screw_exec;
1383         ot->poll = ED_operator_editmesh;
1384
1385         /* flags */
1386         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1387
1388         /* props */
1389         RNA_def_int(ot->srna, "steps", 9, 1, 100000, "Steps", "Steps", 3, 256);
1390         RNA_def_int(ot->srna, "turns", 1, 1, 100000, "Turns", "Turns", 1, 256);
1391
1392         RNA_def_float_vector(ot->srna, "center", 3, NULL, -1e12f, 1e12f,
1393                              "Center", "Center in global view space", -1e4f, 1e4f);
1394         RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f,
1395                              "Axis", "Axis in global view space", -1.0f, 1.0f);
1396 }
1397
1398 /** \} */