Extrude Widget: with/without axis constraint
[blender.git] / source / blender / editors / transform / transform_manipulator_2d.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  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/transform/transform_manipulator_2d.c
22  *  \ingroup edtransform
23  *
24  * \name 2D Transform Manipulator
25  *
26  * Used for UV/Image Editor
27  */
28
29 #include "MEM_guardedalloc.h"
30
31 #include "BLI_listbase.h"
32 #include "BLI_math.h"
33
34 #include "DNA_meshdata_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_screen_types.h"
37 #include "DNA_space_types.h"
38 #include "DNA_view3d_types.h"
39
40 #include "BKE_context.h"
41 #include "BKE_editmesh.h"
42
43 #include "RNA_access.h"
44
45 #include "UI_resources.h"
46 #include "UI_view2d.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50 #include "wm.h" /* XXX */
51
52 #include "ED_image.h"
53 #include "ED_screen.h"
54 #include "ED_uvedit.h"
55 #include "ED_manipulator_library.h"
56
57 #include "transform.h" /* own include */
58
59 /* axes as index */
60 enum {
61         MAN2D_AXIS_TRANS_X = 0,
62         MAN2D_AXIS_TRANS_Y,
63
64         MAN2D_AXIS_LAST,
65 };
66
67 typedef struct ManipulatorGroup2D {
68         wmManipulator *translate_x,
69                  *translate_y;
70
71         wmManipulator *cage;
72
73         /* Current origin in view space, used to update widget origin for possible view changes */
74         float origin[2];
75         float min[2];
76         float max[2];
77
78 } ManipulatorGroup2D;
79
80
81 /* **************** Utilities **************** */
82
83 /* loop over axes */
84 #define MAN2D_ITER_AXES_BEGIN(axis, axis_idx) \
85         { \
86                 wmManipulator *axis; \
87                 int axis_idx; \
88                 for (axis_idx = 0; axis_idx < MAN2D_AXIS_LAST; axis_idx++) { \
89                         axis = manipulator2d_get_axis_from_index(man, axis_idx);
90
91 #define MAN2D_ITER_AXES_END \
92                 } \
93         } ((void)0)
94
95 static wmManipulator *manipulator2d_get_axis_from_index(const ManipulatorGroup2D *man, const short axis_idx)
96 {
97         BLI_assert(IN_RANGE_INCL(axis_idx, (float)MAN2D_AXIS_TRANS_X, (float)MAN2D_AXIS_TRANS_Y));
98
99         switch (axis_idx) {
100                 case MAN2D_AXIS_TRANS_X:
101                         return man->translate_x;
102                 case MAN2D_AXIS_TRANS_Y:
103                         return man->translate_y;
104         }
105
106         return NULL;
107 }
108
109 static void manipulator2d_get_axis_color(const int axis_idx, float *r_col, float *r_col_hi)
110 {
111         const float alpha = 0.6f;
112         const float alpha_hi = 1.0f;
113         int col_id;
114
115         switch (axis_idx) {
116                 case MAN2D_AXIS_TRANS_X:
117                         col_id = TH_AXIS_X;
118                         break;
119                 case MAN2D_AXIS_TRANS_Y:
120                         col_id = TH_AXIS_Y;
121                         break;
122         }
123
124         UI_GetThemeColor4fv(col_id, r_col);
125
126         copy_v4_v4(r_col_hi, r_col);
127         r_col[3] *= alpha;
128         r_col_hi[3] *= alpha_hi;
129 }
130
131 static ManipulatorGroup2D *manipulatorgroup2d_init(wmManipulatorGroup *mgroup)
132 {
133         const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_2d", true);
134         const wmManipulatorType *wt_cage = WM_manipulatortype_find("MANIPULATOR_WT_cage_2d", true);
135
136         ManipulatorGroup2D *man = MEM_callocN(sizeof(ManipulatorGroup2D), __func__);
137
138         man->translate_x = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL);
139         man->translate_y = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL);
140         man->cage = WM_manipulator_new_ptr(wt_cage, mgroup, NULL);
141
142         RNA_enum_set(man->cage->ptr, "transform",
143                      ED_MANIPULATOR_CAGE2D_XFORM_FLAG_TRANSLATE |
144                      ED_MANIPULATOR_CAGE2D_XFORM_FLAG_SCALE |
145                      ED_MANIPULATOR_CAGE2D_XFORM_FLAG_ROTATE);
146
147         return man;
148 }
149
150 /**
151  * Calculates origin in view space, use with #manipulator2d_origin_to_region.
152  */
153 static void manipulator2d_calc_bounds(const bContext *C, float *r_center, float *r_min, float *r_max)
154 {
155         SpaceImage *sima = CTX_wm_space_image(C);
156         Image *ima = ED_space_image(sima);
157
158         float min_buf[2], max_buf[2];
159         if (r_min == NULL) {
160                 r_min = min_buf;
161         }
162         if (r_max == NULL) {
163                 r_max = max_buf;
164         }
165
166         if (!ED_uvedit_minmax(CTX_data_scene(C), ima, CTX_data_edit_object(C), r_min, r_max)) {
167                 zero_v2(r_min);
168                 zero_v2(r_max);
169         }
170         mid_v2_v2v2(r_center, r_min, r_max);
171 }
172
173 /**
174  * Convert origin (or any other point) from view to region space.
175  */
176 BLI_INLINE void manipulator2d_origin_to_region(ARegion *ar, float *r_origin)
177 {
178         UI_view2d_view_to_region_fl(&ar->v2d, r_origin[0], r_origin[1], &r_origin[0], &r_origin[1]);
179 }
180
181 /**
182  * Custom handler for manipulator widgets
183  */
184 static int manipulator2d_modal(
185         bContext *C, wmManipulator *widget, const wmEvent *UNUSED(event),
186         eWM_ManipulatorTweak UNUSED(tweak_flag))
187 {
188         ARegion *ar = CTX_wm_region(C);
189         float origin[3];
190
191         manipulator2d_calc_bounds(C, origin, NULL, NULL);
192         manipulator2d_origin_to_region(ar, origin);
193         WM_manipulator_set_matrix_location(widget, origin);
194
195         ED_region_tag_redraw(ar);
196
197         return OPERATOR_RUNNING_MODAL;
198 }
199
200 void ED_widgetgroup_manipulator2d_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
201 {
202         wmOperatorType *ot_translate = WM_operatortype_find("TRANSFORM_OT_translate", true);
203         ManipulatorGroup2D *man = manipulatorgroup2d_init(mgroup);
204         mgroup->customdata = man;
205
206         MAN2D_ITER_AXES_BEGIN(axis, axis_idx)
207         {
208                 const float offset[3] = {0.0f, 0.2f};
209
210                 float color[4], color_hi[4];
211                 manipulator2d_get_axis_color(axis_idx, color, color_hi);
212
213                 /* custom handler! */
214                 WM_manipulator_set_fn_custom_modal(axis, manipulator2d_modal);
215                 /* set up widget data */
216                 RNA_float_set(axis->ptr, "angle", -M_PI_2 * axis_idx);
217                 RNA_float_set(axis->ptr, "length", 0.8f);
218                 WM_manipulator_set_matrix_offset_location(axis, offset);
219                 WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH);
220                 WM_manipulator_set_scale(axis, U.manipulator_size);
221                 WM_manipulator_set_color(axis, color);
222                 WM_manipulator_set_color_highlight(axis, color_hi);
223
224                 /* assign operator */
225                 PointerRNA *ptr = WM_manipulator_operator_set(axis, 0, ot_translate, NULL);
226                 int constraint[3] = {0};
227                 constraint[(axis_idx + 1) % 2] = 1;
228                 if (RNA_struct_find_property(ptr, "constraint_axis"))
229                         RNA_boolean_set_array(ptr, "constraint_axis", constraint);
230                 RNA_boolean_set(ptr, "release_confirm", 1);
231         }
232         MAN2D_ITER_AXES_END;
233
234         {
235                 wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true);
236                 wmOperatorType *ot_rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true);
237                 PointerRNA *ptr;
238
239                 /* assign operator */
240                 ptr = WM_manipulator_operator_set(man->cage, 0, ot_translate, NULL);
241                 RNA_boolean_set(ptr, "release_confirm", 1);
242
243                 int constraint_x[3] = {1, 0, 0};
244                 int constraint_y[3] = {0, 1, 0};
245
246                 ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X, ot_resize, NULL);
247                 PropertyRNA *prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm");
248                 PropertyRNA *prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis");
249                 RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x);
250                 RNA_property_boolean_set(ptr, prop_release_confirm, true);
251                 ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X, ot_resize, NULL);
252                 RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x);
253                 RNA_property_boolean_set(ptr, prop_release_confirm, true);
254                 ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_Y, ot_resize, NULL);
255                 RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y);
256                 RNA_property_boolean_set(ptr, prop_release_confirm, true);
257                 ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_Y, ot_resize, NULL);
258                 RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y);
259                 RNA_property_boolean_set(ptr, prop_release_confirm, true);
260
261                 ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X_MIN_Y, ot_resize, NULL);
262                 RNA_property_boolean_set(ptr, prop_release_confirm, true);
263                 ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X_MAX_Y, ot_resize, NULL);
264                 RNA_property_boolean_set(ptr, prop_release_confirm, true);
265                 ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X_MIN_Y, ot_resize, NULL);
266                 RNA_property_boolean_set(ptr, prop_release_confirm, true);
267                 ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X_MAX_Y, ot_resize, NULL);
268                 RNA_property_boolean_set(ptr, prop_release_confirm, true);
269                 ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_ROTATE, ot_rotate, NULL);
270                 RNA_property_boolean_set(ptr, prop_release_confirm, true);
271         }
272 }
273
274 void ED_widgetgroup_manipulator2d_refresh(const bContext *C, wmManipulatorGroup *mgroup)
275 {
276         ManipulatorGroup2D *man = mgroup->customdata;
277         float origin[3];
278         manipulator2d_calc_bounds(C, origin, man->min, man->max);
279         copy_v2_v2(man->origin, origin);
280         bool show_cage = !equals_v2v2(man->min, man->max);
281
282         if (show_cage) {
283                 man->cage->flag &= ~WM_MANIPULATOR_HIDDEN;
284                 man->translate_x->flag |= WM_MANIPULATOR_HIDDEN;
285                 man->translate_y->flag |= WM_MANIPULATOR_HIDDEN;
286         }
287         else {
288                 man->cage->flag |= WM_MANIPULATOR_HIDDEN;
289                 man->translate_x->flag &= ~WM_MANIPULATOR_HIDDEN;
290                 man->translate_y->flag &= ~WM_MANIPULATOR_HIDDEN;
291         }
292
293         if (show_cage) {
294                 wmManipulatorOpElem *mpop;
295                 float mid[2];
296                 const float *min = man->min;
297                 const float *max = man->max;
298                 mid_v2_v2v2(mid, min, max);
299
300                 mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X);
301                 PropertyRNA *prop_center_override = RNA_struct_find_property(&mpop->ptr, "center_override");
302                 RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){max[0], mid[1], 0.0f});
303                 mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X);
304                 RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){min[0], mid[1], 0.0f});
305                 mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_Y);
306                 RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){mid[0], max[1], 0.0f});
307                 mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_Y);
308                 RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){mid[0], min[1], 0.0f});
309
310                 mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X_MIN_Y);
311                 RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){max[0], max[1], 0.0f});
312                 mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X_MAX_Y);
313                 RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){max[0], min[1], 0.0f});
314                 mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X_MIN_Y);
315                 RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){min[0], max[1], 0.0f});
316                 mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X_MAX_Y);
317                 RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){min[0], min[1], 0.0f});
318
319                 mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_ROTATE);
320                 RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){mid[0], mid[1], 0.0f});
321         }
322 }
323
324 void ED_widgetgroup_manipulator2d_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup)
325 {
326         ARegion *ar = CTX_wm_region(C);
327         ManipulatorGroup2D *man = mgroup->customdata;
328         float origin[3] = {UNPACK2(man->origin), 0.0f};
329         float origin_aa[3] = {UNPACK2(man->origin), 0.0f};
330
331         manipulator2d_origin_to_region(ar, origin);
332
333         MAN2D_ITER_AXES_BEGIN(axis, axis_idx)
334         {
335                 WM_manipulator_set_matrix_location(axis, origin);
336         }
337         MAN2D_ITER_AXES_END;
338
339         UI_view2d_view_to_region_m4(&ar->v2d, man->cage->matrix_space);
340         WM_manipulator_set_matrix_offset_location(man->cage, origin_aa);
341         man->cage->matrix_offset[0][0] = (man->max[0] - man->min[0]);
342         man->cage->matrix_offset[1][1] = (man->max[1] - man->min[1]);
343 }
344
345 /* TODO (Julian)
346  * - Called on every redraw, better to do a more simple poll and check for selection in _refresh
347  * - UV editing only, could be expanded for other things.
348  */
349 bool ED_widgetgroup_manipulator2d_poll(const bContext *C, wmManipulatorGroupType *UNUSED(wgt))
350 {
351         if ((U.manipulator_flag & USER_MANIPULATOR_DRAW) == 0) {
352                 return false;
353         }
354
355         SpaceImage *sima = CTX_wm_space_image(C);
356         Object *obedit = CTX_data_edit_object(C);
357
358         if (ED_space_image_show_uvedit(sima, obedit)) {
359                 Image *ima = ED_space_image(sima);
360                 Scene *scene = CTX_data_scene(C);
361                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
362                 BMFace *efa;
363                 BMLoop *l;
364                 BMIter iter, liter;
365
366                 const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
367
368                 /* check if there's a selected poly */
369                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
370                         if (!uvedit_face_visible_test(scene, obedit, ima, efa))
371                                 continue;
372
373                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
374                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
375                                         return true;
376                                 }
377                         }
378                 }
379         }
380
381         return false;
382 }