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