Blender  V3.3
gpencil_paint.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2008 Blender Foundation. */
3 
8 #include <math.h>
9 #include <stddef.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include "MEM_guardedalloc.h"
15 
16 #include "BLI_blenlib.h"
17 #include "BLI_hash.h"
18 #include "BLI_math.h"
19 #include "BLI_math_geom.h"
20 #include "BLI_rand.h"
21 #include "BLI_utildefines.h"
22 
23 #include "BLT_translation.h"
24 
25 #include "PIL_time.h"
26 
27 #include "DNA_brush_types.h"
28 #include "DNA_gpencil_types.h"
29 #include "DNA_material_types.h"
30 #include "DNA_meshdata_types.h"
31 #include "DNA_object_types.h"
32 #include "DNA_scene_types.h"
34 
35 #include "BKE_brush.h"
36 #include "BKE_colortools.h"
37 #include "BKE_context.h"
38 #include "BKE_deform.h"
39 #include "BKE_global.h"
40 #include "BKE_gpencil.h"
41 #include "BKE_gpencil_curve.h"
42 #include "BKE_gpencil_geom.h"
44 #include "BKE_layer.h"
45 #include "BKE_main.h"
46 #include "BKE_material.h"
47 #include "BKE_paint.h"
48 #include "BKE_report.h"
49 #include "BKE_screen.h"
50 #include "BKE_tracking.h"
51 
52 #include "UI_view2d.h"
53 
54 #include "ED_clip.h"
55 #include "ED_gpencil.h"
56 #include "ED_keyframing.h"
57 #include "ED_object.h"
58 #include "ED_screen.h"
59 #include "ED_view3d.h"
60 
61 #include "GPU_immediate.h"
62 #include "GPU_immediate_util.h"
63 #include "GPU_state.h"
64 
65 #include "RNA_access.h"
66 #include "RNA_define.h"
67 #include "RNA_prototypes.h"
68 
69 #include "WM_api.h"
70 #include "WM_types.h"
71 
72 #include "DEG_depsgraph.h"
73 #include "DEG_depsgraph_query.h"
74 
75 #include "gpencil_intern.h"
76 
77 /* ******************************************* */
78 /* 'Globals' and Defines */
79 
80 /* values for tGPsdata->status */
81 typedef enum eGPencil_PaintStatus {
82  GP_STATUS_IDLING = 0, /* stroke isn't in progress yet */
83  GP_STATUS_PAINTING, /* a stroke is in progress */
84  GP_STATUS_ERROR, /* something wasn't correctly set up */
85  GP_STATUS_DONE, /* painting done */
87 
88 /* Return flags for adding points to stroke buffer */
89 typedef enum eGP_StrokeAdd_Result {
90  GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */
91  GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */
92  GP_STROKEADD_NORMAL, /* point was successfully added */
93  GP_STROKEADD_FULL, /* cannot add any more points to buffer */
95 
96 /* Runtime flags */
97 typedef enum eGPencil_PaintFlags {
98  GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */
104 
105 /* Temporary Guide data */
106 typedef struct tGPguide {
108  float spacing;
112  float origin[2];
114  float rot_point[2];
116  float rot_angle;
124  float unit[2];
126 
127 /* Temporary 'Stroke' Operation data
128  * "p" = op->customdata
129  */
130 typedef struct tGPsdata {
132 
134  Main *bmain;
136  Scene *scene;
137  struct Depsgraph *depsgraph;
138 
144  wmWindow *win;
146  ScrArea *area;
148  ARegion *region;
150  View2D *v2d;
154  rctf *subrect;
156 
159 
163  bGPdata *gpd;
165  bGPDlayer *gpl;
167  bGPDframe *gpf;
168 
170  char *align_flag;
171 
178 
180  short radius;
181 
183  float mval[2];
185  float mvalo[2];
187  float mvali[2];
188 
190  float pressure;
192  float opressure;
193 
194  /* These need to be doubles, as (at least under unix) they are in seconds since epoch,
195  * float (and its 7 digits precision) is definitively not enough here!
196  * double, with its 15 digits precision,
197  * ensures us millisecond precision for a few centuries at least.
198  */
200  double inittime;
202  double curtime;
204  double ocurtime;
205 
208  float imat[4][4];
209  float mat[4][4];
210 
211  float diff_mat[4][4];
212 
214  float custom_color[4];
215 
217  void *erasercursor;
218 
219  /* mat settings are only used for 3D view */
226 
228  short straight;
233 
235 
237  short keymodifier;
239  bool shift;
241  float totpixlen;
244  /* guide */
246 
248 
251 
253 
254 /* ------ */
255 
256 #define STROKE_HORIZONTAL 1
257 #define STROKE_VERTICAL 2
258 
259 /* Macros for accessing sensitivity thresholds... */
260 /* minimum number of pixels mouse should move before new point created */
261 #define MIN_MANHATTEN_PX (U.gp_manhattandist)
262 /* minimum length of new segment before new point can be added */
263 #define MIN_EUCLIDEAN_PX (U.gp_euclideandist)
264 
265 static void gpencil_update_cache(bGPdata *gpd)
266 {
267  if (gpd) {
270  }
271 }
272 
273 /* ------ */
274 /* Forward defines for some functions... */
275 
277 
278 /* ******************************************* */
279 /* Context Wrangling... */
280 
281 /* check if context is suitable for drawing */
283 {
286  /* 3D Viewport */
287  if (area->spacetype != SPACE_VIEW3D) {
288  return false;
289  }
290 
291  /* check if Grease Pencil isn't already running */
292  if (ED_gpencil_session_active() != 0) {
293  CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active");
294  return false;
295  }
296 
297  /* only grease pencil object type */
299  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
300  return false;
301  }
302 
303  bGPdata *gpd = (bGPdata *)ob->data;
304  if (!GPENCIL_PAINT_MODE(gpd)) {
305  return false;
306  }
307 
309  if (!ts->gp_paint->paint.brush) {
310  CTX_wm_operator_poll_msg_set(C, "Grease Pencil has no active paint tool");
311  return false;
312  }
313 
314  return true;
315  }
316 
317  CTX_wm_operator_poll_msg_set(C, "Active region not set");
318  return false;
319 }
320 
321 /* check if projecting strokes into 3d-geometry in the 3D-View */
323 {
324  bGPdata *gpd = p->gpd;
325  return ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) &&
327 }
328 
329 /* ******************************************* */
330 /* Calculations/Conversions */
331 
332 /* Utilities --------------------------------- */
333 
334 /* get the reference point for stroke-point conversions */
335 static void gpencil_get_3d_reference(tGPsdata *p, float vec[3])
336 {
337  Object *ob = NULL;
338  if (p->ownerPtr.type == &RNA_Object) {
339  ob = (Object *)p->ownerPtr.data;
340  }
342 }
343 
344 /* Stroke Editing ---------------------------- */
345 /* check if the current mouse position is suitable for adding a new point */
346 static bool gpencil_stroke_filtermval(tGPsdata *p, const float mval[2], const float mvalo[2])
347 {
348  Brush *brush = p->brush;
349  int dx = (int)fabsf(mval[0] - mvalo[0]);
350  int dy = (int)fabsf(mval[1] - mvalo[1]);
352 
353  /* if buffer is empty, just let this go through (i.e. so that dots will work) */
354  if (p->gpd->runtime.sbuffer_used == 0) {
355  return true;
356  }
357  /* if lazy mouse, check minimum distance */
358  if (GPENCIL_LAZY_MODE(brush, p->shift) && (!p->disable_stabilizer)) {
360  if ((dx * dx + dy * dy) > (brush->smooth_stroke_radius * brush->smooth_stroke_radius)) {
361  return true;
362  }
363 
364  /* If the mouse is moving within the radius of the last move,
365  * don't update the mouse position. This allows sharp turns. */
366  copy_v2_v2(p->mval, p->mvalo);
367  return false;
368  }
369  /* check if mouse moved at least certain distance on both axes (best case)
370  * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand
371  */
372  if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) {
373  return true;
374  }
375  /* Check if the distance since the last point is significant enough:
376  * - Prevents points being added too densely
377  * - Distance here doesn't use sqrt to prevent slowness.
378  * We should still be safe from overflows though.
379  */
380  if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) {
381  return true;
382  }
383  /* mouse 'didn't move' */
384  return false;
385 }
386 
387 /* reproject stroke to plane locked to axis in 3d cursor location */
389 {
390  bGPdata *gpd = p->gpd;
391  Object *obact = (Object *)p->ownerPtr.data;
392 
393  float origin[3];
394  RegionView3D *rv3d = p->region->regiondata;
395 
396  /* verify the stroke mode is CURSOR 3d space mode */
397  if ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) == 0) {
398  return;
399  }
400  if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) {
401  return;
402  }
404  return;
405  }
406 
407  /* get drawing origin */
408  gpencil_get_3d_reference(p, origin);
409  ED_gpencil_project_stroke_to_plane(p->scene, obact, rv3d, p->gpl, gps, origin, p->lock_axis - 1);
410 }
411 
412 /* convert screen-coordinates to buffer-coordinates */
414  const float mval[2],
415  float out[3],
416  float *depth)
417 {
418  bGPdata *gpd = p->gpd;
419  if (depth && (*depth == DEPTH_INVALID)) {
420  depth = NULL;
421  }
422 
423  /* in 3d-space - pt->x/y/z are 3 side-by-side floats */
425 
426  /* add small offset to keep stroke over the surface */
427  if ((depth) && (gpd->zdepth_offset > 0.0f) && (*p->align_flag & GP_PROJECT_DEPTH_VIEW)) {
428  *depth *= (1.0f - (gpd->zdepth_offset / 1000.0f));
429  }
430 
431  int mval_i[2];
432  float rmval[2];
433  rmval[0] = mval[0] - 0.5f;
434  rmval[1] = mval[1] - 0.5f;
435  round_v2i_v2fl(mval_i, rmval);
436 
437  if (gpencil_project_check(p) &&
438  (ED_view3d_autodist_simple(p->region, mval_i, out, 0, depth))) {
439  /* projecting onto 3D-Geometry
440  * - nothing more needs to be done here, since view_autodist_simple() has already done it
441  */
442 
443  /* verify valid zdepth, if it's wrong, the default drawing mode is used
444  * and the function doesn't return now */
445  if ((depth == NULL) || (*depth <= 1.0f)) {
446  return;
447  }
448  }
449 
450  float mval_prj[2];
451  float rvec[3];
452 
453  /* Current method just converts each point in screen-coordinates to
454  * 3D-coordinates using the 3D-cursor as reference. In general, this
455  * works OK, but it could of course be improved. */
456 
457  gpencil_get_3d_reference(p, rvec);
458  const float zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec);
459 
460  if (ED_view3d_project_float_global(p->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
461  V3D_PROJ_RET_OK) {
462  float dvec[3];
463  float xy_delta[2];
464  sub_v2_v2v2(xy_delta, mval_prj, mval);
465  ED_view3d_win_to_delta(p->region, xy_delta, zfac, dvec);
466  sub_v3_v3v3(out, rvec, dvec);
467  }
468  else {
469  zero_v3(out);
470  }
471  }
472 }
473 
474 /* Apply jitter to stroke point. */
475 static void gpencil_brush_jitter(bGPdata *gpd, tGPspoint *pt, const float amplitude)
476 {
477  const float axis[2] = {0.0f, 1.0f};
478  /* Jitter is applied perpendicular to the mouse movement vector (2D space). */
479  float mvec[2];
480  /* Mouse movement in ints -> floats. */
481  if (gpd->runtime.sbuffer_used > 1) {
482  tGPspoint *pt_prev = pt - 1;
483  sub_v2_v2v2(mvec, pt->m_xy, pt_prev->m_xy);
484  normalize_v2(mvec);
485  /* Rotate mvec by 90 degrees... */
486  float angle = angle_v2v2(mvec, axis);
487  /* Reduce noise in the direction of the stroke. */
488  mvec[0] *= cos(angle);
489  mvec[1] *= sin(angle);
490 
491  /* Scale by displacement amount, and apply. */
492  madd_v2_v2fl(pt->m_xy, mvec, amplitude * 10.0f);
493  }
494 }
495 
496 /* Apply pressure change depending of the angle of the stroke to simulate a pen with shape */
497 static void gpencil_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const float mval[2])
498 {
499  float mvec[2];
500  float sen = brush->gpencil_settings->draw_angle_factor; /* sensitivity */
501  float fac;
502 
503  /* default angle of brush in radians */
504  float angle = brush->gpencil_settings->draw_angle;
505  /* angle vector of the brush with full thickness */
506  const float v0[2] = {cos(angle), sin(angle)};
507 
508  /* Apply to first point (only if there are 2 points because before no data to do it ) */
509  if (gpd->runtime.sbuffer_used == 1) {
510  sub_v2_v2v2(mvec, mval, (pt - 1)->m_xy);
511  normalize_v2(mvec);
512 
513  /* uses > 1.0f to get a smooth transition in first point */
514  fac = 1.4f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */
515  (pt - 1)->pressure = (pt - 1)->pressure - (sen * fac);
516 
517  CLAMP((pt - 1)->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f);
518  }
519 
520  /* apply from second point */
521  if (gpd->runtime.sbuffer_used >= 1) {
522  sub_v2_v2v2(mvec, mval, (pt - 1)->m_xy);
523  normalize_v2(mvec);
524 
525  fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */
526  /* interpolate with previous point for smoother transitions */
527  pt->pressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f);
529  }
530 }
531 
542 static void gpencil_smooth_buffer(tGPsdata *p, float inf, int idx)
543 {
544  bGPdata *gpd = p->gpd;
546  const short num_points = gpd->runtime.sbuffer_used;
547 
548  /* Do nothing if not enough points to smooth out */
549  if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) {
550  return;
551  }
552 
553  tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
554  const float steps = (idx < 4) ? 3.0f : 4.0f;
555 
556  tGPspoint *pta = idx >= 4 ? &points[idx - 4] : NULL;
557  tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : NULL;
558  tGPspoint *ptc = idx >= 2 ? &points[idx - 2] : NULL;
559  tGPspoint *ptd = &points[idx - 1];
560 
561  float sco[2] = {0.0f};
562  float a[2], b[2], c[2], d[2];
563  float pressure = 0.0f;
564  float strength = 0.0f;
565  const float average_fac = 1.0f / steps;
566 
567  /* Compute smoothed coordinate by taking the ones nearby */
568  if (pta) {
569  copy_v2_v2(a, pta->m_xy);
570  madd_v2_v2fl(sco, a, average_fac);
571  pressure += pta->pressure * average_fac;
572  strength += pta->strength * average_fac;
573  }
574  if (ptb) {
575  copy_v2_v2(b, ptb->m_xy);
576  madd_v2_v2fl(sco, b, average_fac);
577  pressure += ptb->pressure * average_fac;
578  strength += ptb->strength * average_fac;
579  }
580  if (ptc) {
581  copy_v2_v2(c, ptc->m_xy);
582  madd_v2_v2fl(sco, c, average_fac);
583  pressure += ptc->pressure * average_fac;
584  strength += ptc->strength * average_fac;
585  }
586  if (ptd) {
587  copy_v2_v2(d, ptd->m_xy);
588  madd_v2_v2fl(sco, d, average_fac);
589  pressure += ptd->pressure * average_fac;
590  strength += ptd->strength * average_fac;
591  }
592 
593  /* Based on influence factor, blend between original and optimal smoothed coordinate but not
594  * for Guide mode. */
595  if (!guide->use_guide) {
596  interp_v2_v2v2(c, c, sco, inf);
597  copy_v2_v2(ptc->m_xy, c);
598  }
599  /* Interpolate pressure. */
600  ptc->pressure = interpf(ptc->pressure, pressure, inf);
601  /* Interpolate strength. */
602  ptc->strength = interpf(ptc->strength, strength, inf);
603 }
604 
605 /* Helper: Apply smooth to segment from Index to Index */
606 static void gpencil_smooth_segment(bGPdata *gpd, const float inf, int from_idx, int to_idx)
607 {
608  const short num_points = to_idx - from_idx;
609  /* Do nothing if not enough points to smooth out */
610  if ((num_points < 3) || (inf == 0.0f)) {
611  return;
612  }
613 
614  if (from_idx <= 2) {
615  return;
616  }
617 
618  tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
619  const float average_fac = 0.25f;
620 
621  for (int i = from_idx; i < to_idx + 1; i++) {
622 
623  tGPspoint *pta = i >= 3 ? &points[i - 3] : NULL;
624  tGPspoint *ptb = i >= 2 ? &points[i - 2] : NULL;
625  tGPspoint *ptc = i >= 1 ? &points[i - 1] : &points[i];
626  tGPspoint *ptd = &points[i];
627 
628  float sco[2] = {0.0f};
629  float pressure = 0.0f;
630  float strength = 0.0f;
631 
632  /* Compute smoothed coordinate by taking the ones nearby */
633  if (pta) {
634  madd_v2_v2fl(sco, pta->m_xy, average_fac);
635  pressure += pta->pressure * average_fac;
636  strength += pta->strength * average_fac;
637  }
638  else {
639  madd_v2_v2fl(sco, ptc->m_xy, average_fac);
640  pressure += ptc->pressure * average_fac;
641  strength += ptc->strength * average_fac;
642  }
643 
644  if (ptb) {
645  madd_v2_v2fl(sco, ptb->m_xy, average_fac);
646  pressure += ptb->pressure * average_fac;
647  strength += ptb->strength * average_fac;
648  }
649  else {
650  madd_v2_v2fl(sco, ptc->m_xy, average_fac);
651  pressure += ptc->pressure * average_fac;
652  strength += ptc->strength * average_fac;
653  }
654 
655  madd_v2_v2fl(sco, ptc->m_xy, average_fac);
656  pressure += ptc->pressure * average_fac;
657  strength += ptc->strength * average_fac;
658 
659  madd_v2_v2fl(sco, ptd->m_xy, average_fac);
660  pressure += ptd->pressure * average_fac;
661  strength += ptd->strength * average_fac;
662 
663  /* Based on influence factor, blend between original and optimal smoothed coordinate. */
664  interp_v2_v2v2(ptc->m_xy, ptc->m_xy, sco, inf);
665 
666  /* Interpolate pressure. */
667  ptc->pressure = interpf(ptc->pressure, pressure, inf);
668  /* Interpolate strength. */
669  ptc->strength = interpf(ptc->strength, strength, inf);
670  }
671 }
672 
674  BrushGpencilSettings *brush_settings,
675  tGPspoint *pt,
676  const bool press,
677  const bool strength,
678  const bool uv)
679 {
680  bGPdata *gpd = p->gpd;
681  GpRandomSettings random_settings = p->random_settings;
682  float value = 0.0f;
683  /* Apply randomness to pressure. */
684  if ((brush_settings->draw_random_press > 0.0f) && (press)) {
685  if ((brush_settings->flag2 & GP_BRUSH_USE_PRESS_AT_STROKE) == 0) {
686  float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f;
687  value = 1.0 + rand * 2.0 * brush_settings->draw_random_press;
688  }
689  else {
690  value = 1.0 + random_settings.pressure * brush_settings->draw_random_press;
691  }
692 
693  /* Apply random curve. */
694  if (brush_settings->flag2 & GP_BRUSH_USE_PRESSURE_RAND_PRESS) {
696  brush_settings->curve_rand_pressure, 0, random_settings.pen_press);
697  }
698 
699  pt->pressure *= value;
700  CLAMP(pt->pressure, 0.1f, 1.0f);
701  }
702 
703  /* Apply randomness to color strength. */
704  if ((brush_settings->draw_random_strength) && (strength)) {
705  if ((brush_settings->flag2 & GP_BRUSH_USE_STRENGTH_AT_STROKE) == 0) {
706  float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f;
707  value = 1.0 + rand * brush_settings->draw_random_strength;
708  }
709  else {
710  value = 1.0 + random_settings.strength * brush_settings->draw_random_strength;
711  }
712 
713  /* Apply random curve. */
714  if (brush_settings->flag2 & GP_BRUSH_USE_STRENGTH_RAND_PRESS) {
716  brush_settings->curve_rand_pressure, 0, random_settings.pen_press);
717  }
718 
719  pt->strength *= value;
720  CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
721  }
722 
723  /* Apply randomness to uv texture rotation. */
724  if ((brush_settings->uv_random > 0.0f) && (uv)) {
725  if ((brush_settings->flag2 & GP_BRUSH_USE_UV_AT_STROKE) == 0) {
726  float rand = BLI_hash_int_01(BLI_hash_int_2d((int)pt->m_xy[0], gpd->runtime.sbuffer_used)) *
727  2.0f -
728  1.0f;
729  value = rand * M_PI_2 * brush_settings->uv_random;
730  }
731  else {
732  value = random_settings.uv * M_PI_2 * brush_settings->uv_random;
733  }
734 
735  /* Apply random curve. */
736  if (brush_settings->flag2 & GP_BRUSH_USE_UV_RAND_PRESS) {
738  brush_settings->curve_rand_uv, 0, random_settings.pen_press);
739  }
740 
741  pt->uv_rot += value;
742  CLAMP(pt->uv_rot, -M_PI_2, M_PI_2);
743  }
744 }
745 
746 /* add current stroke-point to buffer (returns whether point was successfully added) */
748  const float mval[2],
749  float pressure,
750  double curtime)
751 {
752  bGPdata *gpd = p->gpd;
753  Brush *brush = p->brush;
754  BrushGpencilSettings *brush_settings = p->brush->gpencil_settings;
755  tGPspoint *pt;
756  Object *obact = (Object *)p->ownerPtr.data;
757  RegionView3D *rv3d = p->region->regiondata;
758 
759  /* check painting mode */
761  /* straight lines only - i.e. only store start and end point in buffer */
762  if (gpd->runtime.sbuffer_used == 0) {
763  /* first point in buffer (start point) */
764  pt = (tGPspoint *)(gpd->runtime.sbuffer);
765 
766  /* store settings */
767  copy_v2_v2(pt->m_xy, mval);
768  /* T44932 - Pressure vals are unreliable, so ignore for now */
769  pt->pressure = 1.0f;
770  pt->strength = 1.0f;
771  pt->time = (float)(curtime - p->inittime);
772 
773  /* increment buffer size */
774  gpd->runtime.sbuffer_used++;
775  }
776  else {
777  /* just reset the endpoint to the latest value
778  * - assume that pointers for this are always valid...
779  */
780  pt = ((tGPspoint *)(gpd->runtime.sbuffer) + 1);
781 
782  /* store settings */
783  copy_v2_v2(pt->m_xy, mval);
784  /* T44932 - Pressure vals are unreliable, so ignore for now */
785  pt->pressure = 1.0f;
786  pt->strength = 1.0f;
787  pt->time = (float)(curtime - p->inittime);
788 
789  /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */
790  gpd->runtime.sbuffer_used = 2;
791  }
792 
793  /* can keep carrying on this way :) */
794  return GP_STROKEADD_NORMAL;
795  }
796 
797  if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */
798  /* check if still room in buffer or add more */
800  gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, false);
801 
802  /* Check the buffer was created. */
803  if (gpd->runtime.sbuffer == NULL) {
804  return GP_STROKEADD_INVALID;
805  }
806 
807  /* get pointer to destination point */
808  pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_used);
809 
810  /* store settings */
811  pt->strength = brush_settings->draw_strength;
812  pt->pressure = 1.0f;
813  pt->uv_rot = 0.0f;
814  copy_v2_v2(pt->m_xy, mval);
815 
816  /* pressure */
817  if (brush_settings->flag & GP_BRUSH_USE_PRESSURE) {
818  pt->pressure *= BKE_curvemapping_evaluateF(brush_settings->curve_sensitivity, 0, pressure);
819  }
820 
821  /* color strength */
822  if (brush_settings->flag & GP_BRUSH_USE_STRENGTH_PRESSURE) {
823  pt->strength *= BKE_curvemapping_evaluateF(brush_settings->curve_strength, 0, pressure);
824  CLAMP(pt->strength, MIN2(GPENCIL_STRENGTH_MIN, brush_settings->draw_strength), 1.0f);
825  }
826 
827  /* Set vertex colors for buffer. */
829  p->ob,
830  p->scene->toolsettings,
831  p->brush,
832  p->material,
833  p->random_settings.hsv,
835 
836  if (brush_settings->flag & GP_BRUSH_GROUP_RANDOM) {
837  /* Apply jitter to position */
838  if (brush_settings->draw_jitter > 0.0f) {
839  float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f;
840  float jitpress = 1.0f;
841  if (brush_settings->flag & GP_BRUSH_USE_JITTER_PRESSURE) {
842  jitpress = BKE_curvemapping_evaluateF(brush_settings->curve_jitter, 0, pressure);
843  }
844  /* FIXME the +2 means minimum jitter is 4 which is a bit strange for UX. */
845  const float exp_factor = brush_settings->draw_jitter + 2.0f;
846  const float fac = rand * square_f(exp_factor) * jitpress;
847  gpencil_brush_jitter(gpd, pt, fac);
848  }
849 
850  /* Apply other randomness. */
851  gpencil_apply_randomness(p, brush_settings, pt, true, true, true);
852  }
853 
854  /* apply angle of stroke to brush size */
855  if (brush_settings->draw_angle_factor != 0.0f) {
856  gpencil_brush_angle(gpd, brush, pt, mval);
857  }
858 
859  /* point time */
860  pt->time = (float)(curtime - p->inittime);
861 
862  /* point uv (only 3d view) */
863  if (gpd->runtime.sbuffer_used > 0) {
864  tGPspoint *ptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1;
865  bGPDspoint spt, spt2;
866 
867  /* get origin to reproject point */
868  float origin[3];
869  gpencil_get_3d_reference(p, origin);
870  /* reproject current */
871  ED_gpencil_tpoint_to_point(p->region, origin, pt, &spt);
873  p->scene, obact, p->gpl, rv3d, origin, p->lock_axis - 1, &spt);
874 
875  /* reproject previous */
876  ED_gpencil_tpoint_to_point(p->region, origin, ptb, &spt2);
878  p->scene, obact, p->gpl, rv3d, origin, p->lock_axis - 1, &spt2);
879  p->totpixlen += len_v3v3(&spt.x, &spt2.x);
880  pt->uv_fac = p->totpixlen;
881  }
882  else {
883  p->totpixlen = 0.0f;
884  pt->uv_fac = 0.0f;
885  }
886 
887  /* increment counters */
888  gpd->runtime.sbuffer_used++;
889 
890  /* Smooth while drawing previous points with a reduction factor for previous. */
891  if (brush->gpencil_settings->active_smooth > 0.0f) {
892  for (int s = 0; s < 3; s++) {
894  brush->gpencil_settings->active_smooth * ((3.0f - s) / 3.0f),
895  gpd->runtime.sbuffer_used - s);
896  }
897  }
898 
899  /* Update evaluated data. */
901 
902  return GP_STROKEADD_NORMAL;
903  }
904  /* return invalid state for now... */
905  return GP_STROKEADD_INVALID;
906 }
907 
909 {
910  gps->flag &= ~GP_STROKE_SELECT;
912  for (int i = 0; i < gps->totpoints; i++) {
913  gps->points[i].flag &= ~GP_SPOINT_SELECT;
914  }
915  /* Update the selection from the stroke to the curve. */
916  if (gps->editcurve) {
918  }
919 }
920 
921 /* make a new stroke from the buffer data */
923 {
924  bGPdata *gpd = p->gpd;
925  bGPDlayer *gpl = p->gpl;
926  bGPDstroke *gps;
927  bGPDspoint *pt;
928  tGPspoint *ptc;
929  MDeformVert *dvert = NULL;
930  Brush *brush = p->brush;
931  BrushGpencilSettings *brush_settings = brush->gpencil_settings;
932  ToolSettings *ts = p->scene->toolsettings;
934  Object *obact = (Object *)p->ownerPtr.data;
935  RegionView3D *rv3d = p->region->regiondata;
936  const int def_nr = gpd->vertex_group_active_index - 1;
937  const bool have_weight = (bool)BLI_findlink(&gpd->vertex_group_names, def_nr);
938  const char align_flag = ts->gpencil_v3d_align;
939  const bool is_depth = (bool)(align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE));
940  const bool is_lock_axis_view = (bool)(ts->gp_sculpt.lock_axis == 0);
941  const bool is_camera = is_lock_axis_view && (rv3d->persp == RV3D_CAMOB) && (!is_depth);
942  int totelem;
943 
944  /* For very low pressure at the end, truncate stroke. */
945  if (p->paintmode == GP_PAINTMODE_DRAW) {
946  int last_i = gpd->runtime.sbuffer_used - 1;
947  while (last_i > 0) {
948  ptc = (tGPspoint *)gpd->runtime.sbuffer + last_i;
949  if (ptc->pressure > 0.001f) {
950  break;
951  }
952  gpd->runtime.sbuffer_used = last_i - 1;
953  CLAMP_MIN(gpd->runtime.sbuffer_used, 1);
954 
955  last_i--;
956  }
957  }
958  /* Since strokes are so fine,
959  * when using their depth we need a margin otherwise they might get missed. */
960  int depth_margin = (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0;
961 
962  /* get total number of points to allocate space for
963  * - drawing straight-lines only requires the endpoints
964  */
966  totelem = (gpd->runtime.sbuffer_used >= 2) ? 2 : gpd->runtime.sbuffer_used;
967  }
968  else {
969  totelem = gpd->runtime.sbuffer_used;
970  }
971 
972  /* exit with error if no valid points from this stroke */
973  if (totelem == 0) {
974  return;
975  }
976 
977  /* allocate memory for a new stroke */
978  gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
979 
980  /* copy appropriate settings for stroke */
981  gps->totpoints = totelem;
982  gps->thickness = brush->size;
983  gps->fill_opacity_fac = 1.0f;
984  gps->hardeness = brush->gpencil_settings->hardeness;
986  gps->flag = gpd->runtime.sbuffer_sflag;
987  gps->inittime = p->inittime;
988  gps->uv_scale = 1.0f;
989 
990  /* Set stroke caps. */
991  gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type;
992 
993  /* allocate enough memory for a continuous array for storage points */
994  const int subdivide = brush->gpencil_settings->draw_subdivide;
995 
996  gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
997  gps->dvert = NULL;
998 
999  /* set pointer to first non-initialized point */
1000  pt = gps->points + (gps->totpoints - totelem);
1001  if (gps->dvert != NULL) {
1002  dvert = gps->dvert + (gps->totpoints - totelem);
1003  }
1004 
1005  /* Apply the vertex color to fill. */
1006  ED_gpencil_fill_vertex_color_set(ts, brush, gps);
1007 
1008  /* copy points from the buffer to the stroke */
1010  /* straight lines only -> only endpoints */
1011  {
1012  /* first point */
1013  ptc = gpd->runtime.sbuffer;
1014 
1015  /* convert screen-coordinates to appropriate coordinates (and store them) */
1016  gpencil_stroke_convertcoords(p, ptc->m_xy, &pt->x, NULL);
1017  /* copy pressure and time */
1018  pt->pressure = ptc->pressure;
1019  pt->strength = ptc->strength;
1020  CLAMP(pt->strength, MIN2(GPENCIL_STRENGTH_MIN, brush_settings->draw_strength), 1.0f);
1021  copy_v4_v4(pt->vert_color, ptc->vert_color);
1022  pt->time = ptc->time;
1023  /* Apply the vertex color to point. */
1024  ED_gpencil_point_vertex_color_set(ts, brush, pt, ptc);
1025 
1026  pt++;
1027 
1028  if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) {
1030  MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr);
1031  if (dw) {
1032  dw->weight = ts->vgroup_weight;
1033  }
1034  dvert++;
1035  }
1036  else {
1037  if (dvert != NULL) {
1038  dvert->totweight = 0;
1039  dvert->dw = NULL;
1040  dvert++;
1041  }
1042  }
1043  }
1044 
1045  if (totelem == 2) {
1046  /* last point if applicable */
1047  ptc = ((tGPspoint *)gpd->runtime.sbuffer) + (gpd->runtime.sbuffer_used - 1);
1048 
1049  /* convert screen-coordinates to appropriate coordinates (and store them) */
1050  gpencil_stroke_convertcoords(p, ptc->m_xy, &pt->x, NULL);
1051  /* copy pressure and time */
1052  pt->pressure = ptc->pressure;
1053  pt->strength = ptc->strength;
1054  CLAMP(pt->strength, MIN2(GPENCIL_STRENGTH_MIN, brush_settings->draw_strength), 1.0f);
1055  pt->time = ptc->time;
1056  /* Apply the vertex color to point. */
1057  ED_gpencil_point_vertex_color_set(ts, brush, pt, ptc);
1058 
1059  if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) {
1061  MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr);
1062  if (dw) {
1063  dw->weight = ts->vgroup_weight;
1064  }
1065  }
1066  else {
1067  if (dvert != NULL) {
1068  dvert->totweight = 0;
1069  dvert->dw = NULL;
1070  }
1071  }
1072  }
1073 
1074  /* reproject to plane (only in 3d space) */
1075  gpencil_reproject_toplane(p, gps);
1076  pt = gps->points;
1077  for (int i = 0; i < gps->totpoints; i++, pt++) {
1078  /* if parented change position relative to parent object */
1079  gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
1080  }
1081 
1082  /* If camera view or view projection, reproject flat to view to avoid perspective effect. */
1083  if ((!is_depth) &&
1084  (((align_flag & GP_PROJECT_VIEWSPACE) && is_lock_axis_view) || (is_camera))) {
1086  }
1087  }
1088  else {
1089  float *depth_arr = NULL;
1090 
1091  /* get an array of depths, far depths are blended */
1092  if (gpencil_project_check(p)) {
1093  int mval_i[2], mval_prev[2] = {0};
1094  int interp_depth = 0;
1095  int found_depth = 0;
1096 
1097  depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_used, "depth_points");
1098 
1099  const ViewDepths *depths = p->depths;
1100  int i;
1101  for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used; i++, ptc++, pt++) {
1102 
1103  round_v2i_v2fl(mval_i, ptc->m_xy);
1104 
1105  if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) &&
1107  depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) {
1108  interp_depth = true;
1109  }
1110  else {
1111  found_depth = true;
1112  }
1113 
1114  copy_v2_v2_int(mval_prev, mval_i);
1115  }
1116 
1117  if (found_depth == false) {
1118  /* Unfortunately there is not much we can do when the depth isn't found,
1119  * ignore depth in this case, use the 3D cursor. */
1120  for (i = gpd->runtime.sbuffer_used - 1; i >= 0; i--) {
1121  depth_arr[i] = 0.9999f;
1122  }
1123  }
1124  else {
1128  int first_valid = 0;
1129  int last_valid = 0;
1130 
1131  /* find first valid contact point */
1132  for (i = 0; i < gpd->runtime.sbuffer_used; i++) {
1133  if (depth_arr[i] != DEPTH_INVALID) {
1134  break;
1135  }
1136  }
1137  first_valid = i;
1138 
1139  /* find last valid contact point */
1141  last_valid = first_valid;
1142  }
1143  else {
1144  for (i = gpd->runtime.sbuffer_used - 1; i >= 0; i--) {
1145  if (depth_arr[i] != DEPTH_INVALID) {
1146  break;
1147  }
1148  }
1149  last_valid = i;
1150  }
1151  /* invalidate any other point, to interpolate between
1152  * first and last contact in an imaginary line between them */
1153  for (i = 0; i < gpd->runtime.sbuffer_used; i++) {
1154  if (!ELEM(i, first_valid, last_valid)) {
1155  depth_arr[i] = DEPTH_INVALID;
1156  }
1157  }
1158  interp_depth = true;
1159  }
1160 
1161  if (interp_depth) {
1163  }
1164  }
1165  }
1166 
1167  pt = gps->points;
1168  dvert = gps->dvert;
1169 
1170  /* convert all points (normal behavior) */
1171  int i;
1172  for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used && ptc;
1173  i++, ptc++, pt++) {
1174  /* convert screen-coordinates to appropriate coordinates (and store them) */
1175  gpencil_stroke_convertcoords(p, ptc->m_xy, &pt->x, depth_arr ? depth_arr + i : NULL);
1176 
1177  /* copy pressure and time */
1178  pt->pressure = ptc->pressure;
1179  pt->strength = ptc->strength;
1180  CLAMP(pt->strength, MIN2(GPENCIL_STRENGTH_MIN, brush_settings->draw_strength), 1.0f);
1181  copy_v4_v4(pt->vert_color, ptc->vert_color);
1182  pt->time = ptc->time;
1183  pt->uv_fac = ptc->uv_fac;
1184  pt->uv_rot = ptc->uv_rot;
1185  /* Apply the vertex color to point. */
1186  ED_gpencil_point_vertex_color_set(ts, brush, pt, ptc);
1187 
1188  if (dvert != NULL) {
1189  dvert->totweight = 0;
1190  dvert->dw = NULL;
1191  dvert++;
1192  }
1193  }
1194 
1195  /* subdivide and smooth the stroke */
1196  if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) {
1197  gpencil_subdivide_stroke(gpd, gps, subdivide);
1198  }
1199 
1200  /* Smooth stroke after subdiv - only if there's something to do for each iteration.
1201  * Keep the original stroke shape as much as possible. */
1202  const float smoothfac = brush->gpencil_settings->draw_smoothfac;
1203  const int iterations = brush->gpencil_settings->draw_smoothlvl;
1205  BKE_gpencil_stroke_smooth(gps, smoothfac, iterations, true, true, false, false, true, NULL);
1206  }
1207  /* If reproject the stroke using Stroke mode, need to apply a smooth because
1208  * the reprojection creates small jitter. */
1210  float ifac = (float)brush->gpencil_settings->input_samples / 10.0f;
1211  float sfac = interpf(1.0f, 0.2f, ifac);
1212  for (i = 0; i < gps->totpoints; i++) {
1213  BKE_gpencil_stroke_smooth_point(gps, i, sfac, 2, false, true, gps);
1214  BKE_gpencil_stroke_smooth_strength(gps, i, sfac, 2, gps);
1215  }
1216  }
1217 
1218  /* Simplify adaptive */
1219  if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
1220  (brush->gpencil_settings->simplify_f > 0.0f)) {
1222  }
1223 
1224  /* reproject to plane (only in 3d space) */
1225  gpencil_reproject_toplane(p, gps);
1226  /* change position relative to parent object */
1227  gpencil_apply_parent(depsgraph, obact, gpl, gps);
1228  /* If camera view or view projection, reproject flat to view to avoid perspective effect. */
1229  if ((!is_depth) && (((align_flag & GP_PROJECT_VIEWSPACE) && is_lock_axis_view) || is_camera)) {
1231  }
1232 
1233  if (depth_arr) {
1234  MEM_freeN(depth_arr);
1235  }
1236  }
1237 
1238  /* Save material index */
1240  if (gps->mat_nr < 0) {
1241  if (p->ob->actcol - 1 < 0) {
1242  gps->mat_nr = 0;
1243  }
1244  else {
1245  gps->mat_nr = p->ob->actcol - 1;
1246  }
1247  }
1248 
1249  /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke
1250  * is added on listbase head because the drawing order is inverse and the head stroke is the
1251  * first to draw. This is very useful for artist when drawing the background.
1252  */
1254  BLI_addhead(&p->gpf->strokes, gps);
1255  }
1256  else {
1257  BLI_addtail(&p->gpf->strokes, gps);
1258  }
1259  /* add weights */
1260  if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) {
1262  for (int i = 0; i < gps->totpoints; i++) {
1263  MDeformVert *ve = &gps->dvert[i];
1264  MDeformWeight *dw = BKE_defvert_ensure_index(ve, def_nr);
1265  if (dw) {
1266  dw->weight = ts->vgroup_weight;
1267  }
1268  }
1269  }
1270 
1271  /* post process stroke */
1274  BKE_gpencil_stroke_trim(gpd, gps);
1275  }
1276 
1277  /* Join with existing strokes. */
1279  if ((gps->prev != NULL) || (gps->next != NULL)) {
1281  float diff_mat[4][4], ctrl1[2], ctrl2[2];
1283  ED_gpencil_stroke_extremes_to2d(&p->gsc, diff_mat, gps, ctrl1, ctrl2);
1284 
1285  int pt_index = 0;
1286  bool doit = true;
1287  while (doit && gps) {
1289  &p->gsc,
1290  gpl,
1291  gpl->actframe,
1292  gps,
1293  ctrl1,
1294  ctrl2,
1296  &pt_index);
1297 
1298  if (gps_target != NULL) {
1299  /* Unselect all points of source and destination strokes. This is required to avoid
1300  * a change in the resolution of the original strokes during the join. */
1301  gpencil_stroke_unselect(gpd, gps);
1302  gpencil_stroke_unselect(gpd, gps_target);
1303  gps = ED_gpencil_stroke_join_and_trim(p->gpd, p->gpf, gps, gps_target, pt_index);
1304  }
1305  else {
1306  doit = false;
1307  }
1308  }
1309  }
1311  }
1312 
1313  /* Calc geometry data. */
1315 
1316  /* In multi-frame mode, duplicate the stroke in other frames. */
1318  const bool tail = (ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK);
1319  BKE_gpencil_stroke_copy_to_keyframes(gpd, gpl, p->gpf, gps, tail);
1320  }
1321 
1324  p->gpd, gpl, (GPENCIL_MULTIEDIT_SESSIONS_ON(p->gpd)) ? NULL : p->gpf, NULL);
1325 }
1326 
1327 /* --- 'Eraser' for 'Paint' Tool ------ */
1328 
1329 /* only erase stroke points that are visible */
1331  tGPsdata *p, bGPDlayer *gpl, bGPDspoint *pt, const int x, const int y)
1332 {
1333  Object *obact = (Object *)p->ownerPtr.data;
1334  Brush *brush = p->brush;
1335  Brush *eraser = p->eraser;
1336  BrushGpencilSettings *gp_settings = NULL;
1337 
1338  if (brush->gpencil_tool == GPAINT_TOOL_ERASE) {
1339  gp_settings = brush->gpencil_settings;
1340  }
1341  else if ((eraser != NULL) & (eraser->gpencil_tool == GPAINT_TOOL_ERASE)) {
1342  gp_settings = eraser->gpencil_settings;
1343  }
1344 
1345  if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) {
1346  RegionView3D *rv3d = p->region->regiondata;
1347 
1348  const int mval_i[2] = {x, y};
1349  float mval_3d[3];
1350  float fpt[3];
1351 
1352  float diff_mat[4][4];
1353  /* calculate difference matrix if parent object */
1354  BKE_gpencil_layer_transform_matrix_get(p->depsgraph, obact, gpl, diff_mat);
1355 
1356  float p_depth;
1357  if (ED_view3d_depth_read_cached(p->depths, mval_i, 0, &p_depth)) {
1358  ED_view3d_depth_unproject_v3(p->region, mval_i, (double)p_depth, mval_3d);
1359 
1360  const float depth_mval = ED_view3d_calc_depth_for_comparison(rv3d, mval_3d);
1361 
1362  mul_v3_m4v3(fpt, diff_mat, &pt->x);
1363  const float depth_pt = ED_view3d_calc_depth_for_comparison(rv3d, fpt);
1364 
1365  /* Checked occlusion flag. */
1366  pt->flag |= GP_SPOINT_TEMP_TAG;
1367  if (depth_pt > depth_mval) {
1368  /* Is occluded. */
1369  pt->flag |= GP_SPOINT_TEMP_TAG2;
1370  return true;
1371  }
1372  }
1373  }
1374  return false;
1375 }
1376 
1377 /* apply a falloff effect to brush strength, based on distance */
1379  const float mval[2],
1380  const int radius,
1381  const int co[2])
1382 {
1383  Brush *brush = p->brush;
1384  /* Linear Falloff... */
1385  int mval_i[2];
1386  round_v2i_v2fl(mval_i, mval);
1387  float distance = (float)len_v2v2_int(mval_i, co);
1388  float fac;
1389 
1390  CLAMP(distance, 0.0f, (float)radius);
1391  fac = 1.0f - (distance / (float)radius);
1392 
1393  /* apply strength factor */
1394  fac *= brush->gpencil_settings->draw_strength;
1395 
1396  /* Control this further using pen pressure */
1398  fac *= p->pressure;
1399  }
1400  /* Return influence factor computed here */
1401  return fac;
1402 }
1403 
1404 /* helper to free a stroke */
1405 static void gpencil_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps)
1406 {
1407  if (gps->points) {
1408  MEM_freeN(gps->points);
1409  }
1410 
1411  if (gps->dvert) {
1413  MEM_freeN(gps->dvert);
1414  }
1415 
1416  if (gps->triangles) {
1417  MEM_freeN(gps->triangles);
1418  }
1419  BLI_freelinkN(&gpf->strokes, gps);
1420  gpencil_update_cache(gpd);
1421 }
1422 
1429 {
1430  bGPDspoint *pt = NULL;
1431  bGPDspoint *pt2 = NULL;
1432  int i;
1433 
1434  /* Check if enough points. */
1435  if (gps->totpoints < 3) {
1436  return;
1437  }
1438 
1439  /* loop all points to untag any point that next is not tagged */
1440  pt = gps->points;
1441  for (i = 1; i < gps->totpoints - 1; i++, pt++) {
1442  if (pt->flag & GP_SPOINT_TAG) {
1443  pt2 = &gps->points[i + 1];
1444  if ((pt2->flag & GP_SPOINT_TAG) == 0) {
1445  pt->flag &= ~GP_SPOINT_TAG;
1446  }
1447  }
1448  }
1449 
1450  /* loop reverse all points to untag any point that previous is not tagged */
1451  pt = &gps->points[gps->totpoints - 1];
1452  for (i = gps->totpoints - 1; i > 0; i--, pt--) {
1453  if (pt->flag & GP_SPOINT_TAG) {
1454  pt2 = &gps->points[i - 1];
1455  if ((pt2->flag & GP_SPOINT_TAG) == 0) {
1456  pt->flag &= ~GP_SPOINT_TAG;
1457  }
1458  }
1459  }
1460 }
1461 
1462 /* eraser tool - evaluation per stroke */
1464  bGPDlayer *gpl,
1465  bGPDframe *gpf,
1466  bGPDstroke *gps,
1467  const float mval[2],
1468  const int radius,
1469  const rcti *rect)
1470 {
1471  Brush *eraser = p->eraser;
1472  bGPDspoint *pt0, *pt1, *pt2;
1473  int pc0[2] = {0};
1474  int pc1[2] = {0};
1475  int pc2[2] = {0};
1476  int i;
1477  int mval_i[2];
1478  round_v2i_v2fl(mval_i, mval);
1479 
1480  if (gps->totpoints == 0) {
1481  /* just free stroke */
1482  gpencil_free_stroke(p->gpd, gpf, gps);
1483  }
1484  else if (gps->totpoints == 1) {
1485  /* only process if it hasn't been masked out... */
1486  if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) {
1487  bGPDspoint pt_temp;
1488  gpencil_point_to_parent_space(gps->points, p->diff_mat, &pt_temp);
1489  gpencil_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
1490  /* Do bound-box check first. */
1491  if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
1492  /* only check if point is inside */
1493  if (len_v2v2_int(mval_i, pc1) <= radius) {
1494  /* free stroke */
1495  gpencil_free_stroke(p->gpd, gpf, gps);
1496  }
1497  }
1498  }
1499  }
1500  else if ((p->flags & GP_PAINTFLAG_STROKE_ERASER) ||
1502  for (i = 0; (i + 1) < gps->totpoints; i++) {
1503 
1504  /* only process if it hasn't been masked out... */
1505  if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) {
1506  continue;
1507  }
1508 
1509  /* get points to work with */
1510  pt1 = gps->points + i;
1511  bGPDspoint npt;
1512  gpencil_point_to_parent_space(pt1, p->diff_mat, &npt);
1513  gpencil_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
1514 
1515  /* Do bound-box check first. */
1516  if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
1517  /* only check if point is inside */
1518  if (len_v2v2_int(mval_i, pc1) <= radius) {
1519  /* free stroke */
1520  gpencil_free_stroke(p->gpd, gpf, gps);
1521  return;
1522  }
1523  }
1524  }
1525  }
1526  else {
1527  /* Pressure threshold at which stroke should be culled */
1528  const float cull_thresh = 0.005f;
1529 
1530  /* Amount to decrease the pressure of each point with each stroke */
1531  const float strength = 0.1f;
1532 
1533  /* Perform culling? */
1534  bool do_cull = false;
1535 
1536  /* Clear Tags
1537  *
1538  * NOTE: It's better this way, as we are sure that
1539  * we don't miss anything, though things will be
1540  * slightly slower as a result
1541  */
1542  for (i = 0; i < gps->totpoints; i++) {
1543  bGPDspoint *pt = &gps->points[i];
1544  pt->flag &= ~GP_SPOINT_TAG;
1545  /* Occlusion already checked. */
1546  pt->flag &= ~GP_SPOINT_TEMP_TAG;
1547  /* Point is occluded. */
1548  pt->flag &= ~GP_SPOINT_TEMP_TAG2;
1549  }
1550 
1551  /* First Pass: Loop over the points in the stroke
1552  * 1) Thin out parts of the stroke under the brush
1553  * 2) Tag "too thin" parts for removal (in second pass)
1554  */
1555  for (i = 0; (i + 1) < gps->totpoints; i++) {
1556  /* get points to work with */
1557  pt0 = i > 0 ? gps->points + i - 1 : NULL;
1558  pt1 = gps->points + i;
1559  pt2 = gps->points + i + 1;
1560 
1561  float inf1 = 0.0f;
1562  float inf2 = 0.0f;
1563 
1564  /* only process if it hasn't been masked out... */
1565  if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) {
1566  continue;
1567  }
1568 
1569  bGPDspoint npt;
1570  gpencil_point_to_parent_space(pt1, p->diff_mat, &npt);
1571  gpencil_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
1572 
1573  gpencil_point_to_parent_space(pt2, p->diff_mat, &npt);
1574  gpencil_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]);
1575 
1576  if (pt0) {
1577  gpencil_point_to_parent_space(pt0, p->diff_mat, &npt);
1578  gpencil_point_to_xy(&p->gsc, gps, &npt, &pc0[0], &pc0[1]);
1579  }
1580  else {
1581  /* avoid null values */
1582  copy_v2_v2_int(pc0, pc1);
1583  }
1584 
1585  /* Check that point segment of the bound-box of the eraser stroke. */
1586  if (((!ELEM(V2D_IS_CLIPPED, pc0[0], pc0[1])) && BLI_rcti_isect_pt(rect, pc0[0], pc0[1])) ||
1587  ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
1588  ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) {
1589  /* Check if point segment of stroke had anything to do with
1590  * eraser region (either within stroke painted, or on its lines)
1591  * - this assumes that line-width is irrelevant.
1592  */
1593  if (gpencil_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) {
1594 
1595  bool is_occluded_pt0 = true, is_occluded_pt1 = true, is_occluded_pt2 = true;
1596  if (pt0) {
1597  is_occluded_pt0 = ((pt0->flag & GP_SPOINT_TEMP_TAG) != 0) ?
1598  ((pt0->flag & GP_SPOINT_TEMP_TAG2) != 0) :
1599  gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]);
1600  }
1601  if (is_occluded_pt0) {
1602  is_occluded_pt1 = ((pt1->flag & GP_SPOINT_TEMP_TAG) != 0) ?
1603  ((pt1->flag & GP_SPOINT_TEMP_TAG2) != 0) :
1604  gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]);
1605  if (is_occluded_pt1) {
1606  is_occluded_pt2 = ((pt2->flag & GP_SPOINT_TEMP_TAG) != 0) ?
1607  ((pt2->flag & GP_SPOINT_TEMP_TAG2) != 0) :
1608  gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]);
1609  }
1610  }
1611 
1612  if (!is_occluded_pt0 || !is_occluded_pt1 || !is_occluded_pt2) {
1613  /* Point is affected: */
1614  /* Adjust thickness
1615  * - Influence of eraser falls off with distance from the middle of the eraser
1616  * - Second point gets less influence, as it might get hit again in the next segment
1617  */
1618 
1619  /* Adjust strength if the eraser is soft */
1621  float f_strength = eraser->gpencil_settings->era_strength_f / 100.0f;
1622  float f_thickness = eraser->gpencil_settings->era_thickness_f / 100.0f;
1623  float influence = 0.0f;
1624 
1625  if (pt0) {
1626  influence = gpencil_stroke_eraser_calc_influence(p, mval, radius, pc0);
1627  pt0->strength -= influence * strength * f_strength * 0.5f;
1628  CLAMP_MIN(pt0->strength, 0.0f);
1629  pt0->pressure -= influence * strength * f_thickness * 0.5f;
1630  }
1631 
1632  influence = gpencil_stroke_eraser_calc_influence(p, mval, radius, pc1);
1633  pt1->strength -= influence * strength * f_strength;
1634  CLAMP_MIN(pt1->strength, 0.0f);
1635  pt1->pressure -= influence * strength * f_thickness;
1636 
1637  influence = gpencil_stroke_eraser_calc_influence(p, mval, radius, pc2);
1638  pt2->strength -= influence * strength * f_strength * 0.5f;
1639  CLAMP_MIN(pt2->strength, 0.0f);
1640  pt2->pressure -= influence * strength * f_thickness * 0.5f;
1641 
1642  /* if invisible, delete point */
1643  if ((pt0) && ((pt0->strength <= GPENCIL_ALPHA_OPACITY_THRESH) ||
1644  (pt0->pressure < cull_thresh))) {
1645  pt0->flag |= GP_SPOINT_TAG;
1646  do_cull = true;
1647  }
1648  if ((pt1->strength <= GPENCIL_ALPHA_OPACITY_THRESH) ||
1649  (pt1->pressure < cull_thresh)) {
1650  pt1->flag |= GP_SPOINT_TAG;
1651  do_cull = true;
1652  }
1653  if ((pt2->strength <= GPENCIL_ALPHA_OPACITY_THRESH) ||
1654  (pt2->pressure < cull_thresh)) {
1655  pt2->flag |= GP_SPOINT_TAG;
1656  do_cull = true;
1657  }
1658 
1659  inf1 = 1.0f;
1660  inf2 = 1.0f;
1661  }
1662  else {
1663  /* Erase point. Only erase if the eraser is on top of the point. */
1664  inf1 = gpencil_stroke_eraser_calc_influence(p, mval, radius, pc1);
1665  if (inf1 > 0.0f) {
1666  pt1->pressure = 0.0f;
1667  pt1->flag |= GP_SPOINT_TAG;
1668  do_cull = true;
1669  }
1670  inf2 = gpencil_stroke_eraser_calc_influence(p, mval, radius, pc2);
1671  if (inf2 > 0.0f) {
1672  pt2->pressure = 0.0f;
1673  pt2->flag |= GP_SPOINT_TAG;
1674  do_cull = true;
1675  }
1676  }
1677 
1678  /* 2) Tag any point with overly low influence for removal in the next pass */
1679  if ((inf1 > 0.0f) &&
1680  (((pt1->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) ||
1682  pt1->flag |= GP_SPOINT_TAG;
1683  do_cull = true;
1684  }
1685  if ((inf1 > 2.0f) &&
1686  (((pt2->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) ||
1688  pt2->flag |= GP_SPOINT_TAG;
1689  do_cull = true;
1690  }
1691  }
1692  }
1693  }
1694  }
1695 
1696  /* Second Pass: Remove any points that are tagged */
1697  if (do_cull) {
1698  /* if soft eraser, must analyze points to be sure the stroke ends
1699  * don't get rounded */
1702  }
1703 
1705  p->gpd, gpf, gps, gps->next, GP_SPOINT_TAG, false, false, 0);
1706  }
1708  }
1709 }
1710 
1711 /* erase strokes which fall under the eraser strokes */
1713 {
1714  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(p->gpd);
1715 
1716  rcti rect;
1717  Brush *brush = p->brush;
1718  Brush *eraser = p->eraser;
1719  bool use_pressure = false;
1720  float press = 1.0f;
1721  BrushGpencilSettings *gp_settings = NULL;
1722 
1723  /* detect if use pressure in eraser */
1724  if (brush->gpencil_tool == GPAINT_TOOL_ERASE) {
1725  use_pressure = (bool)(brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE);
1726  gp_settings = brush->gpencil_settings;
1727  }
1728  else if ((eraser != NULL) & (eraser->gpencil_tool == GPAINT_TOOL_ERASE)) {
1729  use_pressure = (bool)(eraser->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE);
1730  gp_settings = eraser->gpencil_settings;
1731  }
1732  if (use_pressure) {
1733  press = p->pressure;
1734  CLAMP(press, 0.01f, 1.0f);
1735  }
1736  /* rect is rectangle of eraser */
1737  const int calc_radius = (int)p->radius * press;
1738  rect.xmin = p->mval[0] - calc_radius;
1739  rect.ymin = p->mval[1] - calc_radius;
1740  rect.xmax = p->mval[0] + calc_radius;
1741  rect.ymax = p->mval[1] + calc_radius;
1742 
1743  if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) {
1744  View3D *v3d = p->area->spacedata.first;
1747  }
1748 
1749  /* loop over all layers too, since while it's easy to restrict editing to
1750  * only a subset of layers, it is harder to perform the same erase operation
1751  * on multiple layers...
1752  */
1753  LISTBASE_FOREACH (bGPDlayer *, gpl, &p->gpd->layers) {
1754  /* only affect layer if it's editable (and visible) */
1755  if (BKE_gpencil_layer_is_editable(gpl) == false) {
1756  continue;
1757  }
1758 
1759  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
1760  if (init_gpf == NULL) {
1761  continue;
1762  }
1763 
1764  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1765  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1766  if (gpf == NULL) {
1767  continue;
1768  }
1769  /* calculate difference matrix */
1771 
1772  /* loop over strokes, checking segments for intersections */
1773  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
1774  /* check if the color is editable */
1775  if (ED_gpencil_stroke_material_editable(p->ob, gpl, gps) == false) {
1776  continue;
1777  }
1778 
1779  /* Check if the stroke collide with mouse. */
1781  &p->gsc, gps, p->mval, calc_radius, p->diff_mat)) {
1782  continue;
1783  }
1784 
1785  /* Not all strokes in the datablock may be valid in the current editor/context
1786  * (e.g. 2D space strokes in the 3D view, if the same datablock is shared)
1787  */
1788  if (ED_gpencil_stroke_can_use_direct(p->area, gps)) {
1789  gpencil_stroke_eraser_dostroke(p, gpl, gpf, gps, p->mval, calc_radius, &rect);
1790  }
1791  }
1792 
1793  /* If not multi-edit, exit loop. */
1794  if (!is_multiedit) {
1795  break;
1796  }
1797  }
1798  }
1799  }
1800 }
1801 /* ******************************************* */
1802 /* Sketching Operator */
1803 
1804 /* clear the session buffers (call this before AND after a paint operation) */
1806 {
1807  bGPdata *gpd = p->gpd;
1808  Brush *brush = p->brush;
1809 
1810  /* clear memory of buffer (or allocate it if starting a new session) */
1812  gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, true);
1813 
1814  /* reset flags */
1815  gpd->runtime.sbuffer_sflag = 0;
1816 
1817  /* reset inittime */
1818  p->inittime = 0.0;
1819 
1820  /* reset lazy */
1821  if (brush) {
1823  }
1824 }
1825 
1826 /* helper to get default eraser and create one if no eraser brush */
1828 {
1829  Brush *brush_dft = NULL;
1830  Paint *paint = &ts->gp_paint->paint;
1831  Brush *brush_prev = paint->brush;
1832  for (Brush *brush = bmain->brushes.first; brush; brush = brush->id.next) {
1833  if (brush->gpencil_settings == NULL) {
1834  continue;
1835  }
1836  if ((brush->ob_mode == OB_MODE_PAINT_GPENCIL) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) {
1837  /* save first eraser to use later if no default */
1838  if (brush_dft == NULL) {
1839  brush_dft = brush;
1840  }
1841  /* found default */
1842  if (brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) {
1843  return brush;
1844  }
1845  }
1846  }
1847  /* if no default, but exist eraser brush, return this and set as default */
1848  if (brush_dft) {
1850  return brush_dft;
1851  }
1852  /* create a new soft eraser brush */
1853 
1854  brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser", OB_MODE_PAINT_GPENCIL);
1855  brush_dft->size = 30.0f;
1858  brush_dft->gpencil_tool = GPAINT_TOOL_ERASE;
1860 
1861  /* reset current brush */
1862  BKE_paint_brush_set(paint, brush_prev);
1863 
1864  return brush_dft;
1865 }
1866 
1867 /* helper to set default eraser and disable others */
1868 static void gpencil_set_default_eraser(Main *bmain, Brush *brush_dft)
1869 {
1870  if (brush_dft == NULL) {
1871  return;
1872  }
1873 
1874  for (Brush *brush = bmain->brushes.first; brush; brush = brush->id.next) {
1875  if ((brush->gpencil_settings) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) {
1876  if (brush == brush_dft) {
1877  brush->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER;
1878  }
1879  else if (brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) {
1880  brush->gpencil_settings->flag &= ~GP_BRUSH_DEFAULT_ERASER;
1881  }
1882  }
1883  }
1884 }
1885 
1886 /* initialize a drawing brush */
1888 {
1889  Main *bmain = CTX_data_main(C);
1892 
1893  Paint *paint = &ts->gp_paint->paint;
1894  bool changed = false;
1895  /* if not exist, create a new one */
1896  if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) {
1897  /* create new brushes */
1898  BKE_brush_gpencil_paint_presets(bmain, ts, true);
1899  changed = true;
1900  }
1901  /* Be sure curves are initialized. */
1911 
1912  /* Assign to temp #tGPsdata */
1913  p->brush = paint->brush;
1914  if (paint->brush->gpencil_tool != GPAINT_TOOL_ERASE) {
1916  }
1917  else {
1918  p->eraser = paint->brush;
1919  }
1920  /* set new eraser as default */
1922 
1923  /* use radius of eraser */
1924  p->radius = (short)p->eraser->size;
1925 
1926  /* Need this update to synchronize brush with draw manager. */
1927  if (changed) {
1929  }
1930 }
1931 
1932 /* initialize a paint brush and a default color if not exist */
1934 {
1935  bGPdata *gpd = p->gpd;
1936  Brush *brush = p->brush;
1937 
1938  /* use brush material */
1940 
1942  gpd->runtime.sbuffer_brush = brush;
1943 }
1944 
1945 /* (re)init new painting data */
1947 {
1948  Main *bmain = CTX_data_main(C);
1949  bGPdata **gpd_ptr = NULL;
1950  ScrArea *curarea = CTX_wm_area(C);
1951  ARegion *region = CTX_wm_region(C);
1953  Object *obact = CTX_data_active_object(C);
1954 
1955  /* make sure the active view (at the starting time) is a 3d-view */
1956  if (curarea == NULL) {
1957  p->status = GP_STATUS_ERROR;
1958  return 0;
1959  }
1960 
1961  /* pass on current scene and window */
1962  p->C = C;
1963  p->bmain = CTX_data_main(C);
1964  p->scene = CTX_data_scene(C);
1966  p->win = CTX_wm_window(C);
1967  p->disable_fill = RNA_boolean_get(op->ptr, "disable_fill");
1968  p->disable_stabilizer = RNA_boolean_get(op->ptr, "disable_stabilizer");
1969 
1970  unit_m4(p->imat);
1971  unit_m4(p->mat);
1972 
1973  /* set current area
1974  * - must verify that region data is 3D-view (and not something else)
1975  */
1976  /* CAUTION: If this is the "toolbar", then this will change on the first stroke */
1977  p->area = curarea;
1978  p->region = region;
1979  p->align_flag = &ts->gpencil_v3d_align;
1980 
1981  if (region->regiondata == NULL) {
1982  p->status = GP_STATUS_ERROR;
1983  return 0;
1984  }
1985 
1986  if ((!obact) || (obact->type != OB_GPENCIL)) {
1987  View3D *v3d = p->area->spacedata.first;
1988  /* if active object doesn't exist or isn't a GP Object, create one */
1989  const float *cur = p->scene->cursor.location;
1990 
1991  ushort local_view_bits = 0;
1992  if (v3d->localvd) {
1993  local_view_bits = v3d->local_view_uuid;
1994  }
1995  /* create new default object */
1996  obact = ED_gpencil_add_object(C, cur, local_view_bits);
1997  }
1998  /* assign object after all checks to be sure we have one active */
1999  p->ob = obact;
2001 
2002  /* get gp-data */
2003  gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr);
2004  if ((gpd_ptr == NULL) || ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) {
2005  p->status = GP_STATUS_ERROR;
2006  return 0;
2007  }
2008 
2009  /* if no existing GPencil block exists, add one */
2010  if (*gpd_ptr == NULL) {
2011  *gpd_ptr = BKE_gpencil_data_addnew(bmain, "GPencil");
2012  }
2013  p->gpd = *gpd_ptr;
2014 
2015  /* clear out buffer (stored in gp-data), in case something contaminated it */
2017 
2018  /* set brush and create a new one if null */
2020 
2021  /* setup active color */
2022  /* region where paint was originated */
2023  int totcol = p->ob->totcol;
2025 
2026  /* check whether the material was newly added */
2027  if (totcol != p->ob->totcol) {
2029  }
2030 
2031  /* lock axis (in some modes, disable) */
2032  if (((*p->align_flag & GP_PROJECT_DEPTH_VIEW) == 0) &&
2033  ((*p->align_flag & GP_PROJECT_DEPTH_STROKE) == 0)) {
2034  p->lock_axis = ts->gp_sculpt.lock_axis;
2035  }
2036  else {
2037  p->lock_axis = 0;
2038  }
2039 
2040  return 1;
2041 }
2042 
2043 /* init new painting session */
2045 {
2046  tGPsdata *p = NULL;
2047 
2048  /* Create new context data */
2049  p = MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data");
2050 
2051  /* Try to initialize context data
2052  * WARNING: This may not always succeed (e.g. using GP in an annotation-only context)
2053  */
2054  if (gpencil_session_initdata(C, op, p) == 0) {
2055  /* Invalid state - Exit
2056  * NOTE: It should be safe to just free the data, since failing context checks should
2057  * only happen when no data has been allocated.
2058  */
2059  MEM_freeN(p);
2060  return NULL;
2061  }
2062 
2063  /* Random generator, only init once. */
2064  uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
2065  rng_seed ^= POINTER_AS_UINT(p);
2066  p->rng = BLI_rng_new(rng_seed);
2067 
2068  /* return context data for running paint operator */
2069  return p;
2070 }
2071 
2072 /* cleanup after a painting session */
2074 {
2075  bGPdata *gpd = (p) ? p->gpd : NULL;
2076 
2077  /* error checking */
2078  if (gpd == NULL) {
2079  return;
2080  }
2081 
2082  /* free stroke buffer */
2083  if (gpd->runtime.sbuffer) {
2085  gpd->runtime.sbuffer = NULL;
2086  }
2087 
2088  /* clear flags */
2089  gpd->runtime.sbuffer_used = 0;
2090  gpd->runtime.sbuffer_size = 0;
2091  gpd->runtime.sbuffer_sflag = 0;
2092  /* This update is required for update-on-write because the sbuffer data is not longer overwritten
2093  * by a copy-on-write. */
2095  p->inittime = 0.0;
2096 }
2097 
2099 {
2100  if (p->rng != NULL) {
2101  BLI_rng_free(p->rng);
2102  }
2103  if (p->depths != NULL) {
2105  }
2106 
2107  MEM_freeN(p);
2108 }
2109 
2110 /* init new stroke */
2112  eGPencil_PaintModes paintmode,
2114 {
2115  Scene *scene = p->scene;
2117  bool changed = false;
2118 
2119  /* get active layer (or add a new one if non-existent) */
2121  if (p->gpl == NULL) {
2122  p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true, false);
2124  changed = true;
2125  if (p->custom_color[3]) {
2126  copy_v3_v3(p->gpl->color, p->custom_color);
2127  }
2128  }
2129 
2130  /* Recalculate layer transform matrix to avoid problems if props are animated. */
2133 
2134  if ((paintmode != GP_PAINTMODE_ERASER) && (p->gpl->flag & GP_LAYER_LOCKED)) {
2135  p->status = GP_STATUS_ERROR;
2136  return;
2137  }
2138 
2139  /* Eraser mode: If no active strokes, add one or just return. */
2140  if (paintmode == GP_PAINTMODE_ERASER) {
2141  /* Eraser mode:
2142  * 1) Add new frames to all frames that we might touch,
2143  * 2) Ensure that p->gpf refers to the frame used for the active layer
2144  * (to avoid problems with other tools which expect it to exist)
2145  *
2146  * This is done only if additive drawing is enabled.
2147  */
2148  bool has_layer_to_erase = false;
2149 
2150  LISTBASE_FOREACH (bGPDlayer *, gpl, &p->gpd->layers) {
2151  /* Skip if layer not editable */
2152  if (BKE_gpencil_layer_is_editable(gpl) == false) {
2153  continue;
2154  }
2155 
2156  if (!IS_AUTOKEY_ON(scene) && (gpl->actframe == NULL)) {
2157  continue;
2158  }
2159 
2160  /* Add a new frame if needed (and based off the active frame,
2161  * as we need some existing strokes to erase)
2162  *
2163  * NOTE: We don't add a new frame if there's nothing there now, so
2164  * -> If there are no frames at all, don't add one
2165  * -> If there are no strokes in that frame, don't add a new empty frame
2166  */
2167  if (gpl->actframe && gpl->actframe->strokes.first) {
2170  gpl->actframe = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, frame_mode);
2171  }
2172  has_layer_to_erase = true;
2173  break;
2174  }
2175  }
2176 
2177  /* Ensure this gets set. */
2179  p->gpf = p->gpl->actframe;
2180  }
2181 
2182  if (has_layer_to_erase == false) {
2183  p->status = GP_STATUS_ERROR;
2184  return;
2185  }
2186  /* Ensure this gets set... */
2187  p->gpf = p->gpl->actframe;
2188  }
2189  else {
2190  /* Drawing Modes - Add a new frame if needed on the active layer */
2191  short add_frame_mode;
2192 
2193  if (IS_AUTOKEY_ON(scene)) {
2195  add_frame_mode = GP_GETFRAME_ADD_COPY;
2196  }
2197  else {
2198  add_frame_mode = GP_GETFRAME_ADD_NEW;
2199  }
2200  }
2201  else {
2202  add_frame_mode = GP_GETFRAME_USE_PREV;
2203  }
2204 
2205  bool need_tag = p->gpl->actframe == NULL;
2206  bGPDframe *actframe = p->gpl->actframe;
2207 
2208  p->gpf = BKE_gpencil_layer_frame_get(p->gpl, scene->r.cfra, add_frame_mode);
2209  /* Only if there wasn't an active frame, need update. */
2210  if (need_tag) {
2212  }
2213  if (actframe != p->gpl->actframe) {
2215  }
2216 
2217  if (p->gpf == NULL) {
2218  p->status = GP_STATUS_ERROR;
2219  if (!IS_AUTOKEY_ON(scene)) {
2220  BKE_report(p->reports, RPT_INFO, "No available frame for creating stroke");
2221  }
2222 
2223  return;
2224  }
2225  p->gpf->flag |= GP_FRAME_PAINT;
2226  }
2227 
2228  /* set 'eraser' for this stroke if using eraser */
2229  p->paintmode = paintmode;
2230  if (p->paintmode == GP_PAINTMODE_ERASER) {
2232  }
2233  else {
2234  /* disable eraser flags - so that we can switch modes during a session */
2236  }
2237 
2238  /* set special fill stroke mode */
2239  if (p->disable_fill == true) {
2241  }
2242 
2243  /* set 'initial run' flag, which is only used to denote when a new stroke is starting */
2245 
2246  /* when drawing in the camera view, in 2D space, set the subrect */
2247  p->subrect = NULL;
2248  if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) {
2249  View3D *v3d = p->area->spacedata.first;
2250  RegionView3D *rv3d = p->region->regiondata;
2251 
2252  /* for camera view set the subrect */
2253  if (rv3d->persp == RV3D_CAMOB) {
2254  /* no shift */
2256  p->scene, depsgraph, p->region, v3d, rv3d, &p->subrect_data, true);
2257  p->subrect = &p->subrect_data;
2258  }
2259  }
2260 
2261  /* init stroke point space-conversion settings... */
2262  p->gsc.gpd = p->gpd;
2263  p->gsc.gpl = p->gpl;
2264 
2265  p->gsc.area = p->area;
2266  p->gsc.region = p->region;
2267  p->gsc.v2d = p->v2d;
2268 
2269  p->gsc.subrect_data = p->subrect_data;
2270  p->gsc.subrect = p->subrect;
2271 
2272  copy_m4_m4(p->gsc.mat, p->mat);
2273 
2274  /* check if points will need to be made in view-aligned space */
2275  if (*p->align_flag & GP_PROJECT_VIEWSPACE) {
2277  }
2278  if (!changed) {
2279  /* Copy the brush to avoid a full tag (very slow). */
2280  bGPdata *gpd_eval = (bGPdata *)p->ob_eval->data;
2281  gpd_eval->runtime.sbuffer_brush = p->gpd->runtime.sbuffer_brush;
2282  }
2283  else {
2285  }
2286 }
2287 
2288 /* finish off a stroke (clears buffer, but doesn't finish the paint operation) */
2290 {
2291  ToolSettings *ts = p->scene->toolsettings;
2292  const bool is_eraser = (p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) != 0;
2293  /* for surface sketching, need to set the right OpenGL context stuff so
2294  * that the conversions will project the values correctly...
2295  */
2296  if (gpencil_project_check(p)) {
2297  View3D *v3d = p->area->spacedata.first;
2298 
2299  /* need to restore the original projection settings before packing up */
2302  p->region,
2303  v3d,
2304  NULL,
2308  is_eraser ? NULL : &p->depths);
2309  }
2310 
2311  /* check if doing eraser or not */
2312  if (!is_eraser) {
2313  /* transfer stroke to frame */
2315  }
2316 
2317  /* clean up buffer now */
2319 }
2320 
2321 /* finish off stroke painting operation */
2323 {
2324  /* p->gpd==NULL happens when stroke failed to initialize,
2325  * for example when GP is hidden in current space (sergey)
2326  */
2327  if (p->gpd) {
2328  /* finish off a stroke */
2330  }
2331 
2332  /* "unlock" frame */
2333  if (p->gpf) {
2334  p->gpf->flag &= ~GP_FRAME_PAINT;
2335  }
2336 }
2337 /* ------------------------------- */
2338 
2339 /* Helper callback for drawing the cursor itself */
2340 static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr)
2341 {
2342  tGPsdata *p = (tGPsdata *)p_ptr;
2343 
2344  if (p->paintmode == GP_PAINTMODE_ERASER) {
2346  const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
2348 
2349  GPU_line_smooth(true);
2351 
2352  immUniformColor4ub(255, 100, 100, 20);
2353  imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40);
2354 
2355  immUnbindProgram();
2356 
2358 
2359  float viewport_size[4];
2360  GPU_viewport_size_get_f(viewport_size);
2361  immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
2362 
2363  immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f);
2364  immUniform1i("colors_len", 0); /* "simple" mode */
2365  immUniform1f("dash_width", 12.0f);
2366  immUniform1f("dash_factor", 0.5f);
2367 
2368  imm_draw_circle_wire_2d(shdr_pos,
2369  x,
2370  y,
2371  p->radius,
2372  /* XXX Dashed shader gives bad results with sets of small segments
2373  * currently, temp hack around the issue. :( */
2374  max_ii(8, p->radius / 2)); /* was fixed 40 */
2375 
2376  immUnbindProgram();
2377 
2379  GPU_line_smooth(false);
2380  }
2381 }
2382 
2383 /* Turn brush cursor in 3D view on/off */
2384 static void gpencil_draw_toggle_eraser_cursor(tGPsdata *p, short enable)
2385 {
2386  if (p->erasercursor && !enable) {
2387  /* clear cursor */
2389  p->erasercursor = NULL;
2390  }
2391  else if (enable && !p->erasercursor) {
2393  /* enable cursor */
2395  RGN_TYPE_ANY,
2396  NULL, /* XXX */
2398  p);
2399  }
2400 }
2401 
2402 /* Check if tablet eraser is being used (when processing events) */
2403 static bool gpencil_is_tablet_eraser_active(const wmEvent *event)
2404 {
2405  return (event->tablet.active == EVT_TABLET_ERASER);
2406 }
2407 
2408 /* ------------------------------- */
2409 
2411 {
2412  tGPsdata *p = op->customdata;
2413 
2414  /* don't assume that operator data exists at all */
2415  if (p) {
2416  /* check size of buffer before cleanup, to determine if anything happened here */
2417  if (p->paintmode == GP_PAINTMODE_ERASER) {
2418  /* turn off radial brush cursor */
2420  }
2421 
2422  /* always store the new eraser size to be used again next time
2423  * NOTE: Do this even when not in eraser mode, as eraser may
2424  * have been toggled at some point.
2425  */
2426  if (p->eraser) {
2427  p->eraser->size = p->radius;
2428  }
2429 
2430  /* drawing batch cache is dirty now */
2432  gpencil_update_cache(gpd);
2433 
2434  /* clear undo stack */
2436 
2437  /* cleanup */
2441 
2442  /* finally, free the temp data */
2444  p = NULL;
2445  }
2446 
2447  op->customdata = NULL;
2448 }
2449 
2451 {
2452  /* this is just a wrapper around exit() */
2453  gpencil_draw_exit(C, op);
2454 }
2455 
2456 /* ------------------------------- */
2457 
2458 static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
2459 {
2460  tGPsdata *p;
2461  eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode");
2463  Brush *brush = BKE_paint_brush(&ts->gp_paint->paint);
2464 
2465  /* if mode is draw and the brush is eraser, cancel */
2466  if (paintmode != GP_PAINTMODE_ERASER) {
2467  if ((brush) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) {
2468  return 0;
2469  }
2470  }
2471 
2472  /* check context */
2473  p = op->customdata = gpencil_session_initpaint(C, op);
2474  if ((p == NULL) || (p->status == GP_STATUS_ERROR)) {
2475  /* something wasn't set correctly in context */
2476  gpencil_draw_exit(C, op);
2477  return 0;
2478  }
2479 
2480  p->reports = op->reports;
2481 
2482  /* init painting data */
2484  if (p->status == GP_STATUS_ERROR) {
2485  gpencil_draw_exit(C, op);
2486  return 0;
2487  }
2488 
2489  if (event != NULL) {
2490  p->keymodifier = event->keymodifier;
2491  }
2492  else {
2493  p->keymodifier = -1;
2494  }
2495 
2496  /* everything is now setup ok */
2497  return 1;
2498 }
2499 
2500 /* ------------------------------- */
2501 
2502 /* update UI indicators of status, including cursor and header prints */
2504 {
2505  /* header prints */
2506  switch (p->status) {
2507  case GP_STATUS_IDLING: {
2508  /* print status info */
2509  switch (p->paintmode) {
2510  case GP_PAINTMODE_ERASER: {
2512  C,
2513  TIP_("Grease Pencil Erase Session: Hold and drag LMB or RMB to erase | "
2514  "ESC/Enter to end (or click outside this area)"));
2515  break;
2516  }
2519  TIP_("Grease Pencil Line Session: Hold and drag LMB to draw | "
2520  "ESC/Enter to end (or click outside this area)"));
2521  break;
2522  }
2523  case GP_PAINTMODE_SET_CP: {
2525  C,
2526  TIP_("Grease Pencil Guides: LMB click and release to place reference point | "
2527  "Esc/RMB to cancel"));
2528  break;
2529  }
2530  case GP_PAINTMODE_DRAW: {
2532  if (guide->use_guide) {
2534  C,
2535  TIP_("Grease Pencil Freehand Session: Hold and drag LMB to draw | "
2536  "M key to flip guide | O key to move reference point"));
2537  }
2538  else {
2540  C, TIP_("Grease Pencil Freehand Session: Hold and drag LMB to draw"));
2541  }
2542  break;
2543  }
2544  default: /* unhandled future cases */
2545  {
2547  C, TIP_("Grease Pencil Session: ESC/Enter to end (or click outside this area)"));
2548  break;
2549  }
2550  }
2551  break;
2552  }
2553  case GP_STATUS_ERROR:
2554  case GP_STATUS_DONE: {
2555  /* clear status string */
2557  break;
2558  }
2559  case GP_STATUS_PAINTING:
2560  break;
2561  }
2562 }
2563 
2564 /* ------------------------------- */
2565 
2566 /* Helper to rotate point around origin */
2567 static void gpencil_rotate_v2_v2v2fl(float v[2],
2568  const float p[2],
2569  const float origin[2],
2570  const float angle)
2571 {
2572  float pt[2];
2573  float r[2];
2574  sub_v2_v2v2(pt, p, origin);
2575  rotate_v2_v2fl(r, pt, angle);
2576  add_v2_v2v2(v, r, origin);
2577 }
2578 
2579 /* Helper to snap value to grid */
2580 static float gpencil_snap_to_grid_fl(float v, const float offset, const float spacing)
2581 {
2582  if (spacing > 0.0f) {
2583  v -= spacing * 0.5f;
2584  v -= offset;
2585  v = roundf((v + spacing * 0.5f) / spacing) * spacing;
2586  v += offset;
2587  }
2588  return v;
2589 }
2590 
2591 /* Helper to snap value to grid */
2592 static void gpencil_snap_to_rotated_grid_fl(float v[2],
2593  const float origin[2],
2594  const float spacing,
2595  const float angle)
2596 {
2597  gpencil_rotate_v2_v2v2fl(v, v, origin, -angle);
2598  v[1] = gpencil_snap_to_grid_fl(v[1], origin[1], spacing);
2599  gpencil_rotate_v2_v2v2fl(v, v, origin, angle);
2600 }
2601 
2602 /* get reference point - screen coords to buffer coords */
2603 static void gpencil_origin_set(wmOperator *op, const int mval[2])
2604 {
2605  tGPsdata *p = op->customdata;
2607  float origin[2];
2608  float point[3];
2609  copy_v2fl_v2i(origin, mval);
2611  if (guide->reference_point == GP_GUIDE_REF_CUSTOM) {
2612  copy_v3_v3(guide->location, point);
2613  }
2614  else if (guide->reference_point == GP_GUIDE_REF_CURSOR) {
2616  }
2617 }
2618 
2619 /* get reference point - buffer coords to screen coords */
2620 static void gpencil_origin_get(tGPsdata *p, float origin[2])
2621 {
2623  float location[3];
2624  if (guide->reference_point == GP_GUIDE_REF_CUSTOM) {
2625  copy_v3_v3(location, guide->location);
2626  }
2627  else if (guide->reference_point == GP_GUIDE_REF_OBJECT && guide->reference_object != NULL) {
2628  copy_v3_v3(location, guide->reference_object->loc);
2629  }
2630  else {
2631  copy_v3_v3(location, p->scene->cursor.location);
2632  }
2633  GP_SpaceConversion *gsc = &p->gsc;
2634  gpencil_point_3d_to_xy(gsc, p->gpd->runtime.sbuffer_sflag, location, origin);
2635 }
2636 
2637 /* speed guide initial values */
2639 {
2640  /* calculate initial guide values */
2641  RegionView3D *rv3d = p->region->regiondata;
2642  float scale = 1.0f;
2643  if (rv3d->is_persp) {
2644  float vec[3];
2645  gpencil_get_3d_reference(p, vec);
2646  mul_m4_v3(rv3d->persmat, vec);
2647  scale = vec[2] * rv3d->pixsize;
2648  }
2649  else {
2650  scale = rv3d->pixsize;
2651  }
2652  p->guide.spacing = guide->spacing / scale;
2653  p->guide.half_spacing = p->guide.spacing * 0.5f;
2655 
2656  /* reference for angled snap */
2657  copy_v2_v2(p->guide.unit, p->mvali);
2658  p->guide.unit[0] += 1.0f;
2659 
2660  float xy[2];
2661  sub_v2_v2v2(xy, p->mvali, p->guide.origin);
2662  p->guide.origin_angle = atan2f(xy[1], xy[0]) + (M_PI * 2.0f);
2663 
2665  if (guide->use_snapping && (guide->spacing > 0.0f)) {
2667  p->guide.origin_distance, 0.0f, p->guide.spacing);
2668  }
2669 
2670  if (ELEM(guide->type, GP_GUIDE_RADIAL)) {
2671  float angle;
2672  float half_angle = guide->angle_snap * 0.5f;
2673  angle = p->guide.origin_angle + guide->angle;
2674  angle = fmodf(angle + half_angle, guide->angle_snap);
2675  angle -= half_angle;
2677  }
2678  else {
2680  }
2681 }
2682 
2683 /* apply speed guide */
2684 static void gpencil_snap_to_guide(const tGPsdata *p, const GP_Sculpt_Guide *guide, float point[2])
2685 {
2686  switch (guide->type) {
2687  default:
2688  case GP_GUIDE_CIRCULAR: {
2690  break;
2691  }
2692  case GP_GUIDE_RADIAL: {
2693  if (guide->use_snapping && (guide->angle_snap > 0.0f)) {
2695  }
2696  else {
2698  }
2699  break;
2700  }
2701  case GP_GUIDE_PARALLEL: {
2703  if (guide->use_snapping && (guide->spacing > 0.0f)) {
2705  }
2706  break;
2707  }
2708  case GP_GUIDE_ISO: {
2710  if (guide->use_snapping && (guide->spacing > 0.0f)) {
2712  point, p->guide.origin, p->guide.spacing, p->guide.rot_angle);
2713  }
2714  break;
2715  }
2716  case GP_GUIDE_GRID: {
2717  if (guide->use_snapping && (guide->spacing > 0.0f)) {
2719  if (p->straight == STROKE_HORIZONTAL) {
2721  }
2722  else {
2724  }
2725  }
2726  else if (p->straight == STROKE_HORIZONTAL) {
2727  point[1] = p->mvali[1]; /* replace y */
2728  }
2729  else {
2730  point[0] = p->mvali[0]; /* replace x */
2731  }
2732  break;
2733  }
2734  }
2735 }
2736 
2737 /* create a new stroke point at the point indicated by the painting context */
2739 {
2740  bGPdata *gpd = p->gpd;
2741  tGPspoint *pt = NULL;
2742 
2743  /* handle drawing/erasing -> test for erasing first */
2744  if (p->paintmode == GP_PAINTMODE_ERASER) {
2745  /* do 'live' erasing now */
2747 
2748  /* store used values */
2749  copy_v2_v2(p->mvalo, p->mval);
2750  p->opressure = p->pressure;
2751  }
2752  /* Only add current point to buffer if mouse moved
2753  * (even though we got an event, it might be just noise). */
2754  else if (gpencil_stroke_filtermval(p, p->mval, p->mvalo)) {
2755 
2756  /* if lazy mouse, interpolate the last and current mouse positions */
2757  if (GPENCIL_LAZY_MODE(p->brush, p->shift) && (!p->disable_stabilizer)) {
2758  float now_mouse[2];
2759  float last_mouse[2];
2760  copy_v2_v2(now_mouse, p->mval);
2761  copy_v2_v2(last_mouse, p->mvalo);
2762  interp_v2_v2v2(now_mouse, now_mouse, last_mouse, p->brush->smooth_stroke_factor);
2763  copy_v2_v2(p->mval, now_mouse);
2764 
2766  bool is_speed_guide = ((guide->use_guide) &&
2767  (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));
2768  if (is_speed_guide) {
2769  gpencil_snap_to_guide(p, guide, p->mval);
2770  }
2771  }
2772 
2773  /* try to add point */
2774  short ok = gpencil_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
2775 
2776  /* handle errors while adding point */
2778  /* finish off old stroke */
2780  /* And start a new one!!! Else, projection errors! */
2782 
2783  /* start a new stroke, starting from previous point */
2784  /* XXX Must manually reset inittime... */
2785  /* XXX We only need to reuse previous point if overflow! */
2786  if (ok == GP_STROKEADD_OVERFLOW) {
2787  p->inittime = p->ocurtime;
2789  }
2790  else {
2791  p->inittime = p->curtime;
2792  }
2794  }
2795  else if (ok == GP_STROKEADD_INVALID) {
2796  /* the painting operation cannot continue... */
2797  BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke");
2798  p->status = GP_STATUS_ERROR;
2799 
2800  return;
2801  }
2802 
2803  /* store used values */
2804  copy_v2_v2(p->mvalo, p->mval);
2805  p->opressure = p->pressure;
2806  p->ocurtime = p->curtime;
2807 
2808  pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1;
2809  if (p->paintmode != GP_PAINTMODE_ERASER) {
2811  }
2812  }
2814  (gpd->runtime.sbuffer_used > 0)) {
2815  pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1;
2816  if (p->paintmode != GP_PAINTMODE_ERASER) {
2818  }
2819  }
2820 }
2821 
2822 /* handle draw event */
2824  wmOperator *op,
2825  const wmEvent *event,
2827 {
2828  tGPsdata *p = op->customdata;
2830  PointerRNA itemptr;
2831  float mousef[2];
2832  bool is_speed_guide = ((guide->use_guide) &&
2833  (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));
2834 
2835  /* convert from window-space to area-space mouse coordinates
2836  * add any x,y override position
2837  */
2838  copy_v2fl_v2i(p->mval, event->mval);
2839  p->shift = (event->modifier & KM_SHIFT) != 0;
2840 
2841  /* verify direction for straight lines and guides */
2842  if ((is_speed_guide) ||
2843  ((event->modifier & KM_ALT) && (RNA_boolean_get(op->ptr, "disable_straight") == false))) {
2844  if (p->straight == 0) {
2845  int dx = (int)fabsf(p->mval[0] - p->mvali[0]);
2846  int dy = (int)fabsf(p->mval[1] - p->mvali[1]);
2847  if ((dx > 0) || (dy > 0)) {
2848  /* store mouse direction */
2849  if (dx > dy) {
2851  }
2852  else if (dx < dy) {
2854  }
2855  }
2856  /* reset if a stroke angle is required */
2857  if ((p->flags & GP_PAINTFLAG_REQ_VECTOR) && ((dx == 0) || (dy == 0))) {
2858  p->straight = 0;
2859  }
2860  }
2861  }
2862 
2864 
2865  /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */
2866  p->pressure = event->tablet.pressure;
2867  /* By default use pen pressure for random curves but attenuated. */
2868  p->random_settings.pen_press = pow(p->pressure, 3.0f);
2869 
2870  /* Hack for pressure sensitive eraser on D+RMB when using a tablet:
2871  * The pen has to float over the tablet surface, resulting in
2872  * zero pressure (T47101). Ignore pressure values if floating
2873  * (i.e. "effectively zero" pressure), and only when the "active"
2874  * end is the stylus (i.e. the default when not eraser)
2875  */
2876  if (p->paintmode == GP_PAINTMODE_ERASER) {
2877  if ((event->tablet.active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) {
2878  p->pressure = 1.0f;
2879  }
2880  }
2881 
2882  /* special eraser modes */
2883  if (p->paintmode == GP_PAINTMODE_ERASER) {
2884  if (event->modifier & KM_SHIFT) {
2886  }
2887  else {
2889  }
2890  if (event->modifier & KM_ALT) {
2892  }
2893  else {
2895  }
2896  }
2897 
2898  /* special exception for start of strokes (i.e. maybe for just a dot) */
2899  if (p->flags & GP_PAINTFLAG_FIRSTRUN) {
2900 
2901  /* special exception here for too high pressure values on first touch in
2902  * windows for some tablets, then we just skip first touch...
2903  */
2904  if ((event->tablet.active != EVT_TABLET_NONE) && (p->pressure >= 0.99f)) {
2905  return;
2906  }
2907 
2909 
2910  /* set values */
2911  p->opressure = p->pressure;
2912  p->inittime = p->ocurtime = p->curtime;
2913  p->straight = 0;
2914 
2915  /* save initial mouse */
2916  copy_v2_v2(p->mvalo, p->mval);
2917  copy_v2_v2(p->mvali, p->mval);
2918 
2919  if (is_speed_guide && !ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP) &&
2920  ((guide->use_snapping && (guide->type == GP_GUIDE_GRID)) ||
2921  (guide->type == GP_GUIDE_ISO))) {
2923  }
2924 
2925  /* calculate initial guide values */
2926  if (is_speed_guide) {
2927  gpencil_speed_guide_init(p, guide);
2928  }
2929  }
2930 
2931  /* wait for vector then add initial point */
2932  if (is_speed_guide && (p->flags & GP_PAINTFLAG_REQ_VECTOR)) {
2933  if (p->straight == 0) {
2934  return;
2935  }
2936 
2938 
2939  /* get initial point */
2940  float pt[2];
2941  sub_v2_v2v2(pt, p->mval, p->mvali);
2942 
2943  /* get stroke angle for grids */
2944  if (ELEM(guide->type, GP_GUIDE_ISO)) {
2945  p->guide.stroke_angle = atan2f(pt[1], pt[0]);
2946  /* determine iso angle, less weight is given for vertical strokes */
2947  if (((p->guide.stroke_angle >= 0.0f) && (p->guide.stroke_angle < DEG2RAD(75))) ||
2948  (p->guide.stroke_angle < DEG2RAD(-105))) {
2949  p->guide.rot_angle = guide->angle;
2950  }
2951  else if (((p->guide.stroke_angle < 0.0f) && (p->guide.stroke_angle > DEG2RAD(-75))) ||
2952  (p->guide.stroke_angle > DEG2RAD(105))) {
2953  p->guide.rot_angle = -guide->angle;
2954  }
2955  else {
2956  p->guide.rot_angle = DEG2RAD(90);
2957  }
2959  }
2960  else if (ELEM(guide->type, GP_GUIDE_GRID)) {
2962  p->guide.unit,
2963  p->mvali,
2964  (p->straight == STROKE_VERTICAL) ? M_PI_2 : 0.0f);
2965  }
2966  }
2967 
2968  /* check if stroke is straight or guided */
2969  if ((p->paintmode != GP_PAINTMODE_ERASER) && ((p->straight) || (is_speed_guide))) {
2970  /* guided stroke */
2971  if (is_speed_guide) {
2972  gpencil_snap_to_guide(p, guide, p->mval);
2973  }
2974  else if (p->straight == STROKE_HORIZONTAL) {
2975  p->mval[1] = p->mvali[1]; /* replace y */
2976  }
2977  else {
2978  p->mval[0] = p->mvali[0]; /* replace x */
2979  }
2980  }
2981 
2982  /* fill in stroke data (not actually used directly by gpencil_draw_apply) */
2983  RNA_collection_add(op->ptr, "stroke", &itemptr);
2984 
2985  mousef[0] = p->mval[0];
2986  mousef[1] = p->mval[1];
2987  RNA_float_set_array(&itemptr, "mouse", mousef);
2988  RNA_float_set(&itemptr, "pressure", p->pressure);
2989  RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN) != 0);
2990 
2991  RNA_float_set(&itemptr, "time", p->curtime - p->inittime);
2992 
2993  /* apply the current latest drawing point */
2994  gpencil_draw_apply(C, op, p, depsgraph);
2995 
2996  /* force refresh */
2997  /* just active area for now, since doing whole screen is too slow */
2999 }
3000 
3001 /* ------------------------------- */
3002 
3003 /* operator 'redo' (i.e. after changing some properties, but also for repeat last) */
3005 {
3006  tGPsdata *p = NULL;
3008 
3009  /* try to initialize context data needed while drawing */
3010  if (!gpencil_draw_init(C, op, NULL)) {
3012  return OPERATOR_CANCELLED;
3013  }
3014 
3015  p = op->customdata;
3016 
3017  /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement),
3018  * setting the relevant values in context at each step, then applying
3019  */
3020  RNA_BEGIN (op->ptr, itemptr, "stroke") {
3021  float mousef[2];
3022 
3023  /* get relevant data for this point from stroke */
3024  RNA_float_get_array(&itemptr, "mouse", mousef);
3025  p->mval[0] = mousef[0];
3026  p->mval[1] = mousef[1];
3027  p->pressure = RNA_float_get(&itemptr, "pressure");
3028  p->curtime = (double)RNA_float_get(&itemptr, "time") + p->inittime;
3029 
3030  if (RNA_boolean_get(&itemptr, "is_start")) {
3031  /* if first-run flag isn't set already (i.e. not true first stroke),
3032  * then we must terminate the previous one first before continuing
3033  */
3034  if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) {
3035  /* TODO: both of these ops can set error-status, but we probably don't need to worry */
3038  }
3039  }
3040 
3041  /* if first run, set previous data too */
3042  if (p->flags & GP_PAINTFLAG_FIRSTRUN) {
3044 
3045  p->mvalo[0] = p->mval[0];
3046  p->mvalo[1] = p->mval[1];
3047  p->opressure = p->pressure;
3048  p->ocurtime = p->curtime;
3049  }
3050 
3051  /* apply this data as necessary now (as per usual) */
3052  gpencil_draw_apply(C, op, p, depsgraph);
3053  }
3054  RNA_END;
3055 
3056  /* cleanup */
3057  gpencil_draw_exit(C, op);
3058 
3059  /* refreshes */
3061 
3062  /* done */
3063  return OPERATOR_FINISHED;
3064 }
3065 
3066 /* ------------------------------- */
3067 
3068 /* handle events for guides */
3070  wmOperator *op,
3071  const wmEvent *event,
3072  tGPsdata *p)
3073 {
3074  bool add_notifier = false;
3076 
3077  /* Enter or exit set center point mode */
3078  if ((event->type == EVT_OKEY) && (event->val == KM_RELEASE)) {
3079  if ((p->paintmode == GP_PAINTMODE_DRAW) && guide->use_guide &&
3080  (guide->reference_point != GP_GUIDE_REF_OBJECT)) {
3081  add_notifier = true;
3084  }
3085  }
3086  /* Freehand mode, turn off speed guide */
3087  else if ((event->type == EVT_VKEY) && (event->val == KM_RELEASE)) {
3088  guide->use_guide = false;
3089  add_notifier = true;
3090  }
3091  /* Alternate or flip direction */
3092  else if ((event->type == EVT_MKEY) && (event->val == KM_RELEASE)) {
3093  if (guide->type == GP_GUIDE_CIRCULAR) {
3094  add_notifier = true;
3095  guide->type = GP_GUIDE_RADIAL;
3096  }
3097  else if (guide->type == GP_GUIDE_RADIAL) {
3098  add_notifier = true;
3099  guide->type = GP_GUIDE_CIRCULAR;
3100  }
3101  else if (guide->type == GP_GUIDE_PARALLEL) {
3102  add_notifier = true;
3103  guide->angle += M_PI_2;
3104  guide->angle = angle_compat_rad(guide->angle, M_PI);
3105  }
3106  else {
3107  add_notifier = false;
3108  }
3109  }
3110  /* Line guides */
3111  else if ((event->type == EVT_LKEY) && (event->val == KM_RELEASE)) {
3112  add_notifier = true;
3113  guide->use_guide = true;
3114  if (event->modifier & KM_CTRL) {
3115  guide->angle = 0.0f;
3116  guide->type = GP_GUIDE_PARALLEL;
3117  }
3118  else if (event->modifier & KM_ALT) {
3119  guide->type = GP_GUIDE_PARALLEL;
3120  guide->angle = RNA_float_get(op->ptr, "guide_last_angle");
3121  }
3122  else {
3123  guide->type = GP_GUIDE_PARALLEL;
3124  }
3125  }
3126  /* Point guide */
3127  else if ((event->type == EVT_CKEY) && (event->val == KM_RELEASE)) {
3128  add_notifier = true;
3129  if (!guide->use_guide) {
3130  guide->use_guide = true;
3131  guide->type = GP_GUIDE_CIRCULAR;
3132  }
3133  else if (guide->type == GP_GUIDE_CIRCULAR) {
3134  guide->type = GP_GUIDE_RADIAL;
3135  }
3136  else if (guide->type == GP_GUIDE_RADIAL) {
3137  guide->type = GP_GUIDE_CIRCULAR;
3138  }
3139  else {
3140  guide->type = GP_GUIDE_CIRCULAR;
3141  }
3142  }
3143  /* Change line angle. */
3144  else if (ELEM(event->type, EVT_JKEY, EVT_KKEY) && (event->val == KM_RELEASE)) {
3145  add_notifier = true;
3146  float angle = guide->angle;
3147  float adjust = (float)M_PI / 180.0f;
3148  if (event->modifier & KM_ALT) {
3149  adjust *= 45.0f;
3150  }
3151  else if ((event->modifier & KM_SHIFT) == 0) {
3152  adjust *= 15.0f;
3153  }
3154  angle += (event->type == EVT_JKEY) ? adjust : -adjust;
3156  guide->angle = angle;
3157  }
3158 
3159  if (add_notifier) {
3161  }
3162 }
3163 
3164 /* start of interactive drawing part of operator */
3165 static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
3166 {
3167  tGPsdata *p = NULL;
3169  bGPdata *gpd = (bGPdata *)ob->data;
3170 
3171  /* support for tablets eraser pen */
3173  RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER);
3174  }
3175 
3176  /* do not draw in locked or invisible layers */
3177  eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode");
3178  if (paintmode != GP_PAINTMODE_ERASER) {
3180  if ((gpl) && ((gpl->flag & GP_LAYER_LOCKED) || (gpl->flag & GP_LAYER_HIDE))) {
3181  BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden");
3182  return OPERATOR_CANCELLED;
3183  }
3184  }
3185  else {
3186  /* don't erase empty frames */
3187  bool has_layer_to_erase = false;
3188  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
3189  /* Skip if layer not editable */
3190  if (BKE_gpencil_layer_is_editable(gpl)) {
3191  if (gpl->actframe && gpl->actframe->strokes.first) {
3192  has_layer_to_erase = true;
3193  break;
3194  }
3195  }
3196  }
3197  if (!has_layer_to_erase) {
3198  BKE_report(op->reports, RPT_ERROR, "Nothing to erase or all layers locked");
3199  return OPERATOR_FINISHED;
3200  }
3201  }
3202 
3203  /* try to initialize context data needed while drawing */
3204  if (!gpencil_draw_init(C, op, event)) {
3205  if (op->customdata) {
3206  MEM_freeN(op->customdata);
3207  }
3208  return OPERATOR_CANCELLED;
3209  }
3210 
3211  p = op->customdata;
3212 
3213  /* Init random settings. */
3215 
3216  /* TODO: set any additional settings that we can take from the events?
3217  * if eraser is on, draw radial aid */
3218  if (p->paintmode == GP_PAINTMODE_ERASER) {
3220  }
3221  else {
3223  }
3224 
3225  /* only start drawing immediately if we're allowed to do so... */
3226  if (RNA_boolean_get(op->ptr, "wait_for_input") == false) {
3227  /* hotkey invoked - start drawing */
3229 
3230  /* handle the initial drawing - i.e. for just doing a simple dot */
3233  }
3234  else {
3235  /* toolbar invoked - don't start drawing yet... */
3237  }
3238 
3239  /* enable paint mode */
3240  /* handle speed guide events before drawing inside view3d */
3242  gpencil_guide_event_handling(C, op, event, p);
3243  }
3244 
3245  if ((ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) {
3246  /* FIXME: use the mode switching operator, this misses notifiers, messages. */
3247  /* Just set paintmode flag... */
3249  /* disable other GP modes */
3253  /* set workspace mode */
3254  ob->restore_mode = ob->mode;
3256  /* redraw mode on screen */
3258  }
3259 
3261 
3262  /* add a modal handler for this operator, so that we can then draw continuous strokes */
3264 
3265  return OPERATOR_RUNNING_MODAL;
3266 }
3267 
3268 /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
3269 static bool gpencil_area_exists(bContext *C, ScrArea *area_test)
3270 {
3271  bScreen *screen = CTX_wm_screen(C);
3272  return (BLI_findindex(&screen->areabase, area_test) != -1);
3273 }
3274 
3276 {
3277  tGPsdata *p = op->customdata;
3278 
3279  /* we must check that we're still within the area that we're set up to work from
3280  * otherwise we could crash (see bug T20586)
3281  */
3282  if (CTX_wm_area(C) != p->area) {
3283  printf("\t\t\tGP - wrong area execution abort!\n");
3284  p->status = GP_STATUS_ERROR;
3285  }
3286 
3287  /* we may need to set up paint env again if we're resuming */
3288  if (gpencil_session_initdata(C, op, p)) {
3290  }
3291 
3292  if (p->status != GP_STATUS_ERROR) {
3295  }
3296 
3297  return op->customdata;
3298 }
3299 
3300 /* Apply pressure change depending of the angle of the stroke for a segment. */
3302 {
3303  Brush *brush = p->brush;
3304  /* Sensitivity. */
3305  const float sen = brush->gpencil_settings->draw_angle_factor;
3306  /* Default angle of brush in radians */
3307  const float angle = brush->gpencil_settings->draw_angle;
3308 
3309  float mvec[2];
3310  float fac;
3311 
3312  /* angle vector of the brush with full thickness */
3313  const float v0[2] = {cos(angle), sin(angle)};
3314 
3315  sub_v2_v2v2(mvec, pt->m_xy, pt_prev->m_xy);
3316  normalize_v2(mvec);
3317  fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */
3318  /* interpolate with previous point for smoother transitions */
3319  pt->pressure = interpf(pt->pressure - (sen * fac), pt_prev->pressure, 0.3f);
3321 }
3322 
3323 /* Add arc points between two mouse events using the previous segment to determine the vertex of
3324  * the arc.
3325  * /+ CTL
3326  * / |
3327  * / |
3328  * PtA +...|...+ PtB
3329  * /
3330  * /
3331  * + PtA - 1
3332  * /
3333  * CTL is the vertex of the triangle created between PtA and PtB */
3334 static void gpencil_add_arc_points(tGPsdata *p, const float mval[2], int segments)
3335 {
3336  bGPdata *gpd = p->gpd;
3337  BrushGpencilSettings *brush_settings = p->brush->gpencil_settings;
3338 
3339  if (gpd->runtime.sbuffer_used < 3) {
3340  tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
3341  /* Apply other randomness to first points. */
3342  for (int i = 0; i < gpd->runtime.sbuffer_used; i++) {
3343  tGPspoint *pt = &points[i];
3344  gpencil_apply_randomness(p, brush_settings, pt, false, false, true);
3345  }
3346  return;
3347  }
3348  int idx_prev = gpd->runtime.sbuffer_used;
3349 
3350  /* Add space for new arc points. */
3351  gpd->runtime.sbuffer_used += segments - 1;
3352 
3353  /* Check if still room in buffer or add more. */
3355  gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, false);
3356 
3357  tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
3358  tGPspoint *pt = NULL;
3359  tGPspoint *pt_before = &points[idx_prev - 1]; /* current - 2 */
3360  tGPspoint *pt_prev = &points[idx_prev - 2]; /* previous */
3361 
3362  /* Create two vectors, previous and half way of the actual to get the vertex of the triangle
3363  * for arc curve.
3364  */
3365  float v_prev[2], v_cur[2], v_half[2];
3366  sub_v2_v2v2(v_cur, mval, pt_prev->m_xy);
3367 
3368  sub_v2_v2v2(v_prev, pt_prev->m_xy, pt_before->m_xy);
3369  interp_v2_v2v2(v_half, pt_prev->m_xy, mval, 0.5f);
3370  sub_v2_v2(v_half, pt_prev->m_xy);
3371 
3372  /* If angle is too sharp undo all changes and return. */
3373  const float min_angle = DEG2RADF(120.0f);
3374  float angle = angle_v2v2(v_prev, v_half);
3375  if (angle < min_angle) {
3376  gpd->runtime.sbuffer_used -= segments - 1;
3377  return;
3378  }
3379 
3380  /* Project the half vector to the previous vector and calculate the mid projected point. */
3381  float dot = dot_v2v2(v_prev, v_half);
3382  float l = len_squared_v2(v_prev);
3383  if (l > 0.0f) {
3384  mul_v2_fl(v_prev, dot / l);
3385  }
3386 
3387  /* Calc the position of the control point. */
3388  float ctl[2];
3389  add_v2_v2v2(ctl, pt_prev->m_xy, v_prev);
3390 
3391  float step = M_PI_2 / (float)(segments + 1);
3392  float a = step;
3393 
3394  float midpoint[2], start[2], end[2], cp1[2], corner[2];
3395  mid_v2_v2v2(midpoint, pt_prev->m_xy, mval);
3396  copy_v2_v2(start, pt_prev->m_xy);
3397  copy_v2_v2(end, mval);
3398  copy_v2_v2(cp1, ctl);
3399 
3400  corner[0] = midpoint[0] - (cp1[0] - midpoint[0]);
3401  corner[1] = midpoint[1] - (cp1[1] - midpoint[1]);
3402  float stepcolor = 1.0f / segments;
3403 
3404  tGPspoint *pt_step = pt_prev;
3405  for (int i = 0; i < segments; i++) {
3406  pt = &points[idx_prev + i - 1];
3407  pt->m_xy[0] = corner[0] + (end[0] - corner[0]) * sinf(a) + (start[0] - corner[0]) * cosf(a);
3408  pt->m_xy[1] = corner[1] + (end[1] - corner[1]) * sinf(a) + (start[1] - corner[1]) * cosf(a);
3409 
3410  /* Set pressure and strength equals to previous. It will be smoothed later. */
3411  pt->pressure = pt_prev->pressure;
3412  pt->strength = pt_prev->strength;
3413  /* Interpolate vertex color. */
3415  pt->vert_color, pt_before->vert_color, pt_prev->vert_color, stepcolor * (i + 1));
3416 
3417  /* Apply angle of stroke to brush size to interpolated points but slightly attenuated. */
3418  if (brush_settings->draw_angle_factor != 0.0f) {
3419  gpencil_brush_angle_segment(p, pt_step, pt);
3420  CLAMP(pt->pressure, pt_prev->pressure * 0.5f, 1.0f);
3421  /* Use the previous interpolated point for next segment. */
3422  pt_step = pt;
3423  }
3424 
3425  /* Apply other randomness. */
3426  gpencil_apply_randomness(p, brush_settings, pt, false, false, true);
3427 
3428  a += step;
3429  }
3430 }
3431 
3432 static void gpencil_add_guide_points(const tGPsdata *p,
3433  const GP_Sculpt_Guide *guide,
3434  const float start[2],
3435  const float end[2],
3436  int segments)
3437 {
3438  bGPdata *gpd = p->gpd;
3439  if (gpd->runtime.sbuffer_used == 0) {
3440  return;
3441  }
3442 
3443  int idx_prev = gpd->runtime.sbuffer_used;
3444 
3445  /* Add space for new points. */
3446  gpd->runtime.sbuffer_used += segments - 1;
3447 
3448  /* Check if still room in buffer or add more. */
3450  gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, false);
3451 
3452  tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
3453  tGPspoint *pt = NULL;
3454  tGPspoint *pt_before = &points[idx_prev - 1];
3455 
3456  /* Use arc sampling for circular guide */
3457  if (guide->type == GP_GUIDE_CIRCULAR) {
3458  float cw = cross_tri_v2(start, p->guide.origin, end);
3459  float angle = angle_v2v2v2(start, p->guide.origin, end);
3460 
3461  float step = angle / (float)(segments + 1);
3462  if (cw < 0.0f) {
3463  step = -step;
3464  }
3465 
3466  float a = step;
3467 
3468  for (int i = 0; i < segments; i++) {
3469  pt = &points[idx_prev + i - 1];
3470 
3471  gpencil_rotate_v2_v2v2fl(pt->m_xy, start, p->guide.origin, -a);
3472  gpencil_snap_to_guide(p, guide, pt->m_xy);
3473  a += step;
3474 
3475  /* Set pressure and strength equals to previous. It will be smoothed later. */
3476  pt->pressure = pt_before->pressure;
3477  pt->strength = pt_before->strength;
3478  copy_v4_v4(pt->vert_color, pt_before->vert_color);
3479  }
3480  }
3481  else {
3482  float step = 1.0f / (float)(segments + 1);
3483  float a = step;
3484 
3485  for (int i = 0; i < segments; i++) {
3486  pt = &points[idx_prev + i - 1];
3487 
3488  interp_v2_v2v2(pt->m_xy, start, end, a);
3489  gpencil_snap_to_guide(p, guide, pt->m_xy);
3490  a += step;
3491 
3492  /* Set pressure and strength equals to previous. It will be smoothed later. */
3493  pt->pressure = pt_before->pressure;
3494  pt->strength = pt_before->strength;
3495  copy_v4_v4(pt->vert_color, pt_before->vert_color);
3496  }
3497  }
3498 }
3499 
3504 static void gpencil_add_fake_points(const wmEvent *event, tGPsdata *p)
3505 {
3506  Brush *brush = p->brush;
3507  /* Lazy mode do not use fake events. */
3508  if (GPENCIL_LAZY_MODE(brush, p->shift) && (!p->disable_stabilizer)) {
3509  return;
3510  }
3511 
3513  int input_samples = brush->gpencil_settings->input_samples;
3514  bool is_speed_guide = ((guide->use_guide) &&
3515  (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));
3516 
3517  /* TODO: ensure sampling enough points when using circular guide,
3518  * but the arc must be around the center. (see if above to check other guides only). */
3519  if (is_speed_guide && (guide->type == GP_GUIDE_CIRCULAR)) {
3520  input_samples = GP_MAX_INPUT_SAMPLES;
3521  }
3522 
3523  if (input_samples == 0) {
3524  return;
3525  }
3526 
3527  int samples = GP_MAX_INPUT_SAMPLES - input_samples + 1;
3528 
3529  float mouse_prv[2], mouse_cur[2];
3530  float min_dist = 4.0f * samples;
3531 
3532  copy_v2_v2(mouse_prv, p->mvalo);
3533  copy_v2fl_v2i(mouse_cur, event->mval);
3534 
3535  /* get distance in pixels */
3536  float dist = len_v2v2(mouse_prv, mouse_cur);
3537 
3538  /* get distance for circular guide */
3539  if (is_speed_guide && (guide->type == GP_GUIDE_CIRCULAR)) {
3540  float middle[2];
3541  gpencil_snap_to_guide(p, guide, mouse_prv);
3542  gpencil_snap_to_guide(p, guide, mouse_cur);
3543  mid_v2_v2v2(middle, mouse_cur, mouse_prv);
3544  gpencil_snap_to_guide(p, guide, middle);
3545  dist = len_v2v2(mouse_prv, middle) + len_v2v2(middle, mouse_cur);
3546  }
3547 
3548  if ((dist > 3.0f) && (dist > min_dist)) {
3549  int slices = (dist / min_dist) + 1;
3550 
3551  if (is_speed_guide) {
3552  gpencil_add_guide_points(p, guide, mouse_prv, mouse_cur, slices);
3553  }
3554  else {
3555  gpencil_add_arc_points(p, mouse_cur, slices);
3556  }
3557  }
3558 }
3559 
3560 /* events handling during interactive drawing part of operator */
3561 static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
3562 {
3563  tGPsdata *p = op->customdata;
3564  // ToolSettings *ts = CTX_data_tool_settings(C);
3566 
3567  /* Default exit state - pass through to support MMB view navigation, etc. */
3568  int estate = OPERATOR_PASS_THROUGH;
3569 
3570  /* NOTE(mike erwin): Not quite what I was looking for, but a good start!
3571  * grease-pencil continues to draw on the screen while the 3D mouse moves the viewpoint.
3572  * Problem is that the stroke is converted to 3D only after it is finished.
3573  * This approach should work better in tools that immediately apply in 3D space. */
3574 #if 0
3575  if (event->type == NDOF_MOTION) {
3576  return OPERATOR_PASS_THROUGH;
3577  }
3578 #endif
3579 
3580  if (p->status == GP_STATUS_IDLING) {
3581  ARegion *region = CTX_wm_region(C);
3582  p->region = region;
3583  }
3584 
3585  /* special mode for editing control points */
3586  if (p->paintmode == GP_PAINTMODE_SET_CP) {
3587  wmWindow *win = p->win;
3589  bool drawmode = false;
3590 
3591  switch (event->type) {
3592  /* cancel */
3593  case EVT_ESCKEY:
3594  case RIGHTMOUSE: {
3595  if (ELEM(event->val, KM_RELEASE)) {
3596  drawmode = true;
3597  }
3598  break;
3599  }
3600  /* set */
3601  case LEFTMOUSE: {
3602  if (ELEM(event->val, KM_RELEASE)) {
3603  gpencil_origin_set(op, event->mval);
3604  drawmode = true;
3605  }
3606  break;
3607  }
3608  }
3609  if (drawmode) {
3610  p->status = GP_STATUS_IDLING;
3615  }
3616  else {
3617  return OPERATOR_RUNNING_MODAL;
3618  }
3619  }
3620 
3621  /* We don't pass on key events, GP is used with key-modifiers -
3622  * prevents Dkey to insert drivers. */
3623  if (ISKEYBOARD(event->type)) {
3625  /* allow some keys:
3626  * - For frame changing T33412.
3627  * - For undo (during sketching sessions).
3628  */
3629  }
3630  else if (event->type == EVT_ZKEY) {
3631  if (event->modifier & KM_CTRL) {
3632  p->status = GP_STATUS_DONE;
3633  estate = OPERATOR_FINISHED;
3634  }
3635  }
3636  else if (ELEM(event->type,
3637  EVT_PAD0,
3638  EVT_PAD1,
3639  EVT_PAD2,
3640  EVT_PAD3,
3641  EVT_PAD4,
3642  EVT_PAD5,
3643  EVT_PAD6,
3644  EVT_PAD7,
3645  EVT_PAD8,
3646  EVT_PAD9)) {
3647  /* Allow numpad keys so that camera/view manipulations can still take place
3648  * - #EVT_PAD0 in particular is really important for Grease Pencil drawing,
3649  * as animators may be working "to camera", so having this working
3650  * is essential for ensuring that they can quickly return to that view.
3651  */
3652  }
3654  gpencil_guide_event_handling(C, op, event, p);
3655  estate = OPERATOR_RUNNING_MODAL;
3656  }
3657  else {
3658  estate = OPERATOR_RUNNING_MODAL;
3659  }
3660  }
3661 
3662  /* Exit painting mode (and/or end current stroke).
3663  *
3664  */
3666 
3667  p->status = GP_STATUS_DONE;
3668  estate = OPERATOR_FINISHED;
3669  }
3670 
3671  /* toggle painting mode upon mouse-button movement
3672  * - LEFTMOUSE = standard drawing (all) / straight line drawing (all)
3673  * - RIGHTMOUSE = eraser (all)
3674  * (Disabling RIGHTMOUSE case here results in bugs like T32647)
3675  * also making sure we have a valid event value, to not exit too early
3676  */
3677  if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (ELEM(event->val, KM_PRESS, KM_RELEASE))) {
3678  /* if painting, end stroke */
3679  if (p->status == GP_STATUS_PAINTING) {
3680  p->status = GP_STATUS_DONE;
3681  estate = OPERATOR_FINISHED;
3682  }
3683  else if (event->val == KM_PRESS) {
3684  bool in_bounds = false;
3685 
3686  /* Check if we're outside the bounds of the active region
3687  * NOTE: An exception here is that if launched from the toolbar,
3688  * whatever region we're now in should become the new region
3689  */
3690  if ((p->region) && (p->region->regiontype == RGN_TYPE_TOOLS)) {
3691  /* Change to whatever region is now under the mouse */
3692  ARegion *current_region = BKE_area_find_region_xy(p->area, RGN_TYPE_ANY, event->xy);
3693 
3694  if (current_region) {
3695  /* Assume that since we found the cursor in here, it is in bounds
3696  * and that this should be the region that we begin drawing in
3697  */
3698  p->region = current_region;
3699  in_bounds = true;
3700  }
3701  else {
3702  /* Out of bounds, or invalid in some other way */
3703  p->status = GP_STATUS_ERROR;
3704  estate = OPERATOR_CANCELLED;
3705  }
3706  }
3707  else if (p->region) {
3708  /* Perform bounds check using. */
3709  const rcti *region_rect = ED_region_visible_rect(p->region);
3710  in_bounds = BLI_rcti_isect_pt_v(region_rect, event->mval);
3711  }
3712  else {
3713  /* No region */
3714  p->status = GP_STATUS_ERROR;
3715  estate = OPERATOR_CANCELLED;
3716  }
3717 
3718  if (in_bounds) {
3719  /* Switch paintmode (temporarily if need be) based on which button was used
3720  * NOTE: This is to make it more convenient to erase strokes when using drawing sessions
3721  */
3722  if ((event->type == RIGHTMOUSE) || gpencil_is_tablet_eraser_active(event)) {
3723  /* turn on eraser */
3725  }
3726  else if (event->type == LEFTMOUSE) {
3727  /* restore drawmode to default */
3728  p->paintmode = RNA_enum_get(op->ptr, "mode");
3729  }
3730 
3732 
3733  /* not painting, so start stroke (this should be mouse-button down) */
3734  p = gpencil_stroke_begin(C, op);
3735 
3736  if (p->status == GP_STATUS_ERROR) {
3737  estate = OPERATOR_CANCELLED;
3738  }
3739  }
3740  else if (p->status != GP_STATUS_ERROR) {
3741  /* User clicked outside bounds of window while idling, so exit paintmode
3742  * NOTE: Don't enter this case if an error occurred while finding the
3743  * region (as above)
3744  */
3745  p->status = GP_STATUS_DONE;
3746  estate = OPERATOR_FINISHED;
3747  }
3748  }
3749  else if (event->val == KM_RELEASE) {
3750  p->status = GP_STATUS_IDLING;
3753  }
3754  }
3755 
3756  /* handle mode-specific events */
3757  if (p->status == GP_STATUS_PAINTING) {
3758  /* handle painting mouse-movements? */
3759  if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
3760  /* handle drawing event */
3761  bool is_speed_guide = ((guide->use_guide) &&
3762  (p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));
3763 
3764  int size_before = p->gpd->runtime.sbuffer_used;
3765  if (((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) && (p->paintmode != GP_PAINTMODE_ERASER) &&
3766  !(is_speed_guide && (p->flags & GP_PAINTFLAG_REQ_VECTOR))) {
3767  gpencil_add_fake_points(event, p);
3768  }
3769 
3771  int size_after = p->gpd->runtime.sbuffer_used;
3772 
3773  /* Smooth segments if some fake points were added (need loop to get cumulative smooth).
3774  * the 0.15 value gets a good result in Windows and Linux. */
3775  if (!is_speed_guide && (size_after - size_before > 1)) {
3776  for (int r = 0; r < 5; r++) {
3777  gpencil_smooth_segment(p->gpd, 0.15f, size_before - 1, size_after - 1);
3778  }
3779  }
3780 
3781  /* finish painting operation if anything went wrong just now */
3782  if (p->status == GP_STATUS_ERROR) {
3783  printf("\t\t\t\tGP - add error done!\n");
3784  estate = OPERATOR_CANCELLED;
3785  }
3786  else {
3787  /* event handled, so just tag as running modal */
3788  estate = OPERATOR_RUNNING_MODAL;
3789  }
3790  }
3791  /* eraser size */
3792  else if ((p->paintmode == GP_PAINTMODE_ERASER) &&
3794  /* Just resize the brush (local version). */
3795  switch (event->type) {
3796  case WHEELDOWNMOUSE: /* larger */
3797  case EVT_PADPLUSKEY:
3798  p->radius += 5;
3799  break;
3800 
3801  case WHEELUPMOUSE: /* smaller */
3802  case EVT_PADMINUS:
3803  p->radius -= 5;
3804 
3805  if (p->radius <= 0) {
3806  p->radius = 1;
3807  }
3808  break;
3809  }
3810 
3811  /* force refresh */
3812  /* just active area for now, since doing whole screen is too slow */
3814 
3815  /* event handled, so just tag as running modal */
3816  estate = OPERATOR_RUNNING_MODAL;
3817  }
3818  /* there shouldn't be any other events, but just in case there are, let's swallow them
3819  * (i.e. to prevent problems with undo)
3820  */
3821  else {
3822  /* swallow event to save ourselves trouble */
3823  estate = OPERATOR_RUNNING_MODAL;
3824  }
3825  }
3826 
3827  /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
3828  if (0 == gpencil_area_exists(C, p->area)) {
3829  estate = OPERATOR_CANCELLED;
3830  }
3831  else {
3832  /* update status indicators - cursor, header, etc. */
3834  }
3835 
3836  /* process last operations before exiting */
3837  switch (estate) {
3838  case OPERATOR_FINISHED:
3839  /* store stroke angle for parallel guide */
3840  if ((p->straight == 0) || (guide->use_guide && (guide->type == GP_GUIDE_CIRCULAR))) {
3841  float xy[2];
3842  sub_v2_v2v2(xy, p->mval, p->mvali);
3843  float angle = atan2f(xy[1], xy[0]);
3844  RNA_float_set(op->ptr, "guide_last_angle", angle);
3845  }
3846  /* one last flush before we're done */
3847  gpencil_draw_exit(C, op);
3849  break;
3850 
3851  case OPERATOR_CANCELLED:
3852  gpencil_draw_exit(C, op);
3853  break;
3854 
3856  /* event doesn't need to be handled */
3857  break;
3858  }
3859 
3860  /* return status code */
3861  return estate;
3862 }
3863 
3864 /* ------------------------------- */
3865 
3867  {GP_PAINTMODE_DRAW, "DRAW", 0, "Draw Freehand", "Draw freehand stroke(s)"},
3869  "DRAW_STRAIGHT",
3870  0,
3871  "Draw Straight Lines",
3872  "Draw straight line segment(s)"},
3873  {GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", "Erase Grease Pencil strokes"},
3874  {0, NULL, 0, NULL, NULL},
3875 };
3876 
3878 {
3879  PropertyRNA *prop;
3880 
3881  /* identifiers */
3882  ot->name = "Grease Pencil Draw";
3883  ot->idname = "GPENCIL_OT_draw";
3884  ot->description = "Draw a new stroke in the active Grease Pencil object";
3885 
3886  /* api callbacks */
3892 
3893  /* flags */
3895 
3896  /* settings for drawing */
3897  ot->prop = RNA_def_enum(
3898  ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements");
3899 
3900  prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
3902 
3903  /* NOTE: wait for input is enabled by default,
3904  * so that all UI code can work properly without needing users to know about this */
3905  prop = RNA_def_boolean(ot->srna,
3906  "wait_for_input",
3907  true,
3908  "Wait for Input",
3909  "Wait for first click instead of painting immediately");
3911 
3912  prop = RNA_def_boolean(
3913  ot->srna, "disable_straight", false, "No Straight lines", "Disable key for straight lines");
3915 
3916  prop = RNA_def_boolean(ot->srna,
3917  "disable_fill",
3918  false,
3919  "No Fill Areas",
3920  "Disable fill to use stroke as fill boundary");
3922 
3923  prop = RNA_def_boolean(ot->srna, "disable_stabilizer", false, "No Stabilizer", "");
3925 
3926  /* guides */
3927  prop = RNA_def_float(ot->srna,
3928  "guide_last_angle",
3929  0.0f,
3930  -10000.0f,
3931  10000.0f,
3932  "Angle",
3933  "Speed guide angle",
3934  -10000.0f,
3935  10000.0f);
3936 }
3937 
3938 /* additional OPs */
3939 
3941 {
3943  GP_Sculpt_Guide *guide = &ts->gp_sculpt.guide;
3944  float angle = RNA_float_get(op->ptr, "angle");
3945  bool increment = RNA_boolean_get(op->ptr, "increment");
3946  if (increment) {
3947  float oldangle = guide->angle;
3948  oldangle += angle;
3949  guide->angle = angle_compat_rad(oldangle, M_PI);
3950  }
3951  else {
3952  guide->angle = angle_compat_rad(angle, M_PI);
3953  }
3954 
3955  return OPERATOR_FINISHED;
3956 }
3957 
3959 {
3960  /* identifiers */
3961  ot->name = "Rotate Guide Angle";
3962  ot->idname = "GPENCIL_OT_guide_rotate";
3963  ot->description = "Rotate guide angle";
3964 
3965  /* api callbacks */
3967 
3968  /* flags */
3970 
3971  PropertyRNA *prop;
3972 
3973  prop = RNA_def_boolean(ot->srna, "increment", true, "Increment", "Increment angle");
3975  prop = RNA_def_float(
3976  ot->srna, "angle", 0.0f, -10000.0f, 10000.0f, "Angle", "Guide angle", -10000.0f, 10000.0f);
3978 }
typedef float(TangentPoint)[2]
void BKE_brush_gpencil_paint_presets(struct Main *bmain, struct ToolSettings *ts, bool reset)
Definition: brush.cc:1308
struct Brush * BKE_brush_add_gpencil(struct Main *bmain, struct ToolSettings *ts, const char *name, eObjectMode mode)
Definition: brush.cc:536
void BKE_curvemapping_init(struct CurveMapping *cumap)
Definition: colortools.c:1235
float BKE_curvemapping_evaluateF(const struct CurveMapping *cumap, int cur, float value)
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:738
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct bGPDlayer * CTX_data_active_gpencil_layer(const bContext *C)
Definition: context.c:1450
struct bGPdata * CTX_data_gpencil_data(const bContext *C)
Definition: context.c:1445
struct Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Definition: context.c:1528
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1353
struct bScreen * CTX_wm_screen(const bContext *C)
Definition: context.c:733
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Definition: context.c:1505
void CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg)
Definition: context.c:1042
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
struct ToolSettings * CTX_data_tool_settings(const bContext *C)
Definition: context.c:1282
struct wmWindow * CTX_wm_window(const bContext *C)
Definition: context.c:723
support for deformation groups and hooks.
struct MDeformWeight * BKE_defvert_ensure_index(struct MDeformVert *dv, int defgroup)
Definition: deform.c:748
struct bGPDlayer * BKE_gpencil_layer_active_get(struct bGPdata *gpd)
Definition: gpencil.c:1558
void BKE_gpencil_free_stroke_weights(struct bGPDstroke *gps)
Definition: gpencil.c:361
void BKE_gpencil_stroke_select_index_reset(struct bGPDstroke *gps)
Definition: gpencil.c:1161
struct bGPDlayer * BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive, bool add_to_header)
Definition: gpencil.c:621
void BKE_gpencil_dvert_ensure(struct bGPDstroke *gps)
Definition: gpencil.c:1891
bool BKE_gpencil_layer_is_editable(const struct bGPDlayer *gpl)
struct Material * BKE_gpencil_object_material_ensure_from_active_input_brush(struct Main *bmain, struct Object *ob, struct Brush *brush)
Definition: gpencil.c:1780
#define GPENCIL_ALPHA_OPACITY_THRESH
Definition: BKE_gpencil.h:323
struct bGPDframe * BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
Definition: gpencil.c:1232
int BKE_gpencil_object_material_get_index_from_brush(struct Object *ob, struct Brush *brush)
Definition: gpencil.c:1759
#define GPENCIL_STRENGTH_MIN
Definition: BKE_gpencil.h:324
struct bGPdata * BKE_gpencil_data_addnew(struct Main *bmain, const char name[])
Definition: gpencil.c:705
void BKE_gpencil_layer_transform_matrix_get(const struct Depsgraph *depsgraph, struct Object *obact, struct bGPDlayer *gpl, float diff_mat[4][4])
@ GP_GETFRAME_ADD_NEW
Definition: BKE_gpencil.h:341
@ GP_GETFRAME_ADD_COPY
Definition: BKE_gpencil.h:343
@ GP_GETFRAME_USE_PREV
Definition: BKE_gpencil.h:338
void BKE_gpencil_editcurve_stroke_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps, struct bGPDcurve *gpc)
void BKE_gpencil_stroke_copy_to_keyframes(struct bGPdata *gpd, struct bGPDlayer *gpl, struct bGPDframe *gpf, struct bGPDstroke *gps, bool tail)
void BKE_gpencil_stroke_simplify_adaptive(struct bGPdata *gpd, struct bGPDstroke *gps, float epsilon)
void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd, struct bGPDstroke *gps)
bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence, int iterations, struct bGPDstroke *r_gps)
void BKE_gpencil_stroke_smooth(struct bGPDstroke *gps, const float influence, const int iterations, const bool smooth_position, const bool smooth_strength, const bool smooth_thickness, const bool smooth_uv, const bool keep_shape, const float *weights)
void BKE_gpencil_stroke_boundingbox_calc(struct bGPDstroke *gps)
struct bGPDstroke * BKE_gpencil_stroke_delete_tagged_points(struct bGPdata *gpd, struct bGPDframe *gpf, struct bGPDstroke *gps, struct bGPDstroke *next_stroke, int tag_flags, bool select, bool flat_cap, int limit)
bool BKE_gpencil_stroke_smooth_point(struct bGPDstroke *gps, int point_index, float influence, int iterations, bool smooth_caps, bool keep_shape, struct bGPDstroke *r_gps)
bool BKE_gpencil_stroke_trim(struct bGPdata *gpd, struct bGPDstroke *gps)
void BKE_gpencil_tag_full_update(struct bGPdata *gpd, struct bGPDlayer *gpl, struct bGPDframe *gpf, struct bGPDstroke *gps)
General operations, lookup, etc. for materials.
short BKE_object_material_slot_find_index(struct Object *ob, struct Material *ma)
Definition: material.c:1205
void BKE_paint_brush_set(struct Paint *paint, struct Brush *br)
Definition: paint.c:617
struct Brush * BKE_paint_brush(struct Paint *paint)
Definition: paint.c:607
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
struct ARegion * BKE_area_find_region_xy(struct ScrArea *area, int regiontype, const int xy[2]) ATTR_NONNULL(3)
Definition: screen.c:898
BLI_INLINE float BLI_hash_int_01(unsigned int k)
Definition: BLI_hash.h:94
BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky)
Definition: BLI_hash.h:53
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:60
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:239
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:354
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int max_ii(int a, int b)
#define M_PI_2
Definition: BLI_math_base.h:23
MINLINE float square_f(float a)
MINLINE float interpf(float a, float b, float t)
#define M_PI
Definition: BLI_math_base.h:20
int interp_sparse_array(float *array, int list_size, float skipval)
Definition: math_geom.c:3917
MINLINE float cross_tri_v2(const float v1[2], const float v2[2], const float v3[2])
float closest_to_line_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2])
Definition: math_geom.c:3183
void unit_m4(float m[4][4])
Definition: rct.c:1090
bool invert_m4_m4(float R[4][4], const float A[4][4])
Definition: math_matrix.c:1287
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:729
void copy_m4_m4(float m1[4][4], const float m2[4][4])
Definition: math_matrix.c:77
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:739
void loc_eul_size_to_mat4(float R[4][4], const float loc[3], const float eul[3], const float size[3])
Definition: math_matrix.c:2547
float angle_compat_rad(float angle, float angle_compat)
#define DEG2RAD(_deg)
#define DEG2RADF(_deg)
float angle_v2v2v2(const float a[2], const float b[2], const float c[2]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:395
MINLINE void round_v2i_v2fl(int r[2], const float a[2])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v2fl_v2i(float r[2], const int a[2])
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void madd_v2_v2fl(float r[2], const float a[2], float f)
float angle_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
Definition: math_vector.c:423
MINLINE void sub_v2_v2(float r[2], const float a[2])
MINLINE float len_v2v2_int(const int v1[2], const int v2[2])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void mul_v2_fl(float r[2], float f)
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
Definition: math_vector.c:14
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
void dist_ensure_v2_v2fl(float v1[2], const float v2[2], float dist)
Definition: math_vector.c:928
MINLINE void copy_v3_v3(float r[3], const float a[3])
void interp_v4_v4v4(float r[4], const float a[4], const float b[4], float t)
Definition: math_vector.c:38
void mid_v2_v2v2(float r[2], const float a[2], const float b[2])
Definition: math_vector.c:244
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float dot_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
void rotate_v2_v2fl(float r[2], const float p[2], float angle)
Definition: math_vector.c:765
MINLINE float len_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void zero_v3(float r[3])
MINLINE float normalize_v2(float r[2])
Random number functions.
void BLI_rng_free(struct RNG *rng) ATTR_NONNULL(1)
Definition: rand.cc:58
struct RNG * BLI_rng_new(unsigned int seed)
Definition: rand.cc:39
float BLI_rng_get_float(struct RNG *rng) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition: rand.cc:93
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
bool BLI_rcti_isect_pt(const struct rcti *rect, int x, int y)
unsigned int uint
Definition: BLI_sys_types.h:67
unsigned short ushort
Definition: BLI_sys_types.h:68
#define POINTER_AS_UINT(i)
#define UNUSED(x)
#define ELEM(...)
#define MIN2(a, b)
#define CLAMP_MIN(a, b)
#define TIP_(msgid)
#define DATA_(msgid)
typedef double(DMatrix)[4][4]
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
void DEG_id_tag_update(struct ID *id, int flag)
struct Object * DEG_get_evaluated_object(const struct Depsgraph *depsgraph, struct Object *object)
@ ID_RECALC_TRANSFORM
Definition: DNA_ID.h:771
@ ID_RECALC_COPY_ON_WRITE
Definition: DNA_ID.h:834
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
@ GPAINT_TOOL_ERASE
@ GPAINT_TOOL_DRAW
@ GP_BRUSH_USE_STRENGTH_RAND_PRESS
@ GP_BRUSH_USE_STRENGTH_AT_STROKE
@ GP_BRUSH_USE_UV_RAND_PRESS
@ GP_BRUSH_USE_PRESS_AT_STROKE
@ GP_BRUSH_USE_UV_AT_STROKE
@ GP_BRUSH_USE_PRESSURE_RAND_PRESS
@ GP_BRUSH_STABILIZE_MOUSE_TEMP
@ GP_BRUSH_GROUP_RANDOM
@ GP_BRUSH_TRIM_STROKE
@ GP_BRUSH_OCCLUDE_ERASER
@ GP_BRUSH_DEFAULT_ERASER
@ GP_BRUSH_USE_STRENGTH_PRESSURE
@ GP_BRUSH_GROUP_SETTINGS
@ GP_BRUSH_USE_JITTER_PRESSURE
@ GP_BRUSH_USE_PRESSURE
@ GP_BRUSH_ERASER_SOFT
@ GP_BRUSH_ERASER_STROKE
@ GP_BRUSH_ERASER_HARD
@ GP_BRUSH_ICON_ERASE_SOFT
@ GP_STROKE_NOFILL
@ GP_STROKE_ERASER
@ GP_STROKE_SELECT
@ GP_STROKE_3DSPACE
#define GP_MAX_INPUT_SAMPLES
#define GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)
#define GPENCIL_PAINT_MODE(gpd)
@ GP_LAYER_LOCKED
@ GP_LAYER_HIDE
@ GP_FRAME_SELECT
@ GP_FRAME_PAINT
@ GP_DATA_STROKE_WEIGHTMODE
@ GP_DATA_CACHE_IS_DIRTY
@ GP_DATA_STROKE_PAINTMODE
@ GP_DATA_STROKE_SCULPTMODE
@ GP_DATA_STROKE_EDITMODE
#define GPENCIL_LAZY_MODE(brush, shift)
@ GP_SPOINT_TAG
@ GP_SPOINT_SELECT
@ GP_SPOINT_TEMP_TAG2
@ GP_SPOINT_TEMP_TAG
@ OB_MODE_PAINT_GPENCIL
Object is a sort of wrapper for general info.
@ OB_GPENCIL
@ GP_GUIDE_CIRCULAR
@ GP_GUIDE_ISO
@ GP_GUIDE_PARALLEL
@ GP_GUIDE_RADIAL
@ GP_GUIDE_GRID
@ GP_GUIDE_REF_OBJECT
@ GP_GUIDE_REF_CUSTOM
@ GP_GUIDE_REF_CURSOR
@ GP_PROJECT_VIEWSPACE
@ GP_PROJECT_DEPTH_VIEW
@ GP_PROJECT_DEPTH_STROKE_ENDPOINTS
@ GP_PROJECT_DEPTH_STROKE
@ GP_PROJECT_DEPTH_STROKE_FIRST
@ GP_TOOL_FLAG_RETAIN_LAST
@ GP_TOOL_FLAG_PAINT_ONBACK
@ GP_TOOL_FLAG_AUTOMERGE_STROKE
@ GP_TOOL_FLAG_CREATE_WEIGHTS
@ RGN_TYPE_TOOLS
#define RGN_TYPE_ANY
@ SPACE_VIEW3D
#define SPACE_TYPE_ANY
#define RV3D_CAMOB
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
@ OP_IS_MODAL_CURSOR_REGION
#define GPENCIL_MINIMUM_JOIN_DIST
Definition: ED_gpencil.h:49
#define IS_AUTOKEY_ON(scene)
bool ED_operator_regionactive(struct bContext *C)
Definition: screen_ops.c:91
void ED_region_tag_redraw(struct ARegion *region)
Definition: area.c:655
void ED_workspace_status_text(struct bContext *C, const char *str)
Definition: area.c:816
const rcti * ED_region_visible_rect(ARegion *region)
Definition: area.c:3763
bool ED_view3d_depth_read_cached(const ViewDepths *vd, const int mval[2], int margin, float *r_depth)
@ V3D_PROJ_TEST_NOP
Definition: ED_view3d.h:234
void ED_view3d_win_to_delta(const struct ARegion *region, const float xy_delta[2], float zfac, float r_out[3])
eV3DProjStatus ED_view3d_project_float_global(const struct ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
bool ED_view3d_depth_read_cached_seg(const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *depth)
@ V3D_PROJ_RET_OK
Definition: ED_view3d.h:217
void ED_view3d_depths_free(ViewDepths *depths)
Definition: view3d_draw.c:2366
@ V3D_DEPTH_NO_GPENCIL
Definition: ED_view3d.h:182
@ V3D_DEPTH_GPENCIL_ONLY
Definition: ED_view3d.h:184
float ED_view3d_calc_zfac(const struct RegionView3D *rv3d, const float co[3])
bool ED_view3d_depth_unproject_v3(const struct ARegion *region, const int mval[2], double depth, float r_location_world[3])
void view3d_region_operator_needs_opengl(struct wmWindow *win, struct ARegion *region)
bool ED_view3d_autodist_simple(struct ARegion *region, const int mval[2], float mouse_worldloc[3], int margin, const float *force_depth)
void ED_view3d_calc_camera_border(const struct Scene *scene, struct Depsgraph *depsgraph, const struct ARegion *region, const struct View3D *v3d, const struct RegionView3D *rv3d, struct rctf *r_viewborder, bool no_shift)
void ED_view3d_depth_override(struct Depsgraph *depsgraph, struct ARegion *region, struct View3D *v3d, struct Object *obact, eV3DDepthOverrideMode mode, struct ViewDepths **r_depths)
Definition: view3d_draw.c:2294
float ED_view3d_calc_depth_for_comparison(const struct RegionView3D *rv3d, const float co[3])
void immUniform2f(const char *name, float x, float y)
void immUniformColor4f(float r, float g, float b, float a)
void immUnbindProgram(void)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniform1i(const char *name, int x)
void immUniform1f(const char *name, float x)
void immUniformColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
GPUVertFormat * immVertexFormat(void)
void imm_draw_circle_fill_2d(uint shdr_pos, float x, float y, float radius, int nsegments)
void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float radius, int nsegments)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
@ GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR
Definition: GPU_shader.h:349
@ GPU_SHADER_2D_UNIFORM_COLOR
Definition: GPU_shader.h:201
@ GPU_BLEND_NONE
Definition: GPU_state.h:60
@ GPU_BLEND_ALPHA
Definition: GPU_state.h:62
void GPU_blend(eGPUBlend blend)
Definition: gpu_state.cc:39
void GPU_line_smooth(bool enable)
Definition: gpu_state.cc:75
void GPU_viewport_size_get_f(float coords[4])
Definition: gpu_state.cc:259
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
Platform independent time functions.
#define RNA_BEGIN(sptr, itemptr, propname)
Definition: RNA_access.h:543
#define RNA_END
Definition: RNA_access.h:550
@ PROP_SKIP_SAVE
Definition: RNA_types.h:218
@ PROP_HIDDEN
Definition: RNA_types.h:216
#define C
Definition: RandGen.cpp:25
#define V2D_IS_CLIPPED
Definition: UI_view2d.h:25
@ KM_PRESS
Definition: WM_types.h:267
@ KM_RELEASE
Definition: WM_types.h:268
@ OPTYPE_BLOCKING
Definition: WM_types.h:150
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
#define ND_MODE
Definition: WM_types.h:393
#define ND_SPACE_PROPERTIES
Definition: WM_types.h:472
#define NC_SCENE
Definition: WM_types.h:328
#define ND_TOOLSETTINGS
Definition: WM_types.h:397
#define NA_EDITED
Definition: WM_types.h:523
#define NC_GPENCIL
Definition: WM_types.h:349
@ KM_CTRL
Definition: WM_types.h:239
@ KM_ALT
Definition: WM_types.h:240
@ KM_SHIFT
Definition: WM_types.h:238
#define NC_SPACE
Definition: WM_types.h:342
eGPencil_PaintStatus
eGPencil_PaintFlags
eGP_StrokeAdd_Result
#define DEPTH_INVALID
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE btScalar angle(const btVector3 &v) const
Return the angle between this and another vector.
Definition: btVector3.h:356
#define sinf(x)
Definition: cuda/compat.h:102
#define cosf(x)
Definition: cuda/compat.h:101
Scene scene
const Depsgraph * depsgraph
eGPencil_PaintModes
@ GP_PAINTMODE_ERASER
@ GP_PAINTMODE_DRAW
@ GP_PAINTMODE_DRAW_STRAIGHT
@ GP_PAINTMODE_SET_CP
void gpencil_point_3d_to_xy(const GP_SpaceConversion *gsc, short flag, const float pt[3], float xy[2])
void gpencil_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4], bGPDspoint *r_pt)
void gpencil_apply_parent_point(struct Depsgraph *depsgraph, struct Object *obact, bGPDlayer *gpl, bGPDspoint *pt)
void gpencil_undo_finish(void)
Definition: gpencil_undo.c:164
void gpencil_apply_parent(struct Depsgraph *depsgraph, struct Object *obact, bGPDlayer *gpl, bGPDstroke *gps)
bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1)
void gpencil_subdivide_stroke(bGPdata *gpd, bGPDstroke *gps, int subdivide)
void gpencil_point_to_xy(const GP_SpaceConversion *gsc, const struct bGPDstroke *gps, const struct bGPDspoint *pt, int *r_x, int *r_y)
eGPencil_PaintStatus
Definition: gpencil_paint.c:81
@ GP_STATUS_PAINTING
Definition: gpencil_paint.c:83
@ GP_STATUS_IDLING
Definition: gpencil_paint.c:82
@ GP_STATUS_ERROR
Definition: gpencil_paint.c:84
@ GP_STATUS_DONE
Definition: gpencil_paint.c:85
void GPENCIL_OT_guide_rotate(wmOperatorType *ot)
static tGPsdata * gpencil_session_initpaint(bContext *C, wmOperator *op)
static void gpencil_update_cache(bGPdata *gpd)
static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static float gpencil_stroke_eraser_calc_influence(tGPsdata *p, const float mval[2], const int radius, const int co[2])
void GPENCIL_OT_draw(wmOperatorType *ot)
static void gpencil_stroke_doeraser(tGPsdata *p)
static tGPsdata * gpencil_stroke_begin(bContext *C, wmOperator *op)
static void gpencil_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps)
static void gpencil_paint_strokeend(tGPsdata *p)
static void gpencil_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const float mval[2])
struct tGPguide tGPguide
static void gpencil_session_validatebuffer(tGPsdata *p)
eGPencil_PaintFlags
Definition: gpencil_paint.c:97
@ GP_PAINTFLAG_HARD_ERASER
@ GP_PAINTFLAG_SELECTMASK
Definition: gpencil_paint.c:99
@ GP_PAINTFLAG_FIRSTRUN
Definition: gpencil_paint.c:98
@ GP_PAINTFLAG_REQ_VECTOR
@ GP_PAINTFLAG_STROKE_ERASER
static void gpencil_reproject_toplane(tGPsdata *p, bGPDstroke *gps)
static void gpencil_get_3d_reference(tGPsdata *p, float vec[3])
static void gpencil_rotate_v2_v2v2fl(float v[2], const float p[2], const float origin[2], const float angle)
static int gpencil_draw_exec(bContext *C, wmOperator *op)
static void gpencil_guide_event_handling(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p)
struct tGPsdata tGPsdata
static bool gpencil_project_check(tGPsdata *p)
static void gpencil_brush_angle_segment(tGPsdata *p, tGPspoint *pt_prev, tGPspoint *pt)
static void gpencil_session_free(tGPsdata *p)
static short gpencil_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure, double curtime)
static bool gpencil_is_tablet_eraser_active(const wmEvent *event)
static void gpencil_add_fake_points(const wmEvent *event, tGPsdata *p)
static void gpencil_stroke_eraser_dostroke(tGPsdata *p, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, const float mval[2], const int radius, const rcti *rect)
static void gpencil_add_arc_points(tGPsdata *p, const float mval[2], int segments)
static void gpencil_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[3], float *depth)
#define STROKE_VERTICAL
static void gpencil_snap_to_guide(const tGPsdata *p, const GP_Sculpt_Guide *guide, float point[2])
static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr)
static void gpencil_stroke_newfrombuffer(tGPsdata *p)
static void gpencil_stroke_soft_refine(bGPDstroke *gps)
static bool gpencil_stroke_filtermval(tGPsdata *p, const float mval[2], const float mvalo[2])
static void gpencil_paint_cleanup(tGPsdata *p)
static void gpencil_add_guide_points(const tGPsdata *p, const GP_Sculpt_Guide *guide, const float start[2], const float end[2], int segments)
static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
static void gpencil_origin_get(tGPsdata *p, float origin[2])
static float gpencil_snap_to_grid_fl(float v, const float offset, const float spacing)
#define MIN_MANHATTEN_PX
static bool gpencil_draw_poll(bContext *C)
static void gpencil_apply_randomness(tGPsdata *p, BrushGpencilSettings *brush_settings, tGPspoint *pt, const bool press, const bool strength, const bool uv)
static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
#define STROKE_HORIZONTAL
static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
#define MIN_EUCLIDEAN_PX
static bool gpencil_stroke_eraser_is_occluded(tGPsdata *p, bGPDlayer *gpl, bGPDspoint *pt, const int x, const int y)
static void gpencil_smooth_buffer(tGPsdata *p, float inf, int idx)
static Brush * gpencil_get_default_eraser(Main *bmain, ToolSettings *ts)
static void gpencil_origin_set(wmOperator *op, const int mval[2])
static void gpencil_smooth_segment(bGPdata *gpd, const float inf, int from_idx, int to_idx)
eGP_StrokeAdd_Result
Definition: gpencil_paint.c:89
@ GP_STROKEADD_INVALID
Definition: gpencil_paint.c:90
@ GP_STROKEADD_FULL
Definition: gpencil_paint.c:93
@ GP_STROKEADD_NORMAL
Definition: gpencil_paint.c:92
@ GP_STROKEADD_OVERFLOW
Definition: gpencil_paint.c:91
static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p)
static void gpencil_snap_to_rotated_grid_fl(float v[2], const float origin[2], const float spacing, const float angle)
static int gpencil_guide_rotate(bContext *C, wmOperator *op)
static void gpencil_init_colors(tGPsdata *p)
static void gpencil_brush_jitter(bGPdata *gpd, tGPspoint *pt, const float amplitude)
static void gpencil_draw_apply_event(bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph)
static void gpencil_draw_cancel(bContext *C, wmOperator *op)
static void gpencil_speed_guide_init(tGPsdata *p, GP_Sculpt_Guide *guide)
static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph)
static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p)
static void gpencil_stroke_unselect(bGPdata *gpd, bGPDstroke *gps)
static void gpencil_set_default_eraser(Main *bmain, Brush *brush_dft)
static void gpencil_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Depsgraph *depsgraph)
static const EnumPropertyItem prop_gpencil_drawmodes[]
static void gpencil_session_cleanup(tGPsdata *p)
static void gpencil_draw_exit(bContext *C, wmOperator *op)
static bool gpencil_area_exists(bContext *C, ScrArea *area_test)
static void gpencil_draw_toggle_eraser_cursor(tGPsdata *p, short enable)
int ED_gpencil_session_active(void)
Definition: gpencil_undo.c:44
void ED_gpencil_stroke_close_by_distance(bGPDstroke *gps, const float threshold)
Object * ED_gpencil_add_object(bContext *C, const float loc[3], ushort local_view_bits)
bool ED_gpencil_stroke_material_editable(Object *ob, const bGPDlayer *gpl, const bGPDstroke *gps)
void ED_gpencil_project_point_to_plane(const Scene *scene, const Object *ob, bGPDlayer *gpl, const RegionView3D *rv3d, const float origin[3], const int axis, bGPDspoint *pt)
void ED_gpencil_project_stroke_to_plane(const Scene *scene, const Object *ob, const RegionView3D *rv3d, bGPDlayer *gpl, bGPDstroke *gps, const float origin[3], const int axis)
void ED_gpencil_project_stroke_to_view(bContext *C, bGPDlayer *gpl, bGPDstroke *gps)
bGPDstroke * ED_gpencil_stroke_join_and_trim(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *gps_dst, const int pt_index)
bool ED_gpencil_stroke_can_use_direct(const ScrArea *area, const bGPDstroke *gps)
void ED_gpencil_fill_vertex_color_set(ToolSettings *ts, Brush *brush, bGPDstroke *gps)
void ED_gpencil_toggle_brush_cursor(bContext *C, bool enable, void *customdata)
void ED_gpencil_init_random_settings(Brush *brush, const int mval[2], GpRandomSettings *random_settings)
bGPDstroke * ED_gpencil_stroke_nearest_to_ends(bContext *C, const GP_SpaceConversion *gsc, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, const float ctrl1[2], const float ctrl2[2], const float radius, int *r_index)
bool ED_gpencil_stroke_check_collision(const GP_SpaceConversion *gsc, bGPDstroke *gps, const float mval[2], const int radius, const float diff_mat[4][4])
bGPdata ** ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *r_ptr)
void ED_gpencil_sbuffer_update_eval(bGPdata *gpd, Object *ob_eval)
void ED_gpencil_point_vertex_color_set(ToolSettings *ts, Brush *brush, bGPDspoint *pt, tGPspoint *tpt)
void ED_gpencil_stroke_extremes_to2d(const GP_SpaceConversion *gsc, const float diff_mat[4][4], bGPDstroke *gps, float r_ctrl1[2], float r_ctrl2[2])
void ED_gpencil_tpoint_to_point(ARegion *region, float origin[3], const tGPspoint *tpt, bGPDspoint *pt)
void ED_gpencil_drawing_reference_get(const Scene *scene, const Object *ob, char align_flag, float r_vec[3])
bool ED_gpencil_data_owner_is_annotation(PointerRNA *owner_ptr)
void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph, Object *ob, ToolSettings *ts, Brush *brush, Material *material, float random_color[3], float pen_pressure)
tGPspoint * ED_gpencil_sbuffer_ensure(tGPspoint *buffer_array, int *buffer_size, int *buffer_used, const bool clear)
#define UINT_MAX
Definition: hash_md5.c:43
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
format
Definition: logImageCore.h:38
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
ccl_device_inline float2 fabs(const float2 &a)
Definition: math_float2.h:222
ccl_device_inline float3 pow(float3 v, float e)
Definition: math_float3.h:533
#define atan2f(x, y)
Definition: metal/compat.h:227
#define fmodf(x, y)
Definition: metal/compat.h:230
#define fabsf(x)
Definition: metal/compat.h:219
static unsigned c
Definition: RandGen.cpp:83
static unsigned a[3]
Definition: RandGen.cpp:78
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:319
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:311
static void area(int d1, int d2, int e1, int e2, float weights[2])
T distance(const T &a, const T &b)
T midpoint(const T &a, const T &b)
static const pxr::TfToken out("out", pxr::TfToken::Immortal)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
Definition: rna_access.c:4874
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
Definition: rna_access.c:4980
void RNA_collection_add(PointerRNA *ptr, const char *name, PointerRNA *r_value)
Definition: rna_access.c:5215
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4957
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
Definition: rna_access.c:4968
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
Definition: rna_access.c:5015
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
Definition: rna_access.c:4992
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, float default_value, float hardmin, float hardmax, const char *ui_name, const char *ui_description, float softmin, float softmax)
Definition: rna_define.c:3836
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3493
PropertyRNA * RNA_def_collection_runtime(StructOrFunctionRNA *cont_, const char *identifier, StructRNA *type, const char *ui_name, const char *ui_description)
Definition: rna_define.c:4221
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
Definition: rna_define.c:1490
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3783
static const int steps
Definition: sky_nishita.cpp:19
void * regiondata
short regiontype
struct CurveMapping * curve_sensitivity
struct CurveMapping * curve_strength
struct CurveMapping * curve_jitter
struct CurveMapping * curve_rand_pressure
struct CurveMapping * curve_rand_strength
struct CurveMapping * curve_rand_saturation
struct CurveMapping * curve_rand_hue
struct CurveMapping * curve_rand_uv
struct CurveMapping * curve_rand_value
float smooth_stroke_factor
int smooth_stroke_radius
struct BrushGpencilSettings * gpencil_settings
char gpencil_tool
struct Object * reference_object
struct GP_Sculpt_Guide guide
struct bGPDlayer * gpl
struct ARegion * region
struct bGPdata * gpd
struct View2D * v2d
struct ScrArea * area
void * first
Definition: DNA_listBase.h:31
struct MDeformWeight * dw
Definition: BKE_main.h:121
ListBase brushes
Definition: BKE_main.h:193
int restore_mode
float loc[3]
void * data
struct Brush * brush
struct StructRNA * type
Definition: RNA_types.h:37
void * data
Definition: RNA_types.h:38
Definition: rand.cc:33
float persmat[4][4]
struct ToolSettings * toolsettings
struct RenderData r
View3DCursor cursor
ListBase spacedata
GpPaint * gp_paint
struct GP_Sculpt_Settings gp_sculpt
unsigned short local_view_uuid
struct View3D * localvd
struct bGPDframe * next
ListBase strokes
float color[4]
bGPDframe * actframe
float layer_mat[4][4]
float layer_invmat[4][4]
float rotation[3]
float scale[3]
float location[3]
float vert_color[4]
struct bGPDstroke * prev
bGPDspoint * points
float fill_opacity_fac
float aspect_ratio[2]
bGPDtriangle * triangles
struct bGPDcurve * editcurve
struct MDeformVert * dvert
struct bGPDstroke * next
ListBase vertex_group_names
float zdepth_offset
ListBase layers
int vertex_group_active_index
bGPdata_Runtime runtime
ListBase areabase
int ymin
Definition: DNA_vec_types.h:64
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63
int xmax
Definition: DNA_vec_types.h:63
float spacing
float origin_angle
float origin[2]
float unit[2]
float rot_angle
float origin_distance
float half_spacing
float rot_point[2]
float stroke_angle
char * align_flag
Scene * scene
Object * ob
bool disable_stabilizer
ScrArea * area
short straight[2]
View2D * v2d
Material * material
PointerRNA ownerPtr
Brush * brush
tGPguide guide
rctf subrect_data
float pressure
eGPencil_PaintStatus status
ViewDepths * depths
float imat[4][4]
float mvalo[2]
float opressure
rctf * subrect
eGPencil_PaintModes paintmode
short keymodifier
double curtime
double inittime
float custom_color[4]
ARegion * region
void * erasercursor
bGPDlayer * gpl
eGPencil_PaintFlags flags
double ocurtime
wmWindow * win
Brush * eraser
float mat[4][4]
Main * bmain
float totpixlen
float mvali[2]
bGPdata * gpd
GP_SpaceConversion gsc
bGPDframe * gpf
float mval[2]
GpRandomSettings random_settings
Object * ob_eval
bool disable_fill
float diff_mat[4][4]
bContext * C
struct Depsgraph * depsgraph
ReportList * reports
short radius
float pressure
Definition: ED_gpencil.h:83
float m_xy[2]
Definition: ED_gpencil.h:81
float uv_rot
Definition: ED_gpencil.h:91
float time
Definition: ED_gpencil.h:87
float strength
Definition: ED_gpencil.h:85
float vert_color[4]
Definition: ED_gpencil.h:97
float uv_fac
Definition: ED_gpencil.h:89
short val
Definition: WM_types.h:680
int xy[2]
Definition: WM_types.h:682
int mval[2]
Definition: WM_types.h:684
uint8_t modifier
Definition: WM_types.h:693
wmTabletData tablet
Definition: WM_types.h:705
short type
Definition: WM_types.h:678
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:919
const char * name
Definition: WM_types.h:888
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:935
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
void(* cancel)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:927
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
PropertyRNA * prop
Definition: WM_types.h:981
struct ReportList * reports
struct PointerRNA * ptr
double PIL_check_seconds_timer(void)
Definition: time.c:64
long int PIL_check_seconds_timer_i(void)
Definition: time.c:74
void WM_cursor_modal_set(wmWindow *win, int val)
Definition: wm_cursors.c:191
void WM_cursor_modal_restore(wmWindow *win)
Definition: wm_cursors.c:200
@ WM_CURSOR_NSEW_SCROLL
Definition: wm_cursors.h:51
int xy[2]
Definition: wm_draw.c:135
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ EVT_TABLET_NONE
@ EVT_TABLET_ERASER
#define ISMOUSE_MOTION(event_type)
@ EVT_PAD8
@ EVT_PAD2
@ RIGHTMOUSE
@ EVT_OKEY
@ EVT_PAD4
@ EVT_JKEY
@ EVT_PAD0
@ EVT_VKEY
@ EVT_PAD9
@ EVT_CKEY
@ EVT_KKEY
@ EVT_DOWNARROWKEY
@ EVT_PAD3
@ WHEELUPMOUSE
@ EVT_RIGHTARROWKEY
@ EVT_PADENTER
@ EVT_SPACEKEY
@ WHEELDOWNMOUSE
@ EVT_PAD6
@ EVT_PAD5
@ EVT_MKEY
@ EVT_PADMINUS
@ EVT_UPARROWKEY
@ LEFTMOUSE
@ EVT_LEFTARROWKEY
@ NDOF_MOTION
@ EVT_ZKEY
@ EVT_ESCKEY
@ EVT_PAD1
@ EVT_LKEY
@ EVT_PAD7
@ EVT_PADPLUSKEY
@ EVT_RETKEY
#define ISKEYBOARD(event_type)
wmOperatorType * ot
Definition: wm_files.c:3479
bool WM_paint_cursor_end(wmPaintCursor *handle)
wmPaintCursor * WM_paint_cursor_activate(short space_type, short region_type, bool(*poll)(bContext *C), wmPaintCursorDraw draw, void *customdata)