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