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