Tool System: per space/mode tool support
[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_report.h"
41 #include "BKE_editmesh.h"
42 #include "BKE_global.h"
43 #include "BKE_idprop.h"
44
45 #include "RNA_define.h"
46 #include "RNA_access.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50 #include "WM_message.h"
51
52 #include "ED_mesh.h"
53 #include "ED_screen.h"
54 #include "ED_transform.h"
55 #include "ED_view3d.h"
56 #include "ED_manipulator_library.h"
57
58 #include "UI_resources.h"
59
60 #include "MEM_guardedalloc.h"
61
62 #include "mesh_intern.h"  /* own include */
63
64 #define USE_MANIPULATOR
65
66 /* -------------------------------------------------------------------- */
67 /** \name Extrude Internal Utilities
68  * \{ */
69
70 static void edbm_extrude_edge_exclude_mirror(
71         Object *obedit, BMEditMesh *em,
72         const char hflag,
73         BMOperator *op, BMOpSlot *slot_edges_exclude)
74 {
75         BMesh *bm = em->bm;
76         ModifierData *md;
77
78         /* If a mirror modifier with clipping is on, we need to adjust some
79          * of the cases above to handle edges on the line of symmetry.
80          */
81         for (md = obedit->modifiers.first; md; md = md->next) {
82                 if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) {
83                         MirrorModifierData *mmd = (MirrorModifierData *) md;
84
85                         if (mmd->flag & MOD_MIR_CLIPPING) {
86                                 BMIter iter;
87                                 BMEdge *edge;
88
89                                 float mtx[4][4];
90                                 if (mmd->mirror_ob) {
91                                         float imtx[4][4];
92                                         invert_m4_m4(imtx, mmd->mirror_ob->obmat);
93                                         mul_m4_m4m4(mtx, imtx, obedit->obmat);
94                                 }
95
96                                 BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
97                                         if (BM_elem_flag_test(edge, hflag) &&
98                                             BM_edge_is_boundary(edge) &&
99                                             BM_elem_flag_test(edge->l->f, hflag))
100                                         {
101                                                 float co1[3], co2[3];
102
103                                                 copy_v3_v3(co1, edge->v1->co);
104                                                 copy_v3_v3(co2, edge->v2->co);
105
106                                                 if (mmd->mirror_ob) {
107                                                         mul_v3_m4v3(co1, mtx, co1);
108                                                         mul_v3_m4v3(co2, mtx, co2);
109                                                 }
110
111                                                 if (mmd->flag & MOD_MIR_AXIS_X) {
112                                                         if ((fabsf(co1[0]) < mmd->tolerance) &&
113                                                             (fabsf(co2[0]) < mmd->tolerance))
114                                                         {
115                                                                 BMO_slot_map_empty_insert(op, slot_edges_exclude, edge);
116                                                         }
117                                                 }
118                                                 if (mmd->flag & MOD_MIR_AXIS_Y) {
119                                                         if ((fabsf(co1[1]) < mmd->tolerance) &&
120                                                             (fabsf(co2[1]) < mmd->tolerance))
121                                                         {
122                                                                 BMO_slot_map_empty_insert(op, slot_edges_exclude, edge);
123                                                         }
124                                                 }
125                                                 if (mmd->flag & MOD_MIR_AXIS_Z) {
126                                                         if ((fabsf(co1[2]) < mmd->tolerance) &&
127                                                             (fabsf(co2[2]) < mmd->tolerance))
128                                                         {
129                                                                 BMO_slot_map_empty_insert(op, slot_edges_exclude, edge);
130                                                         }
131                                                 }
132                                         }
133                                 }
134                         }
135                 }
136         }
137 }
138
139 /* individual face extrude */
140 /* will use vertex normals for extrusion directions, so *nor is unaffected */
141 static bool edbm_extrude_discrete_faces(BMEditMesh *em, wmOperator *op, const char hflag)
142 {
143         BMOIter siter;
144         BMIter liter;
145         BMFace *f;
146         BMLoop *l;
147         BMOperator bmop;
148
149         EDBM_op_init(
150                 em, &bmop, op,
151                 "extrude_discrete_faces faces=%hf use_select_history=%b",
152                 hflag, true);
153
154         /* deselect original verts */
155         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
156
157         BMO_op_exec(em->bm, &bmop);
158
159         BMO_ITER (f, &siter, bmop.slots_out, "faces.out", BM_FACE) {
160                 BM_face_select_set(em->bm, f, true);
161
162                 /* set face vertex normals to face normal */
163                 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
164                         copy_v3_v3(l->v->no, f->no);
165                 }
166         }
167
168         if (!EDBM_op_finish(em, &bmop, op, true)) {
169                 return false;
170         }
171
172         return true;
173 }
174
175 /* extrudes individual edges */
176 static bool edbm_extrude_edges_indiv(BMEditMesh *em, wmOperator *op, const char hflag)
177 {
178         BMesh *bm = em->bm;
179         BMOperator bmop;
180
181         EDBM_op_init(
182                 em, &bmop, op,
183                 "extrude_edge_only edges=%he use_select_history=%b",
184                 hflag, true);
185
186         /* deselect original verts */
187         BM_SELECT_HISTORY_BACKUP(bm);
188         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
189         BM_SELECT_HISTORY_RESTORE(bm);
190
191         BMO_op_exec(em->bm, &bmop);
192         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_VERT | BM_EDGE, BM_ELEM_SELECT, true);
193
194         if (!EDBM_op_finish(em, &bmop, op, true)) {
195                 return false;
196         }
197
198         return true;
199 }
200
201 /* extrudes individual vertices */
202 static bool edbm_extrude_verts_indiv(BMEditMesh *em, wmOperator *op, const char hflag)
203 {
204         BMOperator bmop;
205
206         EDBM_op_init(
207                 em, &bmop, op,
208                 "extrude_vert_indiv verts=%hv use_select_history=%b",
209                 hflag, true);
210
211         /* deselect original verts */
212         BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_in, "verts", BM_VERT, BM_ELEM_SELECT, true);
213
214         BMO_op_exec(em->bm, &bmop);
215         BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, true);
216
217         if (!EDBM_op_finish(em, &bmop, op, true)) {
218                 return false;
219         }
220
221         return true;
222 }
223
224 static char edbm_extrude_htype_from_em_select(BMEditMesh *em)
225 {
226         char htype = BM_ALL_NOLOOP;
227
228         if (em->selectmode & SCE_SELECT_VERTEX) {
229                 /* pass */
230         }
231         else if (em->selectmode & SCE_SELECT_EDGE) {
232                 htype &= ~BM_VERT;
233         }
234         else {
235                 htype &= ~(BM_VERT | BM_EDGE);
236         }
237
238         if (em->bm->totedgesel == 0) {
239                 htype &= ~(BM_EDGE | BM_FACE);
240         }
241         else if (em->bm->totfacesel == 0) {
242                 htype &= ~BM_FACE;
243         }
244
245         return htype;
246 }
247
248 static bool edbm_extrude_ex(
249         Object *obedit, BMEditMesh *em,
250         char htype, const char hflag,
251         const bool use_mirror,
252         const bool use_select_history)
253 {
254         BMesh *bm = em->bm;
255         BMOIter siter;
256         BMOperator extop;
257         BMElem *ele;
258
259         /* needed to remove the faces left behind */
260         if (htype & BM_FACE) {
261                 htype |= BM_EDGE;
262         }
263
264         BMO_op_init(bm, &extop, BMO_FLAG_DEFAULTS, "extrude_face_region");
265         BMO_slot_bool_set(extop.slots_in, "use_select_history", use_select_history);
266         BMO_slot_buffer_from_enabled_hflag(bm, &extop, extop.slots_in, "geom", htype, hflag);
267
268         if (use_mirror) {
269                 BMOpSlot *slot_edges_exclude;
270                 slot_edges_exclude = BMO_slot_get(extop.slots_in, "edges_exclude");
271
272                 edbm_extrude_edge_exclude_mirror(obedit, em, hflag, &extop, slot_edges_exclude);
273         }
274
275         BM_SELECT_HISTORY_BACKUP(bm);
276         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
277         BM_SELECT_HISTORY_RESTORE(bm);
278
279         BMO_op_exec(bm, &extop);
280
281         BMO_ITER (ele, &siter, extop.slots_out, "geom.out", BM_ALL_NOLOOP) {
282                 BM_elem_select_set(bm, ele, true);
283         }
284
285         BMO_op_finish(bm, &extop);
286
287         return true;
288 }
289
290 /** \} */
291
292 /* -------------------------------------------------------------------- */
293 /** \name Extrude Repeat Operator
294  * \{ */
295
296 static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op)
297 {
298         RegionView3D *rv3d = CTX_wm_region_view3d(C);
299         const int steps = RNA_int_get(op->ptr, "steps");
300         const float offs = RNA_float_get(op->ptr, "offset");
301         float dvec[3], tmat[3][3], bmat[3][3];
302         short a;
303
304         ViewLayer *view_layer = CTX_data_view_layer(C);
305         uint objects_len = 0;
306         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
307
308         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
309
310                 Object *obedit = objects[ob_index];
311                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
312
313                 /* dvec */
314                 normalize_v3_v3_length(dvec, rv3d->persinv[2], offs);
315
316                 /* base correction */
317                 copy_m3_m4(bmat, obedit->obmat);
318                 invert_m3_m3(tmat, bmat);
319                 mul_m3_v3(tmat, dvec);
320
321                 for (a = 0; a < steps; a++) {
322                         edbm_extrude_ex(obedit, em, BM_ALL_NOLOOP, BM_ELEM_SELECT, false, false);
323
324                         BMO_op_callf(
325                                 em->bm, BMO_FLAG_DEFAULTS,
326                                 "translate vec=%v verts=%hv",
327                                 dvec, BM_ELEM_SELECT);
328                 }
329
330                 EDBM_mesh_normals_update(em);
331
332                 EDBM_update_generic(em, true, true);
333         }
334
335         MEM_freeN(objects);
336
337         return OPERATOR_FINISHED;
338 }
339
340 void MESH_OT_extrude_repeat(wmOperatorType *ot)
341 {
342         /* identifiers */
343         ot->name = "Extrude Repeat Mesh";
344         ot->description = "Extrude selected vertices, edges or faces repeatedly";
345         ot->idname = "MESH_OT_extrude_repeat";
346
347         /* api callbacks */
348         ot->exec = edbm_extrude_repeat_exec;
349         ot->poll = ED_operator_editmesh_view3d;
350
351         /* flags */
352         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
353
354         /* props */
355         RNA_def_float_distance(ot->srna, "offset", 2.0f, 0.0f, 1e12f, "Offset", "", 0.0f, 100.0f);
356         RNA_def_int(ot->srna, "steps", 10, 0, 1000000, "Steps", "", 0, 180);
357 }
358
359 /** \} */
360
361
362 /* -------------------------------------------------------------------- */
363 /** \name Extrude Manipulator
364  * \{ */
365
366 #ifdef USE_MANIPULATOR
367
368 const float extrude_button_scale = 0.15f;
369 const float extrude_button_offset_scale = 1.5f;
370 const float extrude_arrow_scale = 1.0f;
371 const float extrude_arrow_xyz_axis_scale = 1.0f;
372 const float extrude_arrow_normal_axis_scale = 1.75f;
373
374 static const uchar shape_plus[] = {
375         0x5f, 0xfb, 0x40, 0xee, 0x25, 0xda, 0x11, 0xbf, 0x4, 0xa0, 0x0, 0x80, 0x4, 0x5f, 0x11,
376         0x40, 0x25, 0x25, 0x40, 0x11, 0x5f, 0x4, 0x7f, 0x0, 0xa0, 0x4, 0xbf, 0x11, 0xda, 0x25,
377         0xee, 0x40, 0xfb, 0x5f, 0xff, 0x7f, 0xfb, 0xa0, 0xee, 0xbf, 0xda, 0xda, 0xbf, 0xee,
378         0xa0, 0xfb, 0x80, 0xff, 0x6e, 0xd7, 0x92, 0xd7, 0x92, 0x90, 0xd8, 0x90, 0xd8, 0x6d,
379         0x92, 0x6d, 0x92, 0x27, 0x6e, 0x27, 0x6e, 0x6d, 0x28, 0x6d, 0x28, 0x90, 0x6e,
380         0x90, 0x6e, 0xd7, 0x80, 0xff, 0x5f, 0xfb, 0x5f, 0xfb,
381 };
382
383 typedef struct ManipulatorExtrudeGroup {
384
385         /* XYZ & normal. */
386         struct wmManipulator *invoke_xyz_no[4];
387         struct wmManipulator *adjust_xyz_no[5];
388
389         struct {
390                 float normal_mat3[3][3];  /* use Z axis for normal. */
391                 int orientation_type;
392         } data;
393
394         wmOperatorType *ot_extrude;
395 } ManipulatorExtrudeGroup;
396
397 static void manipulator_mesh_extrude_orientation_matrix_set(
398         struct ManipulatorExtrudeGroup *man, const float mat[3][3])
399 {
400         for (int i = 0; i < 3; i++) {
401                 /* Set orientation without location. */
402                 for (int j = 0; j < 3; j++) {
403                         copy_v3_v3(man->adjust_xyz_no[i]->matrix_basis[j], mat[j]);
404                 }
405                 /* nop when (i == 2). */
406                 swap_v3_v3(man->adjust_xyz_no[i]->matrix_basis[i], man->adjust_xyz_no[i]->matrix_basis[2]);
407                 /* Orient to normal gives generally less awkward results. */
408                 if (man->data.orientation_type != V3D_MANIP_NORMAL) {
409                         if (dot_v3v3(man->adjust_xyz_no[i]->matrix_basis[2], man->data.normal_mat3[2]) < 0.0f) {
410                                 negate_v3(man->adjust_xyz_no[i]->matrix_basis[2]);
411                         }
412                 }
413                 mul_v3_v3fl(
414                         man->invoke_xyz_no[i]->matrix_offset[3],
415                         man->adjust_xyz_no[i]->matrix_basis[2],
416                         (extrude_arrow_xyz_axis_scale * extrude_button_offset_scale) / extrude_button_scale);
417         }
418 }
419
420 static bool manipulator_mesh_extrude_poll(const bContext *C, wmManipulatorGroupType *wgt)
421 {
422         WorkSpace *workspace = CTX_wm_workspace(C);
423         const bToolKey tkey = { .space_type = SPACE_VIEW3D, .mode = OB_MODE_EDIT};
424         bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_find(workspace, &tkey);
425         if ((tref_rt == NULL) ||
426             !STREQ(wgt->idname, tref_rt->manipulator_group) ||
427             !ED_operator_editmesh_view3d((bContext *)C))
428         {
429                 WM_manipulator_group_type_unlink_delayed_ptr(wgt);
430                 return false;
431         }
432         return true;
433 }
434
435 static void manipulator_mesh_extrude_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
436 {
437         struct ManipulatorExtrudeGroup *man = MEM_callocN(sizeof(ManipulatorExtrudeGroup), __func__);
438         mgroup->customdata = man;
439
440         const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_3d", true);
441         const wmManipulatorType *wt_grab = WM_manipulatortype_find("MANIPULATOR_WT_button_2d", true);
442
443         for (int i = 0; i < 4; i++) {
444                 man->adjust_xyz_no[i] = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL);
445                 man->invoke_xyz_no[i] = WM_manipulator_new_ptr(wt_grab, mgroup, NULL);
446                 man->invoke_xyz_no[i]->flag |= WM_MANIPULATOR_DRAW_OFFSET_SCALE;
447         }
448
449         {
450                 PropertyRNA *prop = RNA_struct_find_property(man->invoke_xyz_no[3]->ptr, "shape");
451                 for (int i = 0; i < 4; i++) {
452                         RNA_property_string_set_bytes(
453                                 man->invoke_xyz_no[i]->ptr, prop,
454                                 (const char *)shape_plus, ARRAY_SIZE(shape_plus));
455                 }
456         }
457
458         man->ot_extrude = WM_operatortype_find("MESH_OT_extrude_context_move", true);
459
460         for (int i = 0; i < 3; i++) {
461                 UI_GetThemeColor3fv(TH_AXIS_X + i, man->invoke_xyz_no[i]->color);
462                 UI_GetThemeColor3fv(TH_AXIS_X + i, man->adjust_xyz_no[i]->color);
463         }
464         UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, man->invoke_xyz_no[3]->color);
465         UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, man->adjust_xyz_no[3]->color);
466
467         for (int i = 0; i < 4; i++) {
468                 WM_manipulator_set_scale(man->invoke_xyz_no[i], extrude_button_scale);
469                 WM_manipulator_set_scale(man->adjust_xyz_no[i], extrude_arrow_scale);
470         }
471         WM_manipulator_set_scale(man->adjust_xyz_no[3], extrude_arrow_normal_axis_scale);
472
473         for (int i = 0; i < 4; i++) {
474         }
475
476         for (int i = 0; i < 4; i++) {
477                 WM_manipulator_set_flag(man->adjust_xyz_no[i], WM_MANIPULATOR_DRAW_VALUE, true);
478         }
479
480         /* XYZ & normal axis extrude. */
481         for (int i = 0; i < 4; i++) {
482                 PointerRNA *ptr = WM_manipulator_operator_set(man->invoke_xyz_no[i], 0, man->ot_extrude, NULL);
483                 {
484                         int constraint[3] = {0, 0, 0};
485                         constraint[MIN2(i, 2)] = 1;
486                         PointerRNA macroptr = RNA_pointer_get(ptr, "TRANSFORM_OT_translate");
487                         RNA_boolean_set(&macroptr, "release_confirm", true);
488                         RNA_boolean_set_array(&macroptr, "constraint_axis", constraint);
489                 }
490         }
491
492         /* Adjust extrude. */
493         for (int i = 0; i < 4; i++) {
494                 PointerRNA *ptr = WM_manipulator_operator_set(man->adjust_xyz_no[i], 0, man->ot_extrude, NULL);
495                 {
496                         int constraint[3] = {0, 0, 0};
497                         constraint[MIN2(i, 2)] = 1;
498                         PointerRNA macroptr = RNA_pointer_get(ptr, "TRANSFORM_OT_translate");
499                         RNA_boolean_set(&macroptr, "release_confirm", true);
500                         RNA_boolean_set_array(&macroptr, "constraint_axis", constraint);
501                 }
502                 wmManipulatorOpElem *mpop = WM_manipulator_operator_get(man->adjust_xyz_no[i], 0);
503                 mpop->is_redo = true;
504         }
505 }
506
507 static void manipulator_mesh_extrude_refresh(const bContext *C, wmManipulatorGroup *mgroup)
508 {
509         ManipulatorExtrudeGroup *man = mgroup->customdata;
510
511         for (int i = 0; i < 4; i++) {
512                 WM_manipulator_set_flag(man->invoke_xyz_no[i], WM_MANIPULATOR_HIDDEN, true);
513                 WM_manipulator_set_flag(man->adjust_xyz_no[i], WM_MANIPULATOR_HIDDEN, true);
514         }
515
516         if (G.moving) {
517                 return;
518         }
519
520         Scene *scene = CTX_data_scene(C);
521         man->data.orientation_type = scene->orientation_type;
522         bool use_normal = (man->data.orientation_type != V3D_MANIP_NORMAL);
523         const int axis_len_used = use_normal ? 4 : 3;
524
525         struct TransformBounds tbounds;
526
527         if (use_normal) {
528                 struct TransformBounds tbounds_normal;
529                 if (!ED_transform_calc_manipulator_stats(
530                             C, &(struct TransformCalcParams){
531                                 .orientation_type = V3D_MANIP_NORMAL + 1,
532                             }, &tbounds_normal))
533                 {
534                         unit_m3(tbounds_normal.axis);
535                 }
536                 copy_m3_m3(man->data.normal_mat3, tbounds_normal.axis);
537         }
538
539         /* TODO(campbell): run second since this modifies the 3D view, it should not. */
540         if (!ED_transform_calc_manipulator_stats(
541                     C, &(struct TransformCalcParams){
542                         .orientation_type = man->data.orientation_type + 1,
543                     }, &tbounds))
544         {
545                 return;
546         }
547
548         /* Main axis is normal. */
549         if (!use_normal) {
550                 copy_m3_m3(man->data.normal_mat3, tbounds.axis);
551         }
552
553         /* Offset the add icon. */
554         mul_v3_v3fl(
555                 man->invoke_xyz_no[3]->matrix_offset[3],
556                 man->data.normal_mat3[2],
557                 (extrude_arrow_normal_axis_scale * extrude_button_offset_scale) / extrude_button_scale);
558
559         /* Needed for normal orientation. */
560         manipulator_mesh_extrude_orientation_matrix_set(man, tbounds.axis);
561         if (use_normal) {
562                 copy_m4_m3(man->adjust_xyz_no[3]->matrix_basis, man->data.normal_mat3);
563         }
564
565         /* Location. */
566         for (int i = 0; i < axis_len_used; i++) {
567                 WM_manipulator_set_matrix_location(man->invoke_xyz_no[i], tbounds.center);
568                 WM_manipulator_set_matrix_location(man->adjust_xyz_no[i], tbounds.center);
569         }
570
571         wmOperator *op = WM_operator_last_redo(C);
572         bool has_redo = (op && op->type == man->ot_extrude);
573
574         /* Un-hide. */
575         for (int i = 0; i < axis_len_used; i++) {
576                 WM_manipulator_set_flag(man->invoke_xyz_no[i], WM_MANIPULATOR_HIDDEN, false);
577                 WM_manipulator_set_flag(man->adjust_xyz_no[i], WM_MANIPULATOR_HIDDEN, !has_redo);
578         }
579
580         /* Operator properties. */
581         if (use_normal) {
582                 wmManipulatorOpElem *mpop = WM_manipulator_operator_get(man->invoke_xyz_no[3], 0);
583                 PointerRNA macroptr = RNA_pointer_get(&mpop->ptr, "TRANSFORM_OT_translate");
584                 RNA_enum_set(&macroptr, "constraint_orientation", V3D_MANIP_NORMAL);
585         }
586
587         /* Redo with current settings. */
588         if (has_redo) {
589                 wmOperator *op_transform = op->macro.last;
590                 float value[4];
591                 RNA_float_get_array(op_transform->ptr, "value", value);
592                 int constraint_axis[3];
593                 RNA_boolean_get_array(op_transform->ptr, "constraint_axis", constraint_axis);
594                 int orientation_type = RNA_enum_get(op_transform->ptr, "constraint_orientation");
595
596                 /* We could also access this from 'ot->last_properties' */
597                 for (int i = 0; i < 4; i++) {
598                         if ((i != 3) ?
599                             (orientation_type == man->data.orientation_type && constraint_axis[i]) :
600                             (orientation_type == V3D_MANIP_NORMAL && constraint_axis[2]))
601                         {
602                                 wmManipulatorOpElem *mpop = WM_manipulator_operator_get(man->adjust_xyz_no[i], 0);
603
604                                 PointerRNA macroptr = RNA_pointer_get(&mpop->ptr, "TRANSFORM_OT_translate");
605
606                                 RNA_float_set_array(&macroptr, "value", value);
607                                 RNA_boolean_set_array(&macroptr, "constraint_axis", constraint_axis);
608                                 RNA_enum_set(&macroptr, "constraint_orientation", orientation_type);
609                         }
610                         else {
611                                 /* TODO(campbell): ideally we could adjust all,
612                                  * this is complicated by how operator redo and the transform macro works. */
613                                 WM_manipulator_set_flag(man->adjust_xyz_no[i], WM_MANIPULATOR_HIDDEN, true);
614                         }
615                 }
616         }
617
618         for (int i = 0; i < 4; i++) {
619                 RNA_enum_set(
620                         man->invoke_xyz_no[i]->ptr,
621                         "draw_options",
622                         (man->adjust_xyz_no[i]->flag & WM_MANIPULATOR_HIDDEN) ?
623                         ED_MANIPULATOR_BUTTON_SHOW_HELPLINE : 0);
624         }
625 }
626
627 static void manipulator_mesh_extrude_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup)
628 {
629         ManipulatorExtrudeGroup *man = mgroup->customdata;
630         switch (man->data.orientation_type) {
631                 case V3D_MANIP_VIEW:
632                 {
633                         RegionView3D *rv3d = CTX_wm_region_view3d(C);
634                         float mat[3][3];
635                         copy_m3_m4(mat, rv3d->viewinv);
636                         normalize_m3(mat);
637                         manipulator_mesh_extrude_orientation_matrix_set(man, mat);
638                         break;
639                 }
640         }
641 }
642
643 static void manipulator_mesh_extrude_message_subscribe(
644         const bContext *C, wmManipulatorGroup *mgroup, struct wmMsgBus *mbus)
645 {
646         ARegion *ar = CTX_wm_region(C);
647
648         /* Subscribe to view properties */
649         wmMsgSubscribeValue msg_sub_value_mpr_tag_refresh = {
650                 .owner = ar,
651                 .user_data = mgroup->parent_mmap,
652                 .notify = WM_manipulator_do_msg_notify_tag_refresh,
653         };
654
655         {
656                 WM_msg_subscribe_rna_anon_prop(mbus, Scene, transform_orientation, &msg_sub_value_mpr_tag_refresh);
657         }
658
659 }
660
661 static void MESH_WGT_extrude(struct wmManipulatorGroupType *wgt)
662 {
663         wgt->name = "Mesh Extrude";
664         wgt->idname = "MESH_WGT_extrude";
665
666         wgt->flag = WM_MANIPULATORGROUPTYPE_3D;
667
668         wgt->mmap_params.spaceid = SPACE_VIEW3D;
669         wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
670
671         wgt->poll = manipulator_mesh_extrude_poll;
672         wgt->setup = manipulator_mesh_extrude_setup;
673         wgt->refresh = manipulator_mesh_extrude_refresh;
674         wgt->draw_prepare = manipulator_mesh_extrude_draw_prepare;
675         wgt->message_subscribe = manipulator_mesh_extrude_message_subscribe;
676 }
677
678 #endif  /* USE_MANIPULATOR */
679
680 /** \} */
681
682 /* -------------------------------------------------------------------- */
683 /** \name Extrude Operator
684  * \{ */
685
686 /* generic extern called extruder */
687 static bool edbm_extrude_mesh(Object *obedit, BMEditMesh *em, wmOperator *op)
688 {
689         bool changed = false;
690         const char htype = edbm_extrude_htype_from_em_select(em);
691         enum {NONE = 0, ELEM_FLAG, VERT_ONLY, EDGE_ONLY} nr;
692
693         if (em->selectmode & SCE_SELECT_VERTEX) {
694                 if      (em->bm->totvertsel == 0) nr = NONE;
695                 else if (em->bm->totvertsel == 1) nr = VERT_ONLY;
696                 else if (em->bm->totedgesel == 0) nr = VERT_ONLY;
697                 else                              nr = ELEM_FLAG;
698         }
699         else if (em->selectmode & SCE_SELECT_EDGE) {
700                 if      (em->bm->totedgesel == 0) nr = NONE;
701                 else if (em->bm->totfacesel == 0) nr = EDGE_ONLY;
702                 else                              nr = ELEM_FLAG;
703         }
704         else {
705                 if      (em->bm->totfacesel == 0) nr = NONE;
706                 else                              nr = ELEM_FLAG;
707         }
708
709         switch (nr) {
710                 case NONE:
711                         return false;
712                 case ELEM_FLAG:
713                         changed = edbm_extrude_ex(obedit, em, htype, BM_ELEM_SELECT, true, true);
714                         break;
715                 case VERT_ONLY:
716                         changed = edbm_extrude_verts_indiv(em, op, BM_ELEM_SELECT);
717                         break;
718                 case EDGE_ONLY:
719                         changed = edbm_extrude_edges_indiv(em, op, BM_ELEM_SELECT);
720                         break;
721         }
722
723         if (changed) {
724                 return true;
725         }
726         else {
727                 BKE_report(op->reports, RPT_ERROR, "Not a valid selection for extrude");
728                 return false;
729         }
730 }
731
732 /* extrude without transform */
733 static int edbm_extrude_region_exec(bContext *C, wmOperator *op)
734 {
735         ViewLayer *view_layer = CTX_data_view_layer(C);
736         uint objects_len = 0;
737         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
738
739         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
740                 Object *obedit = objects[ob_index];
741                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
742                 if (em->bm->totvertsel == 0) {
743                         continue;
744                 }
745
746                 if (!edbm_extrude_mesh(obedit, em, op)) {
747                         continue;
748                 }
749                 /* This normally happens when pushing undo but modal operators
750                  * like this one don't push undo data until after modal mode is
751                  * done.*/
752                 EDBM_mesh_normals_update(em);
753
754                 EDBM_update_generic(em, true, true);
755         }
756         MEM_freeN(objects);
757         return OPERATOR_FINISHED;
758 }
759
760 void MESH_OT_extrude_region(wmOperatorType *ot)
761 {
762         /* identifiers */
763         ot->name = "Extrude Region";
764         ot->idname = "MESH_OT_extrude_region";
765         ot->description = "Extrude region of faces";
766
767         /* api callbacks */
768         //ot->invoke = mesh_extrude_region_invoke;
769         ot->exec = edbm_extrude_region_exec;
770         ot->poll = ED_operator_editmesh;
771
772         /* flags */
773         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
774
775         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
776 }
777
778 /** \} */
779
780 /* -------------------------------------------------------------------- */
781 /** \name Extrude Context Operator
782  *
783  * Guess what to do based on selection.
784  * \{ */
785
786 /* extrude without transform */
787 static int edbm_extrude_context_exec(bContext *C, wmOperator *op)
788 {
789         ViewLayer *view_layer = CTX_data_view_layer(C);
790         uint objects_len = 0;
791         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
792
793         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
794                 Object *obedit = objects[ob_index];
795                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
796                 if (em->bm->totvertsel == 0) {
797                         continue;
798                 }
799
800                 edbm_extrude_mesh(obedit, em, op);
801                 /* This normally happens when pushing undo but modal operators
802                  * like this one don't push undo data until after modal mode is
803                  * done.*/
804
805                 EDBM_mesh_normals_update(em);
806
807                 EDBM_update_generic(em, true, true);
808         }
809         MEM_freeN(objects);
810         return OPERATOR_FINISHED;
811 }
812
813 void MESH_OT_extrude_context(wmOperatorType *ot)
814 {
815         /* identifiers */
816         ot->name = "Extrude Context";
817         ot->idname = "MESH_OT_extrude_context";
818         ot->description = "Extrude selection";
819
820         /* api callbacks */
821         ot->exec = edbm_extrude_context_exec;
822         ot->poll = ED_operator_editmesh;
823
824         /* flags */
825         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
826
827         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
828
829 #ifdef USE_MANIPULATOR
830         WM_manipulatorgrouptype_append(MESH_WGT_extrude);
831 #endif
832 }
833
834 /** \} */
835
836 /* -------------------------------------------------------------------- */
837 /** \name Extrude Verts Operator
838  * \{ */
839
840 static int edbm_extrude_verts_exec(bContext *C, wmOperator *op)
841 {
842         ViewLayer *view_layer = CTX_data_view_layer(C);
843         uint objects_len = 0;
844         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
845
846         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
847                 Object *obedit = objects[ob_index];
848                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
849                 if (em->bm->totvertsel == 0) {
850                         continue;
851                 }
852
853                 edbm_extrude_verts_indiv(em, op, BM_ELEM_SELECT);
854
855                 EDBM_update_generic(em, true, true);
856         }
857         MEM_freeN(objects);
858
859         return OPERATOR_FINISHED;
860 }
861
862 void MESH_OT_extrude_verts_indiv(wmOperatorType *ot)
863 {
864         /* identifiers */
865         ot->name = "Extrude Only Vertices";
866         ot->idname = "MESH_OT_extrude_verts_indiv";
867         ot->description = "Extrude individual vertices only";
868
869         /* api callbacks */
870         ot->exec = edbm_extrude_verts_exec;
871         ot->poll = ED_operator_editmesh;
872
873         /* flags */
874         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
875
876         /* to give to transform */
877         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
878 }
879
880 /** \} */
881
882 /* -------------------------------------------------------------------- */
883 /** \name Extrude Edges Operator
884  * \{ */
885
886 static int edbm_extrude_edges_exec(bContext *C, wmOperator *op)
887 {
888         ViewLayer *view_layer = CTX_data_view_layer(C);
889         uint objects_len = 0;
890         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
891
892         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
893                 Object *obedit = objects[ob_index];
894                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
895                 if (em->bm->totedgesel == 0) {
896                         continue;
897                 }
898
899                 edbm_extrude_edges_indiv(em, op, BM_ELEM_SELECT);
900
901                 EDBM_update_generic(em, true, true);
902         }
903         MEM_freeN(objects);
904
905         return OPERATOR_FINISHED;
906 }
907
908 void MESH_OT_extrude_edges_indiv(wmOperatorType *ot)
909 {
910         /* identifiers */
911         ot->name = "Extrude Only Edges";
912         ot->idname = "MESH_OT_extrude_edges_indiv";
913         ot->description = "Extrude individual edges only";
914
915         /* api callbacks */
916         ot->exec = edbm_extrude_edges_exec;
917         ot->poll = ED_operator_editmesh;
918
919         /* flags */
920         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
921
922         /* to give to transform */
923         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
924 }
925
926 /** \} */
927
928 /* -------------------------------------------------------------------- */
929 /** \name Extrude Faces Operator
930  * \{ */
931
932 static int edbm_extrude_faces_exec(bContext *C, wmOperator *op)
933 {
934         ViewLayer *view_layer = CTX_data_view_layer(C);
935         uint objects_len = 0;
936         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
937
938         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
939                 Object *obedit = objects[ob_index];
940                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
941                 if (em->bm->totfacesel == 0) {
942                         continue;
943                 }
944
945                 edbm_extrude_discrete_faces(em, op, BM_ELEM_SELECT);
946
947                 EDBM_update_generic(em, true, true);
948         }
949         MEM_freeN(objects);
950
951         return OPERATOR_FINISHED;
952 }
953
954 void MESH_OT_extrude_faces_indiv(wmOperatorType *ot)
955 {
956         /* identifiers */
957         ot->name = "Extrude Individual Faces";
958         ot->idname = "MESH_OT_extrude_faces_indiv";
959         ot->description = "Extrude individual faces only";
960
961         /* api callbacks */
962         ot->exec = edbm_extrude_faces_exec;
963         ot->poll = ED_operator_editmesh;
964
965         /* flags */
966         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
967
968         Transform_Properties(ot, P_NO_DEFAULTS | P_MIRROR_DUMMY);
969 }
970
971 /** \} */
972
973 /* -------------------------------------------------------------------- */
974 /** \name Dupli-Extrude Operator
975  *
976  * Add-click-mesh (extrude) operator.
977  * \{ */
978
979 static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
980 {
981         ViewContext vc;
982         BMVert *v1;
983         BMIter iter;
984         float center[3];
985         uint verts_len;
986         bool use_proj;
987
988         em_setup_viewcontext(C, &vc);
989
990         invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
991
992         ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
993
994         use_proj = ((vc.scene->toolsettings->snap_flag & SCE_SNAP) &&
995                     (vc.scene->toolsettings->snap_mode == SCE_SNAP_MODE_FACE));
996
997         zero_v3(center);
998         verts_len = 0;
999
1000         BM_ITER_MESH (v1, &iter, vc.em->bm, BM_VERTS_OF_MESH) {
1001                 if (BM_elem_flag_test(v1, BM_ELEM_SELECT)) {
1002                         add_v3_v3(center, v1->co);
1003                         verts_len += 1;
1004                 }
1005         }
1006
1007         /* call extrude? */
1008         if (verts_len != 0) {
1009                 const char extrude_htype = edbm_extrude_htype_from_em_select(vc.em);
1010                 const bool rot_src = RNA_boolean_get(op->ptr, "rotate_source");
1011                 BMEdge *eed;
1012                 float mat[3][3];
1013                 float vec[3], ofs[3];
1014                 float nor[3] = {0.0, 0.0, 0.0};
1015
1016                 /* 2D normal calc */
1017                 const float mval_f[2] = {(float)event->mval[0],
1018                                          (float)event->mval[1]};
1019
1020                 mul_v3_fl(center, 1.0f / (float)verts_len);
1021
1022                 /* check for edges that are half selected, use for rotation */
1023                 bool done = false;
1024                 BM_ITER_MESH (eed, &iter, vc.em->bm, BM_EDGES_OF_MESH) {
1025                         if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
1026                                 float co1[2], co2[2];
1027
1028                                 if ((ED_view3d_project_float_object(vc.ar, eed->v1->co, co1, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) &&
1029                                     (ED_view3d_project_float_object(vc.ar, eed->v2->co, co2, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK))
1030                                 {
1031                                         /* 2D rotate by 90d while adding.
1032                                          *  (x, y) = (y, -x)
1033                                          *
1034                                          * accumulate the screenspace normal in 2D,
1035                                          * with screenspace edge length weighting the result. */
1036                                         if (line_point_side_v2(co1, co2, mval_f) >= 0.0f) {
1037                                                 nor[0] +=  (co1[1] - co2[1]);
1038                                                 nor[1] += -(co1[0] - co2[0]);
1039                                         }
1040                                         else {
1041                                                 nor[0] +=  (co2[1] - co1[1]);
1042                                                 nor[1] += -(co2[0] - co1[0]);
1043                                         }
1044                                         done = true;
1045                                 }
1046                         }
1047                 }
1048
1049                 if (done) {
1050                         float view_vec[3], cross[3];
1051
1052                         /* convert the 2D nomal into 3D */
1053                         mul_mat3_m4_v3(vc.rv3d->viewinv, nor); /* worldspace */
1054                         mul_mat3_m4_v3(vc.obedit->imat, nor); /* local space */
1055
1056                         /* correct the normal to be aligned on the view plane */
1057                         mul_v3_mat3_m4v3(view_vec, vc.obedit->imat, vc.rv3d->viewinv[2]);
1058                         cross_v3_v3v3(cross, nor, view_vec);
1059                         cross_v3_v3v3(nor, view_vec, cross);
1060                         normalize_v3(nor);
1061                 }
1062
1063                 /* center */
1064                 copy_v3_v3(ofs, center);
1065
1066                 mul_m4_v3(vc.obedit->obmat, ofs);  /* view space */
1067                 ED_view3d_win_to_3d_int(vc.v3d, vc.ar, ofs, event->mval, ofs);
1068                 mul_m4_v3(vc.obedit->imat, ofs); // back in object space
1069
1070                 sub_v3_v3(ofs, center);
1071
1072                 /* calculate rotation */
1073                 unit_m3(mat);
1074                 if (done) {
1075                         float angle;
1076
1077                         normalize_v3_v3(vec, ofs);
1078
1079                         angle = angle_normalized_v3v3(vec, nor);
1080
1081                         if (angle != 0.0f) {
1082                                 float axis[3];
1083
1084                                 cross_v3_v3v3(axis, nor, vec);
1085
1086                                 /* halve the rotation if its applied twice */
1087                                 if (rot_src) {
1088                                         angle *= 0.5f;
1089                                 }
1090
1091                                 axis_angle_to_mat3(mat, axis, angle);
1092                         }
1093                 }
1094
1095                 if (rot_src) {
1096                         EDBM_op_callf(vc.em, op, "rotate verts=%hv cent=%v matrix=%m3",
1097                                       BM_ELEM_SELECT, center, mat);
1098
1099                         /* also project the source, for retopo workflow */
1100                         if (use_proj)
1101                                 EMBM_project_snap_verts(C, vc.ar, vc.em);
1102                 }
1103
1104                 edbm_extrude_ex(vc.obedit, vc.em, extrude_htype, BM_ELEM_SELECT, true, true);
1105                 EDBM_op_callf(vc.em, op, "rotate verts=%hv cent=%v matrix=%m3",
1106                               BM_ELEM_SELECT, center, mat);
1107                 EDBM_op_callf(vc.em, op, "translate verts=%hv vec=%v",
1108                               BM_ELEM_SELECT, ofs);
1109         }
1110         else {
1111                 const float *cursor = ED_view3d_cursor3d_get(vc.scene, vc.v3d)->location;
1112                 BMOperator bmop;
1113                 BMOIter oiter;
1114
1115                 copy_v3_v3(center, cursor);
1116                 ED_view3d_win_to_3d_int(vc.v3d, vc.ar, center, event->mval, center);
1117
1118                 mul_m4_v3(vc.obedit->imat, center); // back in object space
1119
1120                 EDBM_op_init(vc.em, &bmop, op, "create_vert co=%v", center);
1121                 BMO_op_exec(vc.em->bm, &bmop);
1122
1123                 BMO_ITER (v1, &oiter, bmop.slots_out, "vert.out", BM_VERT) {
1124                         BM_vert_select_set(vc.em->bm, v1, true);
1125                 }
1126
1127                 if (!EDBM_op_finish(vc.em, &bmop, op, true)) {
1128                         return OPERATOR_CANCELLED;
1129                 }
1130         }
1131
1132         if (use_proj)
1133                 EMBM_project_snap_verts(C, vc.ar, vc.em);
1134
1135         /* This normally happens when pushing undo but modal operators
1136          * like this one don't push undo data until after modal mode is
1137          * done. */
1138         EDBM_mesh_normals_update(vc.em);
1139
1140         EDBM_update_generic(vc.em, true, true);
1141
1142         return OPERATOR_FINISHED;
1143 }
1144
1145 void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot)
1146 {
1147         /* identifiers */
1148         ot->name = "Duplicate or Extrude to Cursor";
1149         ot->idname = "MESH_OT_dupli_extrude_cursor";
1150         ot->description = "Duplicate and extrude selected vertices, edges or faces towards the mouse cursor";
1151
1152         /* api callbacks */
1153         ot->invoke = edbm_dupli_extrude_cursor_invoke;
1154         ot->poll = ED_operator_editmesh_region_view3d;
1155
1156         /* flags */
1157         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1158
1159         RNA_def_boolean(ot->srna, "rotate_source", true, "Rotate Source", "Rotate initial selection giving better shape");
1160 }
1161
1162 /** \} */