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