Merge branch 'blender2.7'
[blender.git] / source / blender / editors / gpencil / gpencil_primitive.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) 2017, Blender Foundation
17  * This is a new part of Blender
18  * Operators for creating new Grease Pencil primitives (boxes, circles, ...)
19  */
20
21 /** \file
22  * \ingroup edgpencil
23  */
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stddef.h>
29 #include <math.h>
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_blenlib.h"
34 #include "BLI_utildefines.h"
35 #include "BLI_math.h"
36 #include "BLI_rand.h"
37
38 #include "BLT_translation.h"
39
40 #include "PIL_time.h"
41
42 #include "DNA_brush_types.h"
43 #include "DNA_gpencil_types.h"
44 #include "DNA_meshdata_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_scene_types.h"
47 #include "DNA_screen_types.h"
48 #include "DNA_space_types.h"
49 #include "DNA_view3d_types.h"
50
51 #include "BKE_brush.h"
52 #include "BKE_colortools.h"
53 #include "BKE_context.h"
54 #include "BKE_deform.h"
55 #include "BKE_global.h"
56 #include "BKE_gpencil.h"
57 #include "BKE_main.h"
58 #include "BKE_material.h"
59 #include "BKE_paint.h"
60 #include "BKE_report.h"
61
62 #include "UI_interface.h"
63 #include "UI_resources.h"
64
65 #include "WM_api.h"
66 #include "WM_types.h"
67
68 #include "RNA_access.h"
69 #include "RNA_define.h"
70 #include "RNA_enum_types.h"
71
72 #include "ED_gpencil.h"
73 #include "ED_object.h"
74 #include "ED_screen.h"
75 #include "ED_view3d.h"
76 #include "ED_space_api.h"
77
78 #include "DEG_depsgraph.h"
79 #include "DEG_depsgraph_query.h"
80
81 #include "gpencil_intern.h"
82
83 #define MIN_EDGES 2
84 #define MAX_EDGES 128
85 #define MAX_CP 128
86
87 #define IDLE 0
88 #define IN_PROGRESS 1
89 #define IN_CURVE_EDIT 2
90 #define IN_MOVE 3
91 #define IN_BRUSH_SIZE 4
92 #define IN_BRUSH_STRENGTH 5
93
94 #define SELECT_NONE 0
95 #define SELECT_START 1
96 #define SELECT_CP1 2
97 #define SELECT_CP2 3
98 #define SELECT_END 4
99
100 #define BIG_SIZE_CTL 15
101 #define MID_SIZE_CTL 10
102 #define SMALL_SIZE_CTL 8
103
104 #define MOVE_NONE 0
105 #define MOVE_ENDS 1
106 #define MOVE_CP 2
107
108 /* ************************************************ */
109 /* Core/Shared Utilities */
110
111 /* clear the session buffers (call this before AND after a paint operation) */
112 static void gp_session_validatebuffer(tGPDprimitive *p)
113 {
114   bGPdata *gpd = p->gpd;
115
116   /* clear memory of buffer (or allocate it if starting a new session) */
117   if (gpd->runtime.sbuffer) {
118     memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX);
119   }
120   else {
121     gpd->runtime.sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX,
122                                        "gp_session_strokebuffer");
123   }
124
125   /* reset indices */
126   gpd->runtime.sbuffer_size = 0;
127
128   /* reset flags */
129   gpd->runtime.sbuffer_sflag = 0;
130   gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE;
131
132   if (ELEM(p->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) {
133     gpd->runtime.sbuffer_sflag |= GP_STROKE_CYCLIC;
134   }
135 }
136
137 static void gp_init_colors(tGPDprimitive *p)
138 {
139   bGPdata *gpd = p->gpd;
140   Brush *brush = p->brush;
141
142   MaterialGPencilStyle *gp_style = NULL;
143
144   /* use brush material */
145   p->mat = BKE_gpencil_object_material_ensure_from_active_input_brush(p->bmain, p->ob, brush);
146
147   /* assign color information to temp data */
148   gp_style = p->mat->gp_style;
149   if (gp_style) {
150
151     /* set colors */
152     if (gp_style->flag & GP_STYLE_STROKE_SHOW) {
153       copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba);
154     }
155     else {
156       /* if no stroke, use fill */
157       copy_v4_v4(gpd->runtime.scolor, gp_style->fill_rgba);
158     }
159
160     copy_v4_v4(gpd->runtime.sfill, gp_style->fill_rgba);
161     /* add some alpha to make easy the filling without hide strokes */
162     if (gpd->runtime.sfill[3] > 0.8f) {
163       gpd->runtime.sfill[3] = 0.8f;
164     }
165
166     gpd->runtime.mode = (short)gp_style->mode;
167     gpd->runtime.bstroke_style = gp_style->stroke_style;
168     gpd->runtime.bfill_style = gp_style->fill_style;
169   }
170 }
171
172 /* Helper to square a primitive */
173 static void gpencil_primitive_to_square(tGPDprimitive *tgpi, const float x, const float y)
174 {
175   float w = fabsf(x);
176   float h = fabsf(y);
177   if ((x > 0 && y > 0) || (x < 0 && y < 0)) {
178     if (w > h) {
179       tgpi->end[1] = tgpi->origin[1] + x;
180     }
181     else {
182       tgpi->end[0] = tgpi->origin[0] + y;
183     }
184   }
185   else {
186     if (w > h) {
187       tgpi->end[1] = tgpi->origin[1] - x;
188     }
189     else {
190       tgpi->end[0] = tgpi->origin[0] - y;
191     }
192   }
193 }
194
195 /* Helper to rotate point around origin */
196 static void gp_rotate_v2_v2v2fl(float v[2],
197                                 const float p[2],
198                                 const float origin[2],
199                                 const float angle)
200 {
201   float pt[2];
202   float r[2];
203   sub_v2_v2v2(pt, p, origin);
204   rotate_v2_v2fl(r, pt, angle);
205   add_v2_v2v2(v, r, origin);
206 }
207
208 /* Helper to rotate line around line centre */
209 static void gp_primitive_rotate_line(
210     float va[2], float vb[2], const float a[2], const float b[2], const float angle)
211 {
212   float midpoint[2];
213   mid_v2_v2v2(midpoint, a, b);
214   gp_rotate_v2_v2v2fl(va, a, midpoint, angle);
215   gp_rotate_v2_v2v2fl(vb, b, midpoint, angle);
216 }
217
218 /* Helper to update cps */
219 static void gp_primitive_update_cps(tGPDprimitive *tgpi)
220 {
221   if (!tgpi->curve) {
222     mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
223     copy_v2_v2(tgpi->cp1, tgpi->midpoint);
224     copy_v2_v2(tgpi->cp2, tgpi->cp1);
225   }
226   else if (tgpi->type == GP_STROKE_CURVE) {
227     mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
228     copy_v2_v2(tgpi->cp1, tgpi->midpoint);
229     copy_v2_v2(tgpi->cp2, tgpi->cp1);
230   }
231   else if (tgpi->type == GP_STROKE_ARC) {
232     if (tgpi->flip) {
233       gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->start, tgpi->end, M_PI_2);
234     }
235     else {
236       gp_primitive_rotate_line(tgpi->cp1, tgpi->cp2, tgpi->end, tgpi->start, M_PI_2);
237     }
238   }
239 }
240
241 /* Helper to reflect point */
242 static void UNUSED_FUNCTION(gp_reflect_point_v2_v2v2v2)(float va[2],
243                                                         const float p[2],
244                                                         const float a[2],
245                                                         const float b[2])
246 {
247   float point[2];
248   closest_to_line_v2(point, p, a, b);
249   va[0] = point[0] - (p[0] - point[0]);
250   va[1] = point[1] - (p[1] - point[1]);
251 }
252
253 /* Poll callback for primitive operators */
254 static bool gpencil_primitive_add_poll(bContext *C)
255 {
256   /* only 3D view */
257   ScrArea *sa = CTX_wm_area(C);
258   if (sa && sa->spacetype != SPACE_VIEW3D) {
259     return 0;
260   }
261
262   /* need data to create primitive */
263   bGPdata *gpd = CTX_data_gpencil_data(C);
264   if (gpd == NULL) {
265     return 0;
266   }
267
268   /* only in edit and paint modes
269    * - paint as it's the "drawing/creation mode"
270    * - edit as this is more of an atomic editing operation
271    *   (similar to copy/paste), and also for consistency
272    */
273   if ((gpd->flag & (GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE)) == 0) {
274     CTX_wm_operator_poll_msg_set(C, "Primitives can only be added in Draw or Edit modes");
275     return 0;
276   }
277
278   /* don't allow operator to function if the active layer is locked/hidden
279    * (BUT, if there isn't an active layer, we are free to add new layer when the time comes)
280    */
281   bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
282   if ((gpl) && (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE))) {
283     CTX_wm_operator_poll_msg_set(C,
284                                  "Primitives cannot be added as active layer is locked or hidden");
285     return 0;
286   }
287
288   return 1;
289 }
290
291 /* Allocate memory to stroke, adds MAX_EDGES on every call */
292 static void gpencil_primitive_allocate_memory(tGPDprimitive *tgpi)
293 {
294   tgpi->point_count += (tgpi->type == GP_STROKE_BOX) ? (MAX_EDGES * 4 + 1) : (MAX_EDGES + 1);
295   bGPDstroke *gpsf = tgpi->gpf->strokes.first;
296   gpsf->points = MEM_reallocN(gpsf->points, sizeof(bGPDspoint) * tgpi->point_count);
297   if (gpsf->dvert != NULL) {
298     gpsf->dvert = MEM_reallocN(gpsf->dvert, sizeof(MDeformVert) * tgpi->point_count);
299   }
300   tgpi->points = MEM_reallocN(tgpi->points, sizeof(tGPspoint) * tgpi->point_count);
301 }
302
303 /* ****************** Primitive Interactive *********************** */
304
305 /* Helper: Create internal strokes primitives data */
306 static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
307 {
308   ToolSettings *ts = CTX_data_tool_settings(C);
309   Depsgraph *depsgraph = CTX_data_depsgraph(C);
310   int cfra_eval = (int)DEG_get_ctime(depsgraph);
311
312   bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
313
314   /* if brush doesn't exist, create a new one */
315   Paint *paint = &ts->gp_paint->paint;
316   /* if not exist, create a new one */
317   if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) {
318     /* create new brushes */
319     BKE_brush_gpencil_presets(C);
320   }
321   tgpi->brush = paint->brush;
322
323   /* if layer doesn't exist, create a new one */
324   if (gpl == NULL) {
325     gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true);
326   }
327   tgpi->gpl = gpl;
328
329   /* create a new temporary frame */
330   tgpi->gpf = MEM_callocN(sizeof(bGPDframe), "Temp bGPDframe");
331   tgpi->gpf->framenum = tgpi->cframe = cfra_eval;
332
333   /* create new temp stroke */
334   bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "Temp bGPDstroke");
335   gps->thickness = 2.0f;
336   gps->gradient_f = 1.0f;
337   gps->gradient_s[0] = 1.0f;
338   gps->gradient_s[1] = 1.0f;
339   gps->inittime = 0.0f;
340
341   /* enable recalculation flag by default */
342   gps->flag |= GP_STROKE_RECALC_GEOMETRY;
343   gps->flag &= ~GP_STROKE_SELECT;
344   /* the polygon must be closed, so enabled cyclic */
345   if (ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) {
346     gps->flag |= GP_STROKE_CYCLIC;
347   }
348
349   gps->flag |= GP_STROKE_3DSPACE;
350
351   gps->mat_nr = BKE_gpencil_object_material_get_index(tgpi->ob, tgpi->mat);
352
353   /* allocate memory for storage points, but keep empty */
354   gps->totpoints = 0;
355   gps->points = MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points");
356   /* initialize triangle memory to dummy data */
357   gps->tot_triangles = 0;
358   gps->triangles = NULL;
359   gps->flag |= GP_STROKE_RECALC_GEOMETRY;
360
361   /* add to strokes */
362   BLI_addtail(&tgpi->gpf->strokes, gps);
363
364   /* allocate memory for storage points */
365   gpencil_primitive_allocate_memory(tgpi);
366
367   /* Random generator, only init once. */
368   uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
369   tgpi->rng = BLI_rng_new(rng_seed);
370 }
371
372 /* add new segment to curve */
373 static void gpencil_primitive_add_segment(tGPDprimitive *tgpi)
374 {
375   if (tgpi->tot_stored_edges > 0) {
376     tgpi->tot_stored_edges += (tgpi->tot_edges - 1);
377   }
378   else {
379     tgpi->tot_stored_edges += tgpi->tot_edges;
380   }
381   gpencil_primitive_allocate_memory(tgpi);
382 }
383
384 /* Helper: set control point */
385 static void gp_primitive_set_cp(tGPDprimitive *tgpi, float p[2], float color[4], int size)
386 {
387   if (tgpi->flag == IN_PROGRESS) {
388     return;
389   }
390
391   bGPDcontrolpoint *cp_points = tgpi->gpd->runtime.cp_points;
392
393   if (tgpi->gpd->runtime.tot_cp_points < MAX_CP) {
394     CLAMP(size, 5, 20);
395     bGPDcontrolpoint *cp = &cp_points[tgpi->gpd->runtime.tot_cp_points];
396     copy_v2_v2(&cp->x, p);
397     copy_v4_v4(cp->color, color);
398     color[3] = 0.8f;
399     cp->size = size;
400     tgpi->gpd->runtime.tot_cp_points += 1;
401   }
402 }
403
404 /* Helper: Draw status message while the user is running the operator */
405 static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi)
406 {
407   Scene *scene = tgpi->scene;
408   char status_str[UI_MAX_DRAW_STR];
409   char msg_str[UI_MAX_DRAW_STR];
410
411   if (tgpi->type == GP_STROKE_LINE) {
412     BLI_strncpy(msg_str,
413                 IFACE_("Line: ESC to cancel, LMB set origin, Enter/MMB to confirm, WHEEL/+- to "
414                        "adjust subdivision number, Shift to align, Alt to center, E: extrude"),
415                 UI_MAX_DRAW_STR);
416   }
417   else if (tgpi->type == GP_STROKE_BOX) {
418     BLI_strncpy(msg_str,
419                 IFACE_("Rectangle: ESC to cancel, LMB set origin, Enter/MMB to confirm, WHEEL/+- "
420                        "to adjust subdivision number, Shift to square, Alt to center"),
421                 UI_MAX_DRAW_STR);
422   }
423   else if (tgpi->type == GP_STROKE_CIRCLE) {
424     BLI_strncpy(msg_str,
425                 IFACE_("Circle: ESC to cancel, Enter/MMB to confirm, WHEEL/+- to adjust edge "
426                        "number, Shift to square, Alt to center"),
427                 UI_MAX_DRAW_STR);
428   }
429   else if (tgpi->type == GP_STROKE_ARC) {
430     BLI_strncpy(msg_str,
431                 IFACE_("Arc: ESC to cancel, Enter/MMB to confirm, WHEEL/+- to adjust edge number, "
432                        "Shift to square, Alt to center, M: Flip, E: extrude"),
433                 UI_MAX_DRAW_STR);
434   }
435   else if (tgpi->type == GP_STROKE_CURVE) {
436     BLI_strncpy(msg_str,
437                 IFACE_("Curve: ESC to cancel, Enter/MMB to confirm, WHEEL/+- to adjust edge "
438                        "number, Shift to square, Alt to center, E: extrude"),
439                 UI_MAX_DRAW_STR);
440   }
441
442   if (ELEM(tgpi->type, GP_STROKE_CIRCLE, GP_STROKE_ARC, GP_STROKE_LINE, GP_STROKE_BOX)) {
443     if (hasNumInput(&tgpi->num)) {
444       char str_offs[NUM_STR_REP_LEN];
445
446       outputNumInput(&tgpi->num, str_offs, &scene->unit);
447       BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs);
448     }
449     else {
450       if (tgpi->flag == IN_PROGRESS) {
451         BLI_snprintf(status_str,
452                      sizeof(status_str),
453                      "%s: %d (%d, %d) (%d, %d)",
454                      msg_str,
455                      tgpi->tot_edges,
456                      (int)tgpi->start[0],
457                      (int)tgpi->start[1],
458                      (int)tgpi->end[0],
459                      (int)tgpi->end[1]);
460       }
461       else {
462         BLI_snprintf(status_str,
463                      sizeof(status_str),
464                      "%s: %d (%d, %d)",
465                      msg_str,
466                      tgpi->tot_edges,
467                      (int)tgpi->end[0],
468                      (int)tgpi->end[1]);
469       }
470     }
471   }
472   else {
473     if (tgpi->flag == IN_PROGRESS) {
474       BLI_snprintf(status_str,
475                    sizeof(status_str),
476                    "%s: %d (%d, %d) (%d, %d)",
477                    msg_str,
478                    tgpi->tot_edges,
479                    (int)tgpi->start[0],
480                    (int)tgpi->start[1],
481                    (int)tgpi->end[0],
482                    (int)tgpi->end[1]);
483     }
484     else {
485       BLI_snprintf(status_str,
486                    sizeof(status_str),
487                    "%s: (%d, %d)",
488                    msg_str,
489                    (int)tgpi->end[0],
490                    (int)tgpi->end[1]);
491     }
492   }
493   ED_workspace_status_text(C, status_str);
494 }
495
496 /* create a rectangle */
497 static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D)
498 {
499   float coords[5][2];
500
501   coords[0][0] = tgpi->start[0];
502   coords[0][1] = tgpi->start[1];
503   coords[1][0] = tgpi->end[0];
504   coords[1][1] = tgpi->start[1];
505   coords[2][0] = tgpi->end[0];
506   coords[2][1] = tgpi->end[1];
507   coords[3][0] = tgpi->start[0];
508   coords[3][1] = tgpi->end[1];
509   coords[4][0] = tgpi->start[0];
510   coords[4][1] = tgpi->start[1];
511
512   const float step = 1.0f / (float)(tgpi->tot_edges);
513   int i = tgpi->tot_stored_edges;
514
515   for (int j = 0; j < 4; j++) {
516     float a = 0.0f;
517     for (int k = 0; k < tgpi->tot_edges; k++) {
518       tGPspoint *p2d = &points2D[i];
519       interp_v2_v2v2(&p2d->x, coords[j], coords[j + 1], a);
520       a += step;
521       i++;
522     }
523   }
524
525   mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
526   float color[4];
527   UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
528   gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
529   if (tgpi->tot_stored_edges) {
530     UI_GetThemeColor4fv(TH_REDALERT, color);
531     gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
532   }
533   else {
534     gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
535   }
536   UI_GetThemeColor4fv(TH_REDALERT, color);
537   gp_primitive_set_cp(tgpi, tgpi->midpoint, color, SMALL_SIZE_CTL);
538 }
539
540 /* create a line */
541 static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D)
542 {
543   const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
544   const float step = 1.0f / (float)(tgpi->tot_edges - 1);
545   float a = tgpi->tot_stored_edges ? step : 0.0f;
546
547   for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
548     tGPspoint *p2d = &points2D[i];
549     interp_v2_v2v2(&p2d->x, tgpi->start, tgpi->end, a);
550     a += step;
551   }
552
553   float color[4];
554   UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
555   gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
556   if (tgpi->tot_stored_edges) {
557     UI_GetThemeColor4fv(TH_REDALERT, color);
558     gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
559   }
560   else {
561     gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
562   }
563 }
564
565 /* create an arc */
566 static void gp_primitive_arc(tGPDprimitive *tgpi, tGPspoint *points2D)
567 {
568   const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
569   const float step = M_PI_2 / (float)(tgpi->tot_edges - 1);
570   float start[2];
571   float end[2];
572   float cp1[2];
573   float corner[2];
574   float midpoint[2];
575   float a = tgpi->tot_stored_edges ? step : 0.0f;
576
577   mid_v2_v2v2(tgpi->midpoint, tgpi->start, tgpi->end);
578   copy_v2_v2(start, tgpi->start);
579   copy_v2_v2(end, tgpi->end);
580   copy_v2_v2(cp1, tgpi->cp1);
581   copy_v2_v2(midpoint, tgpi->midpoint);
582
583   corner[0] = midpoint[0] - (cp1[0] - midpoint[0]);
584   corner[1] = midpoint[1] - (cp1[1] - midpoint[1]);
585
586   for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
587     tGPspoint *p2d = &points2D[i];
588     p2d->x = corner[0] + (end[0] - corner[0]) * sinf(a) + (start[0] - corner[0]) * cosf(a);
589     p2d->y = corner[1] + (end[1] - corner[1]) * sinf(a) + (start[1] - corner[1]) * cosf(a);
590     a += step;
591   }
592   float color[4];
593   UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
594   gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
595   if (tgpi->tot_stored_edges) {
596     UI_GetThemeColor4fv(TH_REDALERT, color);
597     gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
598   }
599   else {
600     gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
601   }
602   UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color);
603   gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
604 }
605
606 /* create a bezier */
607 static void gp_primitive_bezier(tGPDprimitive *tgpi, tGPspoint *points2D)
608 {
609   const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
610   const float step = 1.0f / (float)(tgpi->tot_edges - 1);
611   float bcp1[2];
612   float bcp2[2];
613   float bcp3[2];
614   float bcp4[2];
615   float a = tgpi->tot_stored_edges ? step : 0.0f;
616
617   copy_v2_v2(bcp1, tgpi->start);
618   copy_v2_v2(bcp2, tgpi->cp1);
619   copy_v2_v2(bcp3, tgpi->cp2);
620   copy_v2_v2(bcp4, tgpi->end);
621
622   for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
623     tGPspoint *p2d = &points2D[i];
624     interp_v2_v2v2v2v2_cubic(&p2d->x, bcp1, bcp2, bcp3, bcp4, a);
625     a += step;
626   }
627   float color[4];
628   UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
629   gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
630   if (tgpi->tot_stored_edges) {
631     UI_GetThemeColor4fv(TH_REDALERT, color);
632     gp_primitive_set_cp(tgpi, tgpi->start, color, SMALL_SIZE_CTL);
633   }
634   else {
635     gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
636   }
637   UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color);
638   gp_primitive_set_cp(tgpi, tgpi->cp1, color, BIG_SIZE_CTL * 0.9f);
639   gp_primitive_set_cp(tgpi, tgpi->cp2, color, BIG_SIZE_CTL * 0.9f);
640 }
641
642 /* create a circle */
643 static void gp_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D)
644 {
645   const int totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
646   const float step = (2.0f * M_PI) / (float)(tgpi->tot_edges);
647   float center[2];
648   float radius[2];
649   float a = 0.0f;
650
651   center[0] = tgpi->start[0] + ((tgpi->end[0] - tgpi->start[0]) / 2.0f);
652   center[1] = tgpi->start[1] + ((tgpi->end[1] - tgpi->start[1]) / 2.0f);
653   radius[0] = fabsf(((tgpi->end[0] - tgpi->start[0]) / 2.0f));
654   radius[1] = fabsf(((tgpi->end[1] - tgpi->start[1]) / 2.0f));
655
656   for (int i = tgpi->tot_stored_edges; i < totpoints; i++) {
657     tGPspoint *p2d = &points2D[i];
658     p2d->x = (center[0] + cosf(a) * radius[0]);
659     p2d->y = (center[1] + sinf(a) * radius[1]);
660     a += step;
661   }
662   float color[4];
663   UI_GetThemeColor4fv(TH_GIZMO_PRIMARY, color);
664   gp_primitive_set_cp(tgpi, tgpi->end, color, BIG_SIZE_CTL);
665   gp_primitive_set_cp(tgpi, tgpi->start, color, BIG_SIZE_CTL);
666   UI_GetThemeColor4fv(TH_REDALERT, color);
667   gp_primitive_set_cp(tgpi, center, color, SMALL_SIZE_CTL);
668 }
669
670 /* Helper: Update shape of the stroke */
671 static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
672 {
673   ToolSettings *ts = tgpi->scene->toolsettings;
674   bGPdata *gpd = tgpi->gpd;
675   Brush *brush = tgpi->brush;
676   bGPDstroke *gps = tgpi->gpf->strokes.first;
677   GP_Sculpt_Settings *gset = &ts->gp_sculpt;
678   int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0;
679   const char *align_flag = &ts->gpencil_v3d_align;
680   bool is_depth = (bool)(*align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE));
681   const bool is_camera = (bool)(ts->gp_sculpt.lock_axis == 0) &&
682                          (tgpi->rv3d->persp == RV3D_CAMOB) && (!is_depth);
683
684   if (tgpi->type == GP_STROKE_BOX) {
685     gps->totpoints = (tgpi->tot_edges * 4 + tgpi->tot_stored_edges);
686   }
687   else {
688     gps->totpoints = (tgpi->tot_edges + tgpi->tot_stored_edges);
689   }
690
691   if (tgpi->tot_stored_edges) {
692     gps->totpoints--;
693   }
694
695   tgpi->gpd->runtime.tot_cp_points = 0;
696
697   /* compute screen-space coordinates for points */
698   tGPspoint *points2D = tgpi->points;
699
700   if (tgpi->tot_edges > 1) {
701     switch (tgpi->type) {
702       case GP_STROKE_BOX:
703         gp_primitive_rectangle(tgpi, points2D);
704         break;
705       case GP_STROKE_LINE:
706         gp_primitive_line(tgpi, points2D);
707         break;
708       case GP_STROKE_CIRCLE:
709         gp_primitive_circle(tgpi, points2D);
710         break;
711       case GP_STROKE_ARC:
712         gp_primitive_arc(tgpi, points2D);
713         break;
714       case GP_STROKE_CURVE:
715         gp_primitive_bezier(tgpi, points2D);
716       default:
717         break;
718     }
719   }
720
721   /* convert screen-coordinates to 3D coordinates */
722   gp_session_validatebuffer(tgpi);
723   gp_init_colors(tgpi);
724   if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) {
725     curvemapping_initialize(ts->gp_sculpt.cur_primitive);
726   }
727   if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
728     curvemapping_initialize(tgpi->brush->gpencil_settings->curve_jitter);
729   }
730   if (tgpi->brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
731     curvemapping_initialize(tgpi->brush->gpencil_settings->curve_strength);
732   }
733
734   /* get an array of depths, far depths are blended */
735   float *depth_arr = NULL;
736   if (is_depth) {
737     int i;
738     int mval_i[2], mval_prev[2] = {0};
739     bool interp_depth = false;
740     bool found_depth = false;
741
742     /* need to restore the original projection settings before packing up */
743     view3d_region_operator_needs_opengl(tgpi->win, tgpi->ar);
744     ED_view3d_autodist_init(tgpi->depsgraph,
745                             tgpi->ar,
746                             tgpi->v3d,
747                             (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0);
748
749     depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points");
750     tGPspoint *ptc = &points2D[0];
751     for (i = 0; i < gps->totpoints; i++, ptc++) {
752       round_v2i_v2fl(mval_i, &ptc->x);
753       if ((ED_view3d_autodist_depth(tgpi->ar, mval_i, depth_margin, depth_arr + i) == 0) &&
754           (i && (ED_view3d_autodist_depth_seg(
755                      tgpi->ar, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) {
756         interp_depth = true;
757       }
758       else {
759         found_depth = true;
760       }
761       copy_v2_v2_int(mval_prev, mval_i);
762     }
763
764     if (!found_depth) {
765       for (i = 0; i < gps->totpoints; i++) {
766         depth_arr[i] = 0.9999f;
767       }
768     }
769     else {
770       /* if all depth are too high disable */
771       bool valid_depth = false;
772       for (i = 0; i < gps->totpoints; i++) {
773         if (depth_arr[i] < 0.9999f) {
774           valid_depth = true;
775           break;
776         }
777       }
778       if (!valid_depth) {
779         MEM_SAFE_FREE(depth_arr);
780         is_depth = false;
781       }
782       else {
783         if ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) ||
784             (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_FIRST)) {
785           int first_valid = 0;
786           int last_valid = 0;
787
788           /* find first valid contact point */
789           for (i = 0; i < gps->totpoints; i++) {
790             if (depth_arr[i] != FLT_MAX) {
791               break;
792             }
793           }
794           first_valid = i;
795
796           /* find last valid contact point */
797           if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE_FIRST) {
798             last_valid = first_valid;
799           }
800           else {
801             for (i = gps->totpoints - 1; i >= 0; i--) {
802               if (depth_arr[i] != FLT_MAX) {
803                 break;
804               }
805             }
806             last_valid = i;
807           }
808
809           /* invalidate any other point, to interpolate between
810            * first and last contact in an imaginary line between them */
811           for (i = 0; i < gps->totpoints; i++) {
812             if ((i != first_valid) && (i != last_valid)) {
813               depth_arr[i] = FLT_MAX;
814             }
815           }
816           interp_depth = true;
817         }
818
819         if (interp_depth) {
820           interp_sparse_array(depth_arr, gps->totpoints, FLT_MAX);
821         }
822       }
823     }
824   }
825
826   /* load stroke points and sbuffer */
827   for (int i = 0; i < gps->totpoints; i++) {
828     bGPDspoint *pt = &gps->points[i];
829     tGPspoint *p2d = &points2D[i];
830
831     /* set rnd value for reuse */
832     if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && (p2d->rnd_dirty != true)) {
833       p2d->rnd[0] = BLI_rng_get_float(tgpi->rng);
834       p2d->rnd[1] = BLI_rng_get_float(tgpi->rng);
835       p2d->rnd[2] = BLI_rng_get_float(tgpi->rng);
836       p2d->rnd_dirty = true;
837     }
838
839     /* Copy points to buffer */
840     tGPspoint *tpt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size);
841
842     /* Store original points */
843     float tmp_xyp[2];
844     copy_v2_v2(tmp_xyp, &p2d->x);
845
846     /* calc pressure */
847     float curve_pressure = 1.0;
848     float pressure = 1.0;
849     float strength = brush->gpencil_settings->draw_strength;
850
851     /* normalize value to evaluate curve */
852     if (gset->flag & GP_SCULPT_SETT_FLAG_PRIMITIVE_CURVE) {
853       float value = (float)i / (gps->totpoints - 1);
854       curve_pressure = curvemapping_evaluateF(gset->cur_primitive, 0, value);
855       pressure = curve_pressure;
856     }
857
858     /* apply jitter to position */
859     if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
860         (brush->gpencil_settings->draw_jitter > 0.0f)) {
861       float jitter;
862
863       if (brush->gpencil_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
864         jitter = curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, curve_pressure);
865         jitter *= brush->gpencil_settings->draw_sensitivity;
866       }
867       else {
868         jitter = brush->gpencil_settings->draw_jitter;
869       }
870
871       /* exponential value */
872       const float exfactor = SQUARE(brush->gpencil_settings->draw_jitter + 2.0f);
873       const float fac = p2d->rnd[0] * exfactor * jitter;
874
875       /* vector */
876       float mvec[2], svec[2];
877       if (i > 0) {
878         mvec[0] = (p2d->x - (p2d - 1)->x);
879         mvec[1] = (p2d->y - (p2d - 1)->y);
880         normalize_v2(mvec);
881       }
882       else {
883         zero_v2(mvec);
884       }
885       svec[0] = -mvec[1];
886       svec[1] = mvec[0];
887
888       if (p2d->rnd[1] > 0.5f) {
889         mul_v2_fl(svec, -fac);
890       }
891       else {
892         mul_v2_fl(svec, fac);
893       }
894       add_v2_v2(&p2d->x, svec);
895     }
896
897     /* apply randomness to pressure */
898     if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
899         (brush->gpencil_settings->draw_random_press > 0.0f)) {
900       if (p2d->rnd[0] > 0.5f) {
901         pressure -= brush->gpencil_settings->draw_random_press * p2d->rnd[1];
902       }
903       else {
904         pressure += brush->gpencil_settings->draw_random_press * p2d->rnd[2];
905       }
906     }
907
908     /* color strength */
909     if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
910       float curvef = curvemapping_evaluateF(
911           brush->gpencil_settings->curve_strength, 0, curve_pressure);
912       strength *= curvef * brush->gpencil_settings->draw_sensitivity;
913       strength *= brush->gpencil_settings->draw_strength;
914     }
915
916     CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f);
917
918     /* apply randomness to color strength */
919     if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) &&
920         (brush->gpencil_settings->draw_random_strength > 0.0f)) {
921       if (p2d->rnd[2] > 0.5f) {
922         strength -= strength * brush->gpencil_settings->draw_random_strength * p2d->rnd[0];
923       }
924       else {
925         strength += strength * brush->gpencil_settings->draw_random_strength * p2d->rnd[1];
926       }
927       CLAMP(strength, GPENCIL_STRENGTH_MIN, 1.0f);
928     }
929
930     copy_v2_v2(&tpt->x, &p2d->x);
931
932     CLAMP_MIN(pressure, 0.1f);
933
934     tpt->pressure = pressure;
935     tpt->strength = strength;
936     tpt->time = p2d->time;
937
938     /* point uv */
939     if (gpd->runtime.sbuffer_size > 0) {
940       MaterialGPencilStyle *gp_style = tgpi->mat->gp_style;
941       const float pixsize = gp_style->texture_pixsize / 1000000.0f;
942       tGPspoint *tptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 1;
943       bGPDspoint spt, spt2;
944
945       /* get origin to reproject point */
946       float origin[3];
947       ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin);
948       /* reproject current */
949       ED_gpencil_tpoint_to_point(tgpi->ar, origin, tpt, &spt);
950       ED_gp_project_point_to_plane(
951           tgpi->scene, tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt);
952
953       /* reproject previous */
954       ED_gpencil_tpoint_to_point(tgpi->ar, origin, tptb, &spt2);
955       ED_gp_project_point_to_plane(
956           tgpi->scene, tgpi->ob, tgpi->rv3d, origin, tgpi->lock_axis - 1, &spt2);
957       tgpi->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize;
958       tpt->uv_fac = tgpi->totpixlen;
959       if ((gp_style) && (gp_style->sima)) {
960         tpt->uv_fac /= gp_style->sima->gen_x;
961       }
962     }
963     else {
964       tgpi->totpixlen = 0.0f;
965       tpt->uv_fac = 0.0f;
966     }
967
968     tpt->uv_rot = p2d->uv_rot;
969
970     gpd->runtime.sbuffer_size++;
971
972     /* add small offset to keep stroke over the surface */
973     if ((depth_arr) && (gpd->zdepth_offset > 0.0f)) {
974       depth_arr[i] *= (1.0f - gpd->zdepth_offset);
975     }
976
977     /* convert screen-coordinates to 3D coordinates */
978     gp_stroke_convertcoords_tpoint(
979         tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl, p2d, depth_arr ? depth_arr + i : NULL, &pt->x);
980
981     pt->pressure = pressure;
982     pt->strength = strength;
983     pt->time = 0.0f;
984     pt->flag = 0;
985     pt->uv_fac = tpt->uv_fac;
986
987     if (gps->dvert != NULL) {
988       MDeformVert *dvert = &gps->dvert[i];
989       dvert->totweight = 0;
990       dvert->dw = NULL;
991     }
992
993     /* Restore original points */
994     copy_v2_v2(&p2d->x, tmp_xyp);
995   }
996
997   /* store cps and convert coords */
998   if (tgpi->gpd->runtime.tot_cp_points > 0) {
999     bGPDcontrolpoint *cps = tgpi->gpd->runtime.cp_points;
1000     for (int i = 0; i < tgpi->gpd->runtime.tot_cp_points; i++) {
1001       bGPDcontrolpoint *cp = &cps[i];
1002       gp_stroke_convertcoords_tpoint(
1003           tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl, (tGPspoint *)cp, NULL, &cp->x);
1004     }
1005   }
1006
1007   /* reproject to plane */
1008   if (!is_depth) {
1009     float origin[3];
1010     ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl, ts->gpencil_v3d_align, origin);
1011     ED_gp_project_stroke_to_plane(
1012         tgpi->scene, tgpi->ob, tgpi->rv3d, gps, origin, ts->gp_sculpt.lock_axis - 1);
1013   }
1014
1015   /* if parented change position relative to parent object */
1016   for (int i = 0; i < gps->totpoints; i++) {
1017     bGPDspoint *pt = &gps->points[i];
1018     gp_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpd, tgpi->gpl, pt);
1019   }
1020
1021   /* if camera view, reproject flat to view to avoid perspective effect */
1022   if (is_camera) {
1023     ED_gpencil_project_stroke_to_view(C, tgpi->gpl, gps);
1024   }
1025
1026   /* force fill recalc */
1027   gps->flag |= GP_STROKE_RECALC_GEOMETRY;
1028
1029   MEM_SAFE_FREE(depth_arr);
1030
1031   DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
1032   DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1033   WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
1034 }
1035
1036 /* Update screen and stroke */
1037 static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive *tgpi)
1038 {
1039   /* update indicator in header */
1040   gpencil_primitive_status_indicators(C, tgpi);
1041   /* apply... */
1042   tgpi->type = RNA_enum_get(op->ptr, "type");
1043   tgpi->tot_edges = RNA_int_get(op->ptr, "edges");
1044   /* update points position */
1045   gp_primitive_update_strokes(C, tgpi);
1046 }
1047
1048 static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event)
1049 {
1050   copy_v2fl_v2i(tgpi->mval, event->mval);
1051   copy_v2_v2(tgpi->origin, tgpi->mval);
1052   copy_v2_v2(tgpi->start, tgpi->mval);
1053   copy_v2_v2(tgpi->end, tgpi->mval);
1054   copy_v2_v2(tgpi->cp1, tgpi->mval);
1055   copy_v2_v2(tgpi->cp2, tgpi->mval);
1056 }
1057
1058 /* Exit and free memory */
1059 static void gpencil_primitive_exit(bContext *C, wmOperator *op)
1060 {
1061   tGPDprimitive *tgpi = op->customdata;
1062   bGPdata *gpd = tgpi->gpd;
1063
1064   /* don't assume that operator data exists at all */
1065   if (tgpi) {
1066     /* clear status message area */
1067     ED_workspace_status_text(C, NULL);
1068
1069     MEM_SAFE_FREE(tgpi->points);
1070     tgpi->gpd->runtime.tot_cp_points = 0;
1071     MEM_SAFE_FREE(tgpi->gpd->runtime.cp_points);
1072     /* finally, free memory used by temp data */
1073     BKE_gpencil_free_strokes(tgpi->gpf);
1074     MEM_SAFE_FREE(tgpi->gpf);
1075
1076     /* free random seed */
1077     if (tgpi->rng != NULL) {
1078       BLI_rng_free(tgpi->rng);
1079     }
1080
1081     MEM_freeN(tgpi);
1082   }
1083
1084   /* free stroke buffer */
1085   if ((gpd != NULL) && (gpd->runtime.sbuffer)) {
1086     MEM_SAFE_FREE(gpd->runtime.sbuffer);
1087     gpd->runtime.sbuffer = NULL;
1088
1089     /* clear flags */
1090     gpd->runtime.sbuffer_size = 0;
1091     gpd->runtime.sbuffer_sflag = 0;
1092   }
1093
1094   DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
1095   WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
1096
1097   /* clear pointer */
1098   op->customdata = NULL;
1099 }
1100
1101 /* Init new temporary primitive data */
1102 static void gpencil_primitive_init(bContext *C, wmOperator *op)
1103 {
1104   ToolSettings *ts = CTX_data_tool_settings(C);
1105   bGPdata *gpd = CTX_data_gpencil_data(C);
1106   Main *bmain = CTX_data_main(C);
1107   Scene *scene = CTX_data_scene(C);
1108   Depsgraph *depsgraph = CTX_data_depsgraph(C);
1109   int cfra_eval = (int)DEG_get_ctime(depsgraph);
1110
1111   /* create temporary operator data */
1112   tGPDprimitive *tgpi = MEM_callocN(sizeof(tGPDprimitive), "GPencil Primitive Data");
1113   op->customdata = tgpi;
1114
1115   tgpi->points = MEM_callocN(sizeof(tGPspoint), "gp primitive points2D");
1116
1117   /* set current scene and window info */
1118   tgpi->bmain = CTX_data_main(C);
1119   tgpi->scene = scene;
1120   tgpi->ob = CTX_data_active_object(C);
1121   tgpi->sa = CTX_wm_area(C);
1122   tgpi->ar = CTX_wm_region(C);
1123   tgpi->rv3d = tgpi->ar->regiondata;
1124   tgpi->v3d = tgpi->sa->spacedata.first;
1125   tgpi->depsgraph = CTX_data_depsgraph(C);
1126   tgpi->win = CTX_wm_window(C);
1127
1128   /* save original type */
1129   tgpi->orign_type = RNA_enum_get(op->ptr, "type");
1130
1131   /* set current frame number */
1132   tgpi->cframe = cfra_eval;
1133
1134   /* set GP datablock */
1135   tgpi->gpd = gpd;
1136   /* region where paint was originated */
1137   tgpi->gpd->runtime.ar = tgpi->ar;
1138
1139   /* control points */
1140   tgpi->gpd->runtime.cp_points = MEM_callocN(sizeof(bGPDcontrolpoint) * MAX_CP,
1141                                              "gp primitive cpoint");
1142   tgpi->gpd->runtime.tot_cp_points = 0;
1143
1144   /* getcolor info */
1145   tgpi->mat = BKE_gpencil_object_material_ensure_from_active_input_toolsettings(
1146       bmain, tgpi->ob, ts);
1147
1148   /* set parameters */
1149   tgpi->type = RNA_enum_get(op->ptr, "type");
1150
1151   if (ELEM(tgpi->type, GP_STROKE_ARC, GP_STROKE_CURVE)) {
1152     tgpi->curve = true;
1153   }
1154   else {
1155     tgpi->curve = false;
1156   }
1157
1158   /* set default edge count */
1159   switch (tgpi->type) {
1160     case GP_STROKE_LINE: {
1161       RNA_int_set(op->ptr, "edges", 8);
1162       break;
1163     }
1164     case GP_STROKE_BOX: {
1165       RNA_int_set(op->ptr, "edges", 8);
1166       break;
1167     }
1168     case GP_STROKE_CIRCLE: {
1169       RNA_int_set(op->ptr, "edges", 96);
1170       break;
1171     }
1172     default: {
1173       RNA_int_set(op->ptr, "edges", 64);
1174       break;
1175     }
1176   }
1177
1178   tgpi->tot_stored_edges = 0;
1179   tgpi->tot_edges = RNA_int_get(op->ptr, "edges");
1180   tgpi->flag = IDLE;
1181   tgpi->lock_axis = ts->gp_sculpt.lock_axis;
1182
1183   /* set temp layer, frame and stroke */
1184   gp_primitive_set_initdata(C, tgpi);
1185 }
1186
1187 /* Invoke handler: Initialize the operator */
1188 static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1189 {
1190   wmWindow *win = CTX_wm_window(C);
1191   bGPdata *gpd = CTX_data_gpencil_data(C);
1192   tGPDprimitive *tgpi = NULL;
1193
1194   /* initialize operator runtime data */
1195   gpencil_primitive_init(C, op);
1196   tgpi = op->customdata;
1197
1198   const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
1199   if (!is_modal) {
1200     tgpi->flag = IN_PROGRESS;
1201     gpencil_primitive_interaction_begin(tgpi, event);
1202   }
1203
1204   /* if in tools region, wait till we get to the main (3d-space)
1205    * region before allowing drawing to take place.
1206    */
1207   op->flag |= OP_IS_MODAL_CURSOR_REGION;
1208
1209   /* set cursor to indicate modal */
1210   WM_cursor_modal_set(win, BC_CROSSCURSOR);
1211
1212   /* update sindicator in header */
1213   gpencil_primitive_status_indicators(C, tgpi);
1214   DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1215   WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
1216
1217   /* add a modal handler for this operator */
1218   WM_event_add_modal_handler(C, op);
1219
1220   return OPERATOR_RUNNING_MODAL;
1221 }
1222
1223 /* Helper to complete a primitive */
1224 static void gpencil_primitive_interaction_end(bContext *C,
1225                                               wmOperator *op,
1226                                               wmWindow *win,
1227                                               tGPDprimitive *tgpi)
1228 {
1229   bGPDframe *gpf;
1230   bGPDstroke *gps;
1231
1232   ToolSettings *ts = tgpi->scene->toolsettings;
1233   Brush *brush = tgpi->brush;
1234
1235   const int def_nr = tgpi->ob->actdef - 1;
1236   const bool have_weight = (bool)BLI_findlink(&tgpi->ob->defbase, def_nr);
1237
1238   /* return to normal cursor and header status */
1239   ED_workspace_status_text(C, NULL);
1240   WM_cursor_modal_restore(win);
1241
1242   /* insert keyframes as required... */
1243   short add_frame_mode;
1244   if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) {
1245     add_frame_mode = GP_GETFRAME_ADD_COPY;
1246   }
1247   else {
1248     add_frame_mode = GP_GETFRAME_ADD_NEW;
1249   }
1250
1251   gpf = BKE_gpencil_layer_getframe(tgpi->gpl, tgpi->cframe, add_frame_mode);
1252
1253   /* prepare stroke to get transferred */
1254   gps = tgpi->gpf->strokes.first;
1255   if (gps) {
1256     gps->thickness = brush->size;
1257     gps->gradient_f = brush->gpencil_settings->gradient_f;
1258     copy_v2_v2(gps->gradient_s, brush->gpencil_settings->gradient_s);
1259
1260     gps->flag |= GP_STROKE_RECALC_GEOMETRY;
1261     gps->tot_triangles = 0;
1262
1263     /* calculate UVs along the stroke */
1264     ED_gpencil_calc_stroke_uv(tgpi->ob, gps);
1265   }
1266
1267   /* transfer stroke from temporary buffer to the actual frame */
1268   if (ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) {
1269     BLI_movelisttolist_reverse(&gpf->strokes, &tgpi->gpf->strokes);
1270   }
1271   else {
1272     BLI_movelisttolist(&gpf->strokes, &tgpi->gpf->strokes);
1273   }
1274   BLI_assert(BLI_listbase_is_empty(&tgpi->gpf->strokes));
1275
1276   /* add weights if required */
1277   if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) {
1278     BKE_gpencil_dvert_ensure(gps);
1279     for (int i = 0; i < gps->totpoints; i++) {
1280       MDeformVert *ve = &gps->dvert[i];
1281       MDeformWeight *dw = defvert_verify_index(ve, def_nr);
1282       if (dw) {
1283         dw->weight = ts->vgroup_weight;
1284       }
1285     }
1286   }
1287
1288   DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_COPY_ON_WRITE);
1289   DEG_id_tag_update(&tgpi->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
1290
1291   /* clean up temp data */
1292   gpencil_primitive_exit(C, op);
1293 }
1294
1295 /* edit event handling */
1296 static void gpencil_primitive_edit_event_handling(
1297     bContext *C, wmOperator *op, wmWindow *win, const wmEvent *event, tGPDprimitive *tgpi)
1298 {
1299   /* calculate nearest point then set cursor */
1300   int move = MOVE_NONE;
1301   float a = len_v2v2(tgpi->mval, tgpi->start);
1302   float b = len_v2v2(tgpi->mval, tgpi->end);
1303
1304   float c = len_v2v2(tgpi->mval, tgpi->cp1);
1305   float d = len_v2v2(tgpi->mval, tgpi->cp2);
1306
1307   if (tgpi->flag == IN_CURVE_EDIT) {
1308     if ((a < BIG_SIZE_CTL && tgpi->tot_stored_edges == 0) || b < BIG_SIZE_CTL) {
1309       move = MOVE_ENDS;
1310       WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
1311     }
1312     else if (tgpi->curve) {
1313       move = MOVE_CP;
1314       WM_cursor_modal_set(win, BC_HANDCURSOR);
1315     }
1316     else {
1317       WM_cursor_modal_set(win, BC_CROSSCURSOR);
1318     }
1319   }
1320   else if (tgpi->flag == IN_PROGRESS) {
1321     WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
1322   }
1323
1324   switch (event->type) {
1325     case MOUSEMOVE: {
1326       if ((event->val == KM_PRESS) && tgpi->sel_cp != SELECT_NONE) {
1327         if (tgpi->sel_cp == SELECT_START && tgpi->tot_stored_edges == 0) {
1328           copy_v2_v2(tgpi->start, tgpi->mval);
1329         }
1330         else if (tgpi->sel_cp == SELECT_END) {
1331           copy_v2_v2(tgpi->end, tgpi->mval);
1332         }
1333         else if (tgpi->sel_cp == SELECT_CP1 ||
1334                  (tgpi->sel_cp == SELECT_CP2 && tgpi->type != GP_STROKE_CURVE)) {
1335           float dx = (tgpi->mval[0] - tgpi->mvalo[0]);
1336           float dy = (tgpi->mval[1] - tgpi->mvalo[1]);
1337           tgpi->cp1[0] += dx;
1338           tgpi->cp1[1] += dy;
1339           if (event->shift) {
1340             copy_v2_v2(tgpi->cp2, tgpi->cp1);
1341           }
1342         }
1343         else if (tgpi->sel_cp == SELECT_CP2) {
1344           float dx = (tgpi->mval[0] - tgpi->mvalo[0]);
1345           float dy = (tgpi->mval[1] - tgpi->mvalo[1]);
1346           tgpi->cp2[0] += dx;
1347           tgpi->cp2[1] += dy;
1348           if (event->shift) {
1349             copy_v2_v2(tgpi->cp1, tgpi->cp2);
1350           }
1351         }
1352         /* update screen */
1353         gpencil_primitive_update(C, op, tgpi);
1354       }
1355       break;
1356     }
1357     case LEFTMOUSE: {
1358       if ((event->val == KM_PRESS)) {
1359         /* find nearest cp based on stroke end points */
1360         if (move == MOVE_ENDS) {
1361           tgpi->sel_cp = (a < b) ? SELECT_START : SELECT_END;
1362         }
1363         else if (move == MOVE_CP) {
1364           tgpi->sel_cp = (c < d) ? SELECT_CP1 : SELECT_CP2;
1365         }
1366         else {
1367           tgpi->sel_cp = SELECT_NONE;
1368         }
1369         break;
1370       }
1371       else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) {
1372         /* set control points and enter edit mode */
1373         tgpi->flag = IN_CURVE_EDIT;
1374         gp_primitive_update_cps(tgpi);
1375         gpencil_primitive_update(C, op, tgpi);
1376       }
1377       else {
1378         tgpi->sel_cp = SELECT_NONE;
1379       }
1380       break;
1381     }
1382     case MKEY: {
1383       if ((event->val == KM_PRESS) && (tgpi->curve) && (ELEM(tgpi->orign_type, GP_STROKE_ARC))) {
1384         tgpi->flip ^= 1;
1385         gp_primitive_update_cps(tgpi);
1386         gpencil_primitive_update(C, op, tgpi);
1387       }
1388       break;
1389     }
1390     case EKEY: {
1391       if (tgpi->flag == IN_CURVE_EDIT && !ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) {
1392         tgpi->flag = IN_PROGRESS;
1393         WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
1394         gpencil_primitive_add_segment(tgpi);
1395         copy_v2_v2(tgpi->start, tgpi->end);
1396         copy_v2_v2(tgpi->origin, tgpi->start);
1397         gp_primitive_update_cps(tgpi);
1398       }
1399       break;
1400     }
1401   }
1402 }
1403
1404 /* brush strength */
1405 static void gpencil_primitive_strength(tGPDprimitive *tgpi, bool reset)
1406 {
1407   Brush *brush = tgpi->brush;
1408   if (brush) {
1409     if (reset) {
1410       brush->gpencil_settings->draw_strength = tgpi->brush_strength;
1411       tgpi->brush_strength = 0.0f;
1412     }
1413     else {
1414       if (tgpi->brush_strength == 0.0f) {
1415         tgpi->brush_strength = brush->gpencil_settings->draw_strength;
1416       }
1417       float move[2];
1418       sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
1419       float adjust = (move[1] > 0.0f) ? 0.01f : -0.01f;
1420       brush->gpencil_settings->draw_strength += adjust * fabsf(len_manhattan_v2(move));
1421     }
1422
1423     /* limit low limit because below 0.2f the stroke is invisible */
1424     CLAMP(brush->gpencil_settings->draw_strength, 0.2f, 1.0f);
1425   }
1426 }
1427
1428 /* brush size */
1429 static void gpencil_primitive_size(tGPDprimitive *tgpi, bool reset)
1430 {
1431   Brush *brush = tgpi->brush;
1432   if (brush) {
1433     if (reset) {
1434       brush->size = tgpi->brush_size;
1435       tgpi->brush_size = 0;
1436     }
1437     else {
1438       if (tgpi->brush_size == 0) {
1439         tgpi->brush_size = brush->size;
1440       }
1441       float move[2];
1442       sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
1443       int adjust = (move[1] > 0.0f) ? 1 : -1;
1444       brush->size += adjust * (int)fabsf(len_manhattan_v2(move));
1445     }
1446     CLAMP_MIN(brush->size, 1);
1447   }
1448 }
1449
1450 /* move */
1451 static void gpencil_primitive_move(tGPDprimitive *tgpi, bool reset)
1452 {
1453   float move[2];
1454   zero_v2(move);
1455
1456   if (reset) {
1457     sub_v2_v2(move, tgpi->move);
1458     zero_v2(tgpi->move);
1459   }
1460   else {
1461     sub_v2_v2v2(move, tgpi->mval, tgpi->mvalo);
1462     add_v2_v2(tgpi->move, move);
1463   }
1464
1465   bGPDstroke *gps = tgpi->gpf->strokes.first;
1466   tGPspoint *points2D = tgpi->points;
1467
1468   for (int i = 0; i < gps->totpoints; i++) {
1469     tGPspoint *p2d = &points2D[i];
1470     add_v2_v2(&p2d->x, move);
1471   }
1472
1473   add_v2_v2(tgpi->start, move);
1474   add_v2_v2(tgpi->end, move);
1475   add_v2_v2(tgpi->cp1, move);
1476   add_v2_v2(tgpi->cp2, move);
1477   add_v2_v2(tgpi->origin, move);
1478 }
1479
1480 /* Modal handler: Events handling during interactive part */
1481 static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *event)
1482 {
1483   tGPDprimitive *tgpi = op->customdata;
1484   wmWindow *win = CTX_wm_window(C);
1485   const bool has_numinput = hasNumInput(&tgpi->num);
1486
1487   copy_v2fl_v2i(tgpi->mval, event->mval);
1488
1489   if (tgpi->flag == IN_MOVE) {
1490
1491     switch (event->type) {
1492       case MOUSEMOVE: {
1493         gpencil_primitive_move(tgpi, false);
1494         gpencil_primitive_update(C, op, tgpi);
1495         break;
1496       }
1497       case ESCKEY:
1498       case LEFTMOUSE: {
1499         zero_v2(tgpi->move);
1500         tgpi->flag = IN_CURVE_EDIT;
1501         break;
1502       }
1503       case RIGHTMOUSE: {
1504         if (event->val == KM_RELEASE) {
1505           tgpi->flag = IN_CURVE_EDIT;
1506           gpencil_primitive_move(tgpi, true);
1507           gpencil_primitive_update(C, op, tgpi);
1508         }
1509         break;
1510       }
1511     }
1512     copy_v2_v2(tgpi->mvalo, tgpi->mval);
1513     return OPERATOR_RUNNING_MODAL;
1514   }
1515   else if (tgpi->flag == IN_BRUSH_SIZE) {
1516     switch (event->type) {
1517       case MOUSEMOVE:
1518         gpencil_primitive_size(tgpi, false);
1519         gpencil_primitive_update(C, op, tgpi);
1520         break;
1521       case ESCKEY:
1522       case MIDDLEMOUSE:
1523       case LEFTMOUSE:
1524         tgpi->brush_size = 0;
1525         tgpi->flag = IN_CURVE_EDIT;
1526         break;
1527       case RIGHTMOUSE:
1528         if (event->val == KM_RELEASE) {
1529           tgpi->flag = IN_CURVE_EDIT;
1530           gpencil_primitive_size(tgpi, true);
1531           gpencil_primitive_update(C, op, tgpi);
1532         }
1533         break;
1534     }
1535     copy_v2_v2(tgpi->mvalo, tgpi->mval);
1536     return OPERATOR_RUNNING_MODAL;
1537   }
1538   else if (tgpi->flag == IN_BRUSH_STRENGTH) {
1539     switch (event->type) {
1540       case MOUSEMOVE:
1541         gpencil_primitive_strength(tgpi, false);
1542         gpencil_primitive_update(C, op, tgpi);
1543         break;
1544       case ESCKEY:
1545       case MIDDLEMOUSE:
1546       case LEFTMOUSE:
1547         tgpi->brush_strength = 0.0f;
1548         tgpi->flag = IN_CURVE_EDIT;
1549         break;
1550       case RIGHTMOUSE:
1551         if (event->val == KM_RELEASE) {
1552           tgpi->flag = IN_CURVE_EDIT;
1553           gpencil_primitive_strength(tgpi, true);
1554           gpencil_primitive_update(C, op, tgpi);
1555         }
1556         break;
1557     }
1558     copy_v2_v2(tgpi->mvalo, tgpi->mval);
1559     return OPERATOR_RUNNING_MODAL;
1560   }
1561   else if (tgpi->flag != IDLE) {
1562     gpencil_primitive_edit_event_handling(C, op, win, event, tgpi);
1563   }
1564
1565   switch (event->type) {
1566     case LEFTMOUSE: {
1567       if ((event->val == KM_PRESS) && (tgpi->flag == IDLE)) {
1568         /* start drawing primitive */
1569         /* TODO: Ignore if not in main region yet */
1570         tgpi->flag = IN_PROGRESS;
1571         gpencil_primitive_interaction_begin(tgpi, event);
1572       }
1573       else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_MOVE)) {
1574         tgpi->flag = IN_CURVE_EDIT;
1575       }
1576       else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) {
1577         /* set control points and enter edit mode */
1578         tgpi->flag = IN_CURVE_EDIT;
1579         gp_primitive_update_cps(tgpi);
1580         gpencil_primitive_update(C, op, tgpi);
1581       }
1582       else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS) &&
1583                (tgpi->type != GP_STROKE_CURVE)) {
1584         /* stop drawing primitive */
1585         tgpi->flag = IDLE;
1586         gpencil_primitive_interaction_end(C, op, win, tgpi);
1587         /* done! */
1588         return OPERATOR_FINISHED;
1589       }
1590       else {
1591         if (G.debug & G_DEBUG) {
1592           printf("GP Add Primitive Modal: LEFTMOUSE %d, Status = %d\n", event->val, tgpi->flag);
1593         }
1594       }
1595       break;
1596     }
1597     case SPACEKEY: /* confirm */
1598     case MIDDLEMOUSE:
1599     case RETKEY: {
1600       tgpi->flag = IDLE;
1601       gpencil_primitive_interaction_end(C, op, win, tgpi);
1602       /* done! */
1603       return OPERATOR_FINISHED;
1604     }
1605     case RIGHTMOUSE: {
1606       /* exception to cancel current stroke when we have previous strokes in buffer */
1607       if (tgpi->tot_stored_edges > 0) {
1608         tgpi->flag = IDLE;
1609         tgpi->tot_edges = 0;
1610         gp_primitive_update_strokes(C, tgpi);
1611         gpencil_primitive_interaction_end(C, op, win, tgpi);
1612         /* done! */
1613         return OPERATOR_FINISHED;
1614       }
1615       ATTR_FALLTHROUGH;
1616     }
1617     case ESCKEY: {
1618       /* return to normal cursor and header status */
1619       ED_workspace_status_text(C, NULL);
1620       WM_cursor_modal_restore(win);
1621
1622       /* clean up temp data */
1623       gpencil_primitive_exit(C, op);
1624
1625       /* canceled! */
1626       return OPERATOR_CANCELLED;
1627     }
1628     case PADPLUSKEY:
1629     case WHEELUPMOUSE: {
1630       if ((event->val != KM_RELEASE)) {
1631         tgpi->tot_edges = tgpi->tot_edges + 1;
1632         CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
1633         RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
1634
1635         /* update screen */
1636         gpencil_primitive_update(C, op, tgpi);
1637       }
1638       break;
1639     }
1640     case PADMINUS:
1641     case WHEELDOWNMOUSE: {
1642       if ((event->val != KM_RELEASE)) {
1643         tgpi->tot_edges = tgpi->tot_edges - 1;
1644         CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
1645         RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
1646
1647         /* update screen */
1648         gpencil_primitive_update(C, op, tgpi);
1649       }
1650       break;
1651     }
1652     case GKEY: /* grab mode */
1653     {
1654       if ((event->val == KM_PRESS)) {
1655         tgpi->flag = IN_MOVE;
1656         WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
1657       }
1658       break;
1659     }
1660     case FKEY: /* brush thickness/ brush strength */
1661     {
1662       if ((event->val == KM_PRESS)) {
1663         if (event->shift) {
1664           tgpi->flag = IN_BRUSH_STRENGTH;
1665         }
1666         else {
1667           tgpi->flag = IN_BRUSH_SIZE;
1668         }
1669         WM_cursor_modal_set(win, BC_NS_SCROLLCURSOR);
1670       }
1671       break;
1672     }
1673     case CKEY: /* curve mode */
1674     {
1675       if ((event->val == KM_PRESS) && (tgpi->orign_type == GP_STROKE_CURVE)) {
1676         switch (tgpi->type) {
1677           case GP_STROKE_CURVE:
1678             tgpi->type = GP_STROKE_ARC;
1679             break;
1680           default:
1681           case GP_STROKE_ARC:
1682             tgpi->type = GP_STROKE_CURVE;
1683             break;
1684         }
1685
1686         RNA_enum_set(op->ptr, "type", tgpi->type);
1687         gp_primitive_update_cps(tgpi);
1688         gpencil_primitive_update(C, op, tgpi);
1689       }
1690       break;
1691     }
1692     case TABKEY: {
1693       if (tgpi->flag == IN_CURVE_EDIT) {
1694         tgpi->flag = IN_PROGRESS;
1695         WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR);
1696         gp_primitive_update_cps(tgpi);
1697         gpencil_primitive_update(C, op, tgpi);
1698       }
1699       break;
1700     }
1701     case MOUSEMOVE: /* calculate new position */
1702     {
1703       if (tgpi->flag == IN_CURVE_EDIT) {
1704         break;
1705       }
1706       /* only handle mousemove if not doing numinput */
1707       if (has_numinput == false) {
1708         /* update position of mouse */
1709         copy_v2_v2(tgpi->end, tgpi->mval);
1710         copy_v2_v2(tgpi->start, tgpi->origin);
1711         if (tgpi->flag == IDLE) {
1712           copy_v2_v2(tgpi->origin, tgpi->mval);
1713         }
1714         /* Keep square if shift key */
1715         if (event->shift) {
1716           float x = tgpi->end[0] - tgpi->origin[0];
1717           float y = tgpi->end[1] - tgpi->origin[1];
1718           if (tgpi->type == GP_STROKE_LINE || tgpi->curve) {
1719             float angle = fabsf(atan2f(y, x));
1720             if (angle < 0.4f || angle > (M_PI - 0.4f)) {
1721               tgpi->end[1] = tgpi->origin[1];
1722             }
1723             else if (angle > (M_PI_2 - 0.4f) && angle < (M_PI_2 + 0.4f)) {
1724               tgpi->end[0] = tgpi->origin[0];
1725             }
1726             else {
1727               gpencil_primitive_to_square(tgpi, x, y);
1728             }
1729           }
1730           else {
1731             gpencil_primitive_to_square(tgpi, x, y);
1732           }
1733         }
1734         /* Center primitive if alt key */
1735         if (event->alt) {
1736           tgpi->start[0] = tgpi->origin[0] - (tgpi->end[0] - tgpi->origin[0]);
1737           tgpi->start[1] = tgpi->origin[1] - (tgpi->end[1] - tgpi->origin[1]);
1738         }
1739         gp_primitive_update_cps(tgpi);
1740         /* update screen */
1741         gpencil_primitive_update(C, op, tgpi);
1742       }
1743       break;
1744     }
1745     default: {
1746       if (tgpi->flag != IN_CURVE_EDIT && (event->val == KM_PRESS) &&
1747           handleNumInput(C, &tgpi->num, event)) {
1748         float value;
1749
1750         /* Grab data from numeric input, and store this new value (the user see an int) */
1751         value = tgpi->tot_edges;
1752         applyNumInput(&tgpi->num, &value);
1753         tgpi->tot_edges = value;
1754
1755         CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
1756         RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
1757
1758         /* update screen */
1759         gpencil_primitive_update(C, op, tgpi);
1760
1761         break;
1762       }
1763       else {
1764         /* unhandled event - allow to pass through */
1765         return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
1766       }
1767     }
1768   }
1769
1770   copy_v2_v2(tgpi->mvalo, tgpi->mval);
1771   /* still running... */
1772   return OPERATOR_RUNNING_MODAL;
1773 }
1774
1775 /* Cancel handler */
1776 static void gpencil_primitive_cancel(bContext *C, wmOperator *op)
1777 {
1778   /* this is just a wrapper around exit() */
1779   gpencil_primitive_exit(C, op);
1780 }
1781
1782 void GPENCIL_OT_primitive(wmOperatorType *ot)
1783 {
1784   static EnumPropertyItem primitive_type[] = {
1785       {GP_STROKE_BOX, "BOX", 0, "Box", ""},
1786       {GP_STROKE_LINE, "LINE", 0, "Line", ""},
1787       {GP_STROKE_CIRCLE, "CIRCLE", 0, "Circle", ""},
1788       {GP_STROKE_ARC, "ARC", 0, "Arc", ""},
1789       {GP_STROKE_CURVE, "CURVE", 0, "Curve", ""},
1790       {0, NULL, 0, NULL, NULL},
1791   };
1792
1793   /* identifiers */
1794   ot->name = "Grease Pencil Shapes";
1795   ot->idname = "GPENCIL_OT_primitive";
1796   ot->description = "Create predefined grease pencil stroke shapes";
1797
1798   /* callbacks */
1799   ot->invoke = gpencil_primitive_invoke;
1800   ot->modal = gpencil_primitive_modal;
1801   ot->cancel = gpencil_primitive_cancel;
1802   ot->poll = gpencil_primitive_add_poll;
1803
1804   /* flags */
1805   ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
1806
1807   /* properties */
1808   PropertyRNA *prop;
1809
1810   prop = RNA_def_int(ot->srna,
1811                      "edges",
1812                      4,
1813                      MIN_EDGES,
1814                      MAX_EDGES,
1815                      "Edges",
1816                      "Number of polygon edges",
1817                      MIN_EDGES,
1818                      MAX_EDGES);
1819   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1820
1821   RNA_def_enum(ot->srna, "type", primitive_type, GP_STROKE_BOX, "Type", "Type of shape");
1822
1823   prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
1824   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
1825 }