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