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