diff options
Diffstat (limited to 'source/blender/editors/gpencil/gpencil_interpolate.c')
-rw-r--r-- | source/blender/editors/gpencil/gpencil_interpolate.c | 1931 |
1 files changed, 984 insertions, 947 deletions
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 7be157b5d84..5c9b9416330 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -22,7 +22,6 @@ * \ingroup edgpencil */ - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -82,46 +81,48 @@ /* Poll callback for interpolation operators */ static bool gpencil_view3d_poll(bContext *C) { - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - /* only 3D view */ - ScrArea *sa = CTX_wm_area(C); - if (sa && sa->spacetype != SPACE_VIEW3D) { - return 0; - } + /* only 3D view */ + ScrArea *sa = CTX_wm_area(C); + if (sa && sa->spacetype != SPACE_VIEW3D) { + return 0; + } - /* need data to interpolate */ - if (ELEM(NULL, gpd, gpl)) { - return 0; - } + /* need data to interpolate */ + if (ELEM(NULL, gpd, gpl)) { + return 0; + } - return 1; + return 1; } /* Perform interpolation */ -static void gp_interpolate_update_points( - const bGPDstroke *gps_from, const bGPDstroke *gps_to, bGPDstroke *new_stroke, float factor) +static void gp_interpolate_update_points(const bGPDstroke *gps_from, + const bGPDstroke *gps_to, + bGPDstroke *new_stroke, + float factor) { - /* update points */ - for (int i = 0; i < new_stroke->totpoints; i++) { - const bGPDspoint *prev = &gps_from->points[i]; - const bGPDspoint *next = &gps_to->points[i]; - bGPDspoint *pt = &new_stroke->points[i]; - - /* Interpolate all values */ - interp_v3_v3v3(&pt->x, &prev->x, &next->x, factor); - pt->pressure = interpf(prev->pressure, next->pressure, 1.0f - factor); - pt->strength = interpf(prev->strength, next->strength, 1.0f - factor); - CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); - - /* GPXX interpolate dverts */ + /* update points */ + for (int i = 0; i < new_stroke->totpoints; i++) { + const bGPDspoint *prev = &gps_from->points[i]; + const bGPDspoint *next = &gps_to->points[i]; + bGPDspoint *pt = &new_stroke->points[i]; + + /* Interpolate all values */ + interp_v3_v3v3(&pt->x, &prev->x, &next->x, factor); + pt->pressure = interpf(prev->pressure, next->pressure, 1.0f - factor); + pt->strength = interpf(prev->strength, next->strength, 1.0f - factor); + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + + /* GPXX interpolate dverts */ #if 0 - MDeformVert *dvert = &new_stroke->dvert[i]; - dvert->totweight = 0; - dvert->dw = NULL; + MDeformVert *dvert = &new_stroke->dvert[i]; + dvert->totweight = 0; + dvert->dw = NULL; #endif - } + } } /* ****************** Interpolate Interactive *********************** */ @@ -129,212 +130,223 @@ static void gp_interpolate_update_points( /* Helper: Update all strokes interpolated */ static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi) { - bGPdata *gpd = tgpi->gpd; - tGPDinterpolate_layer *tgpil; - const float shift = tgpi->shift; - - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - bGPDstroke *new_stroke; - const float factor = tgpil->factor + shift; - - for (new_stroke = tgpil->interFrame->strokes.first; new_stroke; new_stroke = new_stroke->next) { - bGPDstroke *gps_from, *gps_to; - int stroke_idx; - - if (new_stroke->totpoints == 0) { - continue; - } - - /* get strokes to interpolate */ - stroke_idx = BLI_findindex(&tgpil->interFrame->strokes, new_stroke); - - 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)) { - gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); - } - } - } - - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + bGPdata *gpd = tgpi->gpd; + tGPDinterpolate_layer *tgpil; + const float shift = tgpi->shift; + + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + bGPDstroke *new_stroke; + const float factor = tgpil->factor + shift; + + for (new_stroke = tgpil->interFrame->strokes.first; new_stroke; + new_stroke = new_stroke->next) { + bGPDstroke *gps_from, *gps_to; + int stroke_idx; + + if (new_stroke->totpoints == 0) { + continue; + } + + /* get strokes to interpolate */ + stroke_idx = BLI_findindex(&tgpil->interFrame->strokes, new_stroke); + + 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)) { + gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); + } + } + } + + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } /* Helper: Verify valid strokes for interpolation */ static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd) { - Object *ob = CTX_data_active_object(C); - ToolSettings *ts = CTX_data_tool_settings(C); - eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag; - - /* get layers */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* all layers or only active */ - if (!(flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && !(gpl->flag & GP_LAYER_ACTIVE)) { - continue; - } - /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { - continue; - } - - /* read strokes */ - for (bGPDstroke *gps_from = gpl->actframe->strokes.first; gps_from; gps_from = gps_from->next) { - bGPDstroke *gps_to; - int fFrame; - - /* only selected */ - if ((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_color_use(ob, gpl, gps_from) == false) { - continue; - } - - /* get final stroke to interpolate */ - fFrame = BLI_findindex(&gpl->actframe->strokes, gps_from); - gps_to = BLI_findlink(&gpl->actframe->next->strokes, fFrame); - if (gps_to == NULL) { - continue; - } - - return true; - } - } - return false; + Object *ob = CTX_data_active_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag; + + /* get layers */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* all layers or only active */ + if (!(flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && !(gpl->flag & GP_LAYER_ACTIVE)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + + /* read strokes */ + for (bGPDstroke *gps_from = gpl->actframe->strokes.first; gps_from; + gps_from = gps_from->next) { + bGPDstroke *gps_to; + int fFrame; + + /* only selected */ + if ((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_color_use(ob, gpl, gps_from) == false) { + continue; + } + + /* get final stroke to interpolate */ + fFrame = BLI_findindex(&gpl->actframe->strokes, gps_from); + gps_to = BLI_findlink(&gpl->actframe->next->strokes, fFrame); + if (gps_to == NULL) { + continue; + } + + return true; + } + } + return false; } /* Helper: Create internal strokes interpolated */ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) { - 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) / (actframe->next->framenum - actframe->framenum + 1); - - /* limits are 100% below 0 and 100% over the 100% */ - tgpi->low_limit = -1.0f - tgpi->init_factor; - tgpi->high_limit = 2.0f - tgpi->init_factor; - - /* set layers */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - tGPDinterpolate_layer *tgpil; - - /* all layers or only active */ - if (!(tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && (gpl != active_gpl)) { - continue; - } - /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { - continue; - } - - /* create temp data for each layer */ - tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer"); - - tgpil->gpl = gpl; - tgpil->prevFrame = gpl->actframe; - tgpil->nextFrame = gpl->actframe->next; - - BLI_addtail(&tgpi->ilayers, tgpil); - - /* create a new temporary frame */ - tgpil->interFrame = MEM_callocN(sizeof(bGPDframe), "bGPDframe"); - tgpil->interFrame->framenum = tgpi->cframe; - - /* get interpolation factor by layer (usually must be equal for all layers, but not sure) */ - 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 */ - for (bGPDstroke *gps_from = tgpil->prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { - bGPDstroke *gps_to; - int fFrame; - - bGPDstroke *new_stroke = NULL; - bool valid = true; - - - /* only selected */ - if ((tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { - valid = false; - } - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps_from) == false) { - valid = false; - } - - /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(ob, tgpil->gpl, gps_from) == false) { - valid = false; - } - - /* 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; - } - - /* create new stroke */ - new_stroke = BKE_gpencil_stroke_duplicate(gps_from); - - if (valid) { - /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ - if (gps_from->totpoints > gps_to->totpoints) { - new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); - if (new_stroke->dvert != NULL) { - new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert) * gps_to->totpoints); - } - new_stroke->totpoints = gps_to->totpoints; - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; - } - /* update points position */ - gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor); - } - else { - /* 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)); - } - new_stroke->tot_triangles = 0; - new_stroke->triangles = MEM_recallocN(new_stroke->triangles, sizeof(*new_stroke->triangles)); - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; - } - - /* add to strokes */ - BLI_addtail(&tgpil->interFrame->strokes, new_stroke); - } - } + 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) / + (actframe->next->framenum - actframe->framenum + 1); + + /* limits are 100% below 0 and 100% over the 100% */ + tgpi->low_limit = -1.0f - tgpi->init_factor; + tgpi->high_limit = 2.0f - tgpi->init_factor; + + /* set layers */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + tGPDinterpolate_layer *tgpil; + + /* all layers or only active */ + if (!(tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && (gpl != active_gpl)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + + /* create temp data for each layer */ + tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer"); + + tgpil->gpl = gpl; + tgpil->prevFrame = gpl->actframe; + tgpil->nextFrame = gpl->actframe->next; + + BLI_addtail(&tgpi->ilayers, tgpil); + + /* create a new temporary frame */ + tgpil->interFrame = MEM_callocN(sizeof(bGPDframe), "bGPDframe"); + tgpil->interFrame->framenum = tgpi->cframe; + + /* get interpolation factor by layer (usually must be equal for all layers, but not sure) */ + 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 */ + for (bGPDstroke *gps_from = tgpil->prevFrame->strokes.first; gps_from; + gps_from = gps_from->next) { + bGPDstroke *gps_to; + int fFrame; + + bGPDstroke *new_stroke = NULL; + bool valid = true; + + /* only selected */ + if ((tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && + ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + valid = false; + } + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps_from) == false) { + valid = false; + } + + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, tgpil->gpl, gps_from) == false) { + valid = false; + } + + /* 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; + } + + /* create new stroke */ + new_stroke = BKE_gpencil_stroke_duplicate(gps_from); + + if (valid) { + /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ + if (gps_from->totpoints > gps_to->totpoints) { + new_stroke->points = MEM_recallocN(new_stroke->points, + sizeof(*new_stroke->points) * gps_to->totpoints); + if (new_stroke->dvert != NULL) { + new_stroke->dvert = MEM_recallocN(new_stroke->dvert, + sizeof(*new_stroke->dvert) * gps_to->totpoints); + } + new_stroke->totpoints = gps_to->totpoints; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + } + /* update points position */ + gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor); + } + else { + /* 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)); + } + new_stroke->tot_triangles = 0; + new_stroke->triangles = MEM_recallocN(new_stroke->triangles, + sizeof(*new_stroke->triangles)); + new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + } + + /* add to strokes */ + BLI_addtail(&tgpil->interFrame->strokes, new_stroke); + } + } } /* ----------------------- */ /* Drawing Callbacks */ /* Drawing callback for modal operator in screen mode */ -static void gpencil_interpolate_draw_screen(const struct bContext *C, ARegion *UNUSED(ar), void *arg) +static void gpencil_interpolate_draw_screen(const struct bContext *C, + ARegion *UNUSED(ar), + void *arg) { - tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; - ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_PIXEL); + tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; + ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_PIXEL); } /* Drawing callback for modal operator in 3d mode */ static void gpencil_interpolate_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg) { - tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; - ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_VIEW); + tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; + ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_VIEW); } /* ----------------------- */ @@ -344,52 +356,57 @@ static void gpencil_interpolate_draw_3d(const bContext *C, ARegion *UNUSED(ar), */ static void gpencil_mouse_update_shift(tGPDinterpolate *tgpi, wmOperator *op, const wmEvent *event) { - float mid = (float)(tgpi->ar->winx - tgpi->ar->winrct.xmin) / 2.0f; - float mpos = event->x - tgpi->ar->winrct.xmin; - - if (mpos >= mid) { - tgpi->shift = ((mpos - mid) * tgpi->high_limit) / mid; - } - else { - tgpi->shift = tgpi->low_limit - ((mpos * tgpi->low_limit) / mid); - } - - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); + float mid = (float)(tgpi->ar->winx - tgpi->ar->winrct.xmin) / 2.0f; + float mpos = event->x - tgpi->ar->winrct.xmin; + + if (mpos >= mid) { + tgpi->shift = ((mpos - mid) * tgpi->high_limit) / mid; + } + else { + tgpi->shift = tgpi->low_limit - ((mpos * tgpi->low_limit) / mid); + } + + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); } /* Helper: Draw status message while the user is running the operator */ static void gpencil_interpolate_status_indicators(bContext *C, tGPDinterpolate *p) { - Scene *scene = p->scene; - char status_str[UI_MAX_DRAW_STR]; - char msg_str[UI_MAX_DRAW_STR]; - - BLI_strncpy(msg_str, IFACE_("GPencil Interpolation: "), UI_MAX_DRAW_STR); - - if (hasNumInput(&p->num)) { - char str_offs[NUM_STR_REP_LEN]; - - outputNumInput(&p->num, str_offs, &scene->unit); - BLI_snprintf(status_str, sizeof(status_str), "%s%s", msg_str, str_offs); - } - else { - BLI_snprintf(status_str, sizeof(status_str), "%s%d %%", msg_str, (int)((p->init_factor + p->shift) * 100.0f)); - } - - ED_area_status_text(p->sa, status_str); - ED_workspace_status_text(C, IFACE_("ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust factor")); + Scene *scene = p->scene; + char status_str[UI_MAX_DRAW_STR]; + char msg_str[UI_MAX_DRAW_STR]; + + BLI_strncpy(msg_str, IFACE_("GPencil Interpolation: "), UI_MAX_DRAW_STR); + + if (hasNumInput(&p->num)) { + char str_offs[NUM_STR_REP_LEN]; + + outputNumInput(&p->num, str_offs, &scene->unit); + BLI_snprintf(status_str, sizeof(status_str), "%s%s", msg_str, str_offs); + } + else { + BLI_snprintf(status_str, + sizeof(status_str), + "%s%d %%", + msg_str, + (int)((p->init_factor + p->shift) * 100.0f)); + } + + ED_area_status_text(p->sa, status_str); + ED_workspace_status_text( + C, IFACE_("ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust factor")); } /* Update screen and stroke */ static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpolate *tgpi) { - /* update shift indicator in header */ - gpencil_interpolate_status_indicators(C, tgpi); - /* apply... */ - tgpi->shift = RNA_float_get(op->ptr, "shift"); - /* update points position */ - gp_interpolate_update_strokes(C, tgpi); + /* update shift indicator in header */ + gpencil_interpolate_status_indicators(C, tgpi); + /* apply... */ + tgpi->shift = RNA_float_get(op->ptr, "shift"); + /* update points position */ + gp_interpolate_update_strokes(C, tgpi); } /* ----------------------- */ @@ -397,93 +414,93 @@ static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpol /* Exit and free memory */ static void gpencil_interpolate_exit(bContext *C, wmOperator *op) { - tGPDinterpolate *tgpi = op->customdata; - tGPDinterpolate_layer *tgpil; - bGPdata *gpd = tgpi->gpd; - - /* don't assume that operator data exists at all */ - if (tgpi) { - /* remove drawing handler */ - if (tgpi->draw_handle_screen) { - ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_screen); - } - if (tgpi->draw_handle_3d) { - ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d); - } - - /* clear status message area */ - ED_area_status_text(tgpi->sa, NULL); - ED_workspace_status_text(C, NULL); - - /* finally, free memory used by temp data */ - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - BKE_gpencil_free_strokes(tgpil->interFrame); - MEM_freeN(tgpil->interFrame); - } - - BLI_freelistN(&tgpi->ilayers); - MEM_freeN(tgpi); - } - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - - /* clear pointer */ - op->customdata = NULL; + tGPDinterpolate *tgpi = op->customdata; + tGPDinterpolate_layer *tgpil; + bGPdata *gpd = tgpi->gpd; + + /* don't assume that operator data exists at all */ + if (tgpi) { + /* remove drawing handler */ + if (tgpi->draw_handle_screen) { + ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_screen); + } + if (tgpi->draw_handle_3d) { + ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d); + } + + /* clear status message area */ + ED_area_status_text(tgpi->sa, NULL); + ED_workspace_status_text(C, NULL); + + /* finally, free memory used by temp data */ + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + BKE_gpencil_free_strokes(tgpil->interFrame); + MEM_freeN(tgpil->interFrame); + } + + BLI_freelistN(&tgpi->ilayers); + MEM_freeN(tgpi); + } + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* clear pointer */ + op->customdata = NULL; } /* Init new temporary interpolation data */ static bool gp_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinterpolate *tgpi) { - ToolSettings *ts = CTX_data_tool_settings(C); - bGPdata *gpd = CTX_data_gpencil_data(C); + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = CTX_data_gpencil_data(C); - /* set current scene and window */ - tgpi->scene = CTX_data_scene(C); - tgpi->sa = CTX_wm_area(C); - tgpi->ar = CTX_wm_region(C); - tgpi->flag = ts->gp_interpolate.flag; + /* set current scene and window */ + tgpi->scene = CTX_data_scene(C); + tgpi->sa = CTX_wm_area(C); + tgpi->ar = CTX_wm_region(C); + tgpi->flag = ts->gp_interpolate.flag; - /* set current frame number */ - tgpi->cframe = tgpi->scene->r.cfra; + /* set current frame number */ + tgpi->cframe = tgpi->scene->r.cfra; - /* set GP datablock */ - tgpi->gpd = gpd; + /* set GP datablock */ + tgpi->gpd = gpd; - /* set interpolation weight */ - tgpi->shift = RNA_float_get(op->ptr, "shift"); - /* set layers */ - gp_interpolate_set_points(C, tgpi); + /* set interpolation weight */ + tgpi->shift = RNA_float_get(op->ptr, "shift"); + /* set layers */ + gp_interpolate_set_points(C, tgpi); - return 1; + return 1; } /* Allocate memory and initialize values */ static tGPDinterpolate *gp_session_init_interpolation(bContext *C, wmOperator *op) { - tGPDinterpolate *tgpi = MEM_callocN(sizeof(tGPDinterpolate), "GPencil Interpolate Data"); + tGPDinterpolate *tgpi = MEM_callocN(sizeof(tGPDinterpolate), "GPencil Interpolate Data"); - /* define initial values */ - gp_interpolate_set_init_values(C, op, tgpi); + /* define initial values */ + gp_interpolate_set_init_values(C, op, tgpi); - /* return context data for running operator */ - return tgpi; + /* return context data for running operator */ + return tgpi; } /* Init interpolation: Allocate memory and set init values */ static int gpencil_interpolate_init(bContext *C, wmOperator *op) { - tGPDinterpolate *tgpi; - - /* check context */ - tgpi = op->customdata = gp_session_init_interpolation(C, op); - if (tgpi == NULL) { - /* something wasn't set correctly in context */ - gpencil_interpolate_exit(C, op); - return 0; - } - - /* everything is now setup ok */ - return 1; + tGPDinterpolate *tgpi; + + /* check context */ + tgpi = op->customdata = gp_session_init_interpolation(C, op); + if (tgpi == NULL) { + /* something wasn't set correctly in context */ + gpencil_interpolate_exit(C, op); + return 0; + } + + /* everything is now setup ok */ + return 1; } /* ----------------------- */ @@ -491,219 +508,230 @@ static int gpencil_interpolate_init(bContext *C, wmOperator *op) /* Invoke handler: Initialize the operator */ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - wmWindow *win = CTX_wm_window(C); - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - bGPDframe *actframe = gpl->actframe; - tGPDinterpolate *tgpi = NULL; - - /* cannot interpolate if not between 2 frames */ - if (ELEM(NULL, actframe, actframe->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_eval, 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; - } - - /* need editable strokes */ - if (!gp_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) - MEM_freeN(op->customdata); - return OPERATOR_CANCELLED; - } - else { - tgpi = op->customdata; - } - - /* Enable custom drawing handlers - * It needs 2 handlers because strokes can in 3d space and screen space - * and each handler use different coord system - */ - tgpi->draw_handle_screen = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_screen, tgpi, REGION_DRAW_POST_PIXEL); - tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_3d, tgpi, REGION_DRAW_POST_VIEW); - - /* set cursor to indicate modal */ - WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR); - - /* update shift indicator in header */ - gpencil_interpolate_status_indicators(C, tgpi); - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); - - /* add a modal handler for this operator */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; + wmWindow *win = CTX_wm_window(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPDframe *actframe = gpl->actframe; + tGPDinterpolate *tgpi = NULL; + + /* cannot interpolate if not between 2 frames */ + if (ELEM(NULL, actframe, actframe->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_eval, 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; + } + + /* need editable strokes */ + if (!gp_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) + MEM_freeN(op->customdata); + return OPERATOR_CANCELLED; + } + else { + tgpi = op->customdata; + } + + /* Enable custom drawing handlers + * It needs 2 handlers because strokes can in 3d space and screen space + * and each handler use different coord system + */ + tgpi->draw_handle_screen = ED_region_draw_cb_activate( + tgpi->ar->type, gpencil_interpolate_draw_screen, tgpi, REGION_DRAW_POST_PIXEL); + tgpi->draw_handle_3d = ED_region_draw_cb_activate( + tgpi->ar->type, gpencil_interpolate_draw_3d, tgpi, REGION_DRAW_POST_VIEW); + + /* set cursor to indicate modal */ + WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR); + + /* update shift indicator in header */ + gpencil_interpolate_status_indicators(C, tgpi); + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* add a modal handler for this operator */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; } /* Modal handler: Events handling during interactive part */ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent *event) { - tGPDinterpolate *tgpi = op->customdata; - wmWindow *win = CTX_wm_window(C); - bGPDframe *gpf_dst; - bGPDstroke *gps_src, *gps_dst; - tGPDinterpolate_layer *tgpil; - const bool has_numinput = hasNumInput(&tgpi->num); - - switch (event->type) { - case LEFTMOUSE: /* confirm */ - case RETKEY: - { - /* return to normal cursor and header status */ - ED_area_status_text(tgpi->sa, NULL); - ED_workspace_status_text(C, NULL); - WM_cursor_modal_restore(win); - - /* insert keyframes as required... */ - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - gpf_dst = BKE_gpencil_layer_getframe(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW); - gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN; - - /* copy strokes */ - BLI_listbase_clear(&gpf_dst->strokes); - for (gps_src = tgpil->interFrame->strokes.first; gps_src; gps_src = gps_src->next) { - if (gps_src->totpoints == 0) { - continue; - } - - /* make copy of source stroke, then adjust pointer to points too */ - gps_dst = MEM_dupallocN(gps_src); - gps_dst->points = MEM_dupallocN(gps_src->points); - if (gps_src->dvert != NULL) { - gps_dst->dvert = MEM_dupallocN(gps_src->dvert); - BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); - } - gps_dst->triangles = MEM_dupallocN(gps_src->triangles); - gps_dst->flag |= GP_STROKE_RECALC_GEOMETRY; - BLI_addtail(&gpf_dst->strokes, gps_dst); - } - } - - /* clean up temp data */ - gpencil_interpolate_exit(C, op); - - /* done! */ - return OPERATOR_FINISHED; - } - - case ESCKEY: /* cancel */ - case RIGHTMOUSE: - { - /* return to normal cursor and header status */ - ED_area_status_text(tgpi->sa, NULL); - ED_workspace_status_text(C, NULL); - WM_cursor_modal_restore(win); - - /* clean up temp data */ - gpencil_interpolate_exit(C, op); - - /* canceled! */ - return OPERATOR_CANCELLED; - } - - case WHEELUPMOUSE: - { - tgpi->shift = tgpi->shift + 0.01f; - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); - - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - break; - } - case WHEELDOWNMOUSE: - { - tgpi->shift = tgpi->shift - 0.01f; - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); - - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - break; - } - case MOUSEMOVE: /* calculate new position */ - { - /* only handle mousemove if not doing numinput */ - if (has_numinput == false) { - /* update shift based on position of mouse */ - gpencil_mouse_update_shift(tgpi, op, event); - - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - } - break; - } - default: - { - if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) { - const float factor = tgpi->init_factor; - float value; - - /* Grab shift from numeric input, and store this new value (the user see an int) */ - value = (factor + tgpi->shift) * 100.0f; - applyNumInput(&tgpi->num, &value); - tgpi->shift = value / 100.0f; - - /* recalculate the shift to get the right value in the frame scale */ - tgpi->shift = tgpi->shift - factor; - - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - RNA_float_set(op->ptr, "shift", tgpi->shift); - - /* update screen */ - gpencil_interpolate_update(C, op, tgpi); - - break; - } - else { - /* unhandled event - allow to pass through */ - return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; - } - } - } - - /* still running... */ - return OPERATOR_RUNNING_MODAL; + tGPDinterpolate *tgpi = op->customdata; + wmWindow *win = CTX_wm_window(C); + bGPDframe *gpf_dst; + bGPDstroke *gps_src, *gps_dst; + tGPDinterpolate_layer *tgpil; + const bool has_numinput = hasNumInput(&tgpi->num); + + switch (event->type) { + case LEFTMOUSE: /* confirm */ + case RETKEY: { + /* return to normal cursor and header status */ + ED_area_status_text(tgpi->sa, NULL); + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + + /* insert keyframes as required... */ + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + gpf_dst = BKE_gpencil_layer_getframe(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW); + gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN; + + /* copy strokes */ + BLI_listbase_clear(&gpf_dst->strokes); + for (gps_src = tgpil->interFrame->strokes.first; gps_src; gps_src = gps_src->next) { + if (gps_src->totpoints == 0) { + continue; + } + + /* make copy of source stroke, then adjust pointer to points too */ + gps_dst = MEM_dupallocN(gps_src); + gps_dst->points = MEM_dupallocN(gps_src->points); + if (gps_src->dvert != NULL) { + gps_dst->dvert = MEM_dupallocN(gps_src->dvert); + BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); + } + gps_dst->triangles = MEM_dupallocN(gps_src->triangles); + gps_dst->flag |= GP_STROKE_RECALC_GEOMETRY; + BLI_addtail(&gpf_dst->strokes, gps_dst); + } + } + + /* clean up temp data */ + gpencil_interpolate_exit(C, op); + + /* done! */ + return OPERATOR_FINISHED; + } + + case ESCKEY: /* cancel */ + case RIGHTMOUSE: { + /* return to normal cursor and header status */ + ED_area_status_text(tgpi->sa, NULL); + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + + /* clean up temp data */ + gpencil_interpolate_exit(C, op); + + /* canceled! */ + return OPERATOR_CANCELLED; + } + + case WHEELUPMOUSE: { + tgpi->shift = tgpi->shift + 0.01f; + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + break; + } + case WHEELDOWNMOUSE: { + tgpi->shift = tgpi->shift - 0.01f; + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + break; + } + case MOUSEMOVE: /* calculate new position */ + { + /* only handle mousemove if not doing numinput */ + if (has_numinput == false) { + /* update shift based on position of mouse */ + gpencil_mouse_update_shift(tgpi, op, event); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + } + break; + } + default: { + if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) { + const float factor = tgpi->init_factor; + float value; + + /* Grab shift from numeric input, and store this new value (the user see an int) */ + value = (factor + tgpi->shift) * 100.0f; + applyNumInput(&tgpi->num, &value); + tgpi->shift = value / 100.0f; + + /* recalculate the shift to get the right value in the frame scale */ + tgpi->shift = tgpi->shift - factor; + + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + RNA_float_set(op->ptr, "shift", tgpi->shift); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + + break; + } + else { + /* unhandled event - allow to pass through */ + return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; + } + } + } + + /* still running... */ + return OPERATOR_RUNNING_MODAL; } /* Cancel handler */ static void gpencil_interpolate_cancel(bContext *C, wmOperator *op) { - /* this is just a wrapper around exit() */ - gpencil_interpolate_exit(C, op); + /* this is just a wrapper around exit() */ + gpencil_interpolate_exit(C, op); } void GPENCIL_OT_interpolate(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Grease Pencil Interpolation"; - ot->idname = "GPENCIL_OT_interpolate"; - ot->description = "Interpolate grease pencil strokes between frames"; - - /* callbacks */ - ot->invoke = gpencil_interpolate_invoke; - ot->modal = gpencil_interpolate_modal; - ot->cancel = gpencil_interpolate_cancel; - ot->poll = gpencil_view3d_poll; - - /* flags */ - ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - - /* properties */ - RNA_def_float_percentage(ot->srna, "shift", 0.0f, -1.0f, 1.0f, "Shift", "Bias factor for which frame has more influence on the interpolated strokes", -0.9f, 0.9f); + /* identifiers */ + ot->name = "Grease Pencil Interpolation"; + ot->idname = "GPENCIL_OT_interpolate"; + ot->description = "Interpolate grease pencil strokes between frames"; + + /* callbacks */ + ot->invoke = gpencil_interpolate_invoke; + ot->modal = gpencil_interpolate_modal; + ot->cancel = gpencil_interpolate_cancel; + ot->poll = gpencil_view3d_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + RNA_def_float_percentage( + ot->srna, + "shift", + 0.0f, + -1.0f, + 1.0f, + "Shift", + "Bias factor for which frame has more influence on the interpolated strokes", + -0.9f, + 0.9f); } /* ****************** Interpolate Sequence *********************** */ @@ -711,464 +739,473 @@ void GPENCIL_OT_interpolate(wmOperatorType *ot) /* Helper: Perform easing equation calculations for GP interpolation operator */ static float gp_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_settings, 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; - float result = time; - - switch (ipo_settings->type) { - case GP_IPO_BACK: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_back_ease_in(time, begin, change, duration, back); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_back_ease_out(time, begin, change, duration, back); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_back_ease_in_out(time, begin, change, duration, back); - break; - - default: /* default/auto: same as ease out */ - result = BLI_easing_back_ease_out(time, begin, change, duration, back); - break; - } - break; - - case GP_IPO_BOUNCE: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_bounce_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_bounce_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_bounce_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease out */ - result = BLI_easing_bounce_ease_out(time, begin, change, duration); - break; - } - break; - - case GP_IPO_CIRC: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_circ_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_circ_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_circ_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_circ_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_CUBIC: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_cubic_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_cubic_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_cubic_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_cubic_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_ELASTIC: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period); - break; - - default: /* default/auto: same as ease out */ - result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); - break; - } - break; - - case GP_IPO_EXPO: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_expo_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_expo_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_expo_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_expo_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_QUAD: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_quad_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_quad_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_quad_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_quad_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_QUART: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_quart_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_quart_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_quart_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_quart_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_QUINT: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_quint_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_quint_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_quint_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_quint_ease_in(time, begin, change, duration); - break; - } - break; - - case GP_IPO_SINE: - switch (easing) { - case BEZT_IPO_EASE_IN: - result = BLI_easing_sine_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - result = BLI_easing_sine_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - result = BLI_easing_sine_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - result = BLI_easing_sine_ease_in(time, begin, change, duration); - break; - } - break; - - default: - printf("%s: Unknown interpolation type - %d\n", __func__, ipo_settings->type); - break; - } - - return result; + 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; + float result = time; + + switch (ipo_settings->type) { + case GP_IPO_BACK: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_back_ease_in(time, begin, change, duration, back); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_back_ease_out(time, begin, change, duration, back); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_back_ease_in_out(time, begin, change, duration, back); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_back_ease_out(time, begin, change, duration, back); + break; + } + break; + + case GP_IPO_BOUNCE: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_bounce_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_bounce_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_bounce_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_bounce_ease_out(time, begin, change, duration); + break; + } + break; + + case GP_IPO_CIRC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_circ_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_circ_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_circ_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_circ_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_CUBIC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_cubic_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_cubic_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_cubic_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_cubic_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_ELASTIC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_elastic_ease_in_out( + time, begin, change, duration, amplitude, period); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); + break; + } + break; + + case GP_IPO_EXPO: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_expo_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_expo_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_expo_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_expo_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUAD: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quad_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quad_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quad_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quad_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUART: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quart_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quart_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quart_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quart_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUINT: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quint_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quint_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quint_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quint_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_SINE: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_sine_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_sine_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_sine_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_sine_ease_in(time, begin, change, duration); + break; + } + break; + + default: + printf("%s: Unknown interpolation type - %d\n", __func__, ipo_settings->type); + break; + } + + return result; } static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); - bGPDframe *actframe = active_gpl->actframe; - - Object *ob = CTX_data_active_object(C); - ToolSettings *ts = CTX_data_tool_settings(C); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - int cfra_eval = (int)DEG_get_ctime(depsgraph); - - GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate; - eGP_Interpolate_SettingsFlag flag = ipo_settings->flag; - - /* cannot interpolate if not between 2 frames */ - if (ELEM(NULL, actframe, actframe->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_eval, 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; - } - - /* loop all layer to check if need interpolation */ - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - bGPDframe *prevFrame, *nextFrame; - bGPDstroke *gps_from, *gps_to; - int cframe, fFrame; - - /* all layers or only active */ - if (((flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) { - continue; - } - /* only editable and visible layers are considered */ - if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { - continue; - } - - /* store extremes */ - prevFrame = gpl->actframe; - nextFrame = gpl->actframe->next; - - /* Loop over intermediary frames and create the interpolation */ - for (cframe = prevFrame->framenum + 1; cframe < nextFrame->framenum; cframe++) { - bGPDframe *interFrame = NULL; - float factor; - - /* get interpolation factor */ - factor = (float)(cframe - prevFrame->framenum) / (nextFrame->framenum - prevFrame->framenum + 1); - - if (ipo_settings->type == GP_IPO_CURVEMAP) { - /* custom curvemap */ - if (ipo_settings->custom_ipo) { - factor = curvemapping_evaluateF(ipo_settings->custom_ipo, 0, factor); - } - else { - BKE_report(op->reports, RPT_ERROR, "Custom interpolation curve does not exist"); - } - } - else if (ipo_settings->type >= GP_IPO_BACK) { - /* easing equation... */ - factor = gp_interpolate_seq_easing_calc(ipo_settings, factor); - } - - /* create new strokes data with interpolated points reading original stroke */ - for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { - bGPDstroke *new_stroke = NULL; - - /* only selected */ - if ((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_color_use(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_getframe(gpl, cframe, GP_GETFRAME_ADD_NEW); - interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN; - } - - /* create new stroke */ - new_stroke = BKE_gpencil_stroke_duplicate(gps_from); - - /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ - if (gps_from->totpoints > gps_to->totpoints) { - /* free weights of removed points */ - if (gps_from->dvert != NULL) { - BKE_defvert_array_free_elems(gps_from->dvert + gps_to->totpoints, gps_from->totpoints - gps_to->totpoints); - } - - new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); - - if (new_stroke->dvert != NULL) { - new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert) * gps_to->totpoints); - } - new_stroke->totpoints = gps_to->totpoints; - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; - } - - /* update points position */ - gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); - - /* add to strokes */ - BLI_addtail(&interFrame->strokes, new_stroke); - } - } - } - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); + bGPDframe *actframe = active_gpl->actframe; + + Object *ob = CTX_data_active_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate; + eGP_Interpolate_SettingsFlag flag = ipo_settings->flag; + + /* cannot interpolate if not between 2 frames */ + if (ELEM(NULL, actframe, actframe->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_eval, 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; + } + + /* loop all layer to check if need interpolation */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bGPDframe *prevFrame, *nextFrame; + bGPDstroke *gps_from, *gps_to; + int cframe, fFrame; + + /* all layers or only active */ + if (((flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + + /* store extremes */ + prevFrame = gpl->actframe; + nextFrame = gpl->actframe->next; + + /* Loop over intermediary frames and create the interpolation */ + for (cframe = prevFrame->framenum + 1; cframe < nextFrame->framenum; cframe++) { + bGPDframe *interFrame = NULL; + float factor; + + /* get interpolation factor */ + factor = (float)(cframe - prevFrame->framenum) / + (nextFrame->framenum - prevFrame->framenum + 1); + + if (ipo_settings->type == GP_IPO_CURVEMAP) { + /* custom curvemap */ + if (ipo_settings->custom_ipo) { + factor = curvemapping_evaluateF(ipo_settings->custom_ipo, 0, factor); + } + else { + BKE_report(op->reports, RPT_ERROR, "Custom interpolation curve does not exist"); + } + } + else if (ipo_settings->type >= GP_IPO_BACK) { + /* easing equation... */ + factor = gp_interpolate_seq_easing_calc(ipo_settings, factor); + } + + /* create new strokes data with interpolated points reading original stroke */ + for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { + bGPDstroke *new_stroke = NULL; + + /* only selected */ + if ((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_color_use(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_getframe(gpl, cframe, GP_GETFRAME_ADD_NEW); + interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN; + } + + /* create new stroke */ + new_stroke = BKE_gpencil_stroke_duplicate(gps_from); + + /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ + if (gps_from->totpoints > gps_to->totpoints) { + /* free weights of removed points */ + if (gps_from->dvert != NULL) { + BKE_defvert_array_free_elems(gps_from->dvert + gps_to->totpoints, + gps_from->totpoints - gps_to->totpoints); + } + + new_stroke->points = MEM_recallocN(new_stroke->points, + sizeof(*new_stroke->points) * gps_to->totpoints); + + if (new_stroke->dvert != NULL) { + new_stroke->dvert = MEM_recallocN(new_stroke->dvert, + sizeof(*new_stroke->dvert) * gps_to->totpoints); + } + new_stroke->totpoints = gps_to->totpoints; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY; + } + + /* update points position */ + gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); + + /* add to strokes */ + BLI_addtail(&interFrame->strokes, new_stroke); + } + } + } + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Interpolate Sequence"; - ot->idname = "GPENCIL_OT_interpolate_sequence"; - ot->description = "Generate 'in-betweens' to smoothly interpolate between Grease Pencil frames"; + /* identifiers */ + ot->name = "Interpolate Sequence"; + ot->idname = "GPENCIL_OT_interpolate_sequence"; + ot->description = "Generate 'in-betweens' to smoothly interpolate between Grease Pencil frames"; - /* api callbacks */ - ot->exec = gpencil_interpolate_seq_exec; - ot->poll = gpencil_view3d_poll; + /* api callbacks */ + ot->exec = gpencil_interpolate_seq_exec; + ot->poll = gpencil_view3d_poll; - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ******************** Remove Breakdowns ************************ */ static bool gpencil_interpolate_reverse_poll(bContext *C) { - if (!gpencil_view3d_poll(C)) { - return 0; - } + if (!gpencil_view3d_poll(C)) { + return 0; + } - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - /* 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; - } + /* 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 1; + return 1; } static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - - /* 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) - { - bGPDframe *start_key = NULL; - bGPDframe *end_key = NULL; - bGPDframe *gpf, *gpfn; - - /* Only continue if we're currently on a breakdown keyframe */ - if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) - continue; - - /* Search left for "start_key" (i.e. the first breakdown to remove) */ - gpf = gpl->actframe; - while (gpf) { - if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { - /* A breakdown... keep going left */ - start_key = gpf; - gpf = gpf->prev; - } - else { - /* Not a breakdown (may be a key, or an extreme, - * or something else that wasn't generated)... stop */ - break; - } - } - - /* Search right for "end_key" (i.e. the last breakdown to remove) */ - gpf = gpl->actframe; - while (gpf) { - if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { - /* A breakdown... keep going right */ - end_key = gpf; - gpf = gpf->next; - } - else { - /* Not a breakdown... stop */ - break; - } - } - - /* Did we find anything? */ - /* NOTE: We should only proceed if there's something before/after these extents... - * Otherwise, there's just an extent of breakdowns with no keys to interpolate between - */ - if ((start_key && end_key) && - ELEM(NULL, start_key->prev, end_key->next) == false) - { - /* Set actframe to the key before start_key, since the keys have been removed now */ - gpl->actframe = start_key->prev; - - /* Free each frame we're removing (except the last one) */ - for (gpf = start_key; gpf && gpf != end_key; gpf = gpfn) { - gpfn = gpf->next; - - /* free strokes and their associated memory */ - BKE_gpencil_free_strokes(gpf); - BLI_freelinkN(&gpl->frames, gpf); - } - - /* Now free the last one... */ - BKE_gpencil_free_strokes(end_key); - BLI_freelinkN(&gpl->frames, end_key); - } - } - CTX_DATA_END; - - /* notifiers */ - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* 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) { + bGPDframe *start_key = NULL; + bGPDframe *end_key = NULL; + bGPDframe *gpf, *gpfn; + + /* Only continue if we're currently on a breakdown keyframe */ + if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) + continue; + + /* Search left for "start_key" (i.e. the first breakdown to remove) */ + gpf = gpl->actframe; + while (gpf) { + if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { + /* A breakdown... keep going left */ + start_key = gpf; + gpf = gpf->prev; + } + else { + /* Not a breakdown (may be a key, or an extreme, + * or something else that wasn't generated)... stop */ + break; + } + } + + /* Search right for "end_key" (i.e. the last breakdown to remove) */ + gpf = gpl->actframe; + while (gpf) { + if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { + /* A breakdown... keep going right */ + end_key = gpf; + gpf = gpf->next; + } + else { + /* Not a breakdown... stop */ + break; + } + } + + /* Did we find anything? */ + /* NOTE: We should only proceed if there's something before/after these extents... + * Otherwise, there's just an extent of breakdowns with no keys to interpolate between + */ + if ((start_key && end_key) && ELEM(NULL, start_key->prev, end_key->next) == false) { + /* Set actframe to the key before start_key, since the keys have been removed now */ + gpl->actframe = start_key->prev; + + /* Free each frame we're removing (except the last one) */ + for (gpf = start_key; gpf && gpf != end_key; gpf = gpfn) { + gpfn = gpf->next; + + /* free strokes and their associated memory */ + BKE_gpencil_free_strokes(gpf); + BLI_freelinkN(&gpl->frames, gpf); + } + + /* Now free the last one... */ + BKE_gpencil_free_strokes(end_key); + BLI_freelinkN(&gpl->frames, end_key); + } + } + CTX_DATA_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; } void GPENCIL_OT_interpolate_reverse(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Remove Breakdowns"; - ot->idname = "GPENCIL_OT_interpolate_reverse"; - ot->description = "Remove breakdown frames generated by interpolating between two Grease Pencil frames"; - - /* callbacks */ - ot->exec = gpencil_interpolate_reverse_exec; - ot->poll = gpencil_interpolate_reverse_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* identifiers */ + ot->name = "Remove Breakdowns"; + ot->idname = "GPENCIL_OT_interpolate_reverse"; + ot->description = + "Remove breakdown frames generated by interpolating between two Grease Pencil frames"; + + /* callbacks */ + ot->exec = gpencil_interpolate_reverse_exec; + ot->poll = gpencil_interpolate_reverse_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* *************************************************************** */ |