Merge branch 'blender2.7'
[blender.git] / source / blender / windowmanager / intern / wm_gesture.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file \ingroup wm
21  *
22  * Gestures (cursor motions) creating, evaluating and drawing, shared between operators.
23  */
24
25 #include "DNA_screen_types.h"
26 #include "DNA_vec_types.h"
27 #include "DNA_userdef_types.h"
28 #include "DNA_windowmanager_types.h"
29
30 #include "MEM_guardedalloc.h"
31
32 #include "BLI_bitmap_draw_2d.h"
33 #include "BLI_blenlib.h"
34 #include "BLI_math.h"
35 #include "BLI_utildefines.h"
36 #include "BLI_lasso_2d.h"
37
38 #include "BKE_context.h"
39
40 #include "WM_api.h"
41 #include "WM_types.h"
42
43 #include "wm.h"
44 #include "wm_draw.h"
45
46 #include "GPU_immediate.h"
47 #include "GPU_immediate_util.h"
48 #include "GPU_state.h"
49
50 #include "BIF_glutil.h"
51
52
53 /* context checked on having screen, window and area */
54 wmGesture *WM_gesture_new(bContext *C, const wmEvent *event, int type)
55 {
56         wmGesture *gesture = MEM_callocN(sizeof(wmGesture), "new gesture");
57         wmWindow *window = CTX_wm_window(C);
58         ARegion *ar = CTX_wm_region(C);
59
60         BLI_addtail(&window->gesture, gesture);
61
62         gesture->type = type;
63         gesture->event_type = event->type;
64         gesture->winrct = ar->winrct;
65         gesture->userdata_free = true;   /* Free if userdata is set. */
66         gesture->modal_state = GESTURE_MODAL_NOP;
67
68         if (ELEM(type, WM_GESTURE_RECT, WM_GESTURE_CROSS_RECT, WM_GESTURE_TWEAK,
69                   WM_GESTURE_CIRCLE, WM_GESTURE_STRAIGHTLINE))
70         {
71                 rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new");
72
73                 gesture->customdata = rect;
74                 rect->xmin = event->x - gesture->winrct.xmin;
75                 rect->ymin = event->y - gesture->winrct.ymin;
76                 if (type == WM_GESTURE_CIRCLE) {
77                         /* caller is responsible for initializing 'xmax' to radius. */
78                 }
79                 else {
80                         rect->xmax = event->x - gesture->winrct.xmin;
81                         rect->ymax = event->y - gesture->winrct.ymin;
82                 }
83         }
84         else if (ELEM(type, WM_GESTURE_LINES, WM_GESTURE_LASSO)) {
85                 short *lasso;
86                 gesture->points_alloc = 1024;
87                 gesture->customdata = lasso = MEM_mallocN(sizeof(short[2]) * gesture->points_alloc, "lasso points");
88                 lasso[0] = event->x - gesture->winrct.xmin;
89                 lasso[1] = event->y - gesture->winrct.ymin;
90                 gesture->points = 1;
91         }
92
93         return gesture;
94 }
95
96 void WM_gesture_end(bContext *C, wmGesture *gesture)
97 {
98         wmWindow *win = CTX_wm_window(C);
99
100         if (win->tweak == gesture)
101                 win->tweak = NULL;
102         BLI_remlink(&win->gesture, gesture);
103         MEM_freeN(gesture->customdata);
104         if (gesture->userdata && gesture->userdata_free) {
105                 MEM_freeN(gesture->userdata);
106         }
107         MEM_freeN(gesture);
108 }
109
110 void WM_gestures_remove(bContext *C)
111 {
112         wmWindow *win = CTX_wm_window(C);
113
114         while (win->gesture.first)
115                 WM_gesture_end(C, win->gesture.first);
116 }
117
118
119 /* tweak and line gestures */
120 int wm_gesture_evaluate(wmGesture *gesture)
121 {
122         if (gesture->type == WM_GESTURE_TWEAK) {
123                 rcti *rect = gesture->customdata;
124                 int dx = BLI_rcti_size_x(rect);
125                 int dy = BLI_rcti_size_y(rect);
126                 float tweak_threshold = U.tweak_threshold * U.dpi_fac;
127                 if (abs(dx) + abs(dy) > tweak_threshold) {
128                         int theta = round_fl_to_int(4.0f * atan2f((float)dy, (float)dx) / (float)M_PI);
129                         int val = EVT_GESTURE_W;
130
131                         if (theta == 0) val = EVT_GESTURE_E;
132                         else if (theta == 1) val = EVT_GESTURE_NE;
133                         else if (theta == 2) val = EVT_GESTURE_N;
134                         else if (theta == 3) val = EVT_GESTURE_NW;
135                         else if (theta == -1) val = EVT_GESTURE_SE;
136                         else if (theta == -2) val = EVT_GESTURE_S;
137                         else if (theta == -3) val = EVT_GESTURE_SW;
138
139 #if 0
140                         /* debug */
141                         if (val == 1) printf("tweak north\n");
142                         if (val == 2) printf("tweak north-east\n");
143                         if (val == 3) printf("tweak east\n");
144                         if (val == 4) printf("tweak south-east\n");
145                         if (val == 5) printf("tweak south\n");
146                         if (val == 6) printf("tweak south-west\n");
147                         if (val == 7) printf("tweak west\n");
148                         if (val == 8) printf("tweak north-west\n");
149 #endif
150                         return val;
151                 }
152         }
153         return 0;
154 }
155
156
157 /* ******************* gesture draw ******************* */
158
159 static void wm_gesture_draw_line(wmGesture *gt)
160 {
161         rcti *rect = (rcti *)gt->customdata;
162
163         uint shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
164
165         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
166
167         float viewport_size[4];
168         glGetFloatv(GL_VIEWPORT, viewport_size);
169         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
170
171         immUniform1i("colors_len", 2);  /* "advanced" mode */
172         immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
173         immUniform1f("dash_width", 8.0f);
174
175         float xmin = (float)rect->xmin;
176         float ymin = (float)rect->ymin;
177
178         immBegin(GPU_PRIM_LINES, 2);
179         immVertex2f(shdr_pos, xmin, ymin);
180         immVertex2f(shdr_pos, (float)rect->xmax, (float)rect->ymax);
181         immEnd();
182
183         immUnbindProgram();
184 }
185
186 static void wm_gesture_draw_rect(wmGesture *gt)
187 {
188         rcti *rect = (rcti *)gt->customdata;
189
190         uint shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
191
192         glEnable(GL_BLEND);
193
194         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
195         immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f);
196
197         immRecti(shdr_pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
198
199         immUnbindProgram();
200
201         glDisable(GL_BLEND);
202
203         shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
204
205         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
206
207         float viewport_size[4];
208         glGetFloatv(GL_VIEWPORT, viewport_size);
209         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
210
211         immUniform1i("colors_len", 2);  /* "advanced" mode */
212         immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
213         immUniform1f("dash_width", 8.0f);
214
215         imm_draw_box_wire_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, (float)rect->ymax);
216
217         immUnbindProgram();
218
219         // wm_gesture_draw_line(gt); // draws a diagonal line in the lined box to test wm_gesture_draw_line
220 }
221
222 static void wm_gesture_draw_circle(wmGesture *gt)
223 {
224         rcti *rect = (rcti *)gt->customdata;
225
226         glEnable(GL_BLEND);
227
228         const uint shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
229
230         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
231
232         immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f);
233         imm_draw_circle_fill_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40);
234
235         immUnbindProgram();
236
237         glDisable(GL_BLEND);
238
239         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
240
241         float viewport_size[4];
242         glGetFloatv(GL_VIEWPORT, viewport_size);
243         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
244
245         immUniform1i("colors_len", 2);  /* "advanced" mode */
246         immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
247         immUniform1f("dash_width", 4.0f);
248
249         imm_draw_circle_wire_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40);
250
251         immUnbindProgram();
252 }
253
254 struct LassoFillData {
255         unsigned char *px;
256         int width;
257 };
258
259 static void draw_filled_lasso_px_cb(int x, int x_end, int y, void *user_data)
260 {
261         struct LassoFillData *data = user_data;
262         unsigned char *col = &(data->px[(y * data->width) + x]);
263         memset(col, 0x10, x_end - x);
264 }
265
266 static void draw_filled_lasso(wmGesture *gt)
267 {
268         const short *lasso = (short *)gt->customdata;
269         const int tot = gt->points;
270         int (*moves)[2] = MEM_mallocN(sizeof(*moves) * (tot + 1), __func__);
271         int i;
272         rcti rect;
273         float red[4] = {1.0f, 0.0f, 0.0f, 0.0f};
274
275         for (i = 0; i < tot; i++, lasso += 2) {
276                 moves[i][0] = lasso[0];
277                 moves[i][1] = lasso[1];
278         }
279
280         BLI_lasso_boundbox(&rect, (const int (*)[2])moves, tot);
281
282         BLI_rcti_translate(&rect, gt->winrct.xmin, gt->winrct.ymin);
283         BLI_rcti_isect(&gt->winrct, &rect, &rect);
284         BLI_rcti_translate(&rect, -gt->winrct.xmin, -gt->winrct.ymin);
285
286         /* highly unlikely this will fail, but could crash if (tot == 0) */
287         if (BLI_rcti_is_empty(&rect) == false) {
288                 const int w = BLI_rcti_size_x(&rect);
289                 const int h = BLI_rcti_size_y(&rect);
290                 unsigned char *pixel_buf = MEM_callocN(sizeof(*pixel_buf) * w * h, __func__);
291                 struct LassoFillData lasso_fill_data = {pixel_buf, w};
292
293                 BLI_bitmap_draw_2d_poly_v2i_n(
294                        rect.xmin, rect.ymin, rect.xmax, rect.ymax,
295                        (const int (*)[2])moves, tot,
296                        draw_filled_lasso_px_cb, &lasso_fill_data);
297
298                 /* Additive Blending */
299                 glEnable(GL_BLEND);
300                 glBlendFunc(GL_ONE, GL_ONE);
301
302                 GLint unpack_alignment;
303                 glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment);
304
305                 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
306
307                 IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
308                 GPU_shader_bind(state.shader);
309                 GPU_shader_uniform_vector(state.shader, GPU_shader_get_uniform_ensure(state.shader, "shuffle"), 4, 1, red);
310
311                 immDrawPixelsTex(&state, rect.xmin, rect.ymin, w, h, GL_RED, GL_UNSIGNED_BYTE, GL_NEAREST, pixel_buf, 1.0f, 1.0f, NULL);
312
313                 GPU_shader_unbind();
314
315                 glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment);
316
317                 MEM_freeN(pixel_buf);
318
319                 glDisable(GL_BLEND);
320                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
321         }
322
323         MEM_freeN(moves);
324 }
325
326
327 static void wm_gesture_draw_lasso(wmGesture *gt, bool filled)
328 {
329         const short *lasso = (short *)gt->customdata;
330         int i;
331
332         if (filled) {
333                 draw_filled_lasso(gt);
334         }
335
336         const int numverts = gt->points;
337
338         /* Nothing to draw, do early output. */
339         if (numverts < 2) {
340                 return;
341         }
342
343         const uint shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
344
345         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
346
347         float viewport_size[4];
348         glGetFloatv(GL_VIEWPORT, viewport_size);
349         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
350
351         immUniform1i("colors_len", 2);  /* "advanced" mode */
352         immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
353         immUniform1f("dash_width", 2.0f);
354
355         immBegin((gt->type == WM_GESTURE_LASSO) ? GPU_PRIM_LINE_LOOP : GPU_PRIM_LINE_STRIP, numverts);
356
357         for (i = 0; i < gt->points; i++, lasso += 2) {
358                 immVertex2f(shdr_pos, (float)lasso[0], (float)lasso[1]);
359         }
360
361         immEnd();
362
363         immUnbindProgram();
364 }
365
366 static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt)
367 {
368         rcti *rect = (rcti *)gt->customdata;
369         const int winsize_x = WM_window_pixels_x(win);
370         const int winsize_y = WM_window_pixels_y(win);
371
372         float x1, x2, y1, y2;
373
374         const uint shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
375
376         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
377
378         float viewport_size[4];
379         glGetFloatv(GL_VIEWPORT, viewport_size);
380         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
381
382         immUniform1i("colors_len", 2);  /* "advanced" mode */
383         immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
384         immUniform1f("dash_width", 8.0f);
385
386         immBegin(GPU_PRIM_LINES, 4);
387
388         x1 = (float)(rect->xmin - winsize_x);
389         y1 = (float)rect->ymin;
390         x2 = (float)(rect->xmin + winsize_x);
391         y2 = y1;
392
393         immVertex2f(shdr_pos, x1, y1);
394         immVertex2f(shdr_pos, x2, y2);
395
396         x1 = (float)rect->xmin;
397         y1 = (float)(rect->ymin - winsize_y);
398         x2 = x1;
399         y2 = (float)(rect->ymin + winsize_y);
400
401         immVertex2f(shdr_pos, x1, y1);
402         immVertex2f(shdr_pos, x2, y2);
403
404         immEnd();
405
406         immUnbindProgram();
407 }
408
409 /* called in wm_draw.c */
410 void wm_gesture_draw(wmWindow *win)
411 {
412         wmGesture *gt = (wmGesture *)win->gesture.first;
413
414         GPU_line_width(1.0f);
415         for (; gt; gt = gt->next) {
416                 /* all in subwindow space */
417                 wmViewport(&gt->winrct);
418
419                 if (gt->type == WM_GESTURE_RECT)
420                         wm_gesture_draw_rect(gt);
421 //              else if (gt->type == WM_GESTURE_TWEAK)
422 //                      wm_gesture_draw_line(gt);
423                 else if (gt->type == WM_GESTURE_CIRCLE)
424                         wm_gesture_draw_circle(gt);
425                 else if (gt->type == WM_GESTURE_CROSS_RECT) {
426                         if (gt->is_active) {
427                                 wm_gesture_draw_rect(gt);
428                         }
429                         else {
430                                 wm_gesture_draw_cross(win, gt);
431                         }
432                 }
433                 else if (gt->type == WM_GESTURE_LINES)
434                         wm_gesture_draw_lasso(gt, false);
435                 else if (gt->type == WM_GESTURE_LASSO)
436                         wm_gesture_draw_lasso(gt, true);
437                 else if (gt->type == WM_GESTURE_STRAIGHTLINE)
438                         wm_gesture_draw_line(gt);
439         }
440 }
441
442 void wm_gesture_tag_redraw(bContext *C)
443 {
444         bScreen *screen = CTX_wm_screen(C);
445
446         if (screen)
447                 screen->do_draw_gesture = true;
448 }