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