Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / transform / transform_manipulator2d.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) 2016 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): none yet.
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/transform/transform_manipulator2d.c
27  *  \ingroup edtransform
28  *
29  * \name 2D Transform Manipulator
30  *
31  * Used for UV/Image Editor
32  */
33
34 #include "BKE_context.h"
35 #include "BKE_editmesh.h"
36
37 #include "BLI_listbase.h"
38 #include "BLI_math.h"
39
40 #include "DNA_meshdata_types.h"
41 #include "DNA_object_types.h"
42 #include "DNA_screen_types.h"
43 #include "DNA_space_types.h"
44 #include "DNA_view3d_types.h"
45
46 #include "ED_image.h"
47 #include "ED_screen.h"
48 #include "ED_uvedit.h"
49 #include "ED_manipulator_library.h"
50
51 #include "MEM_guardedalloc.h"
52
53 #include "RNA_access.h"
54
55 #include "UI_resources.h"
56 #include "UI_view2d.h"
57
58 #include "WM_api.h"
59 #include "WM_types.h"
60 #include "wm.h" /* XXX */
61
62 #include "transform.h" /* own include */
63
64 /* axes as index */
65 enum {
66         MAN2D_AXIS_TRANS_X = 0,
67         MAN2D_AXIS_TRANS_Y,
68
69         MAN2D_AXIS_LAST,
70 };
71
72 typedef struct ManipulatorGroup2D {
73         wmManipulator *translate_x,
74                  *translate_y;
75
76         /* Current origin in view space, used to update widget origin for possible view changes */
77         float origin[2];
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
135         ManipulatorGroup2D *man = MEM_callocN(sizeof(ManipulatorGroup2D), __func__);
136
137         man->translate_x = WM_manipulator_new_ptr(wt_arrow, mgroup, "translate_x", NULL);
138         man->translate_y = WM_manipulator_new_ptr(wt_arrow, mgroup, "translate_y", NULL);
139
140         return man;
141 }
142
143 /**
144  * Calculates origin in view space, use with #manipulator2d_origin_to_region.
145  */
146 static void manipulator2d_calc_origin(const bContext *C, float *r_origin)
147 {
148         SpaceImage *sima = CTX_wm_space_image(C);
149         Image *ima = ED_space_image(sima);
150
151         if (sima->around == V3D_AROUND_CURSOR) {
152                 copy_v2_v2(r_origin, sima->cursor);
153         }
154         else {
155                 ED_uvedit_center(CTX_data_scene(C), ima, CTX_data_edit_object(C), r_origin, sima->around);
156         }
157 }
158
159 /**
160  * Convert origin (or any other point) from view to region space.
161  */
162 BLI_INLINE void manipulator2d_origin_to_region(ARegion *ar, float *r_origin)
163 {
164         UI_view2d_view_to_region_fl(&ar->v2d, r_origin[0], r_origin[1], &r_origin[0], &r_origin[1]);
165 }
166
167 /**
168  * Custom handler for manipulator widgets
169  */
170 static void manipulator2d_modal(
171         bContext *C, wmManipulator *widget, const wmEvent *UNUSED(event), const int UNUSED(flag))
172 {
173         ARegion *ar = CTX_wm_region(C);
174         float origin[3];
175
176         manipulator2d_calc_origin(C, origin);
177         manipulator2d_origin_to_region(ar, origin);
178         WM_manipulator_set_matrix_location(widget, origin);
179
180         ED_region_tag_redraw(ar);
181 }
182
183 void ED_widgetgroup_manipulator2d_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup)
184 {
185         wmOperatorType *ot_translate = WM_operatortype_find("TRANSFORM_OT_translate", true);
186         ManipulatorGroup2D *man = manipulatorgroup2d_init(mgroup);
187         mgroup->customdata = man;
188
189         MAN2D_ITER_AXES_BEGIN(axis, axis_idx)
190         {
191                 const float offset[3] = {0.0f, 0.2f};
192
193                 float col[4], col_hi[4];
194                 manipulator2d_get_axis_color(axis_idx, col, col_hi);
195
196                 /* custom handler! */
197                 WM_manipulator_set_fn_custom_modal(axis, manipulator2d_modal);
198                 /* set up widget data */
199                 RNA_float_set(axis->ptr, "angle", -M_PI_2 * axis_idx);
200                 RNA_float_set(axis->ptr, "length", 0.8f);
201                 WM_manipulator_set_matrix_offset_location(axis, offset);
202                 WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH);
203                 WM_manipulator_set_scale(axis, U.manipulator_size);
204                 WM_manipulator_set_color(axis, col);
205                 WM_manipulator_set_color_highlight(axis, col_hi);
206
207                 /* assign operator */
208                 PointerRNA *ptr = WM_manipulator_set_operator(axis, ot_translate, NULL);
209                 int constraint[3] = {0.0f};
210                 constraint[(axis_idx + 1) % 2] = 1;
211                 if (RNA_struct_find_property(ptr, "constraint_axis"))
212                         RNA_boolean_set_array(ptr, "constraint_axis", constraint);
213                 RNA_boolean_set(ptr, "release_confirm", 1);
214         }
215         MAN2D_ITER_AXES_END;
216 }
217
218 void ED_widgetgroup_manipulator2d_refresh(const bContext *C, wmManipulatorGroup *mgroup)
219 {
220         ManipulatorGroup2D *man = mgroup->customdata;
221         float origin[3];
222
223         manipulator2d_calc_origin(C, origin);
224         copy_v2_v2(man->origin, origin);
225 }
226
227 void ED_widgetgroup_manipulator2d_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup)
228 {
229         ManipulatorGroup2D *man = mgroup->customdata;
230         float origin[3] = {UNPACK2(man->origin), 0.0f};
231
232         manipulator2d_origin_to_region(CTX_wm_region(C), origin);
233
234         MAN2D_ITER_AXES_BEGIN(axis, axis_idx)
235         {
236                 WM_manipulator_set_matrix_location(axis, origin);
237         }
238         MAN2D_ITER_AXES_END;
239 }
240
241 /* TODO (Julian)
242  * - Called on every redraw, better to do a more simple poll and check for selection in _refresh
243  * - UV editing only, could be expanded for other things.
244  */
245 bool ED_widgetgroup_manipulator2d_poll(const bContext *C, wmManipulatorGroupType *UNUSED(wgt))
246 {
247         SpaceImage *sima = CTX_wm_space_image(C);
248         Object *obedit = CTX_data_edit_object(C);
249
250         if (ED_space_image_show_uvedit(sima, obedit)) {
251                 Image *ima = ED_space_image(sima);
252                 Scene *scene = CTX_data_scene(C);
253                 BMEditMesh *em = BKE_editmesh_from_object(obedit);
254                 BMFace *efa;
255                 BMLoop *l;
256                 BMIter iter, liter;
257
258                 const int cd_loop_uv_offset  = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
259
260                 /* check if there's a selected poly */
261                 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
262                         if (!uvedit_face_visible_test(scene, ima, efa))
263                                 continue;
264
265                         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
266                                 if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
267                                         return true;
268                                 }
269                         }
270                 }
271         }
272
273         return false;
274 }