[Bf-blender-cvs] [21f201e25e7] master: GPencil: Improve join operator

Antonio Vazquez noreply at git.blender.org
Thu Oct 29 20:15:05 CET 2020


Commit: 21f201e25e70cc4b2259c963b4504d4325fef1c7
Author: Antonio Vazquez
Date:   Thu Oct 29 20:05:49 2020 +0100
Branches: master
https://developer.blender.org/rB21f201e25e70cc4b2259c963b4504d4325fef1c7

GPencil: Improve join operator

Now the strokes join the points near, not always end with start.

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

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

M	source/blender/editors/gpencil/gpencil_edit.c

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

diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 98789706a13..fceb9a00f07 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -3398,24 +3398,50 @@ static void gpencil_stroke_join_strokes(bGPDstroke *gps_a,
   }
 
   /* define start and end points of each stroke */
-  float area[3], sb[3], ea[3], eb[3];
+  float start_a[3], start_b[3], end_a[3], end_b[3];
   pt = &gps_a->points[0];
-  copy_v3_v3(area, &pt->x);
+  copy_v3_v3(start_a, &pt->x);
 
   pt = &gps_a->points[gps_a->totpoints - 1];
-  copy_v3_v3(ea, &pt->x);
+  copy_v3_v3(end_a, &pt->x);
 
   pt = &gps_b->points[0];
-  copy_v3_v3(sb, &pt->x);
+  copy_v3_v3(start_b, &pt->x);
 
   pt = &gps_b->points[gps_b->totpoints - 1];
-  copy_v3_v3(eb, &pt->x);
+  copy_v3_v3(end_b, &pt->x);
 
-  /* review if need flip stroke B */
-  float ea_sb = len_squared_v3v3(ea, sb);
-  float ea_eb = len_squared_v3v3(ea, eb);
-  /* flip if distance to end point is shorter */
-  if (ea_eb < ea_sb) {
+  /* Check if need flip strokes. */
+  float dist = len_squared_v3v3(end_a, start_b);
+  bool flip_a = false;
+  bool flip_b = false;
+  float lowest = dist;
+
+  dist = len_squared_v3v3(end_a, end_b);
+  if (dist < lowest) {
+    lowest = dist;
+    flip_a = false;
+    flip_b = true;
+  }
+
+  dist = len_squared_v3v3(start_a, start_b);
+  if (dist < lowest) {
+    lowest = dist;
+    flip_a = true;
+    flip_b = false;
+  }
+
+  dist = len_squared_v3v3(start_a, end_b);
+  if (dist < lowest) {
+    lowest = dist;
+    flip_a = true;
+    flip_b = true;
+  }
+
+  if (flip_a) {
+    gpencil_flip_stroke(gps_a);
+  }
+  if (flip_b) {
     gpencil_flip_stroke(gps_b);
   }
 
@@ -3439,16 +3465,69 @@ static void gpencil_stroke_join_strokes(bGPDstroke *gps_a,
   }
 }
 
+typedef struct tJoinStrokes {
+  bGPDframe *gpf;
+  bGPDstroke *gps;
+  bool used;
+} tJoinStrokes;
+
+static int gpencil_get_nearest_stroke_index(tJoinStrokes *strokes_list,
+                                            const bGPDstroke *gps,
+                                            const int totstrokes)
+{
+  int index = -1;
+  float min_dist = FLT_MAX;
+  float dist, start_a[3], end_a[3], start_b[3], end_b[3];
+
+  bGPDspoint *pt = &gps->points[0];
+  copy_v3_v3(start_a, &pt->x);
+
+  pt = &gps->points[gps->totpoints - 1];
+  copy_v3_v3(end_a, &pt->x);
+
+  for (int i = 0; i < totstrokes; i++) {
+    tJoinStrokes *elem = &strokes_list[i];
+    if (elem->used) {
+      continue;
+    }
+    pt = &elem->gps->points[0];
+    copy_v3_v3(start_b, &pt->x);
+
+    pt = &elem->gps->points[elem->gps->totpoints - 1];
+    copy_v3_v3(end_b, &pt->x);
+
+    dist = len_squared_v3v3(start_a, start_b);
+    if (dist < min_dist) {
+      min_dist = dist;
+      index = i;
+    }
+    dist = len_squared_v3v3(start_a, end_b);
+    if (dist < min_dist) {
+      min_dist = dist;
+      index = i;
+    }
+    dist = len_squared_v3v3(end_a, start_b);
+    if (dist < min_dist) {
+      min_dist = dist;
+      index = i;
+    }
+    dist = len_squared_v3v3(end_a, end_b);
+    if (dist < min_dist) {
+      min_dist = dist;
+      index = i;
+    }
+  }
+
+  return index;
+}
+
 static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
 {
   bGPdata *gpd = ED_gpencil_data_get_active(C);
   bGPDlayer *activegpl = BKE_gpencil_layer_active_get(gpd);
   Object *ob = CTX_data_active_object(C);
-
-  bGPDframe *gpf_a = NULL;
-  bGPDstroke *stroke_a = NULL;
-  bGPDstroke *stroke_b = NULL;
-  bGPDstroke *new_stroke = NULL;
+  /* Limit the number of strokes to join. */
+  const int max_join_strokes = 64;
 
   const int type = RNA_enum_get(op->ptr, "type");
   const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
@@ -3464,87 +3543,89 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
 
   BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
 
-  /* read all selected strokes */
-  bool first = false;
+  int tot_strokes = 0;
+  /** Alloc memory  */
+  tJoinStrokes *strokes_list = MEM_malloc_arrayN(sizeof(tJoinStrokes), max_join_strokes, __func__);
+
+  /* Read all selected strokes to create a list. */
   CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
     bGPDframe *gpf = gpl->actframe;
     if (gpf == NULL) {
       continue;
     }
 
-    LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+    /* Add all stroke selected of the frame. */
+    LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
       if (gps->flag & GP_STROKE_SELECT) {
         /* skip strokes that are invalid for current view */
         if (ED_gpencil_stroke_can_use(C, gps) == false) {
           continue;
         }
-        /* check if the color is editable */
+        /* check if the color is editable. */
         if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) {
           continue;
         }
-
-        /* to join strokes, cyclic must be disabled */
-        gps->flag &= ~GP_STROKE_CYCLIC;
-
-        /* saves first frame and stroke */
-        if (!first) {
-          first = true;
-          gpf_a = gpf;
-          stroke_a = gps;
+        tJoinStrokes *elem = &strokes_list[tot_strokes];
+        elem->gpf = gpf;
+        elem->gps = gps;
+        elem->used = false;
+
+        tot_strokes++;
+        /* Limit the number of strokes. */
+        if (tot_strokes == max_join_strokes) {
+          BKE_reportf(op->reports,
+                      RPT_WARNING,
+                      "Too many strokes selected. Only joined first %d strokes.",
+                      max_join_strokes);
+          break;
         }
-        else {
-          stroke_b = gps;
-
-          /* create a new stroke if was not created before (only created if something to join) */
-          if (new_stroke == NULL) {
-            new_stroke = BKE_gpencil_stroke_duplicate(stroke_a, true);
+      }
+    }
+  }
+  CTX_DATA_END;
 
-            /* if new, set current color */
-            if (type == GP_STROKE_JOINCOPY) {
-              new_stroke->mat_nr = stroke_a->mat_nr;
-            }
-          }
+  /* Nothing to join. */
+  if (tot_strokes < 2) {
+    MEM_SAFE_FREE(strokes_list);
+    return OPERATOR_CANCELLED;
+  }
 
-          /* join new_stroke and stroke B. New stroke will contain all the previous data */
-          gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps);
+  /* Take first stroke. */
+  tJoinStrokes *elem = &strokes_list[0];
+  elem->used = true;
 
-          /* if join only, delete old strokes */
-          if (type == GP_STROKE_JOIN) {
-            if (stroke_a) {
-              /* Calc geometry data. */
-              BKE_gpencil_stroke_geometry_update(new_stroke);
+  /* Create a new stroke. */
+  bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(elem->gps, true);
+  gps_new->flag &= ~GP_STROKE_CYCLIC;
+  BLI_insertlinkbefore(&elem->gpf->strokes, elem->gps, gps_new);
 
-              BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke);
-              BLI_remlink(&gpf->strokes, stroke_a);
-              BKE_gpencil_free_stroke(stroke_a);
-              stroke_a = NULL;
-            }
-            if (stroke_b) {
-              BLI_remlink(&gpf->strokes, stroke_b);
-              BKE_gpencil_free_stroke(stroke_b);
-              stroke_b = NULL;
-            }
-          }
-        }
-      }
+  /* Join all strokes until the list is completed. */
+  while (true) {
+    int i = gpencil_get_nearest_stroke_index(strokes_list, gps_new, tot_strokes);
+    if (i < 0) {
+      break;
     }
+    tJoinStrokes *elem = &strokes_list[i];
+    /* Join new_stroke and stroke B. */
+    gpencil_stroke_join_strokes(gps_new, elem->gps, leave_gaps);
+    elem->used = true;
   }
-  CTX_DATA_END;
 
-  /* add new stroke if was not added before */
-  if (type == GP_STROKE_JOINCOPY) {
-    if (new_stroke) {
-      /* Add a new frame if needed */
-      if (activegpl->actframe == NULL) {
-        activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum);
-      }
-      /* Calc geometry data. */
-      BKE_gpencil_stroke_geometry_update(new_stroke);
+  /* Calc geometry data for new stroke. */
+  BKE_gpencil_stroke_geometry_update(gps_new);
 
-      BLI_addtail(&activegpl->actframe->strokes, new_stroke);
+  /* If join only, delete old strokes. */
+  if (type == GP_STROKE_JOIN) {
+    for (int i = 0; i < tot_strokes; i++) {
+      tJoinStrokes *elem = &strokes_list[i];
+      BLI_remlink(&elem->gpf->strokes, elem->gps);
+      BKE_gpencil_free_stroke(elem->gps);
     }
   }
 
+  /* Free memory. */
+  MEM_SAFE_FREE(strokes_list);
+
   /* notifiers */
   DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
   WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);



More information about the Bf-blender-cvs mailing list