Code Cleanup
authorJoshua Leung <aligorith@gmail.com>
Fri, 26 Aug 2016 04:25:39 +0000 (16:25 +1200)
committerJoshua Leung <aligorith@gmail.com>
Fri, 26 Aug 2016 04:25:39 +0000 (16:25 +1200)
* Stroke editing functions should be in gpencil_edit.c not gpencil_data.c
  (the latter is only for handling "CRUD" operations on things like
   layers, brushes, and palettes)
* Deduplicate the GP_STROKE_BUFFER_MAX define

source/blender/editors/gpencil/gpencil_data.c
source/blender/editors/gpencil/gpencil_edit.c
source/blender/editors/gpencil/gpencil_intern.h
source/blender/editors/gpencil/gpencil_paint.c

index 2aa6d30c1144a60531b5a6db4c0e5baa91b3b131..83dc0b679ee9a34fb641559d7669f68515fc3685 100644 (file)
@@ -75,9 +75,6 @@
 
 #include "gpencil_intern.h"
 
-/* maximum sizes of gp-session buffer */
-#define GP_STROKE_BUFFER_MAX    5000
-
 /* ************************************************ */
 /* Datablock Operators */
 
@@ -965,431 +962,6 @@ void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot)
        ot->poll = gp_active_layer_poll;
 }
 
-/* ******************* Apply layer thickness change to Strokes ************************** */
-
-static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
-{
-       bGPdata *gpd = ED_gpencil_data_get_active(C);
-       bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
-
-       /* sanity checks */
-       if (ELEM(NULL, gpd, gpl, gpl->frames.first))
-               return OPERATOR_CANCELLED;
-
-       /* loop all strokes */
-       for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
-               for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
-                       /* Apply thickness */
-                       gps->thickness = gps->thickness + gpl->thickness;
-               }
-       }
-       /* clear value */
-       gpl->thickness = 0.0f;
-
-       /* notifiers */
-       WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
-       return OPERATOR_FINISHED;
-}
-
-void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
-{
-       /* identifiers */
-       ot->name = "Apply Stroke Thickness";
-       ot->idname = "GPENCIL_OT_stroke_apply_thickness";
-       ot->description = "Apply the thickness change of the layer to its strokes";
-
-       /* api callbacks */
-       ot->exec = gp_stroke_apply_thickness_exec;
-       ot->poll = gp_active_layer_poll;
-}
-
-/* ******************* Close Strokes ************************** */
-
-enum {
-       GP_STROKE_CYCLIC_CLOSE = 1,
-       GP_STROKE_CYCLIC_OPEN = 2,
-       GP_STROKE_CYCLIC_TOGGLE = 3
-};
-
-static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
-{
-       bGPdata *gpd = ED_gpencil_data_get_active(C);
-       const int type = RNA_enum_get(op->ptr, "type");
-
-       /* sanity checks */
-       if (ELEM(NULL, gpd))
-               return OPERATOR_CANCELLED;
-
-       /* loop all selected strokes */
-       CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
-       {
-               for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
-                       bGPDpalettecolor *palcolor = gps->palcolor;
-
-                       /* skip strokes that are not selected or invalid for current view */
-                       if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
-                               continue;
-                       /* skip hidden or locked colors */
-                       if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED))
-                               continue;
-
-                       switch (type) {
-                               case GP_STROKE_CYCLIC_CLOSE:
-                                       /* Close all (enable) */
-                                       gps->flag |= GP_STROKE_CYCLIC;
-                                       break;
-                               case GP_STROKE_CYCLIC_OPEN:
-                                       /* Open all (disable) */
-                                       gps->flag &= ~GP_STROKE_CYCLIC;
-                                       break;
-                               case GP_STROKE_CYCLIC_TOGGLE:
-                                       /* Just toggle flag... */
-                                       gps->flag ^= GP_STROKE_CYCLIC;
-                                       break;
-                               default:
-                                       BLI_assert(0);
-                                       break;
-                       }
-               }
-       }
-       CTX_DATA_END;
-
-       /* notifiers */
-       WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
-       return OPERATOR_FINISHED;
-}
-
-/**
- * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
- * option to force opened/closed strokes instead of just toggle behavior.
- */
-void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
-{
-       static EnumPropertyItem cyclic_type[] = {
-               {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""},
-               {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""},
-               {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
-               {0, NULL, 0, NULL, NULL}
-       };
-
-       /* identifiers */
-       ot->name = "Set Cyclical State";
-       ot->idname = "GPENCIL_OT_stroke_cyclical_set";
-       ot->description = "Close or open the selected stroke adding an edge from last to first point";
-
-       /* api callbacks */
-       ot->exec = gp_stroke_cyclical_set_exec;
-       ot->poll = gp_active_layer_poll;
-
-       /* flags */
-       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
-       ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
-}
-
-/* ******************* Stroke join ************************** */
-
-/* Helper: flip stroke */
-static void gpencil_flip_stroke(bGPDstroke *gps)
-{
-       bGPDspoint pt, *point, *point2;
-       int end = gps->totpoints - 1;
-
-       for (int i = 0; i < gps->totpoints / 2; i++) {
-               /* save first point */
-               point = &gps->points[i];
-               pt.x = point->x;
-               pt.y = point->y;
-               pt.z = point->z;
-               pt.flag = point->flag;
-               pt.pressure = point->pressure;
-               pt.strength = point->strength;
-               pt.time = point->time;
-
-               /* replace first point with last point */
-               point2 = &gps->points[end];
-               point->x = point2->x;
-               point->y = point2->y;
-               point->z = point2->z;
-               point->flag = point2->flag;
-               point->pressure = point2->pressure;
-               point->strength = point2->strength;
-               point->time = point2->time;
-
-               /* replace last point with first saved before */
-               point = &gps->points[end];
-               point->x = pt.x;
-               point->y = pt.y;
-               point->z = pt.z;
-               point->flag = pt.flag;
-               point->pressure = pt.pressure;
-               point->strength = pt.strength;
-               point->time = pt.time;
-
-               end--;
-       }
-}
-
-/* Helper: copy point between strokes */
-static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3],
-                                      float pressure, float strength, float deltatime)
-{
-       bGPDspoint *newpoint;
-
-       gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
-       gps->totpoints++;
-
-       newpoint = &gps->points[gps->totpoints - 1];
-       newpoint->x = point->x * delta[0];
-       newpoint->y = point->y * delta[1];
-       newpoint->z = point->z * delta[2];
-       newpoint->flag = point->flag;
-       newpoint->pressure = pressure;
-       newpoint->strength = strength;
-       newpoint->time = point->time + deltatime;
-}
-
-/* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */
-static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps)
-{
-       bGPDspoint point, *pt;
-       int i;
-       float delta[3] = {1.0f, 1.0f, 1.0f};
-       float deltatime = 0.0f;
-
-       /* sanity checks */
-       if (ELEM(NULL, gps_a, gps_b))
-               return;
-
-       if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0))
-               return;
-
-       /* define start and end points of each stroke */
-       float sa[3], sb[3], ea[3], eb[3];
-       pt = &gps_a->points[0];
-       copy_v3_v3(sa, &pt->x);
-
-       pt = &gps_a->points[gps_a->totpoints - 1];
-       copy_v3_v3(ea, &pt->x);
-
-       pt = &gps_b->points[0];
-       copy_v3_v3(sb, &pt->x);
-       
-       pt = &gps_b->points[gps_b->totpoints - 1];
-       copy_v3_v3(eb, &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) {
-               gpencil_flip_stroke(gps_b);
-       }
-
-       /* don't visibly link the first and last points? */
-       if (leave_gaps) {
-               /* 1st: add one tail point to start invisible area */
-               point = gps_a->points[gps_a->totpoints - 1];
-               deltatime = point.time;
-               gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f);
-
-               /* 2nd: add one head point to finish invisible area */
-               point = gps_b->points[0];
-               gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime);
-       }
-       
-       /* 3rd: add all points */
-       for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
-               /* check if still room in buffer */
-               if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) {
-                       gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime);
-               }
-       }
-}
-
-static int gp_stroke_join_exec(bContext *C, wmOperator *op)
-{
-       bGPdata *gpd = ED_gpencil_data_get_active(C);
-       bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
-       bGPDstroke *gps, *gpsn;
-       bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
-       bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
-
-       bGPDframe *gpf_a = NULL;
-       bGPDstroke *stroke_a = NULL;
-       bGPDstroke *stroke_b = NULL;
-       bGPDstroke *new_stroke = NULL;
-
-       const int type = RNA_enum_get(op->ptr, "type");
-       const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
-
-       /* sanity checks */
-       if (ELEM(NULL, gpd))
-               return OPERATOR_CANCELLED;
-
-       if (activegpl->flag & GP_LAYER_LOCKED)
-               return OPERATOR_CANCELLED;
-
-       BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
-
-
-       /* read all selected strokes */
-       bool first = false;
-       CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
-       {
-               bGPDframe *gpf = gpl->actframe;
-               for (gps = gpf->strokes.first; gps; gps = gpsn) {
-                       gpsn = gps->next;
-                       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 */
-                               if (ED_gpencil_stroke_color_use(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;
-                               }
-                               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 = MEM_dupallocN(stroke_a);
-                                               new_stroke->points = MEM_dupallocN(stroke_a->points);
-                                               new_stroke->triangles = NULL;
-                                               new_stroke->tot_triangles = 0;
-                                               new_stroke->flag |= GP_STROKE_RECALC_CACHES;
-                                               /* if new, set current color */
-                                               if (type == GP_STROKE_JOINCOPY) {
-                                                       new_stroke->palcolor = palcolor;
-                                                       BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname));
-                                                       new_stroke->flag |= GP_STROKE_RECALC_COLOR;
-                                               }
-                                       }
-                                       /* join new_stroke and stroke B. New stroke will contain all the previous data */
-                                       gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps);
-
-                                       /* if join only, delete old strokes */
-                                       if (type == GP_STROKE_JOIN) {
-                                               if (stroke_a) {
-                                                       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;
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-       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);
-
-                       BLI_addtail(&activegpl->actframe->strokes, new_stroke);
-               }
-       }
-
-       /* notifiers */
-       WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
-       return OPERATOR_FINISHED;
-}
-
-void GPENCIL_OT_stroke_join(wmOperatorType *ot)
-{
-       static EnumPropertyItem join_type[] = {
-               {GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
-               {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
-               {0, NULL, 0, NULL, NULL}
-       };
-
-       /* identifiers */
-       ot->name = "Join Strokes";
-       ot->idname = "GPENCIL_OT_stroke_join";
-       ot->description = "Join selected strokes (optionally as new stroke)";
-
-       /* api callbacks */
-       ot->exec = gp_stroke_join_exec;
-       ot->poll = gp_active_layer_poll;
-
-       /* flags */
-       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
-       ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
-       RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them");
-}
-
-/* ******************* Stroke flip ************************** */
-
-static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
-{
-       bGPdata *gpd = ED_gpencil_data_get_active(C);
-
-       /* sanity checks */
-       if (ELEM(NULL, gpd))
-               return OPERATOR_CANCELLED;
-
-       /* read all selected strokes */
-       CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
-       {
-               bGPDframe *gpf = gpl->actframe;
-               for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
-                       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 */
-                               if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
-                                       continue;
-                               }
-                               /* flip stroke */
-                               gpencil_flip_stroke(gps);
-                       }
-               }
-       }
-       CTX_DATA_END;
-
-       /* notifiers */
-       WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
-       return OPERATOR_FINISHED;
-}
-
-void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
-{
-       /* identifiers */
-       ot->name = "Flip Stroke";
-       ot->idname = "GPENCIL_OT_stroke_flip";
-       ot->description = "Change direction of the points of the selected strokes";
-
-       /* api callbacks */
-       ot->exec = gp_stroke_flip_exec;
-       ot->poll = gp_active_layer_poll;
-
-       /* flags */
-       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
 /* ************************************************ */
 /* Drawing Brushes Operators */
 
index e58178bf2a7fe9659f7f10b8459affd91283ad65..f25e873fa58eb93ccc0f2fa89ee041a498eac6c3 100644 (file)
@@ -1455,5 +1455,429 @@ void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
 }
 
+/* ******************* Apply layer thickness change to Strokes ************************** */
+
+static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       bGPdata *gpd = ED_gpencil_data_get_active(C);
+       bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+
+       /* sanity checks */
+       if (ELEM(NULL, gpd, gpl, gpl->frames.first))
+               return OPERATOR_CANCELLED;
+
+       /* loop all strokes */
+       for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+               for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+                       /* Apply thickness */
+                       gps->thickness = gps->thickness + gpl->thickness;
+               }
+       }
+       /* clear value */
+       gpl->thickness = 0.0f;
+
+       /* notifiers */
+       WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+       return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Apply Stroke Thickness";
+       ot->idname = "GPENCIL_OT_stroke_apply_thickness";
+       ot->description = "Apply the thickness change of the layer to its strokes";
+
+       /* api callbacks */
+       ot->exec = gp_stroke_apply_thickness_exec;
+       ot->poll = gp_active_layer_poll;
+}
+
+/* ******************* Close Strokes ************************** */
+
+enum {
+       GP_STROKE_CYCLIC_CLOSE = 1,
+       GP_STROKE_CYCLIC_OPEN = 2,
+       GP_STROKE_CYCLIC_TOGGLE = 3
+};
+
+static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
+{
+       bGPdata *gpd = ED_gpencil_data_get_active(C);
+       const int type = RNA_enum_get(op->ptr, "type");
+
+       /* sanity checks */
+       if (ELEM(NULL, gpd))
+               return OPERATOR_CANCELLED;
+
+       /* loop all selected strokes */
+       CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
+       {
+               for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
+                       bGPDpalettecolor *palcolor = gps->palcolor;
+
+                       /* skip strokes that are not selected or invalid for current view */
+                       if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
+                               continue;
+                       /* skip hidden or locked colors */
+                       if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED))
+                               continue;
+
+                       switch (type) {
+                               case GP_STROKE_CYCLIC_CLOSE:
+                                       /* Close all (enable) */
+                                       gps->flag |= GP_STROKE_CYCLIC;
+                                       break;
+                               case GP_STROKE_CYCLIC_OPEN:
+                                       /* Open all (disable) */
+                                       gps->flag &= ~GP_STROKE_CYCLIC;
+                                       break;
+                               case GP_STROKE_CYCLIC_TOGGLE:
+                                       /* Just toggle flag... */
+                                       gps->flag ^= GP_STROKE_CYCLIC;
+                                       break;
+                               default:
+                                       BLI_assert(0);
+                                       break;
+                       }
+               }
+       }
+       CTX_DATA_END;
+
+       /* notifiers */
+       WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+       return OPERATOR_FINISHED;
+}
+
+/**
+ * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
+ * option to force opened/closed strokes instead of just toggle behavior.
+ */
+void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
+{
+       static EnumPropertyItem cyclic_type[] = {
+               {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""},
+               {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""},
+               {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
+               {0, NULL, 0, NULL, NULL}
+       };
+
+       /* identifiers */
+       ot->name = "Set Cyclical State";
+       ot->idname = "GPENCIL_OT_stroke_cyclical_set";
+       ot->description = "Close or open the selected stroke adding an edge from last to first point";
+
+       /* api callbacks */
+       ot->exec = gp_stroke_cyclical_set_exec;
+       ot->poll = gp_active_layer_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
+}
+
+/* ******************* Stroke join ************************** */
+
+/* Helper: flip stroke */
+static void gpencil_flip_stroke(bGPDstroke *gps)
+{
+       bGPDspoint pt, *point, *point2;
+       int end = gps->totpoints - 1;
+
+       for (int i = 0; i < gps->totpoints / 2; i++) {
+               /* save first point */
+               point = &gps->points[i];
+               pt.x = point->x;
+               pt.y = point->y;
+               pt.z = point->z;
+               pt.flag = point->flag;
+               pt.pressure = point->pressure;
+               pt.strength = point->strength;
+               pt.time = point->time;
+
+               /* replace first point with last point */
+               point2 = &gps->points[end];
+               point->x = point2->x;
+               point->y = point2->y;
+               point->z = point2->z;
+               point->flag = point2->flag;
+               point->pressure = point2->pressure;
+               point->strength = point2->strength;
+               point->time = point2->time;
+
+               /* replace last point with first saved before */
+               point = &gps->points[end];
+               point->x = pt.x;
+               point->y = pt.y;
+               point->z = pt.z;
+               point->flag = pt.flag;
+               point->pressure = pt.pressure;
+               point->strength = pt.strength;
+               point->time = pt.time;
+
+               end--;
+       }
+}
+
+/* Helper: copy point between strokes */
+static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3],
+                                      float pressure, float strength, float deltatime)
+{
+       bGPDspoint *newpoint;
+
+       gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
+       gps->totpoints++;
+
+       newpoint = &gps->points[gps->totpoints - 1];
+       newpoint->x = point->x * delta[0];
+       newpoint->y = point->y * delta[1];
+       newpoint->z = point->z * delta[2];
+       newpoint->flag = point->flag;
+       newpoint->pressure = pressure;
+       newpoint->strength = strength;
+       newpoint->time = point->time + deltatime;
+}
+
+/* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */
+static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps)
+{
+       bGPDspoint point, *pt;
+       int i;
+       float delta[3] = {1.0f, 1.0f, 1.0f};
+       float deltatime = 0.0f;
+
+       /* sanity checks */
+       if (ELEM(NULL, gps_a, gps_b))
+               return;
+
+       if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0))
+               return;
+
+       /* define start and end points of each stroke */
+       float sa[3], sb[3], ea[3], eb[3];
+       pt = &gps_a->points[0];
+       copy_v3_v3(sa, &pt->x);
+
+       pt = &gps_a->points[gps_a->totpoints - 1];
+       copy_v3_v3(ea, &pt->x);
+
+       pt = &gps_b->points[0];
+       copy_v3_v3(sb, &pt->x);
+       
+       pt = &gps_b->points[gps_b->totpoints - 1];
+       copy_v3_v3(eb, &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) {
+               gpencil_flip_stroke(gps_b);
+       }
+
+       /* don't visibly link the first and last points? */
+       if (leave_gaps) {
+               /* 1st: add one tail point to start invisible area */
+               point = gps_a->points[gps_a->totpoints - 1];
+               deltatime = point.time;
+               gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f);
+
+               /* 2nd: add one head point to finish invisible area */
+               point = gps_b->points[0];
+               gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime);
+       }
+       
+       /* 3rd: add all points */
+       for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
+               /* check if still room in buffer */
+               if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) {
+                       gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime);
+               }
+       }
+}
+
+static int gp_stroke_join_exec(bContext *C, wmOperator *op)
+{
+       bGPdata *gpd = ED_gpencil_data_get_active(C);
+       bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
+       bGPDstroke *gps, *gpsn;
+       bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
+       bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
+
+       bGPDframe *gpf_a = NULL;
+       bGPDstroke *stroke_a = NULL;
+       bGPDstroke *stroke_b = NULL;
+       bGPDstroke *new_stroke = NULL;
+
+       const int type = RNA_enum_get(op->ptr, "type");
+       const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
+
+       /* sanity checks */
+       if (ELEM(NULL, gpd))
+               return OPERATOR_CANCELLED;
+
+       if (activegpl->flag & GP_LAYER_LOCKED)
+               return OPERATOR_CANCELLED;
+
+       BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
+
+
+       /* read all selected strokes */
+       bool first = false;
+       CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
+       {
+               bGPDframe *gpf = gpl->actframe;
+               for (gps = gpf->strokes.first; gps; gps = gpsn) {
+                       gpsn = gps->next;
+                       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 */
+                               if (ED_gpencil_stroke_color_use(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;
+                               }
+                               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 = MEM_dupallocN(stroke_a);
+                                               new_stroke->points = MEM_dupallocN(stroke_a->points);
+                                               new_stroke->triangles = NULL;
+                                               new_stroke->tot_triangles = 0;
+                                               new_stroke->flag |= GP_STROKE_RECALC_CACHES;
+                                               /* if new, set current color */
+                                               if (type == GP_STROKE_JOINCOPY) {
+                                                       new_stroke->palcolor = palcolor;
+                                                       BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname));
+                                                       new_stroke->flag |= GP_STROKE_RECALC_COLOR;
+                                               }
+                                       }
+                                       /* join new_stroke and stroke B. New stroke will contain all the previous data */
+                                       gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps);
+
+                                       /* if join only, delete old strokes */
+                                       if (type == GP_STROKE_JOIN) {
+                                               if (stroke_a) {
+                                                       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;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       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);
+
+                       BLI_addtail(&activegpl->actframe->strokes, new_stroke);
+               }
+       }
+
+       /* notifiers */
+       WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+       return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_join(wmOperatorType *ot)
+{
+       static EnumPropertyItem join_type[] = {
+               {GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
+               {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
+               {0, NULL, 0, NULL, NULL}
+       };
+
+       /* identifiers */
+       ot->name = "Join Strokes";
+       ot->idname = "GPENCIL_OT_stroke_join";
+       ot->description = "Join selected strokes (optionally as new stroke)";
+
+       /* api callbacks */
+       ot->exec = gp_stroke_join_exec;
+       ot->poll = gp_active_layer_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+       ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
+       RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them");
+}
+
+/* ******************* Stroke flip ************************** */
+
+static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+       /* sanity checks */
+       if (ELEM(NULL, gpd))
+               return OPERATOR_CANCELLED;
+
+       /* read all selected strokes */
+       CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
+       {
+               bGPDframe *gpf = gpl->actframe;
+               for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+                       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 */
+                               if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+                                       continue;
+                               }
+                               /* flip stroke */
+                               gpencil_flip_stroke(gps);
+                       }
+               }
+       }
+       CTX_DATA_END;
+
+       /* notifiers */
+       WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+       return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Flip Stroke";
+       ot->idname = "GPENCIL_OT_stroke_flip";
+       ot->description = "Change direction of the points of the selected strokes";
+
+       /* api callbacks */
+       ot->exec = gp_stroke_flip_exec;
+       ot->poll = gp_active_layer_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
 
 /* ************************************************ */
index f37fba4212df17525d575960641695061f3c407d..5f7cef9a852834ec8ab297de4e48873e90a94b0f 100644 (file)
@@ -214,6 +214,9 @@ typedef enum eGPencil_PaintModes {
        GP_PAINTMODE_DRAW_POLY
 } eGPencil_PaintModes;
 
+/* maximum sizes of gp-session buffer */
+#define GP_STROKE_BUFFER_MAX    5000
+
 /* stroke editing ----- */
 
 void GPENCIL_OT_editmode_toggle(struct wmOperatorType *ot);
index dacdc0cf7778e77b51bba98ea5ea2c470104feb4..70a4b2904ed4c53523781bd86cdf11e33cb659a6 100644 (file)
@@ -164,9 +164,6 @@ typedef struct tGPsdata {
 
 /* ------ */
 
-/* maximum sizes of gp-session buffer */
-#define GP_STROKE_BUFFER_MAX    5000
-
 /* Macros for accessing sensitivity thresholds... */
 /* minimum number of pixels mouse should move before new point created */
 #define MIN_MANHATTEN_PX    (U.gp_manhattendist)