[Bf-blender-cvs] [64f5b1d9863] greasepencil-object: GPencil: Add smart smooth for interpolated points

Antonio Vazquez noreply at git.blender.org
Thu Sep 26 16:01:09 CEST 2019


Commit: 64f5b1d986371357d254508c1161d648964d815e
Author: Antonio Vazquez
Date:   Thu Sep 26 15:59:34 2019 +0200
Branches: greasepencil-object
https://developer.blender.org/rB64f5b1d986371357d254508c1161d648964d815e

GPencil: Add smart smooth for interpolated points

When using the samples, the interpolated points get abrupt steps because the system cannot receive all events in a short period of time.

This is more noticeable when the samples are set to 10 and the pen is moved very fast. The problem with post-processing smooth is that is applied to all stroke and this removes details.

The smart smooth is automatic and detect only the segments in the stroke where the system was unable to capture all movements and apply a smooth algorithm.

===================================================================

M	source/blender/editors/gpencil/gpencil_paint.c
M	source/blender/editors/include/ED_gpencil.h

===================================================================

diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 28b7d15b632..071a14cee3c 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -636,6 +636,111 @@ static void gp_smooth_buffer(tGPsdata *p, float inf, int idx)
   ptc->strength = interpf(ptc->strength, strength, inf);
 }
 
+/* Helper: Apply smooth to segment from Index to Index */
+static void gp_smooth_segment(bGPdata *gpd, const float inf, int from_idx, int to_idx)
+{
+  const short num_points = to_idx - from_idx;
+  /* Do nothing if not enough points to smooth out */
+  if ((num_points < 3) || (inf == 0.0f)) {
+    return;
+  }
+
+  if (from_idx <= 2) {
+    return;
+  }
+
+  tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
+  const float steps = (num_points < 4) ? 3.0f : 4.0f;
+
+  for (int i = from_idx; i < to_idx + 1; i++) {
+
+    tGPspoint *pta = i >= 3 ? &points[i - 3] : NULL;
+    tGPspoint *ptb = i >= 2 ? &points[i - 2] : NULL;
+    tGPspoint *ptc = i >= 1 ? &points[i - 1] : &points[i];
+    tGPspoint *ptd = &points[i];
+
+    float sco[2] = {0.0f};
+    const float average_fac = 1.0f / steps;
+
+    /* Compute smoothed coordinate by taking the ones nearby */
+    if (pta) {
+      madd_v2_v2fl(sco, &pta->x, average_fac);
+    }
+    else {
+      madd_v2_v2fl(sco, &ptc->x, average_fac);
+    }
+
+    if (ptb) {
+      madd_v2_v2fl(sco, &ptb->x, average_fac);
+    }
+    else {
+      madd_v2_v2fl(sco, &ptc->x, average_fac);
+    }
+
+    madd_v2_v2fl(sco, &ptc->x, average_fac);
+
+    madd_v2_v2fl(sco, &ptd->x, average_fac);
+
+    /* Based on influence factor, blend between original and optimal smoothed coordinate. */
+    interp_v2_v2v2(&ptc->x, &ptc->x, sco, inf);
+  }
+}
+
+/* Smooth all the sections created with fake events to avoid abrupt transitions.
+ *
+ * As the fake events add points between two real events, this produces a straight line, but if
+ * there is 3 or more real points that used fakes, the stroke is not smooth and produces abrupt
+ * angles.
+ * This function reads these segments and finds the real points and smooth with the surrounding
+ * points. */
+static void gp_smooth_fake_segments(tGPsdata *p)
+{
+  bGPdata *gpd = p->gpd;
+  Brush *brush = p->brush;
+
+  if (brush->gpencil_settings->input_samples < 2) {
+    return;
+  }
+
+  tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
+  tGPspoint *pt = NULL;
+  /* Index where segment starts. */
+  int from_idx = 0;
+  /* Index where segment ends. */
+  int to_idx = 0;
+
+  bool doit = false;
+  /* Loop all points except the extremes. */
+  for (int i = 1; i < gpd->runtime.sbuffer_used - 1; i++) {
+    pt = &points[i];
+    bool is_fake = (bool)(pt->tflag & GP_TPOINT_FAKE);
+    to_idx = i;
+
+    /* Detect fake points in the stroke. */
+    if ((!doit) && (is_fake)) {
+      from_idx = i;
+      doit = true;
+    }
+    /* If detect control point after fake points, select a segment with same length in both sides,
+     * except if is more than stroke length. */
+    if ((doit) && (!is_fake)) {
+      if (i + (i - from_idx) < gpd->runtime.sbuffer_used - 1) {
+        to_idx = i + (i - from_idx);
+        /* Smooth this segments (need loop to get acumulative smooth). */
+        for (int r = 0; r < 10; r++) {
+          gp_smooth_segment(gpd, 0.1f, from_idx, to_idx);
+        }
+      }
+      else {
+        break;
+      }
+      /* Reset to new segments. */
+      from_idx = i;
+      doit = false;
+    }
+  }
+}
+
 /* Smooth the section added with fake events when pen moves very fast. */
 static void gp_smooth_fake_events(tGPsdata *p, int size_before, int size_after)
 {
@@ -724,7 +829,8 @@ static void gp_subdivide_buffer(tGPsdata *p, int step)
 }
 
 /* add current stroke-point to buffer (returns whether point was successfully added) */
-static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure, double curtime)
+static short gp_stroke_addpoint(
+    tGPsdata *p, const float mval[2], float pressure, double curtime, bool is_fake)
 {
   bGPdata *gpd = p->gpd;
   Brush *brush = p->brush;
@@ -783,6 +889,14 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
     /* get pointer to destination point */
     pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_used);
 
+    /* Set if point was created by fake events. */
+    if (is_fake) {
+      pt->tflag |= GP_TPOINT_FAKE;
+    }
+    else {
+      pt->tflag &= ~GP_TPOINT_FAKE;
+    }
+
     /* store settings */
     /* pressure */
     if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) {
@@ -955,8 +1069,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure
       bGPDspoint *pts;
       MDeformVert *dvert = NULL;
 
-      /* first time point is adding to temporary buffer -- need to allocate new point in stroke
-       */
+      /* First time point is adding to temporary buffer (need to allocate new point in stroke) */
       if (gpd->runtime.sbuffer_used == 0) {
         gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
         if (gps->dvert != NULL) {
@@ -1313,6 +1426,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
       }
     }
 
+    /* Smooth any point created with fake events when the mouse/pen move very fast. */
+    gp_smooth_fake_segments(p);
+
     pt = gps->points;
     dvert = gps->dvert;
 
@@ -2695,7 +2811,8 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p)
 /* ------------------------------- */
 
 /* create a new stroke point at the point indicated by the painting context */
-static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph)
+static void gpencil_draw_apply(
+    bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph, bool is_fake)
 {
   bGPdata *gpd = p->gpd;
   tGPspoint *pt = NULL;
@@ -2724,7 +2841,7 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra
     }
 
     /* try to add point */
-    short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
+    short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime, is_fake);
 
     /* handle errors while adding point */
     if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) {
@@ -2738,12 +2855,12 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra
       /* XXX We only need to reuse previous point if overflow! */
       if (ok == GP_STROKEADD_OVERFLOW) {
         p->inittime = p->ocurtime;
-        gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime);
+        gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime, is_fake);
       }
       else {
         p->inittime = p->curtime;
       }
-      gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
+      gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime, is_fake);
     }
     else if (ok == GP_STROKEADD_INVALID) {
       /* the painting operation cannot continue... */
@@ -2949,8 +3066,13 @@ static void gpencil_speed_guide(tGPsdata *p, GP_Sculpt_Guide *guide)
 }
 
 /* handle draw event */
-static void gpencil_draw_apply_event(
-    bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y)
+static void gpencil_draw_apply_event(bContext *C,
+                                     wmOperator *op,
+                                     const wmEvent *event,
+                                     Depsgraph *depsgraph,
+                                     float x,
+                                     float y,
+                                     const bool is_fake)
 {
   tGPsdata *p = op->customdata;
   GP_Sculpt_Guide *guide = &p->scene->toolsettings->gp_sculpt.guide;
@@ -3103,10 +3225,10 @@ static void gpencil_draw_apply_event(
     /* create fake events */
     float tmp[2];
     copy_v2_v2(tmp, p->mval);
-    gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1]);
+    gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], false);
     if (len_v2v2(p->mval, p->mvalo)) {
       sub_v2_v2v2(pt, p->mval, p->mvalo);
-      gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1]);
+      gpencil_draw_apply_event(C, op, event, depsgraph, pt[0], pt[1], false);
     }
     copy_v2_v2(p->mval, tmp);
   }
@@ -3137,7 +3259,7 @@ static void gpencil_draw_apply_event(
   RNA_float_set(&itemptr, "time", p->curtime - p->inittime);
 
   /* apply the current latest drawing point */
-  gpencil_draw_apply(C, op, p, depsgraph);
+  gpencil_draw_apply(C, op, p, depsgraph, is_fake);
 
   /* force refresh */
   /* just active area for now, since doing whole screen is too slow */
@@ -3203,7 +3325,7 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op)
     }
 
     /* apply this data as necessary now (as per usual) */
-    gpencil_draw_apply(C, op, p, depsgraph);
+    gpencil_draw_apply(C, op, p, depsgraph, false);
   }
   RNA_END;
 
@@ -3376,9 +3498,6 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
   /* TODO: set any additional settings that we can take from the events?
    * TODO? if tablet is erasing, force eraser to be on? */
 
-  /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway...
-   */
-
   /* if eraser is on, draw radial aid */
   if (p->paintmode == GP_PAINTMODE_ERASER) {
     gpencil_draw_toggle_eraser_cursor(C, p, true);
@@ -3399,7 +3518,8 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
     p->status = GP_STATUS_PAINTING;
 
     /* handle the initial drawing - i.e. for just doing a simple dot */
-    gpencil_draw_apply_event(C, op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f);
+    gpencil_draw_apply_event(
+        C, op, event, CTX_data_ensure_evaluated_depsgraph(C), 0.0f, 0.0f, false);
     op->flag |= OP_IS_MODAL_CURSOR_REGION;
   }
   else {
@@ -3516,11 +3636,8 @@ static void gpencil_move_last_stroke_to_back(bContext *C)
   BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps);
 }
 
-/* add events for missing mouse movements when the artist draw very fast */
-static bool gpencil_add_missing_events(bCo

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list