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