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