Orientation for 3D cursor
[blender.git] / source / blender / editors / mesh / editmesh_extrude_spin.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_spin.c
29  *  \ingroup edmesh
30  */
31
32 #include "DNA_object_types.h"
33
34 #include "BLI_math.h"
35
36 #include "BKE_context.h"
37 #include "BKE_report.h"
38 #include "BKE_editmesh.h"
39
40 #include "RNA_define.h"
41 #include "RNA_access.h"
42
43 #include "WM_api.h"
44 #include "WM_types.h"
45
46 #include "ED_mesh.h"
47 #include "ED_screen.h"
48 #include "ED_view3d.h"
49
50 #include "UI_resources.h"
51
52 #include "MEM_guardedalloc.h"
53
54 #include "mesh_intern.h"  /* own include */
55
56 #define USE_MANIPULATOR
57
58 #ifdef USE_MANIPULATOR
59 #include "ED_manipulator_library.h"
60 #include "ED_undo.h"
61 #endif
62
63 /* -------------------------------------------------------------------- */
64 /** \name Spin Manipulator
65  * \{ */
66
67 #ifdef USE_MANIPULATOR
68 typedef struct ManipulatorSpinGroup {
69         /* Arrow to change plane depth. */
70         struct wmManipulator *translate_z;
71         /* Translate XYZ */
72         struct wmManipulator *translate_c;
73         /* For grabbing the manipulator and moving freely. */
74         struct wmManipulator *rotate_c;
75         /* Spin angle */
76         struct wmManipulator *angle_z;
77
78         /* We could store more vars here! */
79         struct {
80                 bContext *context;
81                 wmOperator *op;
82                 PropertyRNA *prop_axis_co;
83                 PropertyRNA *prop_axis_no;
84                 PropertyRNA *prop_angle;
85
86                 float rotate_axis[3];
87                 float rotate_up[3];
88         } data;
89 } ManipulatorSpinGroup;
90
91 /**
92  * XXX. calling redo from property updates is not great.
93  * This is needed because changing the RNA doesn't cause a redo
94  * and we're not using operator UI which does just this.
95  */
96 static void manipulator_spin_exec(ManipulatorSpinGroup *man)
97 {
98         wmOperator *op = man->data.op;
99         if (op == WM_operator_last_redo((bContext *)man->data.context)) {
100                 ED_undo_operator_repeat((bContext *)man->data.context, op);
101         }
102 }
103
104 static void manipulator_mesh_spin_update_from_op(ManipulatorSpinGroup *man)
105 {
106         wmOperator *op = man->data.op;
107
108         float plane_co[3], plane_no[3];
109
110         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, plane_co);
111         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
112
113         WM_manipulator_set_matrix_location(man->translate_z, plane_co);
114         WM_manipulator_set_matrix_location(man->rotate_c, plane_co);
115         WM_manipulator_set_matrix_location(man->angle_z, plane_co);
116         /* translate_c location comes from the property. */
117
118         WM_manipulator_set_matrix_rotation_from_z_axis(man->translate_z, plane_no);
119         WM_manipulator_set_matrix_rotation_from_z_axis(man->angle_z, plane_no);
120
121         WM_manipulator_set_scale(man->translate_c, 0.2);
122
123         RegionView3D *rv3d = ED_view3d_context_rv3d(man->data.context);
124         if (rv3d) {
125                 normalize_v3_v3(man->data.rotate_axis, rv3d->viewinv[2]);
126                 normalize_v3_v3(man->data.rotate_up, rv3d->viewinv[1]);
127
128                 /* ensure its orthogonal */
129                 project_plane_normalized_v3_v3v3(man->data.rotate_up, man->data.rotate_up, man->data.rotate_axis);
130                 normalize_v3(man->data.rotate_up);
131
132                 WM_manipulator_set_matrix_rotation_from_z_axis(man->translate_c, plane_no);
133                 WM_manipulator_set_matrix_rotation_from_yz_axis(man->rotate_c, plane_no, man->data.rotate_axis);
134
135                 /* show the axis instead of mouse cursor */
136                 RNA_enum_set(man->rotate_c->ptr, "draw_options",
137                              ED_MANIPULATOR_DIAL_DRAW_FLAG_ANGLE_MIRROR |
138                              ED_MANIPULATOR_DIAL_DRAW_FLAG_ANGLE_START_Y);
139
140         }
141 }
142
143 /* depth callbacks */
144 static void manipulator_spin_prop_depth_get(
145         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
146         void *value_p)
147 {
148         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
149         wmOperator *op = man->data.op;
150         float *value = value_p;
151
152         BLI_assert(mpr_prop->type->array_length == 1);
153         UNUSED_VARS_NDEBUG(mpr_prop);
154
155         float plane_co[3], plane_no[3];
156         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, plane_co);
157         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
158
159         value[0] = dot_v3v3(plane_no, plane_co) - dot_v3v3(plane_no, mpr->matrix_basis[3]);
160 }
161
162 static void manipulator_spin_prop_depth_set(
163         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
164         const void *value_p)
165 {
166         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
167         wmOperator *op = man->data.op;
168         const float *value = value_p;
169
170         BLI_assert(mpr_prop->type->array_length == 1);
171         UNUSED_VARS_NDEBUG(mpr_prop);
172
173         float plane_co[3], plane[4];
174         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, plane_co);
175         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane);
176         normalize_v3(plane);
177
178         plane[3] = -value[0] - dot_v3v3(plane, mpr->matrix_basis[3]);
179
180         /* Keep our location, may be offset simply to be inside the viewport. */
181         closest_to_plane_normalized_v3(plane_co, plane, plane_co);
182
183         RNA_property_float_set_array(op->ptr, man->data.prop_axis_co, plane_co);
184
185         manipulator_spin_exec(man);
186 }
187
188 /* translate callbacks */
189 static void manipulator_spin_prop_translate_get(
190         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
191         void *value_p)
192 {
193         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
194         wmOperator *op = man->data.op;
195         float *value = value_p;
196
197         BLI_assert(mpr_prop->type->array_length == 3);
198         UNUSED_VARS_NDEBUG(mpr_prop);
199
200         RNA_property_float_get_array(op->ptr, man->data.prop_axis_co, value);
201 }
202
203 static void manipulator_spin_prop_translate_set(
204         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
205         const void *value)
206 {
207         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
208         wmOperator *op = man->data.op;
209
210         BLI_assert(mpr_prop->type->array_length == 3);
211         UNUSED_VARS_NDEBUG(mpr_prop);
212
213         RNA_property_float_set_array(op->ptr, man->data.prop_axis_co, value);
214
215         manipulator_spin_exec(man);
216 }
217
218 /* angle callbacks */
219 static void manipulator_spin_prop_axis_angle_get(
220         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
221         void *value_p)
222 {
223         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
224         wmOperator *op = man->data.op;
225         float *value = value_p;
226
227         BLI_assert(mpr_prop->type->array_length == 1);
228         UNUSED_VARS_NDEBUG(mpr_prop);
229
230         float plane_no[4];
231         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
232         normalize_v3(plane_no);
233
234         float plane_no_proj[3];
235         project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, man->data.rotate_axis);
236
237         if (!is_zero_v3(plane_no_proj)) {
238                 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, man->data.rotate_up, man->data.rotate_axis);
239                 value[0] = angle;
240         }
241         else {
242                 value[0] = 0.0f;
243         }
244 }
245
246 static void manipulator_spin_prop_axis_angle_set(
247         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
248         const void *value_p)
249 {
250         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
251         wmOperator *op = man->data.op;
252         const float *value = value_p;
253
254         BLI_assert(mpr_prop->type->array_length == 1);
255         UNUSED_VARS_NDEBUG(mpr_prop);
256
257         float plane_no[4];
258         RNA_property_float_get_array(op->ptr, man->data.prop_axis_no, plane_no);
259         normalize_v3(plane_no);
260
261         float plane_no_proj[3];
262         project_plane_normalized_v3_v3v3(plane_no_proj, plane_no, man->data.rotate_axis);
263
264         if (!is_zero_v3(plane_no_proj)) {
265                 const float angle = -angle_signed_on_axis_v3v3_v3(plane_no_proj, man->data.rotate_up, man->data.rotate_axis);
266                 const float angle_delta = angle - angle_compat_rad(value[0], angle);
267                 if (angle_delta != 0.0f) {
268                         float mat[3][3];
269                         axis_angle_normalized_to_mat3(mat, man->data.rotate_axis, angle_delta);
270                         mul_m3_v3(mat, plane_no);
271
272                         /* re-normalize - seems acceptable */
273                         RNA_property_float_set_array(op->ptr, man->data.prop_axis_no, plane_no);
274
275                         manipulator_spin_exec(man);
276                 }
277         }
278 }
279
280 /* angle callbacks */
281 static void manipulator_spin_prop_angle_get(
282         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
283         void *value_p)
284 {
285         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
286         wmOperator *op = man->data.op;
287         float *value = value_p;
288
289         BLI_assert(mpr_prop->type->array_length == 1);
290         UNUSED_VARS_NDEBUG(mpr_prop);
291         value[0] = RNA_property_float_get(op->ptr, man->data.prop_angle);
292 }
293
294 static void manipulator_spin_prop_angle_set(
295         const wmManipulator *mpr, wmManipulatorProperty *mpr_prop,
296         const void *value_p)
297 {
298         ManipulatorSpinGroup *man = mpr->parent_mgroup->customdata;
299         wmOperator *op = man->data.op;
300         BLI_assert(mpr_prop->type->array_length == 1);
301         UNUSED_VARS_NDEBUG(mpr_prop);
302         const float *value = value_p;
303         RNA_property_float_set(op->ptr, man->data.prop_angle, value[0]);
304
305         manipulator_spin_exec(man);
306 }
307
308 static bool manipulator_mesh_spin_poll(const bContext *C, wmManipulatorGroupType *wgt)
309 {
310         wmOperator *op = WM_operator_last_redo(C);
311         if (op == NULL || !STREQ(op->type->idname, "MESH_OT_spin")) {
312                 WM_manipulator_group_type_unlink_delayed_ptr(wgt);
313                 return false;
314         }
315         return true;
316 }
317
318 static void manipulator_mesh_spin_setup(const bContext *C, wmManipulatorGroup *mgroup)
319 {
320         wmOperator *op = WM_operator_last_redo(C);
321
322         if (op == NULL || !STREQ(op->type->idname, "MESH_OT_spin")) {
323                 return;
324         }
325
326         struct ManipulatorSpinGroup *man = MEM_callocN(sizeof(ManipulatorSpinGroup), __func__);
327         mgroup->customdata = man;
328
329         const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_3d", true);
330         const wmManipulatorType *wt_grab = WM_manipulatortype_find("MANIPULATOR_WT_grab_3d", true);
331         const wmManipulatorType *wt_dial = WM_manipulatortype_find("MANIPULATOR_WT_dial_3d", true);
332
333         man->translate_z = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL);
334         man->translate_c = WM_manipulator_new_ptr(wt_grab, mgroup, NULL);
335         man->rotate_c = WM_manipulator_new_ptr(wt_dial, mgroup, NULL);
336         man->angle_z = WM_manipulator_new_ptr(wt_dial, mgroup, NULL);
337
338         UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, man->translate_z->color);
339         UI_GetThemeColor3fv(TH_MANIPULATOR_PRIMARY, man->translate_c->color);
340         UI_GetThemeColor3fv(TH_MANIPULATOR_SECONDARY, man->rotate_c->color);
341         UI_GetThemeColor3fv(TH_AXIS_Z, man->angle_z->color);
342
343
344         RNA_enum_set(man->translate_z->ptr, "draw_style", ED_MANIPULATOR_ARROW_STYLE_NORMAL);
345         RNA_enum_set(man->translate_c->ptr, "draw_style", ED_MANIPULATOR_GRAB_STYLE_RING_2D);
346
347         WM_manipulator_set_flag(man->translate_c, WM_MANIPULATOR_DRAW_VALUE, true);
348         WM_manipulator_set_flag(man->rotate_c, WM_MANIPULATOR_DRAW_VALUE, true);
349         WM_manipulator_set_flag(man->angle_z, WM_MANIPULATOR_DRAW_VALUE, true);
350
351         WM_manipulator_set_scale(man->angle_z, 0.5f);
352
353         {
354                 man->data.context = (bContext *)C;
355                 man->data.op = op;
356                 man->data.prop_axis_co = RNA_struct_find_property(op->ptr, "center");
357                 man->data.prop_axis_no = RNA_struct_find_property(op->ptr, "axis");
358                 man->data.prop_angle = RNA_struct_find_property(op->ptr, "angle");
359         }
360
361         manipulator_mesh_spin_update_from_op(man);
362
363         /* Setup property callbacks */
364         {
365                 WM_manipulator_target_property_def_func(
366                         man->translate_z, "offset",
367                         &(const struct wmManipulatorPropertyFnParams) {
368                             .value_get_fn = manipulator_spin_prop_depth_get,
369                             .value_set_fn = manipulator_spin_prop_depth_set,
370                             .range_get_fn = NULL,
371                             .user_data = NULL,
372                         });
373
374                 WM_manipulator_target_property_def_func(
375                         man->translate_c, "offset",
376                         &(const struct wmManipulatorPropertyFnParams) {
377                             .value_get_fn = manipulator_spin_prop_translate_get,
378                             .value_set_fn = manipulator_spin_prop_translate_set,
379                             .range_get_fn = NULL,
380                             .user_data = NULL,
381                         });
382
383                 WM_manipulator_target_property_def_func(
384                         man->rotate_c, "offset",
385                         &(const struct wmManipulatorPropertyFnParams) {
386                             .value_get_fn = manipulator_spin_prop_axis_angle_get,
387                             .value_set_fn = manipulator_spin_prop_axis_angle_set,
388                             .range_get_fn = NULL,
389                             .user_data = NULL,
390                         });
391
392                 WM_manipulator_target_property_def_func(
393                         man->angle_z, "offset",
394                         &(const struct wmManipulatorPropertyFnParams) {
395                             .value_get_fn = manipulator_spin_prop_angle_get,
396                             .value_set_fn = manipulator_spin_prop_angle_set,
397                             .range_get_fn = NULL,
398                             .user_data = NULL,
399                         });
400
401         }
402 }
403
404 static void manipulator_mesh_spin_draw_prepare(
405         const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
406 {
407         ManipulatorSpinGroup *man = mgroup->customdata;
408         if (man->data.op->next) {
409                 man->data.op = WM_operator_last_redo((bContext *)man->data.context);
410         }
411         manipulator_mesh_spin_update_from_op(man);
412 }
413
414 static void MESH_WGT_spin(struct wmManipulatorGroupType *wgt)
415 {
416         wgt->name = "Mesh Spin";
417         wgt->idname = "MESH_WGT_spin";
418
419         wgt->flag = WM_MANIPULATORGROUPTYPE_3D;
420
421         wgt->mmap_params.spaceid = SPACE_VIEW3D;
422         wgt->mmap_params.regionid = RGN_TYPE_WINDOW;
423
424         wgt->poll = manipulator_mesh_spin_poll;
425         wgt->setup = manipulator_mesh_spin_setup;
426         wgt->draw_prepare = manipulator_mesh_spin_draw_prepare;
427 }
428
429 /** \} */
430
431 #endif  /* USE_MANIPULATOR */
432
433 /* -------------------------------------------------------------------- */
434 /** \name Spin Operator
435  * \{ */
436
437 static int edbm_spin_exec(bContext *C, wmOperator *op)
438 {
439         Object *obedit = CTX_data_edit_object(C);
440         BMEditMesh *em = BKE_editmesh_from_object(obedit);
441         BMesh *bm = em->bm;
442         BMOperator spinop;
443         float cent[3], axis[3];
444         float d[3] = {0.0f, 0.0f, 0.0f};
445         int steps, dupli;
446         float angle;
447
448         RNA_float_get_array(op->ptr, "center", cent);
449         RNA_float_get_array(op->ptr, "axis", axis);
450         steps = RNA_int_get(op->ptr, "steps");
451         angle = RNA_float_get(op->ptr, "angle");
452         //if (ts->editbutflag & B_CLOCKWISE)
453         angle = -angle;
454         dupli = RNA_boolean_get(op->ptr, "dupli");
455
456         if (is_zero_v3(axis)) {
457                 BKE_report(op->reports, RPT_ERROR, "Invalid/unset axis");
458                 return OPERATOR_CANCELLED;
459         }
460
461         /* keep the values in worldspace since we're passing the obmat */
462         if (!EDBM_op_init(em, &spinop, op,
463                           "spin geom=%hvef cent=%v axis=%v dvec=%v steps=%i angle=%f space=%m4 use_duplicate=%b",
464                           BM_ELEM_SELECT, cent, axis, d, steps, angle, obedit->obmat, dupli))
465         {
466                 return OPERATOR_CANCELLED;
467         }
468         BMO_op_exec(bm, &spinop);
469         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
470         BMO_slot_buffer_hflag_enable(bm, spinop.slots_out, "geom_last.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true);
471         if (!EDBM_op_finish(em, &spinop, op, true)) {
472                 return OPERATOR_CANCELLED;
473         }
474
475         EDBM_update_generic(em, true, true);
476
477         return OPERATOR_FINISHED;
478 }
479
480 /* get center and axis, in global coords */
481 static int edbm_spin_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
482 {
483         Scene *scene = CTX_data_scene(C);
484         View3D *v3d = CTX_wm_view3d(C);
485         RegionView3D *rv3d = ED_view3d_context_rv3d(C);
486
487         PropertyRNA *prop;
488         prop = RNA_struct_find_property(op->ptr, "center");
489         if (!RNA_property_is_set(op->ptr, prop)) {
490                 RNA_property_float_set_array(op->ptr, prop, ED_view3d_cursor3d_get(scene, v3d)->location);
491         }
492         if (rv3d) {
493                 prop = RNA_struct_find_property(op->ptr, "axis");
494                 if (!RNA_property_is_set(op->ptr, prop)) {
495                         RNA_property_float_set_array(op->ptr, prop, rv3d->viewinv[2]);
496                 }
497         }
498
499         int ret = edbm_spin_exec(C, op);
500
501 #ifdef USE_MANIPULATOR
502         if (ret & OPERATOR_FINISHED) {
503                 /* Setup manipulators */
504                 if (v3d && (v3d->twflag & V3D_MANIPULATOR_DRAW)) {
505                         WM_manipulator_group_type_ensure("MESH_WGT_spin");
506                 }
507         }
508 #endif
509
510         return ret;
511 }
512
513 void MESH_OT_spin(wmOperatorType *ot)
514 {
515         PropertyRNA *prop;
516
517         /* identifiers */
518         ot->name = "Spin";
519         ot->description = "Extrude selected vertices in a circle around the cursor in indicated viewport";
520         ot->idname = "MESH_OT_spin";
521
522         /* api callbacks */
523         ot->invoke = edbm_spin_invoke;
524         ot->exec = edbm_spin_exec;
525         ot->poll = ED_operator_editmesh;
526
527         /* flags */
528         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
529
530         /* props */
531         RNA_def_int(ot->srna, "steps", 9, 0, 1000000, "Steps", "Steps", 0, 1000);
532         RNA_def_boolean(ot->srna, "dupli", 0, "Dupli", "Make Duplicates");
533         prop = RNA_def_float(ot->srna, "angle", DEG2RADF(90.0f), -1e12f, 1e12f, "Angle", "Rotation for each step",
534                              DEG2RADF(-360.0f), DEG2RADF(360.0f));
535         RNA_def_property_subtype(prop, PROP_ANGLE);
536
537         RNA_def_float_vector(ot->srna, "center", 3, NULL, -1e12f, 1e12f,
538                              "Center", "Center in global view space", -1e4f, 1e4f);
539         RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f, "Axis", "Axis in global view space", -1.0f, 1.0f);
540
541 #ifdef USE_MANIPULATOR
542         WM_manipulatorgrouptype_append(MESH_WGT_spin);
543 #endif
544 }