0403a42a2c965dfb3a16a1a8d19a08efacf381e2
[blender.git] / source / blender / editors / gpencil / gpencil_brush.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) 2015, Blender Foundation
17  * This is a new part of Blender
18  * Brush based operators for editing Grease Pencil strokes
19  */
20
21 /** \file
22  * \ingroup edgpencil
23  */
24
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stddef.h>
30 #include <math.h>
31
32 #include "MEM_guardedalloc.h"
33
34 #include "BLI_blenlib.h"
35 #include "BLI_ghash.h"
36 #include "BLI_math.h"
37 #include "BLI_rand.h"
38 #include "BLI_utildefines.h"
39
40 #include "PIL_time.h"
41
42 #include "BLT_translation.h"
43
44 #include "DNA_meshdata_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 #include "DNA_gpencil_types.h"
50 #include "DNA_object_types.h"
51
52 #include "BKE_colortools.h"
53 #include "BKE_context.h"
54 #include "BKE_deform.h"
55 #include "BKE_gpencil.h"
56 #include "BKE_material.h"
57 #include "BKE_object_deform.h"
58 #include "BKE_report.h"
59
60 #include "UI_interface.h"
61
62 #include "WM_api.h"
63 #include "WM_types.h"
64
65 #include "RNA_access.h"
66 #include "RNA_define.h"
67 #include "RNA_enum_types.h"
68
69 #include "UI_view2d.h"
70
71 #include "ED_gpencil.h"
72 #include "ED_screen.h"
73 #include "ED_view3d.h"
74
75 #include "GPU_immediate.h"
76 #include "GPU_immediate_util.h"
77 #include "GPU_state.h"
78
79 #include "DEG_depsgraph.h"
80 #include "DEG_depsgraph_query.h"
81
82 #include "gpencil_intern.h"
83
84 /* ************************************************ */
85 /* General Brush Editing Context */
86
87 /* Context for brush operators */
88 typedef struct tGP_BrushEditData {
89         /* Current editor/region/etc. */
90         /* NOTE: This stuff is mainly needed to handle 3D view projection stuff... */
91         Depsgraph *depsgraph;
92         Scene *scene;
93         Object *object;
94
95         ScrArea *sa;
96         ARegion *ar;
97
98         /* Current GPencil datablock */
99         bGPdata *gpd;
100
101         /* Brush Settings */
102         GP_Sculpt_Settings *settings;
103         GP_Sculpt_Data *gp_brush;
104         GP_Sculpt_Data *gp_brush_old;
105
106         eGP_Sculpt_Types brush_type;
107         eGP_Sculpt_Types brush_type_old;
108         eGP_Sculpt_Flag  flag;
109
110         /* Space Conversion Data */
111         GP_SpaceConversion gsc;
112
113
114         /* Is the brush currently painting? */
115         bool is_painting;
116         bool is_weight_mode;
117
118         /* Start of new sculpt stroke */
119         bool first;
120
121         /* Is multiframe editing enabled, and are we using falloff for that? */
122         bool is_multiframe;
123         bool use_multiframe_falloff;
124
125         /* Current frame */
126         int cfra;
127
128
129         /* Brush Runtime Data: */
130         /* - position and pressure
131          * - the *_prev variants are the previous values
132          */
133         float   mval[2], mval_prev[2];
134         float pressure, pressure_prev;
135
136         /* - effect vector (e.g. 2D/3D translation for grab brush) */
137         float dvec[3];
138
139         /* - multiframe falloff factor */
140         float mf_falloff;
141
142         /* active vertex group */
143         int vrgroup;
144
145
146         /* brush geometry (bounding box) */
147         rcti brush_rect;
148
149         /* Custom data for certain brushes */
150         /* - map from bGPDstroke's to structs containing custom data about those strokes */
151         GHash *stroke_customdata;
152         /* - general customdata */
153         void *customdata;
154
155
156         /* Timer for in-place accumulation of brush effect */
157         wmTimer *timer;
158         bool timerTick; /* is this event from a timer */
159
160         RNG *rng;
161 } tGP_BrushEditData;
162
163
164 /* Callback for performing some brush operation on a single point */
165 typedef bool (*GP_BrushApplyCb)(
166         tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
167         const int radius, const int co[2]);
168
169 /* ************************************************ */
170 /* Utility Functions */
171
172 /* apply lock axis reset */
173 static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, bGPDspoint *pt, const float save_pt[3])
174 {
175         if (gso->sa->spacetype != SPACE_VIEW3D) {
176                 return;
177         }
178
179         ToolSettings *ts = gso->scene->toolsettings;
180         int axis = ts->gp_sculpt.lock_axis;
181
182         /* lock axis control */
183         if (axis == 1) {
184                 pt->x = save_pt[0];
185         }
186         if (axis == 2) {
187                 pt->y = save_pt[1];
188         }
189         if (axis == 3) {
190                 pt->z = save_pt[2];
191         }
192 }
193
194 /* Context ---------------------------------------- */
195
196 /* Get the sculpting settings */
197 static GP_Sculpt_Settings *gpsculpt_get_settings(Scene *scene)
198 {
199         return &scene->toolsettings->gp_sculpt;
200 }
201
202 /* Get the active brush */
203 static GP_Sculpt_Data *gpsculpt_get_brush(Scene *scene, bool is_weight_mode)
204 {
205         GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt;
206         GP_Sculpt_Data *gp_brush = NULL;
207         if (is_weight_mode) {
208                 gp_brush = &gset->brush[gset->weighttype];
209         }
210         else {
211                 gp_brush = &gset->brush[gset->brushtype];
212         }
213
214         return gp_brush;
215 }
216
217 /* Brush Operations ------------------------------- */
218
219 /* Invert behavior of brush? */
220 static bool gp_brush_invert_check(tGP_BrushEditData *gso)
221 {
222         /* The basic setting is the brush's setting (from the panel) */
223         bool invert = ((gso->gp_brush->flag & GP_SCULPT_FLAG_INVERT) != 0);
224
225         /* During runtime, the user can hold down the Ctrl key to invert the basic behavior */
226         if (gso->flag & GP_SCULPT_FLAG_INVERT) {
227                 invert ^= true;
228         }
229
230         /* set temporary status */
231         if (invert) {
232                 gso->gp_brush->flag |= GP_SCULPT_FLAG_TMP_INVERT;
233         }
234         else {
235                 gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT;
236         }
237
238         return invert;
239 }
240
241 /* Compute strength of effect */
242 static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, const int co[2])
243 {
244         GP_Sculpt_Data *gp_brush = gso->gp_brush;
245
246         /* basic strength factor from brush settings */
247         float influence = gp_brush->strength;
248
249         /* use pressure? */
250         if (gp_brush->flag & GP_SCULPT_FLAG_USE_PRESSURE) {
251                 influence *= gso->pressure;
252         }
253
254         /* distance fading */
255         if (gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) {
256                 int mval_i[2];
257                 round_v2i_v2fl(mval_i, gso->mval);
258                 float distance = (float)len_v2v2_int(mval_i, co);
259                 float fac;
260
261                 CLAMP(distance, 0.0f, (float)radius);
262                 fac = 1.0f - (distance / (float)radius);
263
264                 influence *= fac;
265         }
266
267         /* apply multiframe falloff */
268         influence *= gso->mf_falloff;
269
270         /* return influence */
271         return influence;
272 }
273
274 /* ************************************************ */
275 /* Brush Callbacks */
276 /* This section defines the callbacks used by each brush to perform their magic.
277  * These are called on each point within the brush's radius.
278  */
279
280 /* ----------------------------------------------- */
281 /* Smooth Brush */
282
283 /* A simple (but slower + inaccurate) smooth-brush implementation to test the algorithm for stroke smoothing */
284 static bool gp_brush_smooth_apply(
285         tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
286         const int radius, const int co[2])
287 {
288         // GP_Sculpt_Data *gp_brush = gso->brush;
289         float inf = gp_brush_influence_calc(gso, radius, co);
290         /* need one flag enabled by default */
291         if ((gso->settings->flag &
292              (GP_SCULPT_SETT_FLAG_APPLY_POSITION |
293               GP_SCULPT_SETT_FLAG_APPLY_STRENGTH |
294               GP_SCULPT_SETT_FLAG_APPLY_THICKNESS |
295               GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0)
296         {
297                 gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION;
298         }
299
300         /* perform smoothing */
301         if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) {
302                 BKE_gpencil_smooth_stroke(gps, pt_index, inf);
303         }
304         if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) {
305                 BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf);
306         }
307         if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) {
308                 BKE_gpencil_smooth_stroke_thickness(gps, pt_index, inf);
309         }
310         if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) {
311                 BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf);
312         }
313
314         gps->flag |= GP_STROKE_RECALC_GEOMETRY;
315
316         return true;
317 }
318
319 /* ----------------------------------------------- */
320 /* Line Thickness Brush */
321
322 /* Make lines thicker or thinner by the specified amounts */
323 static bool gp_brush_thickness_apply(
324         tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
325         const int radius, const int co[2])
326 {
327         bGPDspoint *pt = gps->points + pt_index;
328         float inf;
329
330         /* Compute strength of effect
331          * - We divide the strength by 10, so that users can set "sane" values.
332          *   Otherwise, good default values are in the range of 0.093
333          */
334         inf = gp_brush_influence_calc(gso, radius, co) / 10.0f;
335
336         /* apply */
337         // XXX: this is much too strong, and it should probably do some smoothing with the surrounding stuff
338         if (gp_brush_invert_check(gso)) {
339                 /* make line thinner - reduce stroke pressure */
340                 pt->pressure -= inf;
341         }
342         else {
343                 /* make line thicker - increase stroke pressure */
344                 pt->pressure += inf;
345         }
346
347         /* Pressure should stay within [0.0, 1.0]
348          * However, it is nice for volumetric strokes to be able to exceed
349          * the upper end of this range. Therefore, we don't actually clamp
350          * down on the upper end.
351          */
352         if (pt->pressure < 0.0f)
353                 pt->pressure = 0.0f;
354
355         return true;
356 }
357
358
359 /* ----------------------------------------------- */
360 /* Color Strength Brush */
361
362 /* Make color more or less transparent by the specified amounts */
363 static bool gp_brush_strength_apply(
364         tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
365         const int radius, const int co[2])
366 {
367         bGPDspoint *pt = gps->points + pt_index;
368         float inf;
369
370         /* Compute strength of effect
371          * - We divide the strength, so that users can set "sane" values.
372          *   Otherwise, good default values are in the range of 0.093
373          */
374         inf = gp_brush_influence_calc(gso, radius, co) / 20.0f;
375
376         /* apply */
377         if (gp_brush_invert_check(gso)) {
378                 /* make line more transparent - reduce alpha factor */
379                 pt->strength -= inf;
380         }
381         else {
382                 /* make line more opaque - increase stroke strength */
383                 pt->strength += inf;
384         }
385         /* smooth the strength */
386         BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf);
387
388         /* Strength should stay within [0.0, 1.0] */
389         CLAMP(pt->strength, 0.0f, 1.0f);
390
391         return true;
392 }
393
394
395 /* ----------------------------------------------- */
396 /* Grab Brush */
397
398 /* Custom data per stroke for the Grab Brush
399  *
400  * This basically defines the strength of the effect for each
401  * affected stroke point that was within the initial range of
402  * the brush region.
403  */
404 typedef struct tGPSB_Grab_StrokeData {
405         /* array of indices to corresponding points in the stroke */
406         int   *points;
407         /* array of influence weights for each of the included points */
408         float *weights;
409
410         /* capacity of the arrays */
411         int capacity;
412         /* actual number of items currently stored */
413         int size;
414 } tGPSB_Grab_StrokeData;
415
416 /* initialise custom data for handling this stroke */
417 static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps)
418 {
419         tGPSB_Grab_StrokeData *data = NULL;
420
421         BLI_assert(gps->totpoints > 0);
422
423         /* Check if there are buffers already (from a prior run) */
424         if (BLI_ghash_haskey(gso->stroke_customdata, gps)) {
425                 /* Ensure that the caches are empty
426                  * - Since we reuse these between different strokes, we don't
427                  *   want the previous invocation's data polluting the arrays
428                  */
429                 data = BLI_ghash_lookup(gso->stroke_customdata, gps);
430                 BLI_assert(data != NULL);
431
432                 data->size = 0; /* minimum requirement - so that we can repopulate again */
433
434                 memset(data->points, 0, sizeof(int) * data->capacity);
435                 memset(data->weights, 0, sizeof(float) * data->capacity);
436         }
437         else {
438                 /* Create new instance */
439                 data = MEM_callocN(sizeof(tGPSB_Grab_StrokeData), "GP Stroke Grab Data");
440
441                 data->capacity = gps->totpoints;
442                 data->size = 0;
443
444                 data->points  = MEM_callocN(sizeof(int) * data->capacity, "GP Stroke Grab Indices");
445                 data->weights = MEM_callocN(sizeof(float) * data->capacity, "GP Stroke Grab Weights");
446
447                 /* hook up to the cache */
448                 BLI_ghash_insert(gso->stroke_customdata, gps, data);
449         }
450 }
451
452 /* store references to stroke points in the initial stage */
453 static bool gp_brush_grab_store_points(
454         tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
455         const int radius, const int co[2])
456 {
457         tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps);
458         float inf = gp_brush_influence_calc(gso, radius, co);
459
460         BLI_assert(data != NULL);
461         BLI_assert(data->size < data->capacity);
462
463         /* insert this point into the set of affected points */
464         data->points[data->size]  = pt_index;
465         data->weights[data->size] = inf;
466         data->size++;
467
468         /* done */
469         return true;
470 }
471
472 /* Compute effect vector for grab brush */
473 static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso)
474 {
475         /* Convert mouse-movements to movement vector */
476         // TODO: incorporate pressure into this?
477         // XXX: screen-space strokes in 3D space will suffer!
478         if (gso->sa->spacetype == SPACE_VIEW3D) {
479                 RegionView3D *rv3d = gso->ar->regiondata;
480                 float *rvec = gso->scene->cursor.location;
481                 float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
482
483                 float mval_f[2];
484
485                 /* convert from 2D screenspace to 3D... */
486                 mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
487                 mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
488
489                 ED_view3d_win_to_delta(gso->ar, mval_f, gso->dvec, zfac);
490         }
491         else {
492                 /* 2D - just copy */
493                 // XXX: view2d?
494                 gso->dvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
495                 gso->dvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
496                 gso->dvec[2] = 0.0f;  /* unused */
497         }
498 }
499
500 /* Apply grab transform to all relevant points of the affected strokes */
501 static void gp_brush_grab_apply_cached(
502         tGP_BrushEditData *gso, bGPDstroke *gps, float diff_mat[4][4])
503 {
504         tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps);
505         int i;
506
507         /* Apply dvec to all of the stored points */
508         for (i = 0; i < data->size; i++) {
509                 bGPDspoint *pt = &gps->points[data->points[i]];
510                 float delta[3] = {0.0f};
511
512                 /* adjust the amount of displacement to apply */
513                 mul_v3_v3fl(delta, gso->dvec, data->weights[i]);
514
515                 float fpt[3];
516                 float save_pt[3];
517                 copy_v3_v3(save_pt, &pt->x);
518                 /* apply transformation */
519                 mul_v3_m4v3(fpt, diff_mat, &pt->x);
520                 /* apply */
521                 add_v3_v3v3(&pt->x, fpt, delta);
522                 /* undo transformation to the init parent position */
523                 float inverse_diff_mat[4][4];
524                 invert_m4_m4(inverse_diff_mat, diff_mat);
525                 mul_m4_v3(inverse_diff_mat, &pt->x);
526
527                 /* compute lock axis */
528                 gpsculpt_compute_lock_axis(gso, pt, save_pt);
529         }
530         gps->flag |= GP_STROKE_RECALC_GEOMETRY;
531 }
532
533 /* free customdata used for handling this stroke */
534 static void gp_brush_grab_stroke_free(void *ptr)
535 {
536         tGPSB_Grab_StrokeData *data = (tGPSB_Grab_StrokeData *)ptr;
537
538         /* free arrays */
539         MEM_freeN(data->points);
540         MEM_freeN(data->weights);
541
542         /* ... and this item itself, since it was also allocated */
543         MEM_freeN(data);
544 }
545
546 /* ----------------------------------------------- */
547 /* Push Brush */
548 /* NOTE: Depends on gp_brush_grab_calc_dvec() */
549
550 static bool gp_brush_push_apply(
551         tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
552         const int radius, const int co[2])
553 {
554         bGPDspoint *pt = gps->points + pt_index;
555         float save_pt[3];
556         copy_v3_v3(save_pt, &pt->x);
557
558         float inf = gp_brush_influence_calc(gso, radius, co);
559         float delta[3] = {0.0f};
560
561         /* adjust the amount of displacement to apply */
562         mul_v3_v3fl(delta, gso->dvec, inf);
563
564         /* apply */
565         add_v3_v3(&pt->x, delta);
566
567         /* compute lock axis */
568         gpsculpt_compute_lock_axis(gso, pt, save_pt);
569
570         gps->flag |= GP_STROKE_RECALC_GEOMETRY;
571
572         /* done */
573         return true;
574 }
575
576 /* ----------------------------------------------- */
577 /* Pinch Brush */
578
579 /* Compute reference midpoint for the brush - this is what we'll be moving towards */
580 static void gp_brush_calc_midpoint(tGP_BrushEditData *gso)
581 {
582         if (gso->sa->spacetype == SPACE_VIEW3D) {
583                 /* Convert mouse position to 3D space
584                  * See: gpencil_paint.c :: gp_stroke_convertcoords()
585                  */
586                 RegionView3D *rv3d = gso->ar->regiondata;
587                 const float *rvec = gso->scene->cursor.location;
588                 float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL);
589
590                 float mval_f[2];
591                 copy_v2_v2(mval_f, gso->mval);
592                 float mval_prj[2];
593                 float dvec[3];
594
595
596                 if (ED_view3d_project_float_global(gso->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
597                         sub_v2_v2v2(mval_f, mval_prj, mval_f);
598                         ED_view3d_win_to_delta(gso->ar, mval_f, dvec, zfac);
599                         sub_v3_v3v3(gso->dvec, rvec, dvec);
600                 }
601                 else {
602                         zero_v3(gso->dvec);
603                 }
604         }
605         else {
606                 /* Just 2D coordinates */
607                 // XXX: fix View2D offsets later
608                 gso->dvec[0] = (float)gso->mval[0];
609                 gso->dvec[1] = (float)gso->mval[1];
610                 gso->dvec[2] = 0.0f;
611         }
612 }
613
614 /* Shrink distance between midpoint and this point... */
615 static bool gp_brush_pinch_apply(
616         tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
617         const int radius, const int co[2])
618 {
619         bGPDspoint *pt = gps->points + pt_index;
620         float fac, inf;
621         float vec[3];
622         float save_pt[3];
623         copy_v3_v3(save_pt, &pt->x);
624
625         /* Scale down standard influence value to get it more manageable...
626          * - No damping = Unmanageable at > 0.5 strength
627          * - Div 10     = Not enough effect
628          * - Div 5      = Happy medium... (by trial and error)
629          */
630         inf = gp_brush_influence_calc(gso, radius, co) / 5.0f;
631
632         /* 1) Make this point relative to the cursor/midpoint (dvec) */
633         sub_v3_v3v3(vec, &pt->x, gso->dvec);
634
635         /* 2) Shrink the distance by pulling the point towards the midpoint
636          *    (0.0 = at midpoint, 1 = at edge of brush region)
637          *                         OR
638          *    Increase the distance (if inverting the brush action!)
639          */
640         if (gp_brush_invert_check(gso)) {
641                 /* Inflate (inverse) */
642                 fac = 1.0f + (inf * inf); /* squared to temper the effect... */
643         }
644         else {
645                 /* Shrink (default) */
646                 fac = 1.0f - (inf * inf); /* squared to temper the effect... */
647         }
648         mul_v3_fl(vec, fac);
649
650         /* 3) Translate back to original space, with the shrinkage applied */
651         add_v3_v3v3(&pt->x, gso->dvec, vec);
652
653         /* compute lock axis */
654         gpsculpt_compute_lock_axis(gso, pt, save_pt);
655
656         gps->flag |= GP_STROKE_RECALC_GEOMETRY;
657
658         /* done */
659         return true;
660 }
661
662 /* ----------------------------------------------- */
663 /* Twist Brush - Rotate Around midpoint */
664
665 /* Take the screenspace coordinates of the point, rotate this around the brush midpoint,
666  * convert the rotated point and convert it into "data" space
667  */
668
669 static bool gp_brush_twist_apply(
670         tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
671         const int radius, const int co[2])
672 {
673         bGPDspoint *pt = gps->points + pt_index;
674         float angle, inf;
675         float save_pt[3];
676         copy_v3_v3(save_pt, &pt->x);
677
678         /* Angle to rotate by */
679         inf = gp_brush_influence_calc(gso, radius, co);
680         angle = DEG2RADF(1.0f) * inf;
681
682         if (gp_brush_invert_check(gso)) {
683                 /* invert angle that we rotate by */
684                 angle *= -1;
685         }
686
687         /* Rotate in 2D or 3D space? */
688         if (gps->flag & GP_STROKE_3DSPACE) {
689                 /* Perform rotation in 3D space... */
690                 RegionView3D *rv3d = gso->ar->regiondata;
691                 float rmat[3][3];
692                 float axis[3];
693                 float vec[3];
694
695                 /* Compute rotation matrix - rotate around view vector by angle */
696                 negate_v3_v3(axis, rv3d->persinv[2]);
697                 normalize_v3(axis);
698
699                 axis_angle_normalized_to_mat3(rmat, axis, angle);
700
701                 /* Rotate point (no matrix-space transforms needed, as GP points are in world space) */
702                 sub_v3_v3v3(vec, &pt->x, gso->dvec); /* make relative to center
703                                                       * (center is stored in dvec) */
704                 mul_m3_v3(rmat, vec);
705                 add_v3_v3v3(&pt->x, vec, gso->dvec); /* restore */
706
707                 /* compute lock axis */
708                 gpsculpt_compute_lock_axis(gso, pt, save_pt);
709         }
710         else {
711                 const float axis[3] = {0.0f, 0.0f, 1.0f};
712                 float vec[3] = {0.0f};
713                 float rmat[3][3];
714
715                 /* Express position of point relative to cursor, ready to rotate */
716                 // XXX: There is still some offset here, but it's close to working as expected...
717                 vec[0] = (float)(co[0] - gso->mval[0]);
718                 vec[1] = (float)(co[1] - gso->mval[1]);
719
720                 /* rotate point */
721                 axis_angle_normalized_to_mat3(rmat, axis, angle);
722                 mul_m3_v3(rmat, vec);
723
724                 /* Convert back to screen-coordinates */
725                 vec[0] += (float)gso->mval[0];
726                 vec[1] += (float)gso->mval[1];
727
728                 /* Map from screen-coordinates to final coordinate space */
729                 if (gps->flag & GP_STROKE_2DSPACE) {
730                         View2D *v2d = gso->gsc.v2d;
731                         UI_view2d_region_to_view(v2d, vec[0], vec[1], &pt->x, &pt->y);
732                 }
733                 else {
734                         // XXX
735                         copy_v2_v2(&pt->x, vec);
736                 }
737         }
738
739         gps->flag |= GP_STROKE_RECALC_GEOMETRY;
740
741         /* done */
742         return true;
743 }
744
745
746 /* ----------------------------------------------- */
747 /* Randomize Brush */
748
749 /* Apply some random jitter to the point */
750 static bool gp_brush_randomize_apply(
751         tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
752         const int radius, const int co[2])
753 {
754         bGPDspoint *pt = gps->points + pt_index;
755         float save_pt[3];
756         copy_v3_v3(save_pt, &pt->x);
757
758         /* Amount of jitter to apply depends on the distance of the point to the cursor,
759          * as well as the strength of the brush
760          */
761         const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f;
762         const float fac = BLI_rng_get_float(gso->rng) * inf;
763         /* need one flag enabled by default */
764         if ((gso->settings->flag &
765              (GP_SCULPT_SETT_FLAG_APPLY_POSITION |
766               GP_SCULPT_SETT_FLAG_APPLY_STRENGTH |
767               GP_SCULPT_SETT_FLAG_APPLY_THICKNESS |
768               GP_SCULPT_SETT_FLAG_APPLY_UV)) == 0)
769         {
770                 gso->settings->flag |= GP_SCULPT_SETT_FLAG_APPLY_POSITION;
771         }
772
773         /* apply random to position */
774         if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_POSITION) {
775                 /* Jitter is applied perpendicular to the mouse movement vector
776                  * - We compute all effects in screenspace (since it's easier)
777                  *   and then project these to get the points/distances in
778                  *   viewspace as needed
779                  */
780                 float mvec[2], svec[2];
781
782                 /* mouse movement in ints -> floats */
783                 mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
784                 mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
785
786                 /* rotate mvec by 90 degrees... */
787                 svec[0] = -mvec[1];
788                 svec[1] =  mvec[0];
789
790                 /* scale the displacement by the random displacement, and apply */
791                 if (BLI_rng_get_float(gso->rng) > 0.5f) {
792                         mul_v2_fl(svec, -fac);
793                 }
794                 else {
795                         mul_v2_fl(svec, fac);
796                 }
797
798                 //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]);
799
800                 /* convert to dataspace */
801                 if (gps->flag & GP_STROKE_3DSPACE) {
802                         /* 3D: Project to 3D space */
803                         if (gso->sa->spacetype == SPACE_VIEW3D) {
804                                 bool flip;
805                                 RegionView3D *rv3d = gso->ar->regiondata;
806                                 float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip);
807                                 if (flip == false) {
808                                         float dvec[3];
809                                         ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac);
810                                         add_v3_v3(&pt->x, dvec);
811                                         /* compute lock axis */
812                                         gpsculpt_compute_lock_axis(gso, pt, save_pt);
813                                 }
814                         }
815                         else {
816                                 /* ERROR */
817                                 BLI_assert(!"3D stroke being sculpted in non-3D view");
818                         }
819                 }
820                 else {
821                         /* 2D: As-is */
822                         // XXX: v2d scaling/offset?
823                         float nco[2];
824                         nco[0] = (float)co[0] + svec[0];
825                         nco[1] = (float)co[1] + svec[1];
826
827                         copy_v2_v2(&pt->x, nco);
828                 }
829         }
830         /* apply random to strength */
831         if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_STRENGTH) {
832                 if (BLI_rng_get_float(gso->rng) > 0.5f) {
833                         pt->strength += fac;
834                 }
835                 else {
836                         pt->strength -= fac;
837                 }
838                 CLAMP_MIN(pt->strength, 0.0f);
839                 CLAMP_MAX(pt->strength, 1.0f);
840         }
841         /* apply random to thickness (use pressure) */
842         if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_THICKNESS) {
843                 if (BLI_rng_get_float(gso->rng) > 0.5f) {
844                         pt->pressure += fac;
845                 }
846                 else {
847                         pt->pressure -= fac;
848                 }
849                 /* only limit lower value */
850                 CLAMP_MIN(pt->pressure, 0.0f);
851         }
852         /* apply random to UV (use pressure) */
853         if (gso->settings->flag & GP_SCULPT_SETT_FLAG_APPLY_UV) {
854                 if (BLI_rng_get_float(gso->rng) > 0.5f) {
855                         pt->uv_rot += fac;
856                 }
857                 else {
858                         pt->uv_rot -= fac;
859                 }
860                 CLAMP(pt->uv_rot, -M_PI_2, M_PI_2);
861         }
862
863         gps->flag |= GP_STROKE_RECALC_GEOMETRY;
864
865         /* done */
866         return true;
867 }
868
869 /* Weight Paint Brush */
870
871 /* Change weight paint for vertex groups */
872 static bool gp_brush_weight_apply(
873         tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index,
874         const int radius, const int co[2])
875 {
876         /* create dvert */
877         BKE_gpencil_dvert_ensure(gps);
878
879         bGPDspoint *pt = gps->points + pt_index;
880         MDeformVert *dvert = gps->dvert + pt_index;
881         float inf;
882
883         /* Compute strength of effect
884          * - We divide the strength by 10, so that users can set "sane" values.
885          *   Otherwise, good default values are in the range of 0.093
886          */
887         inf = gp_brush_influence_calc(gso, radius, co) / 10.0f;
888
889         /* need a vertex group */
890         if (gso->vrgroup == -1) {
891                 if (gso->object) {
892                         BKE_object_defgroup_add(gso->object);
893                         gso->vrgroup = 0;
894                 }
895         }
896         /* get current weight */
897         MDeformWeight *dw = defvert_verify_index(dvert, gso->vrgroup);
898         float curweight = dw ? dw->weight : 0.0f;
899
900         if (gp_brush_invert_check(gso)) {
901                 /* reduce weight */
902                 curweight -= inf;
903         }
904         else {
905                 /* increase weight */
906                 curweight += inf;
907         }
908
909         /* verify target weight */
910         CLAMP_MAX(curweight, gso->gp_brush->weight);
911
912         CLAMP(curweight, 0.0f, 1.0f);
913         if (dw) {
914                 dw->weight = curweight;
915         }
916
917         /* weight should stay within [0.0, 1.0] */
918         if (pt->pressure < 0.0f)
919                 pt->pressure = 0.0f;
920
921         return true;
922 }
923
924
925
926 /* ************************************************ */
927 /* Non Callback-Based Brushes */
928
929 /* Clone Brush ------------------------------------- */
930 /* How this brush currently works:
931  * - If this is start of the brush stroke, paste immediately under the cursor
932  *   by placing the midpoint of the buffer strokes under the cursor now
933  *
934  * - Otherwise, in:
935  *   "Stamp Mode" - Move the newly pasted strokes so that their center follows the cursor
936  *   "Continuous" - Repeatedly just paste new copies for where the brush is now
937  */
938
939 /* Custom state data for clone brush */
940 typedef struct tGPSB_CloneBrushData {
941         /* midpoint of the strokes on the clipboard */
942         float buffer_midpoint[3];
943
944         /* number of strokes in the paste buffer (and/or to be created each time) */
945         size_t totitems;
946
947         /* for "stamp" mode, the currently pasted brushes */
948         bGPDstroke **new_strokes;
949
950         /* mapping from colors referenced per stroke, to the new colours in the "pasted" strokes */
951         GHash *new_colors;
952 } tGPSB_CloneBrushData;
953
954 /* Initialise "clone" brush data */
955 static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso)
956 {
957         tGPSB_CloneBrushData *data;
958         bGPDstroke *gps;
959
960         /* init custom data */
961         gso->customdata = data = MEM_callocN(sizeof(tGPSB_CloneBrushData), "CloneBrushData");
962
963         /* compute midpoint of strokes on clipboard */
964         for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
965                 if (ED_gpencil_stroke_can_use(C, gps)) {
966                         const float dfac = 1.0f / ((float)gps->totpoints);
967                         float mid[3] = {0.0f};
968
969                         bGPDspoint *pt;
970                         int i;
971
972                         /* compute midpoint of this stroke */
973                         for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
974                                 float co[3];
975
976                                 mul_v3_v3fl(co, &pt->x, dfac);
977                                 add_v3_v3(mid, co);
978                         }
979
980                         /* combine this stroke's data with the main data */
981                         add_v3_v3(data->buffer_midpoint, mid);
982                         data->totitems++;
983                 }
984         }
985
986         /* Divide the midpoint by the number of strokes, to finish averaging it */
987         if (data->totitems > 1) {
988                 mul_v3_fl(data->buffer_midpoint, 1.0f / (float)data->totitems);
989         }
990
991         /* Create a buffer for storing the current strokes */
992         if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) {
993                 data->new_strokes = MEM_callocN(sizeof(bGPDstroke *) * data->totitems, "cloned strokes ptr array");
994         }
995
996         /* Init colormap for mapping between the pasted stroke's source colour(names)
997          * and the final colours that will be used here instead...
998          */
999         data->new_colors = gp_copybuf_validate_colormap(C);
1000 }
1001
1002 /* Free custom data used for "clone" brush */
1003 static void gp_brush_clone_free(tGP_BrushEditData *gso)
1004 {
1005         tGPSB_CloneBrushData *data = gso->customdata;
1006
1007         /* free strokes array */
1008         if (data->new_strokes) {
1009                 MEM_freeN(data->new_strokes);
1010                 data->new_strokes = NULL;
1011         }
1012
1013         /* free copybuf colormap */
1014         if (data->new_colors) {
1015                 BLI_ghash_free(data->new_colors, NULL, NULL);
1016                 data->new_colors = NULL;
1017         }
1018
1019         /* free the customdata itself */
1020         MEM_freeN(data);
1021         gso->customdata = NULL;
1022 }
1023
1024 /* Create new copies of the strokes on the clipboard */
1025 static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
1026 {
1027         tGPSB_CloneBrushData *data = gso->customdata;
1028
1029         Object *ob = CTX_data_active_object(C);
1030         bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
1031         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1032         int cfra_eval = (int)DEG_get_ctime(depsgraph);
1033
1034         bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW);
1035         bGPDstroke *gps;
1036
1037         float delta[3];
1038         size_t strokes_added = 0;
1039
1040         /* Compute amount to offset the points by */
1041         /* NOTE: This assumes that screenspace strokes are NOT used in the 3D view... */
1042
1043         gp_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */
1044         sub_v3_v3v3(delta, gso->dvec, data->buffer_midpoint);
1045
1046         /* Copy each stroke into the layer */
1047         for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
1048                 if (ED_gpencil_stroke_can_use(C, gps)) {
1049                         bGPDstroke *new_stroke;
1050                         bGPDspoint *pt;
1051                         int i;
1052
1053                         /* Make a new stroke */
1054                         new_stroke = MEM_dupallocN(gps);
1055
1056                         new_stroke->points = MEM_dupallocN(gps->points);
1057                         if (gps->dvert != NULL) {
1058                                 new_stroke->dvert = MEM_dupallocN(gps->dvert);
1059                                 BKE_gpencil_stroke_weights_duplicate(gps, new_stroke);
1060                         }
1061                         new_stroke->triangles = MEM_dupallocN(gps->triangles);
1062
1063                         new_stroke->next = new_stroke->prev = NULL;
1064                         BLI_addtail(&gpf->strokes, new_stroke);
1065
1066                         /* Fix color references */
1067                         Material *ma = BLI_ghash_lookup(data->new_colors, &new_stroke->mat_nr);
1068                         if ((ma) && (BKE_gpencil_get_material_index(ob, ma) > 0)) {
1069                                 gps->mat_nr = BKE_gpencil_get_material_index(ob, ma) - 1;
1070                                 CLAMP_MIN(gps->mat_nr, 0);
1071                         }
1072                         else {
1073                                 gps->mat_nr = 0; /* only if the color is not found */
1074                         }
1075
1076                         /* Adjust all the stroke's points, so that the strokes
1077                          * get pasted relative to where the cursor is now
1078                          */
1079                         for (i = 0, pt = new_stroke->points; i < new_stroke->totpoints; i++, pt++) {
1080                                 /* assume that the delta can just be applied, and then everything works */
1081                                 add_v3_v3(&pt->x, delta);
1082                         }
1083
1084                         /* Store ref for later */
1085                         if ((data->new_strokes) && (strokes_added < data->totitems)) {
1086                                 data->new_strokes[strokes_added] = new_stroke;
1087                                 strokes_added++;
1088                         }
1089                 }
1090         }
1091 }
1092
1093 /* Move newly-added strokes around - "Stamp" mode of the Clone brush */
1094 static void gp_brush_clone_adjust(tGP_BrushEditData *gso)
1095 {
1096         tGPSB_CloneBrushData *data = gso->customdata;
1097         size_t snum;
1098
1099         /* Compute the amount of movement to apply (overwrites dvec) */
1100         gp_brush_grab_calc_dvec(gso);
1101
1102         /* For each of the stored strokes, apply the offset to each point */
1103         /* NOTE: Again this assumes that in the 3D view, we only have 3d space and not screenspace strokes... */
1104         for (snum = 0; snum < data->totitems; snum++) {
1105                 bGPDstroke *gps = data->new_strokes[snum];
1106                 bGPDspoint *pt;
1107                 int i;
1108
1109                 for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1110                         if (gso->gp_brush->flag & GP_SCULPT_FLAG_USE_FALLOFF) {
1111                                 /* "Smudge" Effect when falloff is enabled */
1112                                 float delta[3] = {0.0f};
1113                                 int sco[2] = {0};
1114                                 float influence;
1115
1116                                 /* compute influence on point */
1117                                 gp_point_to_xy(&gso->gsc, gps, pt, &sco[0], &sco[1]);
1118                                 influence = gp_brush_influence_calc(gso, gso->gp_brush->size, sco);
1119
1120                                 /* adjust the amount of displacement to apply */
1121                                 mul_v3_v3fl(delta, gso->dvec, influence);
1122
1123                                 /* apply */
1124                                 add_v3_v3(&pt->x, delta);
1125                         }
1126                         else {
1127                                 /* Just apply the offset - All points move perfectly in sync with the cursor */
1128                                 add_v3_v3(&pt->x, gso->dvec);
1129                         }
1130                 }
1131         }
1132 }
1133
1134 /* Entrypoint for applying "clone" brush */
1135 static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso)
1136 {
1137         /* Which "mode" are we operating in? */
1138         if (gso->first) {
1139                 /* Create initial clones */
1140                 gp_brush_clone_add(C, gso);
1141         }
1142         else {
1143                 /* Stamp or Continuous Mode */
1144                 if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) {
1145                         /* Stamp - Proceed to translate the newly added strokes */
1146                         gp_brush_clone_adjust(gso);
1147                 }
1148                 else {
1149                         /* Continuous - Just keep pasting everytime we move */
1150                         /* TODO: The spacing of repeat should be controlled using a "stepsize" or similar property? */
1151                         gp_brush_clone_add(C, gso);
1152                 }
1153         }
1154
1155         return true;
1156 }
1157
1158 /* ************************************************ */
1159 /* Header Info for GPencil Sculpt */
1160
1161 static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso)
1162 {
1163         const char *brush_name = NULL;
1164         char str[UI_MAX_DRAW_STR] = "";
1165
1166         RNA_enum_name(rna_enum_gpencil_sculpt_brush_items, gso->brush_type, &brush_name);
1167
1168         BLI_snprintf(str, sizeof(str),
1169                      IFACE_("GPencil Sculpt: %s Stroke  | LMB to paint | RMB/Escape to Exit"
1170                             " | Ctrl to Invert Action | Wheel Up/Down for Size "
1171                             " | Shift-Wheel Up/Down for Strength"),
1172                      (brush_name) ? brush_name : "<?>");
1173
1174         ED_workspace_status_text(C, str);
1175 }
1176
1177 /* ************************************************ */
1178 /* Grease Pencil Sculpting Operator */
1179
1180 /* Init/Exit ----------------------------------------------- */
1181
1182 static bool gpsculpt_brush_init(bContext *C, wmOperator *op)
1183 {
1184         Scene *scene = CTX_data_scene(C);
1185         ToolSettings *ts = CTX_data_tool_settings(C);
1186         Object *ob = CTX_data_active_object(C);
1187
1188         const bool is_weight_mode = ob->mode == OB_MODE_WEIGHT_GPENCIL;
1189         /* set the brush using the tool */
1190 #if 0
1191         GP_Sculpt_Settings *gset = &ts->gp_sculpt;
1192         eGP_Sculpt_Types mode = is_weight_mode ? gset->weighttype : gset->brushtype;
1193 #endif
1194         tGP_BrushEditData *gso;
1195
1196         /* setup operator data */
1197         gso = MEM_callocN(sizeof(tGP_BrushEditData), "tGP_BrushEditData");
1198         op->customdata = gso;
1199
1200         gso->depsgraph = CTX_data_depsgraph(C);
1201         /* store state */
1202         gso->settings = gpsculpt_get_settings(scene);
1203         gso->gp_brush = gpsculpt_get_brush(scene, is_weight_mode);
1204         gso->is_weight_mode = is_weight_mode;
1205
1206         if (is_weight_mode) {
1207                 gso->brush_type = gso->settings->weighttype;
1208         }
1209         else {
1210                 gso->brush_type = gso->settings->brushtype;
1211         }
1212
1213         /* Random generator, only init once. */
1214         uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
1215         rng_seed ^= POINTER_AS_UINT(gso);
1216         gso->rng = BLI_rng_new(rng_seed);
1217
1218         gso->is_painting = false;
1219         gso->first = true;
1220
1221         gso->gpd = ED_gpencil_data_get_active(C);
1222         gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */
1223
1224         /* some brushes cannot use pressure for radius */
1225         if (ELEM(gso->brush_type, GP_SCULPT_TYPE_GRAB, GP_SCULPT_TYPE_CLONE)) {
1226                 gso->gp_brush->flag &= ~GP_SCULPT_FLAG_PRESSURE_RADIUS;
1227         }
1228
1229         gso->scene = scene;
1230         gso->object = ob;
1231         if (ob) {
1232                 gso->vrgroup = ob->actdef - 1;
1233                 if (!BLI_findlink(&ob->defbase, gso->vrgroup)) {
1234                         gso->vrgroup = -1;
1235                 }
1236         }
1237         else {
1238                 gso->vrgroup = - 1;
1239         }
1240
1241         gso->sa = CTX_wm_area(C);
1242         gso->ar = CTX_wm_region(C);
1243
1244         /* multiframe settings */
1245         gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd);
1246         gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0;
1247
1248         /* Init multi-edit falloff curve data before doing anything,
1249          * so we won't have to do it again later. */
1250         if (gso->is_multiframe) {
1251                 curvemapping_initialize(ts->gp_sculpt.cur_falloff);
1252         }
1253
1254         /* initialise custom data for brushes */
1255         switch (gso->brush_type) {
1256                 case GP_SCULPT_TYPE_CLONE:
1257                 {
1258                         bGPDstroke *gps;
1259                         bool found = false;
1260
1261                         /* check that there are some usable strokes in the buffer */
1262                         for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
1263                                 if (ED_gpencil_stroke_can_use(C, gps)) {
1264                                         found = true;
1265                                         break;
1266                                 }
1267                         }
1268
1269                         if (found == false) {
1270                                 /* STOP HERE! Nothing to paste! */
1271                                 BKE_report(op->reports, RPT_ERROR,
1272                                            "Copy some strokes to the clipboard before using the Clone brush to paste copies of them");
1273
1274                                 MEM_freeN(gso);
1275                                 op->customdata = NULL;
1276                                 return false;
1277                         }
1278                         else {
1279                                 /* initialise customdata */
1280                                 gp_brush_clone_init(C, gso);
1281                         }
1282                         break;
1283                 }
1284
1285                 case GP_SCULPT_TYPE_GRAB:
1286                 {
1287                         /* initialise the cache needed for this brush */
1288                         gso->stroke_customdata = BLI_ghash_ptr_new("GP Grab Brush - Strokes Hash");
1289                         break;
1290                 }
1291
1292                 /* Others - No customdata needed */
1293                 default:
1294                         break;
1295         }
1296
1297
1298         /* setup space conversions */
1299         gp_point_conversion_init(C, &gso->gsc);
1300
1301         /* update header */
1302         gpsculpt_brush_header_set(C, gso);
1303
1304         /* setup cursor drawing */
1305         //WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR);
1306         if (gso->sa->spacetype != SPACE_VIEW3D) {
1307                 ED_gpencil_toggle_brush_cursor(C, true, NULL);
1308         }
1309         return true;
1310 }
1311
1312 static void gpsculpt_brush_exit(bContext *C, wmOperator *op)
1313 {
1314         tGP_BrushEditData *gso = op->customdata;
1315         wmWindow *win = CTX_wm_window(C);
1316
1317         /* free brush-specific data */
1318         switch (gso->brush_type) {
1319                 case GP_SCULPT_TYPE_GRAB:
1320                 {
1321                         /* Free per-stroke customdata
1322                          * - Keys don't need to be freed, as those are the strokes
1323                          * - Values assigned to those keys do, as they are custom structs
1324                          */
1325                         BLI_ghash_free(gso->stroke_customdata, NULL, gp_brush_grab_stroke_free);
1326                         break;
1327                 }
1328
1329                 case GP_SCULPT_TYPE_CLONE:
1330                 {
1331                         /* Free customdata */
1332                         gp_brush_clone_free(gso);
1333                         break;
1334                 }
1335
1336                 default:
1337                         break;
1338         }
1339
1340         /* unregister timer (only used for realtime) */
1341         if (gso->timer) {
1342                 WM_event_remove_timer(CTX_wm_manager(C), win, gso->timer);
1343         }
1344
1345         if (gso->rng != NULL) {
1346                 BLI_rng_free(gso->rng);
1347         }
1348
1349         /* disable cursor and headerprints */
1350         ED_workspace_status_text(C, NULL);
1351         WM_cursor_modal_restore(win);
1352         if (gso->sa->spacetype != SPACE_VIEW3D) {
1353                 ED_gpencil_toggle_brush_cursor(C, false, NULL);
1354         }
1355
1356         /* disable temp invert flag */
1357         gso->gp_brush->flag &= ~GP_SCULPT_FLAG_TMP_INVERT;
1358
1359         /* free operator data */
1360         MEM_freeN(gso);
1361         op->customdata = NULL;
1362 }
1363
1364 /* poll callback for stroke sculpting operator(s) */
1365 static bool gpsculpt_brush_poll(bContext *C)
1366 {
1367         /* NOTE: this is a bit slower, but is the most accurate... */
1368         return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
1369 }
1370
1371 /* Init Sculpt Stroke ---------------------------------- */
1372
1373 static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso)
1374 {
1375         bGPdata *gpd = gso->gpd;
1376
1377         bGPDlayer *gpl;
1378         int cfra_eval = (int)DEG_get_ctime(gso->depsgraph);
1379
1380         /* only try to add a new frame if this is the first stroke, or the frame has changed */
1381         if ((gpd == NULL) || (cfra_eval == gso->cfra))
1382                 return;
1383
1384         /* go through each layer, and ensure that we've got a valid frame to use */
1385         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
1386                 /* only editable and visible layers are considered */
1387                 if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
1388                         bGPDframe *gpf = gpl->actframe;
1389
1390                         /* Make a new frame to work on if the layer's frame and the current scene frame don't match up
1391                          * - This is useful when animating as it saves that "uh-oh" moment when you realize you've
1392                          *   spent too much time editing the wrong frame...
1393                          */
1394                         // XXX: should this be allowed when framelock is enabled?
1395                         if (gpf->framenum != cfra_eval) {
1396                                 BKE_gpencil_frame_addcopy(gpl, cfra_eval);
1397                         }
1398                 }
1399         }
1400
1401         /* save off new current frame, so that next update works fine */
1402         gso->cfra = cfra_eval;
1403 }
1404
1405 /* Apply ----------------------------------------------- */
1406
1407 /* Apply brush operation to points in this stroke */
1408 static bool gpsculpt_brush_do_stroke(
1409         tGP_BrushEditData *gso, bGPDstroke *gps,
1410         float diff_mat[4][4], GP_BrushApplyCb apply)
1411 {
1412         GP_SpaceConversion *gsc = &gso->gsc;
1413         rcti *rect = &gso->brush_rect;
1414         GP_Sculpt_Data *gp_brush = gso->gp_brush;
1415         const int radius = (gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ? gso->gp_brush->size * gso->pressure : gso->gp_brush->size;
1416
1417         bGPDspoint *pt1, *pt2;
1418         int pc1[2] = {0};
1419         int pc2[2] = {0};
1420         int i;
1421         bool include_last = false;
1422         bool changed = false;
1423
1424         if (gps->totpoints == 1) {
1425                 bGPDspoint pt_temp;
1426                 gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
1427                 gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
1428
1429                 /* do boundbox check first */
1430                 if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
1431                         /* only check if point is inside */
1432                         int mval_i[2];
1433                         round_v2i_v2fl(mval_i, gso->mval);
1434                         if (len_v2v2_int(mval_i, pc1) <= radius) {
1435                                 /* apply operation to this point */
1436                                 changed = apply(gso, gps, 0, radius, pc1);
1437                         }
1438                 }
1439         }
1440         else {
1441                 /* Loop over the points in the stroke, checking for intersections
1442                  * - an intersection means that we touched the stroke
1443                  */
1444                 for (i = 0; (i + 1) < gps->totpoints; i++) {
1445                         /* Get points to work with */
1446                         pt1 = gps->points + i;
1447                         pt2 = gps->points + i + 1;
1448
1449                         /* Skip if neither one is selected (and we are only allowed to edit/consider selected points) */
1450                         if (gso->settings->flag & GP_SCULPT_SETT_FLAG_SELECT_MASK) {
1451                                 if (!(pt1->flag & GP_SPOINT_SELECT) && !(pt2->flag & GP_SPOINT_SELECT)) {
1452                                         include_last = false;
1453                                         continue;
1454                                 }
1455                         }
1456                         bGPDspoint npt;
1457                         gp_point_to_parent_space(pt1, diff_mat, &npt);
1458                         gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
1459
1460                         gp_point_to_parent_space(pt2, diff_mat, &npt);
1461                         gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
1462
1463                         /* Check that point segment of the boundbox of the selection stroke */
1464                         if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
1465                             ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1])))
1466                         {
1467                                 /* Check if point segment of stroke had anything to do with
1468                                  * brush region  (either within stroke painted, or on its lines)
1469                                  * - this assumes that linewidth is irrelevant
1470                                  */
1471                                 if (gp_stroke_inside_circle(gso->mval, gso->mval_prev, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
1472                                         /* Apply operation to these points */
1473                                         bool ok = false;
1474
1475                                         /* To each point individually... */
1476                                         ok = apply(gso, gps, i, radius, pc1);
1477
1478                                         /* Only do the second point if this is the last segment,
1479                                          * and it is unlikely that the point will get handled
1480                                          * otherwise.
1481                                          *
1482                                          * NOTE: There is a small risk here that the second point wasn't really
1483                                          *       actually in-range. In that case, it only got in because
1484                                          *       the line linking the points was!
1485                                          */
1486                                         if (i + 1 == gps->totpoints - 1) {
1487                                                 ok |= apply(gso, gps, i + 1, radius, pc2);
1488                                                 include_last = false;
1489                                         }
1490                                         else {
1491                                                 include_last = true;
1492                                         }
1493
1494                                         changed |= ok;
1495                                 }
1496                                 else if (include_last) {
1497                                         /* This case is for cases where for whatever reason the second vert (1st here) doesn't get included
1498                                          * because the whole edge isn't in bounds, but it would've qualified since it did with the
1499                                          * previous step (but wasn't added then, to avoid double-ups)
1500                                          */
1501                                         changed |= apply(gso, gps, i, radius, pc1);
1502                                         include_last = false;
1503                                 }
1504                         }
1505                 }
1506         }
1507
1508         return changed;
1509 }
1510
1511 /* Apply sculpt brushes to strokes in the given frame */
1512 static bool gpsculpt_brush_do_frame(
1513         bContext *C, tGP_BrushEditData *gso,
1514         bGPDlayer *gpl, bGPDframe *gpf,
1515         float diff_mat[4][4])
1516 {
1517         bool changed = false;
1518         Object *ob = CTX_data_active_object(C);
1519
1520         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
1521                 /* skip strokes that are invalid for current view */
1522                 if (ED_gpencil_stroke_can_use(C, gps) == false) {
1523                         continue;
1524                 }
1525                 /* check if the color is editable */
1526                 if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
1527                         continue;
1528                 }
1529
1530                 switch (gso->brush_type) {
1531                         case GP_SCULPT_TYPE_SMOOTH: /* Smooth strokes */
1532                         {
1533                                 changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply);
1534                                 break;
1535                         }
1536
1537                         case GP_SCULPT_TYPE_THICKNESS: /* Adjust stroke thickness */
1538                         {
1539                                 changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply);
1540                                 break;
1541                         }
1542
1543                         case GP_SCULPT_TYPE_STRENGTH: /* Adjust stroke color strength */
1544                         {
1545                                 changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_strength_apply);
1546                                 break;
1547                         }
1548
1549                         case GP_SCULPT_TYPE_GRAB: /* Grab points */
1550                         {
1551                                 if (gso->first) {
1552                                         /* First time this brush stroke is being applied:
1553                                          * 1) Prepare data buffers (init/clear) for this stroke
1554                                          * 2) Use the points now under the cursor
1555                                          */
1556                                         gp_brush_grab_stroke_init(gso, gps);
1557                                         changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_grab_store_points);
1558                                 }
1559                                 else {
1560                                         /* Apply effect to the stored points */
1561                                         gp_brush_grab_apply_cached(gso, gps, diff_mat);
1562                                         changed |= true;
1563                                 }
1564                                 break;
1565                         }
1566
1567                         case GP_SCULPT_TYPE_PUSH: /* Push points */
1568                         {
1569                                 changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply);
1570                                 break;
1571                         }
1572
1573                         case GP_SCULPT_TYPE_PINCH: /* Pinch points */
1574                         {
1575                                 changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply);
1576                                 break;
1577                         }
1578
1579                         case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */
1580                         {
1581                                 changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply);
1582                                 break;
1583                         }
1584
1585                         case GP_SCULPT_TYPE_RANDOMIZE: /* Apply jitter */
1586                         {
1587                                 changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply);
1588                                 break;
1589                         }
1590
1591                         case GP_SCULPT_TYPE_WEIGHT: /* Adjust vertex group weight */
1592                         {
1593                                 changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_weight_apply);
1594                                 break;
1595                         }
1596
1597
1598                         default:
1599                                 printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type);
1600                                 break;
1601                 }
1602                 /* Triangulation must be calculated if changed */
1603                 if (changed) {
1604                         gps->flag |= GP_STROKE_RECALC_GEOMETRY;
1605                         gps->tot_triangles = 0;
1606                 }
1607         }
1608
1609         return changed;
1610 }
1611
1612 /* Perform two-pass brushes which modify the existing strokes */
1613 static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
1614 {
1615         ToolSettings *ts = CTX_data_tool_settings(C);
1616         Depsgraph *depsgraph = CTX_data_depsgraph(C);
1617         Object *obact = gso->object;
1618         bGPdata *gpd = gso->gpd;
1619         bool changed = false;
1620
1621         /* Calculate brush-specific data which applies equally to all points */
1622         switch (gso->brush_type) {
1623                 case GP_SCULPT_TYPE_GRAB: /* Grab points */
1624                 case GP_SCULPT_TYPE_PUSH: /* Push points */
1625                 {
1626                         /* calculate amount of displacement to apply */
1627                         gp_brush_grab_calc_dvec(gso);
1628                         break;
1629                 }
1630
1631                 case GP_SCULPT_TYPE_PINCH: /* Pinch points */
1632                 case GP_SCULPT_TYPE_TWIST: /* Twist points around midpoint */
1633                 {
1634                         /* calculate midpoint of the brush (in data space) */
1635                         gp_brush_calc_midpoint(gso);
1636                         break;
1637                 }
1638
1639                 case GP_SCULPT_TYPE_RANDOMIZE: /* Random jitter */
1640                 {
1641                         /* compute the displacement vector for the cursor (in data space) */
1642                         gp_brush_grab_calc_dvec(gso);
1643                         break;
1644                 }
1645
1646                 default:
1647                         break;
1648         }
1649
1650
1651         /* Find visible strokes, and perform operations on those if hit */
1652         CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
1653         {
1654                 /* If no active frame, don't do anything... */
1655                 if (gpl->actframe == NULL) {
1656                         continue;
1657                 }
1658
1659                 /* calculate difference matrix */
1660                 float diff_mat[4][4];
1661                 ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
1662
1663                 /* Active Frame or MultiFrame? */
1664                 if (gso->is_multiframe) {
1665                         /* init multiframe falloff options */
1666                         int f_init = 0;
1667                         int f_end = 0;
1668
1669                         if (gso->use_multiframe_falloff) {
1670                                 BKE_gpencil_get_range_selected(gpl, &f_init, &f_end);
1671                         }
1672
1673                         for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
1674                                 /* Always do active frame; Otherwise, only include selected frames */
1675                                 if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) {
1676                                         /* compute multiframe falloff factor */
1677                                         if (gso->use_multiframe_falloff) {
1678                                                 /* Faloff depends on distance to active frame (relative to the overall frame range) */
1679                                                 gso->mf_falloff = BKE_gpencil_multiframe_falloff_calc(
1680                                                         gpf, gpl->actframe->framenum,
1681                                                         f_init, f_end,
1682                                                         ts->gp_sculpt.cur_falloff);
1683                                         }
1684                                         else {
1685                                                 /* No falloff */
1686                                                 gso->mf_falloff = 1.0f;
1687                                         }
1688
1689                                         /* affect strokes in this frame */
1690                                         changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat);
1691                                 }
1692                         }
1693                 }
1694                 else {
1695                         /* Apply to active frame's strokes */
1696                         gso->mf_falloff = 1.0f;
1697                         changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat);
1698                 }
1699         }
1700         CTX_DATA_END;
1701
1702         return changed;
1703 }
1704
1705 /* Calculate settings for applying brush */
1706 static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
1707 {
1708         tGP_BrushEditData *gso = op->customdata;
1709         GP_Sculpt_Data *gp_brush = gso->gp_brush;
1710         const int radius = (
1711                 (gp_brush->flag & GP_SCULPT_FLAG_PRESSURE_RADIUS) ?
1712                 gso->gp_brush->size * gso->pressure : gso->gp_brush->size);
1713         float mousef[2];
1714         int mouse[2];
1715         bool changed = false;
1716
1717         /* Get latest mouse coordinates */
1718         RNA_float_get_array(itemptr, "mouse", mousef);
1719         gso->mval[0] = mouse[0] = (int)(mousef[0]);
1720         gso->mval[1] = mouse[1] = (int)(mousef[1]);
1721
1722         gso->pressure = RNA_float_get(itemptr, "pressure");
1723
1724         if (RNA_boolean_get(itemptr, "pen_flip"))
1725                 gso->flag |= GP_SCULPT_FLAG_INVERT;
1726         else
1727                 gso->flag &= ~GP_SCULPT_FLAG_INVERT;
1728
1729
1730         /* Store coordinates as reference, if operator just started running */
1731         if (gso->first) {
1732                 gso->mval_prev[0]  = gso->mval[0];
1733                 gso->mval_prev[1]  = gso->mval[1];
1734                 gso->pressure_prev = gso->pressure;
1735         }
1736
1737         /* Update brush_rect, so that it represents the bounding rectangle of brush */
1738         gso->brush_rect.xmin = mouse[0] - radius;
1739         gso->brush_rect.ymin = mouse[1] - radius;
1740         gso->brush_rect.xmax = mouse[0] + radius;
1741         gso->brush_rect.ymax = mouse[1] + radius;
1742
1743
1744         /* Apply brush */
1745         if (gso->brush_type == GP_SCULPT_TYPE_CLONE) {
1746                 changed = gpsculpt_brush_apply_clone(C, gso);
1747         }
1748         else {
1749                 changed = gpsculpt_brush_apply_standard(C, gso);
1750         }
1751
1752
1753         /* Updates */
1754         if (changed) {
1755                 DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY);
1756                 WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
1757         }
1758
1759         /* Store values for next step */
1760         gso->mval_prev[0]  = gso->mval[0];
1761         gso->mval_prev[1]  = gso->mval[1];
1762         gso->pressure_prev = gso->pressure;
1763         gso->first = false;
1764 }
1765
1766 /* Running --------------------------------------------- */
1767
1768 /* helper - a record stroke, and apply paint event */
1769 static void gpsculpt_brush_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
1770 {
1771         tGP_BrushEditData *gso = op->customdata;
1772         ToolSettings *ts = CTX_data_tool_settings(C);
1773         GP_Sculpt_Settings *gset = &ts->gp_sculpt;
1774         PointerRNA itemptr;
1775         float mouse[2];
1776         int tablet = 0;
1777
1778         mouse[0] = event->mval[0] + 1;
1779         mouse[1] = event->mval[1] + 1;
1780
1781         /* fill in stroke */
1782         RNA_collection_add(op->ptr, "stroke", &itemptr);
1783
1784         RNA_float_set_array(&itemptr, "mouse", mouse);
1785         RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false);
1786         RNA_boolean_set(&itemptr, "is_start", gso->first);
1787
1788         /* handle pressure sensitivity (which is supplied by tablets) */
1789         if (event->tablet_data) {
1790                 const wmTabletData *wmtab = event->tablet_data;
1791                 float pressure = wmtab->Pressure;
1792
1793                 tablet = (wmtab->Active != EVT_TABLET_NONE);
1794
1795                 /* special exception here for too high pressure values on first touch in
1796                  * windows for some tablets: clamp the values to be sane
1797                  */
1798                 if (tablet && (pressure >= 0.99f)) {
1799                         pressure = 1.0f;
1800                 }
1801                 RNA_float_set(&itemptr, "pressure", pressure);
1802         }
1803         else {
1804                 RNA_float_set(&itemptr, "pressure", 1.0f);
1805         }
1806
1807         if (!gso->is_weight_mode) {
1808                 if (event->shift) {
1809                         gso->gp_brush_old = gso->gp_brush;
1810                         gso->brush_type_old = gso->brush_type;
1811
1812                         gso->gp_brush = &gset->brush[GP_SCULPT_TYPE_SMOOTH];
1813                         gso->brush_type = GP_SCULPT_TYPE_SMOOTH;
1814                 }
1815                 else {
1816                         if (gso->gp_brush_old != NULL) {
1817                                 gso->gp_brush = gso->gp_brush_old;
1818                                 gso->brush_type = gso->brush_type_old;
1819                         }
1820                 }
1821         }
1822
1823         /* apply */
1824         gpsculpt_brush_apply(C, op, &itemptr);
1825 }
1826
1827 /* reapply */
1828 static int gpsculpt_brush_exec(bContext *C, wmOperator *op)
1829 {
1830         if (!gpsculpt_brush_init(C, op))
1831                 return OPERATOR_CANCELLED;
1832
1833         RNA_BEGIN(op->ptr, itemptr, "stroke")
1834         {
1835                 gpsculpt_brush_apply(C, op, &itemptr);
1836         }
1837         RNA_END;
1838
1839         gpsculpt_brush_exit(C, op);
1840
1841         return OPERATOR_FINISHED;
1842 }
1843
1844
1845 /* start modal painting */
1846 static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1847 {
1848         tGP_BrushEditData *gso = NULL;
1849         const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
1850         const bool is_playing = ED_screen_animation_playing(CTX_wm_manager(C)) != NULL;
1851         bool needs_timer = false;
1852         float brush_rate = 0.0f;
1853
1854         /* the operator cannot work while play animation */
1855         if (is_playing) {
1856                 BKE_report(op->reports, RPT_ERROR, "Cannot sculpt while play animation");
1857
1858                 return OPERATOR_CANCELLED;
1859         }
1860
1861         /* init painting data */
1862         if (!gpsculpt_brush_init(C, op))
1863                 return OPERATOR_CANCELLED;
1864
1865         gso = op->customdata;
1866
1867         /* initialise type-specific data (used for the entire session) */
1868         switch (gso->brush_type) {
1869                 /* Brushes requiring timer... */
1870                 case GP_SCULPT_TYPE_THICKNESS:
1871                         brush_rate = 0.01f; // XXX: hardcoded
1872                         needs_timer = true;
1873                         break;
1874
1875                 case GP_SCULPT_TYPE_STRENGTH:
1876                         brush_rate = 0.01f; // XXX: hardcoded
1877                         needs_timer = true;
1878                         break;
1879
1880                 case GP_SCULPT_TYPE_PINCH:
1881                         brush_rate = 0.001f; // XXX: hardcoded
1882                         needs_timer = true;
1883                         break;
1884
1885                 case GP_SCULPT_TYPE_TWIST:
1886                         brush_rate = 0.01f; // XXX: hardcoded
1887                         needs_timer = true;
1888                         break;
1889
1890                 default:
1891                         break;
1892         }
1893
1894         /* register timer for increasing influence by hovering over an area */
1895         if (needs_timer) {
1896                 gso->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, brush_rate);
1897         }
1898
1899         /* register modal handler */
1900         WM_event_add_modal_handler(C, op);
1901
1902         /* start drawing immediately? */
1903         if (is_modal == false) {
1904                 ARegion *ar = CTX_wm_region(C);
1905
1906                 /* ensure that we'll have a new frame to draw on */
1907                 gpsculpt_brush_init_stroke(gso);
1908
1909                 /* apply first dab... */
1910                 gso->is_painting = true;
1911                 gpsculpt_brush_apply_event(C, op, event);
1912
1913                 /* redraw view with feedback */
1914                 ED_region_tag_redraw(ar);
1915         }
1916
1917         return OPERATOR_RUNNING_MODAL;
1918 }
1919
1920 /* painting - handle events */
1921 static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *event)
1922 {
1923         tGP_BrushEditData *gso = op->customdata;
1924         const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
1925         bool redraw_region = false;
1926         bool redraw_toolsettings = false;
1927
1928         /* The operator can be in 2 states: Painting and Idling */
1929         if (gso->is_painting) {
1930                 /* Painting  */
1931                 switch (event->type) {
1932                         /* Mouse Move = Apply somewhere else */
1933                         case MOUSEMOVE:
1934                         case INBETWEEN_MOUSEMOVE:
1935                                 /* apply brush effect at new position */
1936                                 gpsculpt_brush_apply_event(C, op, event);
1937
1938                                 /* force redraw, so that the cursor will at least be valid */
1939                                 redraw_region = true;
1940                                 break;
1941
1942                         /* Timer Tick - Only if this was our own timer */
1943                         case TIMER:
1944                                 if (event->customdata == gso->timer) {
1945                                         gso->timerTick = true;
1946                                         gpsculpt_brush_apply_event(C, op, event);
1947                                         gso->timerTick = false;
1948                                 }
1949                                 break;
1950
1951                         /* Adjust brush settings */
1952                         /* FIXME: Step increments and modifier keys are hardcoded here! */
1953                         case WHEELUPMOUSE:
1954                         case PADPLUSKEY:
1955                                 if (event->shift) {
1956                                         /* increase strength */
1957                                         gso->gp_brush->strength += 0.05f;
1958                                         CLAMP_MAX(gso->gp_brush->strength, 1.0f);
1959                                 }
1960                                 else {
1961                                         /* increase brush size */
1962                                         gso->gp_brush->size += 3;
1963                                         CLAMP_MAX(gso->gp_brush->size, 300);
1964                                 }
1965
1966                                 redraw_region = true;
1967                                 redraw_toolsettings = true;
1968                                 break;
1969
1970                         case WHEELDOWNMOUSE:
1971                         case PADMINUS:
1972                                 if (event->shift) {
1973                                         /* decrease strength */
1974                                         gso->gp_brush->strength -= 0.05f;
1975                                         CLAMP_MIN(gso->gp_brush->strength, 0.0f);
1976                                 }
1977                                 else {
1978                                         /* decrease brush size */
1979                                         gso->gp_brush->size -= 3;
1980                                         CLAMP_MIN(gso->gp_brush->size, 1);
1981                                 }
1982
1983                                 redraw_region = true;
1984                                 redraw_toolsettings = true;
1985                                 break;
1986
1987                         /* Painting mbut release = Stop painting (back to idle) */
1988                         case LEFTMOUSE:
1989                                 //BLI_assert(event->val == KM_RELEASE);
1990                                 if (is_modal) {
1991                                         /* go back to idling... */
1992                                         gso->is_painting = false;
1993                                 }
1994                                 else {
1995                                         /* end sculpt session, since we're not modal */
1996                                         gso->is_painting = false;
1997
1998                                         gpsculpt_brush_exit(C, op);
1999                                         return OPERATOR_FINISHED;
2000                                 }
2001                                 break;
2002
2003                         /* Abort painting if any of the usual things are tried */
2004                         case MIDDLEMOUSE:
2005                         case RIGHTMOUSE:
2006                         case ESCKEY:
2007                                 gpsculpt_brush_exit(C, op);
2008                                 return OPERATOR_FINISHED;
2009                 }
2010         }
2011         else {
2012                 /* Idling */
2013                 BLI_assert(is_modal == true);
2014
2015                 switch (event->type) {
2016                         /* Painting mbut press = Start painting (switch to painting state) */
2017                         case LEFTMOUSE:
2018                                 /* do initial "click" apply */
2019                                 gso->is_painting = true;
2020                                 gso->first = true;
2021
2022                                 gpsculpt_brush_init_stroke(gso);
2023                                 gpsculpt_brush_apply_event(C, op, event);
2024                                 break;
2025
2026                         /* Exit modal operator, based on the "standard" ops */
2027                         case RIGHTMOUSE:
2028                         case ESCKEY:
2029                                 gpsculpt_brush_exit(C, op);
2030                                 return OPERATOR_FINISHED;
2031
2032                         /* MMB is often used for view manipulations */
2033                         case MIDDLEMOUSE:
2034                                 return OPERATOR_PASS_THROUGH;
2035
2036                         /* Mouse movements should update the brush cursor - Just redraw the active region */
2037                         case MOUSEMOVE:
2038                         case INBETWEEN_MOUSEMOVE:
2039                                 redraw_region = true;
2040                                 break;
2041
2042                         /* Adjust brush settings */
2043                         /* FIXME: Step increments and modifier keys are hardcoded here! */
2044                         case WHEELUPMOUSE:
2045                         case PADPLUSKEY:
2046                                 if (event->shift) {
2047                                         /* increase strength */
2048                                         gso->gp_brush->strength += 0.05f;
2049                                         CLAMP_MAX(gso->gp_brush->strength, 1.0f);
2050                                 }
2051                                 else {
2052                                         /* increase brush size */
2053                                         gso->gp_brush->size += 3;
2054                                         CLAMP_MAX(gso->gp_brush->size, 300);
2055                                 }
2056
2057                                 redraw_region = true;
2058                                 redraw_toolsettings = true;
2059                                 break;
2060
2061                         case WHEELDOWNMOUSE:
2062                         case PADMINUS:
2063                                 if (event->shift) {
2064                                         /* decrease strength */
2065                                         gso->gp_brush->strength -= 0.05f;
2066                                         CLAMP_MIN(gso->gp_brush->strength, 0.0f);
2067                                 }
2068                                 else {
2069                                         /* decrease brush size */
2070                                         gso->gp_brush->size -= 3;
2071                                         CLAMP_MIN(gso->gp_brush->size, 1);
2072                                 }
2073
2074                                 redraw_region = true;
2075                                 redraw_toolsettings = true;
2076                                 break;
2077
2078                         /* Change Frame - Allowed */
2079                         case LEFTARROWKEY:
2080                         case RIGHTARROWKEY:
2081                         case UPARROWKEY:
2082                         case DOWNARROWKEY:
2083                                 return OPERATOR_PASS_THROUGH;
2084
2085                         /* Camera/View Gizmo's - Allowed */
2086                         /* (See rationale in gpencil_paint.c -> gpencil_draw_modal()) */
2087                         case PAD0:  case PAD1:  case PAD2:  case PAD3:  case PAD4:
2088                         case PAD5:  case PAD6:  case PAD7:  case PAD8:  case PAD9:
2089                                 return OPERATOR_PASS_THROUGH;
2090
2091                         /* Unhandled event */
2092                         default:
2093                                 break;
2094                 }
2095         }
2096
2097         /* Redraw region? */
2098         if (redraw_region) {
2099                 ARegion *ar = CTX_wm_region(C);
2100                 ED_region_tag_redraw(ar);
2101         }
2102
2103         /* Redraw toolsettings (brush settings)? */
2104         if (redraw_toolsettings) {
2105                 DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY);
2106                 WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
2107         }
2108
2109         return OPERATOR_RUNNING_MODAL;
2110 }
2111
2112 void GPENCIL_OT_sculpt_paint(wmOperatorType *ot)
2113 {
2114         /* identifiers */
2115         ot->name = "Stroke Sculpt";
2116         ot->idname = "GPENCIL_OT_sculpt_paint";
2117         ot->description = "Apply tweaks to strokes by painting over the strokes"; // XXX
2118
2119         /* api callbacks */
2120         ot->exec = gpsculpt_brush_exec;
2121         ot->invoke = gpsculpt_brush_invoke;
2122         ot->modal = gpsculpt_brush_modal;
2123         ot->cancel = gpsculpt_brush_exit;
2124         ot->poll = gpsculpt_brush_poll;
2125
2126         /* flags */
2127         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
2128
2129         /* properties */
2130         PropertyRNA *prop;
2131         prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
2132         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
2133
2134         prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input",
2135                                "Enter a mini 'sculpt-mode' if enabled, otherwise, exit after drawing a single stroke");
2136         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
2137 }