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