[Bf-blender-cvs] [29259a318de] greasepencil-object: GPencil: Merge by distance (WIP)

Antonioya noreply at git.blender.org
Mon Jul 29 16:00:31 CEST 2019


Commit: 29259a318de3a73a4df52a736b559244cd4dfa47
Author: Antonioya
Date:   Mon Jul 29 13:56:48 2019 +0200
Branches: greasepencil-object
https://developer.blender.org/rB29259a318de3a73a4df52a736b559244cd4dfa47

GPencil: Merge by distance (WIP)

Initial implementation of merge by distance
GPencil: Fix duplicated CMake entry


GPencil: Add missing code for operator in previous commit

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

M	release/scripts/startup/bl_ui/properties_grease_pencil_common.py
M	source/blender/blenkernel/BKE_gpencil.h
M	source/blender/blenkernel/intern/gpencil.c
M	source/blender/blenkernel/intern/gpencil_modifier.c
M	source/blender/editors/gpencil/annotate_paint.c
M	source/blender/editors/gpencil/gpencil_edit.c
M	source/blender/editors/gpencil/gpencil_intern.h
M	source/blender/editors/gpencil/gpencil_merge.c
M	source/blender/editors/gpencil/gpencil_ops.c
M	source/blender/editors/gpencil/gpencil_paint.c
M	source/blender/gpencil_modifiers/CMakeLists.txt

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

diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
index 112fb4361d6..3a623604934 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -616,6 +616,7 @@ class GPENCIL_MT_cleanup(Menu):
 
     def draw(self, _context):
         layout = self.layout
+        layout.operator("gpencil.stroke_merge_by_distance", text="Merge by Distance")
         layout.operator("gpencil.frame_clean_loose", text="Loose Points")
         layout.separator()
 
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index c145d8acd15..2a33c486288 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -277,6 +277,10 @@ void BKE_gpencil_simplify_stroke(struct bGPDstroke *gps, float factor);
 void BKE_gpencil_simplify_fixed(struct bGPDstroke *gps);
 void BKE_gpencil_subdivide(struct bGPDstroke *gps, int level, int flag);
 bool BKE_gpencil_trim_stroke(struct bGPDstroke *gps);
+void BKE_gpencil_merge_distance_stroke(struct bGPDframe *gpf,
+                                       struct bGPDstroke *gps,
+                                       const float threshold,
+                                       const bool use_unselected);
 
 void BKE_gpencil_stroke_2d_flat(const struct bGPDspoint *points,
                                 int totpoints,
@@ -309,6 +313,14 @@ bool BKE_gpencil_smooth_stroke_thickness(struct bGPDstroke *gps, int point_index
 bool BKE_gpencil_smooth_stroke_uv(struct bGPDstroke *gps, int point_index, float influence);
 bool BKE_gpencil_close_stroke(struct bGPDstroke *gps);
 
+void BKE_gpencil_delete_tagged_points(struct bGPDframe *gpf,
+                                      struct bGPDstroke *gps,
+                                      struct bGPDstroke *next_stroke,
+                                      int tag_flags,
+                                      bool select,
+                                      int limit);
+void BKE_gpencil_dissolve_points(struct bGPDframe *gpf, struct bGPDstroke *gps, const short tag);
+
 void BKE_gpencil_get_range_selected(struct bGPDlayer *gpl, int *r_initframe, int *r_endframe);
 float BKE_gpencil_multiframe_falloff_calc(
     struct bGPDframe *gpf, int actnum, int f_init, int f_end, struct CurveMapping *cur_falloff);
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 0d935f47805..78c1caf5385 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -2647,6 +2647,340 @@ bool BKE_gpencil_close_stroke(bGPDstroke *gps)
   return true;
 }
 
+/* Temp data for storing information about an "island" of points
+ * that should be kept when splitting up a stroke. Used in:
+ * BKE_gpencil_delete_tagged_points()
+ */
+typedef struct tGPDeleteIsland {
+  int start_idx;
+  int end_idx;
+} tGPDeleteIsland;
+
+static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDstroke *gps_last)
+{
+  bGPDspoint *pt = NULL;
+  bGPDspoint *pt_final = NULL;
+  const int totpoints = gps_first->totpoints + gps_last->totpoints;
+
+  /* create new stroke */
+  bGPDstroke *join_stroke = MEM_dupallocN(gps_first);
+
+  join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__);
+  join_stroke->totpoints = totpoints;
+  join_stroke->flag &= ~GP_STROKE_CYCLIC;
+
+  /* copy points (last before) */
+  int e1 = 0;
+  int e2 = 0;
+  float delta = 0.0f;
+
+  for (int i = 0; i < totpoints; i++) {
+    pt_final = &join_stroke->points[i];
+    if (i < gps_last->totpoints) {
+      pt = &gps_last->points[e1];
+      e1++;
+    }
+    else {
+      pt = &gps_first->points[e2];
+      e2++;
+    }
+
+    /* copy current point */
+    copy_v3_v3(&pt_final->x, &pt->x);
+    pt_final->pressure = pt->pressure;
+    pt_final->strength = pt->strength;
+    pt_final->time = delta;
+    pt_final->flag = pt->flag;
+
+    /* retiming with fixed time interval (we cannot determine real time) */
+    delta += 0.01f;
+  }
+
+  /* Copy over vertex weight data (if available) */
+  if ((gps_first->dvert != NULL) || (gps_last->dvert != NULL)) {
+    join_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * totpoints, __func__);
+    MDeformVert *dvert_src = NULL;
+    MDeformVert *dvert_dst = NULL;
+
+    /* Copy weights (last before)*/
+    e1 = 0;
+    e2 = 0;
+    for (int i = 0; i < totpoints; i++) {
+      dvert_dst = &join_stroke->dvert[i];
+      dvert_src = NULL;
+      if (i < gps_last->totpoints) {
+        if (gps_last->dvert) {
+          dvert_src = &gps_last->dvert[e1];
+          e1++;
+        }
+      }
+      else {
+        if (gps_first->dvert) {
+          dvert_src = &gps_first->dvert[e2];
+          e2++;
+        }
+      }
+
+      if ((dvert_src) && (dvert_src->dw)) {
+        dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
+      }
+    }
+  }
+
+  /* add new stroke at head */
+  BLI_addhead(&gpf->strokes, join_stroke);
+
+  /* remove first stroke */
+  BLI_remlink(&gpf->strokes, gps_first);
+  BKE_gpencil_free_stroke(gps_first);
+
+  /* remove last stroke */
+  BLI_remlink(&gpf->strokes, gps_last);
+  BKE_gpencil_free_stroke(gps_last);
+}
+
+/* Split the given stroke into several new strokes, partitioning
+ * it based on whether the stroke points have a particular flag
+ * is set (e.g. "GP_SPOINT_SELECT" in most cases, but not always)
+ *
+ * The algorithm used here is as follows:
+ * 1) We firstly identify the number of "islands" of non-tagged points
+ *    which will all end up being in new strokes.
+ *    - In the most extreme case (i.e. every other vert is a 1-vert island),
+ *      we have at most n / 2 islands
+ *    - Once we start having larger islands than that, the number required
+ *      becomes much less
+ * 2) Each island gets converted to a new stroke
+ * If the number of points is <= limit, the stroke is deleted
+ */
+void BKE_gpencil_delete_tagged_points(bGPDframe *gpf,
+                                      bGPDstroke *gps,
+                                      bGPDstroke *next_stroke,
+                                      int tag_flags,
+                                      bool select,
+                                      int limit)
+{
+  tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2,
+                                         "gp_point_islands");
+  bool in_island = false;
+  int num_islands = 0;
+
+  bGPDstroke *gps_first = NULL;
+  const bool is_cyclic = (bool)(gps->flag & GP_STROKE_CYCLIC);
+
+  /* First Pass: Identify start/end of islands */
+  bGPDspoint *pt = gps->points;
+  for (int i = 0; i < gps->totpoints; i++, pt++) {
+    if (pt->flag & tag_flags) {
+      /* selected - stop accumulating to island */
+      in_island = false;
+    }
+    else {
+      /* unselected - start of a new island? */
+      int idx;
+
+      if (in_island) {
+        /* extend existing island */
+        idx = num_islands - 1;
+        islands[idx].end_idx = i;
+      }
+      else {
+        /* start of new island */
+        in_island = true;
+        num_islands++;
+
+        idx = num_islands - 1;
+        islands[idx].start_idx = islands[idx].end_idx = i;
+      }
+    }
+  }
+
+  /* Watch out for special case where No islands = All points selected = Delete Stroke only */
+  if (num_islands) {
+    /* There are islands, so create a series of new strokes,
+     * adding them before the "next" stroke. */
+    int idx;
+    bGPDstroke *new_stroke = NULL;
+
+    /* Create each new stroke... */
+    for (idx = 0; idx < num_islands; idx++) {
+      tGPDeleteIsland *island = &islands[idx];
+      new_stroke = MEM_dupallocN(gps);
+
+      /* if cyclic and first stroke, save to join later */
+      if ((is_cyclic) && (gps_first == NULL)) {
+        gps_first = new_stroke;
+      }
+
+      /* initialize triangle memory  - to be calculated on next redraw */
+      new_stroke->triangles = NULL;
+      new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
+      new_stroke->flag &= ~GP_STROKE_CYCLIC;
+      new_stroke->tot_triangles = 0;
+
+      /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
+      new_stroke->totpoints = island->end_idx - island->start_idx + 1;
+
+      /* Copy over the relevant point data */
+      new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints,
+                                       "gp delete stroke fragment");
+      memcpy(new_stroke->points,
+             gps->points + island->start_idx,
+             sizeof(bGPDspoint) * new_stroke->totpoints);
+
+      /* Copy over vertex weight data (if available) */
+      if (gps->dvert != NULL) {
+        /* Copy over the relevant vertex-weight points */
+        new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints,
+                                        "gp delete stroke fragment weight");
+        memcpy(new_stroke->dvert,
+               gps->dvert + island->start_idx,
+               sizeof(MDeformVert) * new_stroke->totpoints);
+
+        /* Copy weights */
+        int e = island->start_idx;
+        for (int i = 0; i < new_stroke->totpoints; i++) {
+          MDeformVert *dvert_src = &gps->dvert[e];
+          MDeformVert *dvert_dst = &new_stroke->dvert[i];
+          if (dvert_src->dw) {
+            dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
+          }
+          e++;
+        }
+      }
+      /* Each island corresponds to a new stroke.
+       * We must adjust the timings of these new strokes:
+       *
+       * Each point's timing data is a delta from stroke's inittime, so as we erase some points
+       * from the start of the stroke, we have to offset this inittime and all remaining points'
+       * delta values. This way we get a new stroke with exactly the same timing as if user had
+       * started drawing from the first non-removed point.
+       */
+      {
+        bGPDspoint *pts;
+        float delta = gps->points[island->start_idx].time;
+        int j;
+
+        new_stroke->inittime += (double)delta;
+
+        pts = new_stroke->

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list