Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntonio Vazquez <blendergit@gmail.com>2021-02-19 19:18:12 +0300
committerAntonio Vazquez <blendergit@gmail.com>2021-02-19 19:41:28 +0300
commit6bba830589860c32a2543b14cd2c9bcd2b9e4948 (patch)
tree079027534dc4c6ea10d49816cde608c751602476 /source/blender/editors/gpencil
parent2441886c58e1258645195214e0aa1987c29f0c35 (diff)
GPencil: Interpolate Tools refactor
Following with the changes included to interpolate strokes of different number of points, a full review has been done in the interpolation tools. * Interpolate now is a tool and not an operator. It was not logic to have this tool as a button. * Interpolate tool parameters have been moved to topbar. * Interpolate popover has been removed from topbar and interpolate `Sequence` operator has been moved to grease pencil menu. * Interpolate Sequence now include a Redo panel. * Interpolate tool now allows to select the strokes by pairs. This allows to interpolate any stroke with any stroke and not as before that it was only possible by drawing order. If no stroke is selected, the interpolation is done as before. * Now is possible interpolate again if a previous keyframe exist. Before, it was impossible to interpolate two times in same frame and this made impossible to do the interpolation by groups of frames. * New automatic option to `Flip strokes` if the stroke and end are not in the right position. Also the flip can be set manually for corner cases. * Cleanup of menus related to interpolate. * Fixed some bugs and removed parameters from scene because now all are tool or operator contained. * Some code cleanup and function renames. This commit also includes the some codebase to future implementation of the concept `Vertex Active` that now does not exist in grease pencil.
Diffstat (limited to 'source/blender/editors/gpencil')
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c12
-rw-r--r--source/blender/editors/gpencil/gpencil_edit_curve.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h49
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c1011
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c3
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c55
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c22
9 files changed, 854 insertions, 303 deletions
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 4c9bde77103..0597de445f4 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -3418,9 +3418,11 @@ static int gpencil_material_select_exec(bContext *C, wmOperator *op)
if (!deselected) {
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
}
else {
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (!deselected) {
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index bf1aff5a34a..aec593b97ea 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -214,7 +214,7 @@ static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op)
if (gpc->flag & GP_CURVE_NEEDS_STROKE_UPDATE) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
/* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
+ BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
@@ -992,6 +992,7 @@ static int gpencil_duplicate_exec(bContext *C, wmOperator *op)
pt->flag &= ~GP_SPOINT_SELECT;
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
changed = true;
}
@@ -1193,6 +1194,7 @@ static void gpencil_add_move_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gp
/* if the stroke is not reused, deselect */
if (!do_stroke) {
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
@@ -1709,6 +1711,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
CTX_DATA_END;
@@ -2548,6 +2551,7 @@ static bool gpencil_dissolve_selected_stroke_points(bContext *C,
/* deselect the stroke, since none of its selected points will still be selected */
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
pt->flag &= ~GP_SPOINT_SELECT;
}
@@ -2620,6 +2624,7 @@ static int gpencil_delete_selected_points(bContext *C)
if (gps->flag & GP_STROKE_SELECT) {
/* deselect old stroke, since it will be used as template for the new strokes */
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
if (is_curve_edit) {
bGPDcurve *gpc = gps->editcurve;
@@ -3790,7 +3795,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
if (is_curve_edit && gps->editcurve != NULL) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
/* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
+ BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
@@ -4574,6 +4579,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
else if (mode == GP_SEPARATE_STROKE) {
/* deselect old stroke */
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
/* unlink from source frame */
BLI_remlink(&gpf->strokes, gps);
gps->prev = gps->next = NULL;
@@ -4982,6 +4988,7 @@ static int gpencil_cutter_lasso_select(bContext *C,
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
CTX_DATA_END;
@@ -5022,6 +5029,7 @@ static int gpencil_cutter_lasso_select(bContext *C,
changed = true;
pt->flag |= GP_SPOINT_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
float r_hita[3], r_hitb[3];
if (gps->totpoints > 1) {
ED_gpencil_select_stroke_segment(
diff --git a/source/blender/editors/gpencil/gpencil_edit_curve.c b/source/blender/editors/gpencil/gpencil_edit_curve.c
index 031bbd61173..0f9a8c93df9 100644
--- a/source/blender/editors/gpencil/gpencil_edit_curve.c
+++ b/source/blender/editors/gpencil/gpencil_edit_curve.c
@@ -88,7 +88,7 @@ static int gpencil_stroke_enter_editcurve_mode_exec(bContext *C, wmOperator *op)
(gps->editcurve != NULL && gps->editcurve->flag & GP_CURVE_NEEDS_STROKE_UPDATE)) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
/* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
+ BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 454f8d21590..c6f74c39beb 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -103,55 +103,6 @@ typedef struct tGPDdraw {
float diff_mat[4][4]; /* matrix */
} tGPDdraw;
-/* Temporary interpolate operation data */
-typedef struct tGPDinterpolate_layer {
- struct tGPDinterpolate_layer *next, *prev;
-
- /** layer */
- struct bGPDlayer *gpl;
- /** frame before current frame (interpolate-from) */
- struct bGPDframe *prevFrame;
- /** frame after current frame (interpolate-to) */
- struct bGPDframe *nextFrame;
- /** interpolated frame */
- struct bGPDframe *interFrame;
- /** interpolate factor */
- float factor;
-
-} tGPDinterpolate_layer;
-
-typedef struct tGPDinterpolate {
- /** Current depsgraph from context */
- struct Depsgraph *depsgraph;
- /** current scene from context */
- struct Scene *scene;
- /** area where painting originated */
- struct ScrArea *area;
- /** region where painting originated */
- struct ARegion *region;
- /** current GP datablock */
- struct bGPdata *gpd;
- /** current material */
- struct Material *mat;
-
- /** current frame number */
- int cframe;
- /** (tGPDinterpolate_layer) layers to be interpolated */
- ListBase ilayers;
- /** value for determining the displacement influence */
- float shift;
- /** initial interpolation factor for active layer */
- float init_factor;
- /** shift low limit (-100%) */
- float low_limit;
- /** shift upper limit (200%) */
- float high_limit;
- /** flag from toolsettings */
- int flag;
-
- NumInput num; /* numeric input */
-} tGPDinterpolate;
-
/* Modal Operator Drawing Callbacks ------------------------ */
void ED_gpencil_draw_fill(struct tGPDdraw *tgpw);
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index ecd243ed595..6a2eae934a2 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -32,6 +32,7 @@
#include "BLI_blenlib.h"
#include "BLI_easing.h"
+#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -53,6 +54,7 @@
#include "BKE_report.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -67,6 +69,78 @@
#include "gpencil_intern.h"
+/* Temporary interpolate operation data */
+typedef struct tGPDinterpolate_layer {
+ struct tGPDinterpolate_layer *next, *prev;
+
+ /** layer */
+ struct bGPDlayer *gpl;
+ /** frame before current frame (interpolate-from) */
+ struct bGPDframe *prevFrame;
+ /** frame after current frame (interpolate-to) */
+ struct bGPDframe *nextFrame;
+ /** interpolated frame */
+ struct bGPDframe *interFrame;
+ /** interpolate factor */
+ float factor;
+
+ /* Hash tablets to create temp relationship between strokes. */
+ struct GHash *used_strokes;
+ struct GHash *pair_strokes;
+
+} tGPDinterpolate_layer;
+
+typedef struct tGPDinterpolate {
+ /** Current depsgraph from context */
+ struct Depsgraph *depsgraph;
+ /** current scene from context */
+ struct Scene *scene;
+ /** area where painting originated */
+ struct ScrArea *area;
+ /** region where painting originated */
+ struct ARegion *region;
+ /** current object */
+ struct Object *ob;
+ /** current GP datablock */
+ struct bGPdata *gpd;
+ /** current material */
+ struct Material *mat;
+ /* Space Conversion Data */
+ struct GP_SpaceConversion gsc;
+
+ /** current frame number */
+ int cframe;
+ /** (tGPDinterpolate_layer) layers to be interpolated */
+ ListBase ilayers;
+ /** value for determining the displacement influence */
+ float shift;
+ /** initial interpolation factor for active layer */
+ float init_factor;
+ /** shift low limit (-100%) */
+ float low_limit;
+ /** shift upper limit (200%) */
+ float high_limit;
+ /** flag from toolsettings */
+ int flag;
+ /** Flip mode. */
+ int flipmode;
+ /** smooth factor */
+ float smooth_factor;
+ /** smooth iterations */
+ int smooth_steps;
+
+ NumInput num; /* numeric input */
+} tGPDinterpolate;
+
+typedef enum eGP_InterpolateFlipMode {
+ /* No flip. */
+ GP_INTERPOLATE_NOFLIP = 0,
+ /* Flip always. */
+ GP_INTERPOLATE_FLIP = 1,
+ /* Flip if needed. */
+ GP_INTERPOLATE_FLIPAUTO = 2,
+} eGP_InterpolateFlipMode;
+
/* ************************************************ */
/* Core/Shared Utilities */
@@ -79,17 +153,150 @@ static bool gpencil_view3d_poll(bContext *C)
/* only 3D view */
ScrArea *area = CTX_wm_area(C);
if (area && area->spacetype != SPACE_VIEW3D) {
- return 0;
+ return false;
}
/* need data to interpolate */
if (ELEM(NULL, gpd, gpl)) {
- return 0;
+ return false;
}
- return 1;
+ return true;
+}
+
+/* Return if the stroke must be flipped or not. The logic of the calculation
+ * is to check if the lines from extremes crossed. All is done in 2D. */
+static bool gpencil_stroke_need_flip(Depsgraph *depsgraph,
+ Object *ob,
+ bGPDlayer *gpl,
+ GP_SpaceConversion *gsc,
+ bGPDstroke *gps_from,
+ bGPDstroke *gps_to)
+{
+ float diff_mat[4][4];
+ /* calculate parent matrix */
+ BKE_gpencil_layer_transform_matrix_get(depsgraph, ob, gpl, diff_mat);
+ bGPDspoint *pt, pt_dummy_ps;
+ float v1a[2], v1b[2], v2a[2], v2b[2];
+
+ /* Line from start of strokes. */
+ pt = &gps_from->points[0];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps);
+ gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v1a[0], &v1a[1]);
+
+ pt = &gps_to->points[0];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps);
+ gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v1b[0], &v1b[1]);
+
+ /* Line from end of strokes. */
+ pt = &gps_from->points[gps_from->totpoints - 1];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps);
+ gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v2a[0], &v2a[1]);
+
+ pt = &gps_to->points[gps_to->totpoints - 1];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps);
+ gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v2b[0], &v2b[1]);
+
+ if (isect_seg_seg_v2(v1a, v1b, v2a, v2b) == ISECT_LINE_LINE_CROSS) {
+ return true;
+ }
+
+ return false;
+}
+
+/* Return the stroke related to the selection index, returning the stroke with
+ * the smallest selection index greater than reference index. */
+static bGPDstroke *gpencil_stroke_get_related(GHash *used_strokes,
+ bGPDframe *gpf,
+ const int reference_index)
+{
+ bGPDstroke *gps_found = NULL;
+ int lower_index = INT_MAX;
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->select_index > reference_index) {
+ if (!BLI_ghash_haskey(used_strokes, gps)) {
+ if (gps->select_index < lower_index) {
+ lower_index = gps->select_index;
+ gps_found = gps;
+ }
+ }
+ }
+ }
+
+ /* Set as used. */
+ if (gps_found) {
+ BLI_ghash_insert(used_strokes, gps_found, gps_found);
+ }
+
+ return gps_found;
+}
+
+/* Load a Hash with the relationship between strokes. */
+static void gpencil_stroke_pair_table(bContext *C,
+ tGPDinterpolate *tgpi,
+ tGPDinterpolate_layer *tgpil)
+{
+ bGPdata *gpd = tgpi->gpd;
+ const bool only_selected = ((GPENCIL_EDIT_MODE(gpd)) &&
+ ((tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) != 0));
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+
+ /* Create hash tablets with relationship between strokes. */
+ tgpil->used_strokes = BLI_ghash_ptr_new(__func__);
+ tgpil->pair_strokes = BLI_ghash_ptr_new(__func__);
+
+ /* Create a table with source and target pair of strokes. */
+ LISTBASE_FOREACH (bGPDstroke *, gps_from, &tgpil->prevFrame->strokes) {
+ bGPDstroke *gps_to = NULL;
+ /* only selected */
+ if ((GPENCIL_EDIT_MODE(gpd)) && (only_selected) &&
+ ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
+ continue;
+ }
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
+ continue;
+ }
+ /* Check if the material is editable. */
+ if (ED_gpencil_stroke_material_editable(tgpi->ob, tgpil->gpl, gps_from) == false) {
+ continue;
+ }
+ /* Try to get the related stroke. */
+ if ((is_multiedit) && (gps_from->select_index > 0)) {
+ gps_to = gpencil_stroke_get_related(
+ tgpil->used_strokes, tgpil->nextFrame, gps_from->select_index);
+ }
+ /* If not found, get final stroke to interpolate using position in the array. */
+ if (gps_to == NULL) {
+ int fFrame = BLI_findindex(&tgpil->prevFrame->strokes, gps_from);
+ gps_to = BLI_findlink(&tgpil->nextFrame->strokes, fFrame);
+ }
+
+ if (ELEM(NULL, gps_from, gps_to)) {
+ continue;
+ }
+ /* Insert the pair entry in the hash table. */
+ BLI_ghash_insert(tgpil->pair_strokes, gps_from, gps_to);
+ }
}
+static void gpencil_interpolate_smooth_stroke(bGPDstroke *gps,
+ float smooth_factor,
+ int smooth_steps)
+{
+ if (smooth_factor == 0.0f) {
+ return;
+ }
+
+ float reduce = 0.0f;
+ for (int r = 0; r < smooth_steps; r++) {
+ for (int i = 0; i < gps->totpoints - 1; i++) {
+ BKE_gpencil_stroke_smooth(gps, i, smooth_factor - reduce);
+ BKE_gpencil_stroke_smooth_strength(gps, i, smooth_factor);
+ }
+ reduce += 0.25f; /* reduce the factor */
+ }
+}
/* Perform interpolation */
static void gpencil_interpolate_update_points(const bGPDstroke *gps_from,
const bGPDstroke *gps_to,
@@ -112,7 +319,7 @@ static void gpencil_interpolate_update_points(const bGPDstroke *gps_from,
/* ****************** Interpolate Interactive *********************** */
/* Helper: free all temp strokes for display. */
-static void gpencil_interpolate_free_temp_strokes(bGPDframe *gpf)
+static void gpencil_interpolate_free_tagged_strokes(bGPDframe *gpf)
{
if (gpf == NULL) {
return;
@@ -152,33 +359,34 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp
const float factor = tgpil->factor + shift;
bGPDframe *gpf = tgpil->gpl->actframe;
- /* Free temp strokes. */
- gpencil_interpolate_free_temp_strokes(gpf);
+ /* Free temp strokes used for display. */
+ gpencil_interpolate_free_tagged_strokes(gpf);
- LISTBASE_FOREACH (bGPDstroke *, new_stroke, &tgpil->interFrame->strokes) {
- bGPDstroke *gps_from, *gps_to;
- int stroke_idx;
+ /* Clear previous interpolations. */
+ gpencil_interpolate_free_tagged_strokes(tgpil->interFrame);
- if (new_stroke->totpoints == 0) {
- continue;
- }
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, tgpil->pair_strokes) {
+ bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
+ /* Create new stroke. */
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+ new_stroke->flag |= GP_STROKE_TAG;
+ new_stroke->select_index = 0;
- /* get strokes to interpolate */
- stroke_idx = BLI_findindex(&tgpil->interFrame->strokes, new_stroke);
+ /* Update points position. */
+ gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
- gps_from = BLI_findlink(&tgpil->prevFrame->strokes, stroke_idx);
- gps_to = BLI_findlink(&tgpil->nextFrame->strokes, stroke_idx);
-
- /* update points position */
- if ((gps_from) && (gps_to)) {
- gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ /* Add to strokes. */
+ BLI_addtail(&tgpil->interFrame->strokes, new_stroke);
- /* Add temp strokes. */
- if (gpf) {
- bGPDstroke *gps_eval = BKE_gpencil_stroke_duplicate(new_stroke, true, true);
- gps_eval->flag |= GP_STROKE_TAG;
- BLI_addtail(&gpf->strokes, gps_eval);
- }
+ /* Add temp strokes to display. */
+ if (gpf) {
+ bGPDstroke *gps_eval = BKE_gpencil_stroke_duplicate(new_stroke, true, true);
+ gps_eval->flag |= GP_STROKE_TAG;
+ BLI_addtail(&gpf->strokes, gps_eval);
}
}
}
@@ -187,65 +395,50 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
}
-/* Helper: Verify valid strokes for interpolation */
-static bool gpencil_interpolate_check_todo(bContext *C, bGPdata *gpd)
+/* Helper: Get previous keyframe. */
+static bGPDframe *gpencil_get_previous_keyframe(bGPDlayer *gpl, int cfra)
{
- Object *ob = CTX_data_active_object(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
- eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag;
+ if (gpl->actframe != NULL && gpl->actframe->framenum < cfra &&
+ gpl->actframe->key_type == BEZT_KEYTYPE_KEYFRAME) {
+ return gpl->actframe;
+ }
- /* get layers */
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- /* all layers or only active */
- if (!(flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && !(gpl->flag & GP_LAYER_ACTIVE)) {
+ LISTBASE_FOREACH_BACKWARD (bGPDframe *, gpf, &gpl->frames) {
+ if (gpf->key_type != BEZT_KEYTYPE_KEYFRAME) {
continue;
}
- /* only editable and visible layers are considered */
- if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
+ if (gpf->framenum >= cfra) {
continue;
}
+ return gpf;
+ }
- /* read strokes */
- LISTBASE_FOREACH (bGPDstroke *, gps_from, &gpl->actframe->strokes) {
- bGPDstroke *gps_to;
- int fFrame;
-
- /* only selected */
- if ((GPENCIL_EDIT_MODE(gpd)) && (flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) &&
- ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
- continue;
- }
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
- continue;
- }
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps_from) == false) {
- continue;
- }
-
- /* get final stroke to interpolate */
- fFrame = BLI_findindex(&gpl->actframe->strokes, gps_from);
- gps_to = (gpl->actframe->next != NULL) ?
- BLI_findlink(&gpl->actframe->next->strokes, fFrame) :
- NULL;
- if (gps_to == NULL) {
- continue;
- }
+ return NULL;
+}
- return true;
+/* Helper: Get next keyframe. */
+static bGPDframe *gpencil_get_next_keyframe(bGPDlayer *gpl, int cfra)
+{
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ if (gpf->key_type != BEZT_KEYTYPE_KEYFRAME) {
+ continue;
+ }
+ if (gpf->framenum <= cfra) {
+ continue;
}
+ return gpf;
}
- return false;
+
+ return NULL;
}
/* Helper: Create internal strokes interpolated */
static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
{
+ Scene *scene = tgpi->scene;
bGPdata *gpd = tgpi->gpd;
bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C);
bGPDframe *actframe = active_gpl->actframe;
- Object *ob = CTX_data_active_object(C);
/* save initial factor for active layer to define shift limits */
tgpi->init_factor = (float)(tgpi->cframe - actframe->framenum) /
@@ -274,12 +467,15 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer");
tgpil->gpl = gpl;
- tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe, true);
- tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next, true);
+ bGPDframe *gpf = gpencil_get_previous_keyframe(gpl, CFRA);
+ tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpf, true);
+
+ gpf = gpencil_get_next_keyframe(gpl, CFRA);
+ tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpf, true);
BLI_addtail(&tgpi->ilayers, tgpil);
- /* create a new temporary frame */
+ /* Create a new temporary frame. */
tgpil->interFrame = MEM_callocN(sizeof(bGPDframe), "bGPDframe");
tgpil->interFrame->framenum = tgpi->cframe;
@@ -287,62 +483,42 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
tgpil->factor = (float)(tgpi->cframe - tgpil->prevFrame->framenum) /
(tgpil->nextFrame->framenum - tgpil->prevFrame->framenum + 1);
- /* create new strokes data with interpolated points reading original stroke */
- LISTBASE_FOREACH (bGPDstroke *, gps_from, &tgpil->prevFrame->strokes) {
- bGPDstroke *gps_to;
- int fFrame;
+ /* Load the relationship between frames. */
+ gpencil_stroke_pair_table(C, tgpi, tgpil);
- bGPDstroke *new_stroke = NULL;
- bool valid = true;
+ /* Create new strokes data with interpolated points reading original stroke. */
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, tgpil->pair_strokes) {
+ bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
- /* only selected */
- if ((GPENCIL_EDIT_MODE(gpd)) && (tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) &&
- ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
- valid = false;
+ /* If destination stroke is smaller, resize new_stroke to size of gps_to stroke. */
+ if (gps_from->totpoints > gps_to->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
}
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
- valid = false;
+ if (gps_to->totpoints > gps_from->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
}
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(ob, tgpil->gpl, gps_from) == false) {
- valid = false;
+ /* Flip stroke. */
+ if (tgpi->flipmode == GP_INTERPOLATE_FLIP) {
+ BKE_gpencil_stroke_flip(gps_to);
}
-
- /* get final stroke to interpolate */
- fFrame = BLI_findindex(&tgpil->prevFrame->strokes, gps_from);
- gps_to = BLI_findlink(&tgpil->nextFrame->strokes, fFrame);
- if (gps_to == NULL) {
- valid = false;
- }
-
- if (valid) {
- /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
- if (gps_from->totpoints > gps_to->totpoints) {
- BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
+ else if (tgpi->flipmode == GP_INTERPOLATE_FLIPAUTO) {
+ if (gpencil_stroke_need_flip(
+ tgpi->depsgraph, tgpi->ob, gpl, &tgpi->gsc, gps_from, gps_to)) {
+ BKE_gpencil_stroke_flip(gps_to);
}
- if (gps_to->totpoints > gps_from->totpoints) {
- BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
- }
-
- /* Create new stroke. */
- new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
-
- /* Update points position. */
- gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
}
- else {
- /* Create new stroke. */
- new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
- /* need an empty stroke to keep index correct for lookup, but resize to smallest size */
- new_stroke->totpoints = 0;
- new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points));
- if (new_stroke->dvert != NULL) {
- new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert));
- }
- }
+ /* Create new stroke. */
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+ new_stroke->flag |= GP_STROKE_TAG;
+ new_stroke->select_index = 0;
+
+ /* Update points position. */
+ gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
+ gpencil_interpolate_smooth_stroke(new_stroke, tgpi->smooth_factor, tgpi->smooth_steps);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
@@ -429,7 +605,7 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
/* Clear any temp stroke. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
- gpencil_interpolate_free_temp_strokes(gpf);
+ gpencil_interpolate_free_tagged_strokes(gpf);
}
}
@@ -441,9 +617,18 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
MEM_SAFE_FREE(tgpil->prevFrame);
MEM_SAFE_FREE(tgpil->nextFrame);
MEM_SAFE_FREE(tgpil->interFrame);
+
+ /* Free Hash tablets. */
+ if (tgpil->used_strokes != NULL) {
+ BLI_ghash_free(tgpil->used_strokes, NULL, NULL);
+ }
+ if (tgpil->pair_strokes != NULL) {
+ BLI_ghash_free(tgpil->pair_strokes, NULL, NULL);
+ }
}
BLI_freelistN(&tgpi->ilayers);
+
MEM_SAFE_FREE(tgpi);
}
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -456,24 +641,33 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
/* Init new temporary interpolation data */
static bool gpencil_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinterpolate *tgpi)
{
- ToolSettings *ts = CTX_data_tool_settings(C);
- bGPdata *gpd = CTX_data_gpencil_data(C);
-
/* set current scene and window */
tgpi->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
tgpi->scene = CTX_data_scene(C);
tgpi->area = CTX_wm_area(C);
tgpi->region = CTX_wm_region(C);
- tgpi->flag = ts->gp_interpolate.flag;
+ tgpi->ob = CTX_data_active_object(C);
+ /* Setup space conversions. */
+ gpencil_point_conversion_init(C, &tgpi->gsc);
/* set current frame number */
tgpi->cframe = tgpi->scene->r.cfra;
/* set GP datablock */
- tgpi->gpd = gpd;
-
+ tgpi->gpd = tgpi->ob->data;
/* set interpolation weight */
tgpi->shift = RNA_float_get(op->ptr, "shift");
+ SET_FLAG_FROM_TEST(
+ tgpi->flag, (RNA_enum_get(op->ptr, "layers") == 1), GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS);
+ SET_FLAG_FROM_TEST(
+ tgpi->flag,
+ ((GPENCIL_EDIT_MODE(tgpi->gpd)) && (RNA_boolean_get(op->ptr, "interpolate_selected_only"))),
+ GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED);
+
+ tgpi->flipmode = RNA_enum_get(op->ptr, "flip");
+
+ tgpi->smooth_factor = RNA_float_get(op->ptr, "smooth_factor");
+ tgpi->smooth_steps = RNA_int_get(op->ptr, "smooth_steps");
/* Untag strokes to be sure nothing is pending due any canceled process. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &tgpi->gpd->layers) {
@@ -524,12 +718,13 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent
bGPdata *gpd = CTX_data_gpencil_data(C);
bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
Scene *scene = CTX_data_scene(C);
- int cfra = CFRA;
- bGPDframe *actframe = gpl->actframe;
tGPDinterpolate *tgpi = NULL;
/* cannot interpolate if not between 2 frames */
- if (ELEM(NULL, actframe, actframe->next)) {
+ int cfra = CFRA;
+ bGPDframe *gpf_prv = gpencil_get_previous_keyframe(gpl, cfra);
+ bGPDframe *gpf_next = gpencil_get_next_keyframe(gpl, cfra);
+ if (ELEM(NULL, gpf_prv, gpf_next)) {
BKE_report(
op->reports,
RPT_ERROR,
@@ -537,25 +732,10 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_CANCELLED;
}
- /* cannot interpolate in extremes */
- if (ELEM(cfra, actframe->framenum, actframe->next->framenum)) {
- BKE_report(op->reports,
- RPT_ERROR,
- "Cannot interpolate as current frame already has existing grease pencil frames");
- return OPERATOR_CANCELLED;
- }
-
if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
BKE_report(op->reports, RPT_ERROR, "Cannot interpolate in curve edit mode");
return OPERATOR_CANCELLED;
}
-
- /* need editable strokes */
- if (!gpencil_interpolate_check_todo(C, gpd)) {
- BKE_report(op->reports, RPT_ERROR, "Interpolation requires some editable strokes");
- return OPERATOR_CANCELLED;
- }
-
/* try to initialize context data needed */
if (!gpencil_interpolate_init(C, op)) {
if (op->customdata) {
@@ -602,8 +782,7 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
gpf_dst = BKE_gpencil_layer_frame_get(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW);
gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN;
- /* copy strokes */
- BLI_listbase_clear(&gpf_dst->strokes);
+ /* Copy strokes. */
LISTBASE_FOREACH (bGPDstroke *, gps_src, &tgpil->interFrame->strokes) {
if (gps_src->totpoints == 0) {
continue;
@@ -710,6 +889,15 @@ static void gpencil_interpolate_cancel(bContext *C, wmOperator *op)
void GPENCIL_OT_interpolate(wmOperatorType *ot)
{
+ static const EnumPropertyItem flip_modes[] = {
+ {GP_INTERPOLATE_NOFLIP, "NOFLIP", 0, "No Flip", ""},
+ {GP_INTERPOLATE_FLIP, "FLIP", 0, "Flip", ""},
+ {GP_INTERPOLATE_FLIPAUTO, "AUTO", 0, "Automatic", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
/* identifiers */
ot->name = "Grease Pencil Interpolation";
ot->idname = "GPENCIL_OT_interpolate";
@@ -724,6 +912,12 @@ void GPENCIL_OT_interpolate(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
+ static const EnumPropertyItem gpencil_interpolation_layer_items[] = {
+ {0, "ACTIVE", 0, "Active", ""},
+ {1, "ALL", 0, "All Layers", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
/* properties */
RNA_def_float_factor(
ot->srna,
@@ -735,25 +929,68 @@ void GPENCIL_OT_interpolate(wmOperatorType *ot)
"Bias factor for which frame has more influence on the interpolated strokes",
-0.9f,
0.9f);
+
+ RNA_def_enum(ot->srna,
+ "layers",
+ gpencil_interpolation_layer_items,
+ 0,
+ "Layer",
+ "Layers included in the interpolation");
+
+ RNA_def_boolean(ot->srna,
+ "interpolate_selected_only",
+ 0,
+ "Only Selected",
+ "Interpolate only selected strokes");
+
+ RNA_def_enum(ot->srna,
+ "flip",
+ flip_modes,
+ GP_INTERPOLATE_FLIPAUTO,
+ "Flip Mode",
+ "Invert destination stroke to match start and end with source stroke");
+
+ RNA_def_int(ot->srna,
+ "smooth_steps",
+ 1,
+ 1,
+ 3,
+ "Iterations",
+ "Number of times to smooth newly created strokes",
+ 1,
+ 3);
+
+ RNA_def_float(ot->srna,
+ "smooth_factor",
+ 0.0f,
+ 0.0f,
+ 2.0f,
+ "Smooth",
+ "Amount of smoothing to apply to interpolated strokes, to reduce jitter/noise",
+ 0.0f,
+ 2.0f);
+
+ prop = RNA_def_boolean(ot->srna, "release_confirm", 0, "Confirm on Release", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* ****************** Interpolate Sequence *********************** */
/* Helper: Perform easing equation calculations for GP interpolation operator */
-static float gpencil_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_settings, float time)
+static float gpencil_interpolate_seq_easing_calc(wmOperator *op, float time)
{
const float begin = 0.0f;
const float change = 1.0f;
const float duration = 1.0f;
- const float back = ipo_settings->back;
- const float amplitude = ipo_settings->amplitude;
- const float period = ipo_settings->period;
-
- eBezTriple_Easing easing = ipo_settings->easing;
+ const float back = RNA_float_get(op->ptr, "back");
+ const float amplitude = RNA_float_get(op->ptr, "amplitude");
+ const float period = RNA_float_get(op->ptr, "period");
+ const eBezTriple_Easing easing = RNA_enum_get(op->ptr, "easing");
+ const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type");
float result = time;
- switch (ipo_settings->type) {
+ switch (type) {
case GP_IPO_BACK:
switch (easing) {
case BEZT_IPO_EASE_IN:
@@ -936,7 +1173,7 @@ static float gpencil_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_se
break;
default:
- printf("%s: Unknown interpolation type - %d\n", __func__, ipo_settings->type);
+ printf("%s: Unknown interpolation type\n", __func__);
break;
}
@@ -945,34 +1182,47 @@ static float gpencil_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_se
static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = CTX_data_gpencil_data(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C);
- bGPDframe *actframe = active_gpl->actframe;
+ /* Setup space conversions. */
+ GP_SpaceConversion gsc;
+ gpencil_point_conversion_init(C, &gsc);
- Object *ob = CTX_data_active_object(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
- Scene *scene = CTX_data_scene(C);
int cfra = CFRA;
GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate;
- eGP_Interpolate_SettingsFlag flag = ipo_settings->flag;
- const int step = ipo_settings->step;
+ const int step = RNA_int_get(op->ptr, "step");
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ const bool all_layers = (bool)(RNA_enum_get(op->ptr, "layers") == 1);
+ const bool only_selected = ((GPENCIL_EDIT_MODE(gpd)) &&
+ (RNA_boolean_get(op->ptr, "interpolate_selected_only") != 0));
- /* cannot interpolate if not between 2 frames */
- if (ELEM(NULL, actframe, actframe->next)) {
+ eGP_InterpolateFlipMode flipmode = RNA_enum_get(op->ptr, "flip");
+
+ const float smooth_factor = RNA_float_get(op->ptr, "smooth_factor");
+ const int smooth_steps = RNA_int_get(op->ptr, "smooth_steps");
+
+ const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type");
+
+ if (ipo_settings->custom_ipo == NULL) {
+ ipo_settings->custom_ipo = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ }
+ BKE_curvemapping_init(ipo_settings->custom_ipo);
+
+ /* Cannot interpolate if not between 2 frames. */
+ bGPDframe *gpf_prv = gpencil_get_previous_keyframe(active_gpl, cfra);
+ bGPDframe *gpf_next = gpencil_get_next_keyframe(active_gpl, cfra);
+ if (ELEM(NULL, gpf_prv, gpf_next)) {
BKE_report(
op->reports,
RPT_ERROR,
"Cannot find a pair of grease pencil frames to interpolate between in active layer");
return OPERATOR_CANCELLED;
}
- /* cannot interpolate in extremes */
- if (ELEM(cfra, actframe->framenum, actframe->next->framenum)) {
- BKE_report(op->reports,
- RPT_ERROR,
- "Cannot interpolate as current frame already has existing grease pencil frames");
- return OPERATOR_CANCELLED;
- }
if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
BKE_report(op->reports, RPT_ERROR, "Cannot interpolate in curve edit mode");
@@ -981,103 +1231,137 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
/* loop all layer to check if need interpolation */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- bGPDframe *prevFrame, *nextFrame;
- bGPDstroke *gps_from, *gps_to;
- int cframe, fFrame;
-
- /* Need a set of frames to interpolate. */
- if ((gpl->actframe == NULL) || (gpl->actframe->next == NULL)) {
- continue;
- }
/* all layers or only active */
- if (((flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) {
+ if ((!all_layers) && (gpl != active_gpl)) {
continue;
}
/* only editable and visible layers are considered */
if (!BKE_gpencil_layer_is_editable(gpl)) {
continue;
}
+ gpf_prv = gpencil_get_previous_keyframe(gpl, cfra);
+ gpf_next = gpencil_get_next_keyframe(gpl, cfra);
+
+ /* Need a set of frames to interpolate. */
+ if ((gpf_prv == NULL) || (gpf_next == NULL)) {
+ continue;
+ }
+
+ /* Store extremes. */
+ bGPDframe *prevFrame = BKE_gpencil_frame_duplicate(gpf_prv, true);
+ bGPDframe *nextFrame = BKE_gpencil_frame_duplicate(gpf_next, true);
+
+ /* Create a table with source and target pair of strokes. */
+ GHash *used_strokes = BLI_ghash_ptr_new(__func__);
+ GHash *pair_strokes = BLI_ghash_ptr_new(__func__);
+
+ LISTBASE_FOREACH (bGPDstroke *, gps_from, &prevFrame->strokes) {
+ bGPDstroke *gps_to = NULL;
+ /* Only selected. */
+ if ((GPENCIL_EDIT_MODE(gpd)) && (only_selected) &&
+ ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
+ continue;
+ }
+ /* Skip strokes that are invalid for current view. */
+ if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
+ continue;
+ }
+ /* Check if the material is editable. */
+ if (ED_gpencil_stroke_material_editable(ob, gpl, gps_from) == false) {
+ continue;
+ }
+ /* Try to get the related stroke. */
+ if ((is_multiedit) && (gps_from->select_index > 0)) {
+ gps_to = gpencil_stroke_get_related(used_strokes, nextFrame, gps_from->select_index);
+ }
+ /* If not found, get final stroke to interpolate using position in the array. */
+ if (gps_to == NULL) {
+ int fFrame = BLI_findindex(&prevFrame->strokes, gps_from);
+ gps_to = BLI_findlink(&nextFrame->strokes, fFrame);
+ }
+
+ if (ELEM(NULL, gps_from, gps_to)) {
+ continue;
+ }
+
+ /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
+ if (gps_from->totpoints > gps_to->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
+ }
+ if (gps_to->totpoints > gps_from->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
+ }
- /* store extremes */
- prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe, true);
- nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next, true);
+ /* Flip stroke. */
+ if (flipmode == GP_INTERPOLATE_FLIP) {
+ BKE_gpencil_stroke_flip(gps_to);
+ }
+ else if (flipmode == GP_INTERPOLATE_FLIPAUTO) {
+ if (gpencil_stroke_need_flip(depsgraph, ob, gpl, &gsc, gps_from, gps_to)) {
+ BKE_gpencil_stroke_flip(gps_to);
+ }
+ }
- /* Loop over intermediary frames and create the interpolation */
- for (cframe = prevFrame->framenum + step; cframe < nextFrame->framenum; cframe += step) {
- bGPDframe *interFrame = NULL;
- float factor;
+ /* Insert the pair entry in the hash table. */
+ BLI_ghash_insert(pair_strokes, gps_from, gps_to);
+ }
- /* get interpolation factor */
+ /* Loop over intermediary frames and create the interpolation. */
+ for (int cframe = prevFrame->framenum + step; cframe < nextFrame->framenum; cframe += step) {
+ /* Get interpolation factor. */
float framerange = nextFrame->framenum - prevFrame->framenum;
CLAMP_MIN(framerange, 1.0f);
- factor = (float)(cframe - prevFrame->framenum) / framerange;
+ float factor = (float)(cframe - prevFrame->framenum) / framerange;
- if (ipo_settings->type == GP_IPO_CURVEMAP) {
+ if (type == GP_IPO_CURVEMAP) {
/* custom curvemap */
if (ipo_settings->custom_ipo) {
factor = BKE_curvemapping_evaluateF(ipo_settings->custom_ipo, 0, factor);
}
else {
BKE_report(op->reports, RPT_ERROR, "Custom interpolation curve does not exist");
+ continue;
}
}
- else if (ipo_settings->type >= GP_IPO_BACK) {
+ else if (type >= GP_IPO_BACK) {
/* easing equation... */
- factor = gpencil_interpolate_seq_easing_calc(ipo_settings, factor);
+ factor = gpencil_interpolate_seq_easing_calc(op, factor);
}
- /* create new strokes data with interpolated points reading original stroke */
- for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) {
-
- /* only selected */
- if ((GPENCIL_EDIT_MODE(gpd)) && (flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) &&
- ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
- continue;
- }
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
- continue;
- }
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps_from) == false) {
- continue;
- }
-
- /* get final stroke to interpolate */
- fFrame = BLI_findindex(&prevFrame->strokes, gps_from);
- gps_to = BLI_findlink(&nextFrame->strokes, fFrame);
- if (gps_to == NULL) {
- continue;
- }
-
- /* create a new frame if needed */
- if (interFrame == NULL) {
- interFrame = BKE_gpencil_layer_frame_get(gpl, cframe, GP_GETFRAME_ADD_NEW);
- interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN;
- }
-
- /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
- if (gps_from->totpoints > gps_to->totpoints) {
- BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
- }
- if (gps_to->totpoints > gps_from->totpoints) {
- BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
- }
+ /* Apply the factor to all pair of strokes. */
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, pair_strokes) {
+ bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
- /* create new stroke */
+ /* Create new stroke. */
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+ new_stroke->flag |= GP_STROKE_TAG;
+ new_stroke->select_index = 0;
- /* update points position */
+ /* Update points position. */
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
+ gpencil_interpolate_smooth_stroke(new_stroke, smooth_factor, smooth_steps);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
- /* add to strokes */
+ /* Add strokes to frame. */
+ bGPDframe *interFrame = BKE_gpencil_layer_frame_get(gpl, cframe, GP_GETFRAME_ADD_NEW);
+ interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN;
+
BLI_addtail(&interFrame->strokes, new_stroke);
}
}
+ /* Free Hash tablets. */
+ if (used_strokes != NULL) {
+ BLI_ghash_free(used_strokes, NULL, NULL);
+ }
+ if (pair_strokes != NULL) {
+ BLI_ghash_free(pair_strokes, NULL, NULL);
+ }
+
BKE_gpencil_free_strokes(prevFrame);
BKE_gpencil_free_strokes(nextFrame);
MEM_SAFE_FREE(prevFrame);
@@ -1091,8 +1375,148 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayout *col, *row;
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+ const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ col = uiLayoutColumn(layout, true);
+ uiItemR(col, &ptr, "step", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "layers", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "interpolate_selected_only", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "flip", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "smooth_factor", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "smooth_steps", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "type", 0, NULL, ICON_NONE);
+
+ if (type == GP_IPO_CURVEMAP) {
+ /* Get an RNA pointer to ToolSettings to give to the custom curve. */
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = scene->toolsettings;
+ PointerRNA gpsettings_ptr;
+ RNA_pointer_create(
+ &scene->id, &RNA_GPencilInterpolateSettings, &ts->gp_interpolate, &gpsettings_ptr);
+ uiTemplateCurveMapping(
+ layout, &gpsettings_ptr, "interpolation_curve", 0, false, true, true, false);
+ }
+ else if (type != GP_IPO_LINEAR) {
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "easing", 0, NULL, ICON_NONE);
+ if (type == GP_IPO_BACK) {
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "back", 0, NULL, ICON_NONE);
+ }
+ else if (type == GP_IPO_ELASTIC) {
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "amplitude", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "period", 0, NULL, ICON_NONE);
+ }
+ }
+}
+
void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
{
+ static const EnumPropertyItem gpencil_interpolation_layer_items[] = {
+ {0, "ACTIVE", 0, "Active", ""},
+ {1, "ALL", 0, "All Layers", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem gpencil_interpolation_type_items[] = {
+ /* interpolation */
+ {0, "", 0, N_("Interpolation"), "Standard transitions between keyframes"},
+ {GP_IPO_LINEAR,
+ "LINEAR",
+ ICON_IPO_LINEAR,
+ "Linear",
+ "Straight-line interpolation between A and B (i.e. no ease in/out)"},
+ {GP_IPO_CURVEMAP,
+ "CUSTOM",
+ ICON_IPO_BEZIER,
+ "Custom",
+ "Custom interpolation defined using a curve map"},
+
+ /* easing */
+ {0,
+ "",
+ 0,
+ N_("Easing (by strength)"),
+ "Predefined inertial transitions, useful for motion graphics (from least to most "
+ "''dramatic'')"},
+ {GP_IPO_SINE,
+ "SINE",
+ ICON_IPO_SINE,
+ "Sinusoidal",
+ "Sinusoidal easing (weakest, almost linear but with a slight curvature)"},
+ {GP_IPO_QUAD, "QUAD", ICON_IPO_QUAD, "Quadratic", "Quadratic easing"},
+ {GP_IPO_CUBIC, "CUBIC", ICON_IPO_CUBIC, "Cubic", "Cubic easing"},
+ {GP_IPO_QUART, "QUART", ICON_IPO_QUART, "Quartic", "Quartic easing"},
+ {GP_IPO_QUINT, "QUINT", ICON_IPO_QUINT, "Quintic", "Quintic easing"},
+ {GP_IPO_EXPO, "EXPO", ICON_IPO_EXPO, "Exponential", "Exponential easing (dramatic)"},
+ {GP_IPO_CIRC,
+ "CIRC",
+ ICON_IPO_CIRC,
+ "Circular",
+ "Circular easing (strongest and most dynamic)"},
+
+ {0, "", 0, N_("Dynamic Effects"), "Simple physics-inspired easing effects"},
+ {GP_IPO_BACK, "BACK", ICON_IPO_BACK, "Back", "Cubic easing with overshoot and settle"},
+ {GP_IPO_BOUNCE,
+ "BOUNCE",
+ ICON_IPO_BOUNCE,
+ "Bounce",
+ "Exponentially decaying parabolic bounce, like when objects collide"},
+ {GP_IPO_ELASTIC,
+ "ELASTIC",
+ ICON_IPO_ELASTIC,
+ "Elastic",
+ "Exponentially decaying sine wave, like an elastic band"},
+
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem gpencil_interpolation_easing_items[] = {
+ {BEZT_IPO_EASE_AUTO,
+ "AUTO",
+ ICON_IPO_EASE_IN_OUT,
+ "Automatic Easing",
+ "Easing type is chosen automatically based on what the type of interpolation used "
+ "(e.g. 'Ease In' for transitional types, and 'Ease Out' for dynamic effects)"},
+
+ {BEZT_IPO_EASE_IN,
+ "EASE_IN",
+ ICON_IPO_EASE_IN,
+ "Ease In",
+ "Only on the end closest to the next keyframe"},
+ {BEZT_IPO_EASE_OUT,
+ "EASE_OUT",
+ ICON_IPO_EASE_OUT,
+ "Ease Out",
+ "Only on the end closest to the first keyframe"},
+ {BEZT_IPO_EASE_IN_OUT,
+ "EASE_IN_OUT",
+ ICON_IPO_EASE_IN_OUT,
+ "Ease In and Out",
+ "Segment between both keyframes"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem flip_modes[] = {
+ {GP_INTERPOLATE_NOFLIP, "NOFLIP", 0, "No Flip", ""},
+ {GP_INTERPOLATE_FLIP, "FLIP", 0, "Flip", ""},
+ {GP_INTERPOLATE_FLIPAUTO, "AUTO", 0, "Automatic", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
/* identifiers */
ot->name = "Interpolate Sequence";
ot->idname = "GPENCIL_OT_interpolate_sequence";
@@ -1101,6 +1525,103 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
/* api callbacks */
ot->exec = gpencil_interpolate_seq_exec;
ot->poll = gpencil_view3d_poll;
+ ot->ui = gpencil_interpolate_seq_ui;
+
+ RNA_def_int(ot->srna,
+ "step",
+ 1,
+ 1,
+ MAXFRAME,
+ "Step",
+ "Number of frames between generated interpolated frames",
+ 1,
+ MAXFRAME);
+
+ RNA_def_enum(ot->srna,
+ "layers",
+ gpencil_interpolation_layer_items,
+ 0,
+ "Layer",
+ "Layers included in the interpolation");
+
+ RNA_def_boolean(ot->srna,
+ "interpolate_selected_only",
+ 0,
+ "Only Selected",
+ "Interpolate only selected strokes");
+
+ RNA_def_enum(ot->srna,
+ "flip",
+ flip_modes,
+ GP_INTERPOLATE_FLIPAUTO,
+ "Flip Mode",
+ "Invert destination stroke to match start and end with source stroke");
+
+ RNA_def_int(ot->srna,
+ "smooth_steps",
+ 1,
+ 1,
+ 3,
+ "Iterations",
+ "Number of times to smooth newly created strokes",
+ 1,
+ 3);
+
+ RNA_def_float(ot->srna,
+ "smooth_factor",
+ 0.0f,
+ 0.0f,
+ 2.0f,
+ "Smooth",
+ "Amount of smoothing to apply to interpolated strokes, to reduce jitter/noise",
+ 0.0f,
+ 2.0f);
+
+ RNA_def_enum(ot->srna,
+ "type",
+ gpencil_interpolation_type_items,
+ 0,
+ "Type",
+ "Interpolation method to use the next time 'Interpolate Sequence' is run");
+
+ RNA_def_enum(
+ ot->srna,
+ "easing",
+ gpencil_interpolation_easing_items,
+ 0,
+ "Easing",
+ "Which ends of the segment between the preceding and following grease pencil frames "
+ "easing interpolation is applied to");
+
+ RNA_def_float(ot->srna,
+ "back",
+ 1.702f,
+ 0.0f,
+ FLT_MAX,
+ "Back",
+ "Amount of overshoot for 'back' easing",
+ 0.0f,
+ FLT_MAX);
+
+ RNA_def_float(ot->srna,
+ "amplitude",
+ 0.15f,
+ 0.0f,
+ FLT_MAX,
+ "Amplitude",
+ "Amount to boost elastic bounces for 'elastic' easing",
+ 0.0f,
+ FLT_MAX);
+
+ RNA_def_float(ot->srna,
+ "period",
+ 0.15f,
+ -FLT_MAX,
+ FLT_MAX,
+ "Period",
+ "Time between bounces for elastic easing",
+ -FLT_MAX,
+ FLT_MAX);
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1110,19 +1631,30 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
static bool gpencil_interpolate_reverse_poll(bContext *C)
{
- if (!gpencil_view3d_poll(C)) {
- return 0;
+ ScrArea *area = CTX_wm_area(C);
+ if (area == NULL) {
+ return false;
+ }
+ if ((area->spacetype != SPACE_VIEW3D) && (area->spacetype != SPACE_ACTION)) {
+ return false;
}
- bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ if (gpd == NULL) {
+ return false;
+ }
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+ if (gpl == NULL) {
+ return false;
+ }
/* need to be on a breakdown frame */
if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) {
CTX_wm_operator_poll_msg_set(C, "Expected current frame to be a breakdown");
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op))
@@ -1132,7 +1664,11 @@ static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op))
/* Go through each layer, deleting the breakdowns around the current frame,
* but only if there is a keyframe nearby to stop at
*/
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* only editable and visible layers are considered */
+ if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
+ continue;
+ }
bGPDframe *start_key = NULL;
bGPDframe *end_key = NULL;
bGPDframe *gpf, *gpfn;
@@ -1193,7 +1729,6 @@ static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op))
BLI_freelinkN(&gpl->frames, end_key);
}
}
- CTX_DATA_END;
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -1205,7 +1740,7 @@ static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op))
void GPENCIL_OT_interpolate_reverse(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Remove Breakdowns";
+ ot->name = "Delete Breakdowns";
ot->idname = "GPENCIL_OT_interpolate_reverse";
ot->description =
"Remove breakdown frames generated by interpolating between two Grease Pencil frames";
@@ -1217,5 +1752,3 @@ void GPENCIL_OT_interpolate_reverse(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-
-/* *************************************************************** */
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 435bff34998..8d8734dfd1f 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -104,6 +104,7 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo
Main *bmain = CTX_data_main(C);
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
Scene *scene = CTX_data_scene(C);
@@ -133,6 +134,7 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo
/* stroke */
bGPDstroke *gps = BKE_gpencil_stroke_new(MAX2(ob->actcol - 1, 0), totpoints, brush->size);
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
if (cyclic) {
gps->flag |= GP_STROKE_CYCLIC;
@@ -241,6 +243,7 @@ static void gpencil_calc_points_factor(bContext *C,
}
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
}
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index f347a4fe6b9..4eec4c7d00e 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -338,6 +338,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
ED_gpencil_fill_vertex_color_set(ts, brush, gps);
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
/* the polygon must be closed, so enabled cyclic */
if (ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) {
gps->flag |= GP_STROKE_CYCLIC;
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index d1d8abd6775..fd5c5bb5346 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -156,6 +156,11 @@ static bool gpencil_3d_point_to_screen_space(ARegion *region,
/* helper to deselect all selected strokes/points */
static void deselect_all_selected(bContext *C)
{
+ /* Set selection index to 0. */
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+ gpd->select_last_index = 0;
+
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
/* deselect stroke and its points if selected */
if (gps->flag & GP_STROKE_SELECT) {
@@ -169,6 +174,7 @@ static void deselect_all_selected(bContext *C)
/* deselect stroke itself too */
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
/* deselect curve and curve points */
@@ -187,7 +193,7 @@ static void deselect_all_selected(bContext *C)
CTX_DATA_END;
}
-static void select_all_curve_points(bGPDstroke *gps, bGPDcurve *gpc, bool deselect)
+static void select_all_curve_points(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc, bool deselect)
{
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -205,10 +211,12 @@ static void select_all_curve_points(bGPDstroke *gps, bGPDcurve *gpc, bool desele
if (deselect == false) {
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
}
else {
gpc->flag &= ~GP_CURVE_SELECT;
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
@@ -423,7 +431,7 @@ static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
BEZT_DESEL_ALL(&gpc_pt->bezt);
}
- BKE_gpencil_curve_sync_selection(gps);
+ BKE_gpencil_curve_sync_selection(gpd, gps);
changed = true;
}
}
@@ -562,6 +570,7 @@ static bool gpencil_select_same_layer(bContext *C)
}
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
changed = true;
}
@@ -578,6 +587,7 @@ static bool gpencil_select_same_layer(bContext *C)
}
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
changed = true;
}
@@ -622,6 +632,7 @@ static bool gpencil_select_same_material(bContext *C)
}
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
changed = true;
}
@@ -640,6 +651,7 @@ static bool gpencil_select_same_material(bContext *C)
}
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
changed = true;
}
@@ -756,6 +768,8 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op)
BEZT_SEL_ALL(&gpc->curve_points[0].bezt);
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
+
if ((extend == false) && (gps->totpoints > 1)) {
for (int i = 1; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -769,6 +783,7 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op)
else {
gps->points->flag |= GP_SPOINT_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
/* deselect rest? */
if ((extend == false) && (gps->totpoints > 1)) {
@@ -863,6 +878,7 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op)
BEZT_SEL_ALL(&gpc->curve_points[gpc->tot_curve_points - 1].bezt);
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
if ((extend == false) && (gps->totpoints > 1)) {
for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -876,6 +892,7 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op)
else {
gps->points[gps->totpoints - 1].flag |= GP_SPOINT_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
/* deselect rest? */
if ((extend == false) && (gps->totpoints > 1)) {
@@ -1272,10 +1289,12 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *gpd,
if (select) {
pt_active->flag |= GP_SPOINT_SELECT;
gps_active->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps_active, false);
}
else {
pt_active->flag &= ~GP_SPOINT_SELECT;
gps_active->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps_active, true);
}
changed = true;
/* if stroke mode, don't check more points */
@@ -1314,13 +1333,13 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *gpd,
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps_active);
gps_active->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
/* Select all curve points. */
- select_all_curve_points(gps_active, gps_active->editcurve, false);
+ select_all_curve_points(gpd, gps_active, gps_active->editcurve, false);
BKE_gpencil_stroke_geometry_update(gpd, gps_active);
changed = true;
}
/* Ensure that stroke selection is in sync with its points. */
- BKE_gpencil_stroke_sync_selection(gps_active);
+ BKE_gpencil_stroke_sync_selection(gpd, gps_active);
return changed;
}
@@ -1338,6 +1357,9 @@ static bool gpencil_do_curve_circle_sel(bContext *C,
{
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+
const bool only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
bool hit = false;
@@ -1411,7 +1433,7 @@ static bool gpencil_do_curve_circle_sel(bContext *C,
}
}
- BKE_gpencil_curve_sync_selection(gps);
+ BKE_gpencil_curve_sync_selection(gpd, gps);
return hit;
}
@@ -1631,7 +1653,7 @@ static bool gpencil_stroke_fill_isect_rect(ARegion *region,
#endif
static bool gpencil_generic_curve_select(bContext *C,
- Object *UNUSED(ob),
+ Object *ob,
GPencilTestFn is_inside_fn,
rcti UNUSED(box),
GP_SelectUserData *user_data,
@@ -1640,6 +1662,7 @@ static bool gpencil_generic_curve_select(bContext *C,
{
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
+ bGPdata *gpd = ob->data;
const bool handle_only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
const bool handle_all = (v3d->overlay.handle_display == CURVE_HANDLE_ALL);
@@ -1771,7 +1794,7 @@ static bool gpencil_generic_curve_select(bContext *C,
}
}
- BKE_gpencil_curve_sync_selection(gps);
+ BKE_gpencil_curve_sync_selection(gpd, gps);
}
GP_EDITABLE_CURVES_END(gps_iter);
@@ -1797,6 +1820,8 @@ static bool gpencil_generic_stroke_select(bContext *C,
/* deselect all strokes first? */
if (SEL_OP_USE_PRE_DESELECT(sel_op) || (GPENCIL_PAINT_MODE(gpd))) {
+ /* Set selection index to 0. */
+ gpd->select_last_index = 0;
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
bGPDspoint *pt;
@@ -1807,6 +1832,7 @@ static bool gpencil_generic_stroke_select(bContext *C,
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
CTX_DATA_END;
@@ -1891,13 +1917,13 @@ static bool gpencil_generic_stroke_select(bContext *C,
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps_active);
gps_active->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
/* Select all curve points. */
- select_all_curve_points(gps_active, gps_active->editcurve, false);
+ select_all_curve_points(gpd, gps_active, gps_active->editcurve, false);
BKE_gpencil_stroke_geometry_update(gpd, gps_active);
changed = true;
}
/* Ensure that stroke selection is in sync with its points */
- BKE_gpencil_stroke_sync_selection(gps_active);
+ BKE_gpencil_stroke_sync_selection(gpd, gps_active);
}
GP_EVALUATED_STROKES_END(gpstroke_iter);
@@ -2313,7 +2339,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
}
/* select all curve points */
if (hit_curve != NULL) {
- select_all_curve_points(hit_stroke, hit_curve, deselect);
+ select_all_curve_points(gpd, hit_stroke, hit_curve, deselect);
}
else {
bGPDspoint *pt;
@@ -2332,9 +2358,11 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
/* stroke too... */
if (deselect == false) {
hit_stroke->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, hit_stroke, false);
}
else {
hit_stroke->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, hit_stroke, true);
}
}
}
@@ -2346,11 +2374,13 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
BEZT_SEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
hit_curve->flag |= GP_CURVE_SELECT;
hit_stroke->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, hit_stroke, false);
}
else {
/* we're adding selection, so selection must be true */
hit_point->flag |= GP_SPOINT_SELECT;
hit_stroke->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, hit_stroke, false);
/* expand selection to segment */
int selectmode;
@@ -2378,14 +2408,14 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
if (!BEZT_ISSEL_ANY(&hit_curve_point->bezt)) {
hit_curve_point->flag &= ~GP_CURVE_POINT_SELECT;
}
- BKE_gpencil_curve_sync_selection(hit_stroke);
+ BKE_gpencil_curve_sync_selection(gpd, hit_stroke);
}
else {
/* deselect point */
hit_point->flag &= ~GP_SPOINT_SELECT;
/* ensure that stroke is selected correctly */
- BKE_gpencil_stroke_sync_selection(hit_stroke);
+ BKE_gpencil_stroke_sync_selection(gpd, hit_stroke);
}
}
}
@@ -2572,6 +2602,7 @@ static int gpencil_select_vertex_color_exec(bContext *C, wmOperator *op)
if (gps_selected) {
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
/* Extend stroke selection. */
if (selectmode == GP_SELECTMODE_STROKE) {
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 9b12772bc9b..f9242c5a1a8 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -1167,6 +1167,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
if (keep_original) {
gps_active = BKE_gpencil_stroke_duplicate(gps, true, true);
gps_active->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps_active, true);
for (i = 0, pt = gps_active->points; i < gps_active->totpoints; i++, pt++) {
pt->flag &= ~GP_SPOINT_SELECT;
}
@@ -1689,6 +1690,10 @@ void ED_gpencil_vgroup_select(bContext *C, Object *ob)
gps->flag |= GP_STROKE_SELECT;
}
}
+
+ if (gps->flag & GP_STROKE_SELECT) {
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
+ }
}
}
@@ -2564,6 +2569,9 @@ int ED_gpencil_select_stroke_segment(bGPdata *gpd,
void ED_gpencil_select_toggle_all(bContext *C, int action)
{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+
/* for "toggle", test for existing selected strokes */
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
@@ -2588,6 +2596,9 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
* NOTE: we limit ourselves to editable layers, since once a layer is "locked/hidden
* nothing should be able to touch it
*/
+ /* Set selection index to 0. */
+ gpd->select_last_index = 0;
+
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
/* deselect all strokes on all frames */
@@ -2605,6 +2616,7 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
}
@@ -2642,9 +2654,11 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
/* Change status of stroke */
if (selected) {
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
}
else {
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
CTX_DATA_END;
@@ -2666,6 +2680,11 @@ void ED_gpencil_select_curve_toggle_all(bContext *C, int action)
}
if (action == SEL_DESELECT) {
+ /* Set selection index to 0. */
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+ gpd->select_last_index = 0;
+
GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
{
for (int i = 0; i < gpc->tot_curve_points; i++) {
@@ -2676,6 +2695,7 @@ void ED_gpencil_select_curve_toggle_all(bContext *C, int action)
}
gpc->flag &= ~GP_CURVE_SELECT;
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
GP_EDITABLE_CURVES_END(gps_iter);
}
@@ -2717,10 +2737,12 @@ void ED_gpencil_select_curve_toggle_all(bContext *C, int action)
if (selected) {
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
}
else {
gpc->flag &= ~GP_CURVE_SELECT;
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
GP_EDITABLE_STROKES_END(gps_iter);