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