[Bf-blender-cvs] [468f2ccc0ec] master: GPencil: Add more types of stroke extensions when filling

Dave Pagurek noreply at git.blender.org
Sat Sep 17 12:41:19 CEST 2022


Commit: 468f2ccc0ecbcb368aab950c61c2cc0bc95b27e1
Author: Dave Pagurek
Date:   Fri Sep 16 10:53:07 2022 +0200
Branches: master
https://developer.blender.org/rB468f2ccc0ecbcb368aab950c61c2cc0bc95b27e1

GPencil: Add more types of stroke extensions when filling

The motivation for this change: while working on an animation recently, I found that there are some gaps that won't close easily via stroke extension or leak size checking. In D14698, I attempted to address this by changing the algorithm of the raster-space flood fill. This patch attempts to address the same issue in vector space by adding two new cases where stroke extensions are added, as suggested by @frogstomp:

  # **Points of high curvature:** when the curvature at a point is high enough that it's hard to visually distinguish between it and an endpoint, add a stroke extension out along the normal (pointing in the opposite direction of the stroke's acceleration.) This addresses cases where technically the endpoint points up, but there's a sharp corner right below it that should extend to connect.

  # **Stroke endpoints within a radius**: when two endpoints are close together, regardless of the angle they make, connect them if they are within a radius. This addresses cases like where the two endpoints have effectively parallel tangents, so extensions won't close the gap.

Reviewed By: antoniov, mendio, frogstomp

Differential Revision: https://developer.blender.org/D14809

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

M	release/scripts/startup/bl_ui/space_view3d_toolbar.py
M	source/blender/editors/gpencil/gpencil_fill.c
M	source/blender/makesdna/DNA_brush_enums.h
M	source/blender/makesdna/DNA_brush_types.h
M	source/blender/makesdna/DNA_gpencil_types.h
M	source/blender/makesrna/intern/rna_brush.c

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

diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 78e7932433e..88add2a8f3b 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -1605,6 +1605,10 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel):
                 row = col.row(align=True)
                 row.prop(gp_settings, "fill_layer_mode", text="Layers")
 
+                col.separator()
+                row = col.row(align=True)
+                row.prop(gp_settings, "fill_extend_mode")
+
                 col.separator()
                 row = col.row(align=True)
                 row.prop(gp_settings, "extend_stroke_factor")
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 5305c764b3a..819e3b271ac 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -140,6 +140,8 @@ typedef struct tGPDfill {
   int fill_simplylvl;
   /** boundary limits drawing mode */
   int fill_draw_mode;
+  /** types of extensions **/
+  int fill_extend_mode;
   /* scaling factor */
   float fill_factor;
 
@@ -197,7 +199,10 @@ static void gpencil_delete_temp_stroke_extension(tGPDfill *tgpf, const bool all_
     for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
       LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
         /* free stroke */
-        if ((gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG)) {
+        if (
+            (gps->flag & GP_STROKE_NOFILL) &&
+            (gps->flag & GP_STROKE_TAG || gps->flag & GP_STROKE_HELP))
+        {
           BLI_remlink(&gpf->strokes, gps);
           BKE_gpencil_free_stroke(gps);
         }
@@ -209,6 +214,70 @@ static void gpencil_delete_temp_stroke_extension(tGPDfill *tgpf, const bool all_
   }
 }
 
+static bool extended_bbox_overlap(float min1[3],
+                                  float max1[3],
+                                  float min2[3],
+                                  float max2[3],
+                                  float extend)
+{
+  for (int axis = 0; axis < 3; axis++) {
+    float intersection_min = max_ff(min1[axis], min2[axis]) - extend;
+    float intersection_max = min_ff(max1[axis], max2[axis]) + extend;
+    if (intersection_min > intersection_max) {
+      return false;
+    }
+  }
+  return true;
+}
+
+static void add_stroke_extension(bGPDframe *gpf,
+                                 bGPDstroke *gps,
+                                 float p1[3],
+                                 float p2[3])
+{
+  bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
+  gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
+  BLI_addtail(&gpf->strokes, gps_new);
+
+  bGPDspoint *pt = &gps_new->points[0];
+  copy_v3_v3(&pt->x, p1);
+  pt->strength = 1.0f;
+  pt->pressure = 1.0f;
+
+  pt = &gps_new->points[1];
+  copy_v3_v3(&pt->x, p2);
+  pt->strength = 1.0f;
+  pt->pressure = 1.0f;
+}
+
+static void add_endpoint_radius_help(bGPDframe *gpf,
+                                     bGPDstroke *gps,
+                                     const float endpoint[3],
+                                     const float radius,
+                                     const bool focused)
+{
+  float circumference = 2.0f * M_PI * radius;
+  float vertex_spacing = 0.005f;
+  int num_vertices = min_ii(max_ii((int)ceilf(circumference / vertex_spacing), 3), 40);
+
+  bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, num_vertices, gps->thickness);
+  gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_CYCLIC | GP_STROKE_HELP;
+  if (focused) {
+    gps_new->flag |= GP_STROKE_TAG;
+  }
+  BLI_addtail(&gpf->strokes, gps_new);
+
+  for (int i = 0; i < num_vertices; i++) {
+    float angle = ((float)i / (float)num_vertices) * 2.0f * M_PI;
+    bGPDspoint *pt = &gps_new->points[i];
+    pt->x = endpoint[0] + radius * cosf(angle);
+    pt->y = endpoint[1];
+    pt->z = endpoint[2] + radius * sinf(angle);
+    pt->strength = 1.0f;
+    pt->pressure = 1.0f;
+  }
+}
+
 static void extrapolate_points_by_length(bGPDspoint *a,
                                          bGPDspoint *b,
                                          float length,
@@ -235,6 +304,9 @@ static void gpencil_create_extensions(tGPDfill *tgpf)
   const int gpl_active_index = BLI_findindex(&gpd->layers, gpl_active);
   BLI_assert(gpl_active_index >= 0);
 
+  float connection_dist = tgpf->fill_extend_fac * 0.1f;
+  GSet *connected_endpoints = BLI_gset_ptr_new(__func__);
+
   LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
     if (gpl->flag & GP_LAYER_HIDE) {
       continue;
@@ -266,41 +338,161 @@ static void gpencil_create_extensions(tGPDfill *tgpf)
         continue;
       }
 
-      /* Extend start. */
-      bGPDspoint *pt0 = &gps->points[1];
-      bGPDspoint *pt1 = &gps->points[0];
-      bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
-      gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
-      BLI_addtail(&gpf->strokes, gps_new);
-
-      bGPDspoint *pt = &gps_new->points[0];
-      copy_v3_v3(&pt->x, &pt1->x);
-      pt->strength = 1.0f;
-      pt->pressure = 1.0f;
-
-      pt = &gps_new->points[1];
-      pt->strength = 1.0f;
-      pt->pressure = 1.0f;
-      extrapolate_points_by_length(pt0, pt1, tgpf->fill_extend_fac * 0.1f, &pt->x);
-
-      /* Extend end. */
-      pt0 = &gps->points[gps->totpoints - 2];
-      pt1 = &gps->points[gps->totpoints - 1];
-      gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
-      gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
-      BLI_addtail(&gpf->strokes, gps_new);
-
-      pt = &gps_new->points[0];
-      copy_v3_v3(&pt->x, &pt1->x);
-      pt->strength = 1.0f;
-      pt->pressure = 1.0f;
-
-      pt = &gps_new->points[1];
-      pt->strength = 1.0f;
-      pt->pressure = 1.0f;
-      extrapolate_points_by_length(pt0, pt1, tgpf->fill_extend_fac * 0.1f, &pt->x);
+      /* Find points of high curvature. */
+      float tan1[3];
+      float tan2[3];
+      float d1;
+      float d2;
+      float total_length = 0.f;
+      for (int i = 1; i < gps->totpoints; i++) {
+        if (i > 1) {
+          copy_v3_v3(tan1, tan2);
+          d1 = d2;
+        }
+        bGPDspoint *pt1 = &gps->points[i - 1];
+        bGPDspoint *pt2 = &gps->points[i];
+        sub_v3_v3v3(tan2, &pt2->x, &pt1->x);
+        d2 = normalize_v3(tan2);
+        total_length += d2;
+        if (i > 1) {
+          if (tgpf->fill_extend_mode == GP_FILL_EMODE_CIRCLES) {
+            continue;
+          }
+          float curvature[3];
+          sub_v3_v3v3(curvature, tan2, tan1);
+          float k = normalize_v3(curvature);
+          k /= min_ff(d1, d2);
+          float radius = 1.f / k;
+          /*
+           * The smaller the radius of curvature, the sharper the corner.
+           * The thicker the line, the larger the radius of curvature it
+           * takes to be visually indistinguishable from an endpoint.
+           */
+          float min_radius = gps->thickness * 0.0001f;
+
+          if (radius < min_radius) {
+            /* Extend along direction of curvature. */
+            bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
+            gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
+            BLI_addtail(&gpf->strokes, gps_new);
+
+            bGPDspoint *pt = &gps_new->points[0];
+            copy_v3_v3(&pt->x, &pt1->x);
+            pt->strength = 1.0f;
+            pt->pressure = 1.0f;
+
+            pt = &gps_new->points[1];
+            pt->strength = 1.0f;
+            pt->pressure = 1.0f;
+            mul_v3_fl(curvature, -connection_dist);
+            add_v3_v3v3(&pt->x, &pt1->x, curvature);
+          }
+        }
+      }
+
+      if (tgpf->fill_extend_mode != GP_FILL_EMODE_CIRCLES) {
+        /* Extend start. */
+        bGPDspoint *pt0 = &gps->points[1];
+        bGPDspoint *pt1 = &gps->points[0];
+        bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
+        gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
+        BLI_addtail(&gpf->strokes, gps_new);
+
+        bGPDspoint *pt = &gps_new->points[0];
+        copy_v3_v3(&pt->x, &pt1->x);
+        pt->strength = 1.0f;
+        pt->pressure = 1.0f;
+
+        pt = &gps_new->points[1];
+        pt->strength = 1.0f;
+        pt->pressure = 1.0f;
+        extrapolate_points_by_length(pt0, pt1, connection_dist, &pt->x);
+
+        /* Extend end. */
+        pt0 = &gps->points[gps->totpoints - 2];
+        pt1 = &gps->points[gps->totpoints - 1];
+        gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
+        gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
+        BLI_addtail(&gpf->strokes, gps_new);
+
+        pt = &gps_new->points[0];
+        copy_v3_v3(&pt->x, &pt1->x);
+        pt->strength = 1.0f;
+        pt->pressure = 1.0f;
+
+        pt = &gps_new->points[1];
+        pt->strength = 1.0f;
+        pt->pressure = 1.0f;
+        extrapolate_points_by_length(pt0, pt1, connection_dist, &pt->x);
+      }
+
+      /* Connect endpoints within a radius */
+      if (tgpf->fill_extend_mode == GP_FILL_EMODE_LINES) {
+        continue;
+      }
+      float *stroke1_start = &gps->points[0].x;
+      float *stroke1_end = &gps->points[gps->totpoints - 1].x;
+      /* Connect the start of the stroke to its own end if the whole stroke
+       * isn't already so short that it's within that distance
+       */
+      if (len_v3v3(stroke1_start, stroke1_end) < connection_dist &&
+          total_length > connection_dist)
+      {
+        add_stroke_extension(gpf, gps, stroke1_start, stroke1_end);
+        BLI_gset_add(connected_endpoints, stroke1_start);
+        BLI_gset_add(connected_endpoints, stroke1_end);
+      }
+      for (bGPDstroke *gps2 = (bGPDstroke *)(((Link *)gps)->next);
+          gps2 != NULL;
+          gps2 = (bGPDstroke *)(((Link *)gps2)->next))
+      {
+        /* Don't check distance to temporary extensions. */
+        if ((gps2->flag & GP_STROKE_NOFILL) && (gps2->flag & GP_STROKE_TAG)) {
+          continue;
+  

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list