2017-01-03 23:29:21 +13:00
/*
* * * * * * BEGIN GPL LICENSE BLOCK * * * * *
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* The Original Code is Copyright ( C ) 2016 , Blender Foundation
* This is a new part of Blender
*
* Contributor ( s ) : Antonio Vazquez , Joshua Leung
*
* * * * * * END GPL LICENSE BLOCK * * * * *
*
* Operators for interpolating new Grease Pencil frames from existing strokes
*/
/** \file blender/editors/gpencil/gpencil_interpolate.c
* \ ingroup edgpencil
*/
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <stddef.h>
# include <math.h>
# include "MEM_guardedalloc.h"
# include "BLI_blenlib.h"
# include "BLI_utildefines.h"
2017-01-18 19:00:17 +13:00
# include "BLI_easing.h"
# include "BLI_math.h"
2017-01-03 23:29:21 +13:00
# include "BLT_translation.h"
2017-01-18 19:00:17 +13:00
# include "DNA_color_types.h"
# include "DNA_gpencil_types.h"
2017-01-03 23:29:21 +13:00
# include "DNA_object_types.h"
# include "DNA_scene_types.h"
# include "DNA_screen_types.h"
# include "DNA_space_types.h"
# include "DNA_view3d_types.h"
2017-01-18 19:00:17 +13:00
# include "BKE_colortools.h"
2017-01-03 23:29:21 +13:00
# include "BKE_context.h"
# include "BKE_global.h"
# include "BKE_gpencil.h"
# include "BKE_library.h"
# include "BKE_report.h"
# include "BKE_screen.h"
# include "UI_interface.h"
# include "UI_resources.h"
# include "WM_api.h"
# include "WM_types.h"
# include "RNA_access.h"
# include "RNA_define.h"
# include "RNA_enum_types.h"
# include "UI_view2d.h"
# include "ED_gpencil.h"
# include "ED_object.h"
# include "ED_screen.h"
# include "ED_view3d.h"
# include "ED_screen.h"
# include "ED_space_api.h"
# include "gpencil_intern.h"
/* ************************************************ */
2017-01-18 00:48:15 +13:00
/* Core/Shared Utilities */
2017-01-03 23:29:21 +13:00
2017-01-18 00:48:15 +13:00
/* Poll callback for interpolation operators */
static int gpencil_interpolate_poll ( bContext * C )
{
bGPdata * gpd = CTX_data_gpencil_data ( C ) ;
bGPDlayer * gpl = CTX_data_active_gpencil_layer ( C ) ;
/* only 3D view */
if ( CTX_wm_area ( C ) - > spacetype ! = SPACE_VIEW3D ) {
return 0 ;
}
/* need data to interpolate */
if ( ELEM ( NULL , gpd , gpl ) ) {
return 0 ;
}
return 1 ;
}
/* Perform interpolation */
2017-01-03 23:29:21 +13:00
static void gp_interpolate_update_points ( bGPDstroke * gps_from , bGPDstroke * gps_to , bGPDstroke * new_stroke , float factor )
{
bGPDspoint * prev , * pt , * next ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* update points */
for ( int i = 0 ; i < new_stroke - > totpoints ; i + + ) {
prev = & gps_from - > points [ i ] ;
pt = & new_stroke - > points [ i ] ;
next = & gps_to - > points [ i ] ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* Interpolate all values */
interp_v3_v3v3 ( & pt - > x , & prev - > x , & next - > x , factor ) ;
pt - > pressure = interpf ( prev - > pressure , next - > pressure , factor ) ;
pt - > strength = interpf ( prev - > strength , next - > strength , factor ) ;
CLAMP ( pt - > strength , GPENCIL_STRENGTH_MIN , 1.0f ) ;
}
}
2017-01-18 00:48:15 +13:00
/* ****************** Interpolate Interactive *********************** */
2017-01-03 23:29:21 +13:00
/* Helper: Update all strokes interpolated */
static void gp_interpolate_update_strokes ( bContext * C , tGPDinterpolate * tgpi )
{
tGPDinterpolate_layer * tgpil ;
2017-01-18 00:48:15 +13:00
const float shift = tgpi - > shift ;
2017-01-03 23:29:21 +13:00
for ( tgpil = tgpi - > ilayers . first ; tgpil ; tgpil = tgpil - > next ) {
2017-01-18 00:48:15 +13:00
bGPDstroke * new_stroke ;
const float factor = tgpil - > factor + shift ;
2017-01-03 23:29:21 +13:00
for ( new_stroke = tgpil - > interFrame - > strokes . first ; new_stroke ; new_stroke = new_stroke - > next ) {
2017-01-18 00:48:15 +13:00
bGPDstroke * gps_from , * gps_to ;
int stroke_idx ;
2017-01-03 23:29:21 +13:00
if ( new_stroke - > totpoints = = 0 ) {
continue ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* get strokes to interpolate */
2017-01-18 00:48:15 +13:00
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 ) ;
2017-01-03 23:29:21 +13:00
/* update points position */
if ( ( gps_from ) & & ( gps_to ) ) {
gp_interpolate_update_points ( gps_from , gps_to , new_stroke , factor ) ;
}
}
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
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 )
{
ToolSettings * ts = CTX_data_tool_settings ( C ) ;
2017-01-18 16:43:17 +13:00
eGP_Interpolate_SettingsFlag flag = ts - > gp_interpolate . flag ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* get layers */
2017-01-18 01:29:40 +13:00
for ( bGPDlayer * gpl = gpd - > layers . first ; gpl ; gpl = gpl - > next ) {
2017-01-03 23:29:21 +13:00
/* all layers or only active */
2017-01-18 16:43:17 +13:00
if ( ! ( flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS ) & & ! ( gpl - > flag & GP_LAYER_ACTIVE ) ) {
2017-01-03 23:29:21 +13:00
continue ;
}
/* only editable and visible layers are considered */
if ( ! gpencil_layer_is_editable ( gpl ) | | ( gpl - > actframe = = NULL ) ) {
continue ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* read strokes */
2017-01-18 01:29:40 +13:00
for ( bGPDstroke * gps_from = gpl - > actframe - > strokes . first ; gps_from ; gps_from = gps_from - > next ) {
bGPDstroke * gps_to ;
int fFrame ;
2017-01-03 23:29:21 +13:00
/* only selected */
2017-01-18 16:43:17 +13:00
if ( ( flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED ) & & ( ( gps_from - > flag & GP_STROKE_SELECT ) = = 0 ) ) {
2017-01-03 23:29:21 +13:00
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 ( gpl , gps_from ) = = false ) {
continue ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ;
}
2017-01-18 00:48:15 +13:00
2017-01-18 01:29:40 +13:00
return true ;
2017-01-03 23:29:21 +13:00
}
}
2017-01-18 01:29:40 +13:00
return false ;
2017-01-03 23:29:21 +13:00
}
/* 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 ) ;
2017-01-18 01:29:40 +13:00
bGPDframe * actframe = active_gpl - > actframe ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* save initial factor for active layer to define shift limits */
2017-01-18 01:29:40 +13:00
tgpi - > init_factor = ( float ) ( tgpi - > cframe - actframe - > framenum ) / ( actframe - > next - > framenum - actframe - > framenum + 1 ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* set layers */
2017-01-18 01:29:40 +13:00
for ( bGPDlayer * gpl = gpd - > layers . first ; gpl ; gpl = gpl - > next ) {
tGPDinterpolate_layer * tgpil ;
2017-01-03 23:29:21 +13:00
/* all layers or only active */
2017-01-18 16:43:17 +13:00
if ( ! ( tgpi - > flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS ) & & ( gpl ! = active_gpl ) ) {
2017-01-03 23:29:21 +13:00
continue ;
}
/* only editable and visible layers are considered */
if ( ! gpencil_layer_is_editable ( gpl ) | | ( gpl - > actframe = = NULL ) ) {
continue ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* create temp data for each layer */
tgpil = MEM_callocN ( sizeof ( tGPDinterpolate_layer ) , " GPencil Interpolate Layer " ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
tgpil - > gpl = gpl ;
tgpil - > prevFrame = gpl - > actframe ;
tgpil - > nextFrame = gpl - > actframe - > next ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
BLI_addtail ( & tgpi - > ilayers , tgpil ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* create a new temporary frame */
tgpil - > interFrame = MEM_callocN ( sizeof ( bGPDframe ) , " bGPDframe " ) ;
tgpil - > interFrame - > framenum = tgpi - > cframe ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* create new strokes data with interpolated points reading original stroke */
2017-01-18 01:29:40 +13:00
for ( bGPDstroke * gps_from = tgpil - > prevFrame - > strokes . first ; gps_from ; gps_from = gps_from - > next ) {
bGPDstroke * gps_to ;
int fFrame ;
bGPDstroke * new_stroke ;
2017-01-03 23:29:21 +13:00
bool valid = true ;
2017-01-18 00:48:15 +13:00
2017-01-18 01:29:40 +13:00
2017-01-03 23:29:21 +13:00
/* only selected */
2017-01-18 16:43:17 +13:00
if ( ( tgpi - > flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED ) & & ( ( gps_from - > flag & GP_STROKE_SELECT ) = = 0 ) ) {
2017-01-03 23:29:21 +13:00
valid = false ;
}
/* skip strokes that are invalid for current view */
if ( ED_gpencil_stroke_can_use ( C , gps_from ) = = false ) {
valid = false ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* check if the color is editable */
if ( ED_gpencil_stroke_color_use ( tgpil - > gpl , gps_from ) = = false ) {
valid = false ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* create new stroke */
new_stroke = MEM_dupallocN ( gps_from ) ;
new_stroke - > points = MEM_dupallocN ( gps_from - > points ) ;
new_stroke - > triangles = MEM_dupallocN ( gps_from - > triangles ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
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 ) ;
new_stroke - > totpoints = gps_to - > totpoints ;
new_stroke - > tot_triangles = 0 ;
new_stroke - > flag | = GP_STROKE_RECALC_CACHES ;
}
/* 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 ) ) ;
new_stroke - > tot_triangles = 0 ;
new_stroke - > triangles = MEM_recallocN ( new_stroke - > triangles , sizeof ( * new_stroke - > triangles ) ) ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* add to strokes */
BLI_addtail ( & tgpil - > interFrame - > strokes , new_stroke ) ;
}
}
}
2017-01-18 00:48:15 +13:00
/* ----------------------- */
/* Drawing Callbacks */
/* Drawing callback for modal operator in screen mode */
static void gpencil_interpolate_draw_screen ( const struct bContext * UNUSED ( C ) , ARegion * UNUSED ( ar ) , void * arg )
{
tGPDinterpolate * tgpi = ( tGPDinterpolate * ) arg ;
ED_gp_draw_interpolation ( tgpi , REGION_DRAW_POST_PIXEL ) ;
}
/* Drawing callback for modal operator in 3d mode */
static void gpencil_interpolate_draw_3d ( const bContext * UNUSED ( C ) , ARegion * UNUSED ( ar ) , void * arg )
{
tGPDinterpolate * tgpi = ( tGPDinterpolate * ) arg ;
ED_gp_draw_interpolation ( tgpi , REGION_DRAW_POST_VIEW ) ;
}
/* ----------------------- */
2017-01-03 23:29:21 +13:00
/* Helper: calculate shift based on position of mouse (we only use x-axis for now.
2017-01-18 00:48:15 +13:00
* since this is more convenient for users to do ) , and store new shift value
*/
2017-01-03 23:29:21 +13:00
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 ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
if ( mpos > = mid ) {
tgpi - > shift = ( ( mpos - mid ) * tgpi - > high_limit ) / mid ;
}
else {
tgpi - > shift = tgpi - > low_limit - ( ( mpos * tgpi - > low_limit ) / mid ) ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
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 ( tGPDinterpolate * p )
{
Scene * scene = p - > scene ;
char status_str [ UI_MAX_DRAW_STR ] ;
char msg_str [ UI_MAX_DRAW_STR ] ;
2017-01-18 00:48:15 +13:00
BLI_strncpy ( msg_str , IFACE_ ( " GPencil Interpolation: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust factor " ) , UI_MAX_DRAW_STR ) ;
2017-01-03 23:29:21 +13:00
if ( hasNumInput ( & p - > num ) ) {
char str_offs [ NUM_STR_REP_LEN ] ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
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 ) ) ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
ED_area_headerprint ( p - > sa , status_str ) ;
}
2017-01-18 00:48:15 +13:00
/* Update screen and stroke */
2017-01-03 23:29:21 +13:00
static void gpencil_interpolate_update ( bContext * C , wmOperator * op , tGPDinterpolate * tgpi )
{
/* update shift indicator in header */
gpencil_interpolate_status_indicators ( tgpi ) ;
/* apply... */
tgpi - > shift = RNA_float_get ( op - > ptr , " shift " ) ;
/* update points position */
gp_interpolate_update_strokes ( C , tgpi ) ;
}
2017-01-18 00:48:15 +13:00
/* ----------------------- */
2017-01-03 23:29:21 +13:00
/* Exit and free memory */
static void gpencil_interpolate_exit ( bContext * C , wmOperator * op )
{
tGPDinterpolate * tgpi = op - > customdata ;
tGPDinterpolate_layer * tgpil ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ) ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* clear status message area */
ED_area_headerprint ( tgpi - > sa , NULL ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ) ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
BLI_freelistN ( & tgpi - > ilayers ) ;
MEM_freeN ( tgpi ) ;
}
WM_event_add_notifier ( C , NC_GPENCIL | NA_EDITED , NULL ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* clear pointer */
op - > customdata = NULL ;
}
2017-01-18 00:48:15 +13:00
/* Init new temporary interpolation data */
static bool gp_interpolate_set_init_values ( bContext * C , wmOperator * op , tGPDinterpolate * tgpi )
2017-01-03 23:29:21 +13:00
{
2017-01-18 00:48:15 +13:00
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 ) ;
2017-01-18 16:43:17 +13:00
tgpi - > flag = ts - > gp_interpolate . flag ;
2017-01-18 00:48:15 +13:00
/* set current frame number */
tgpi - > cframe = tgpi - > scene - > r . cfra ;
/* 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 ) ;
return 1 ;
2017-01-03 23:29:21 +13:00
}
2017-01-18 00:48:15 +13:00
/* Allocate memory and initialize values */
static tGPDinterpolate * gp_session_init_interpolation ( bContext * C , wmOperator * op )
{
tGPDinterpolate * tgpi = MEM_callocN ( sizeof ( tGPDinterpolate ) , " GPencil Interpolate Data " ) ;
/* define initial values */
gp_interpolate_set_init_values ( C , op , tgpi ) ;
/* return context data for running operator */
return tgpi ;
}
/* Init interpolation: Allocate memory and set init values */
2017-01-03 23:29:21 +13:00
static int gpencil_interpolate_init ( bContext * C , wmOperator * op )
{
tGPDinterpolate * tgpi ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* everything is now setup ok */
return 1 ;
}
2017-01-18 00:48:15 +13:00
/* ----------------------- */
2017-01-03 23:29:21 +13:00
/* Invoke handler: Initialize the operator */
static int gpencil_interpolate_invoke ( bContext * C , wmOperator * op , const wmEvent * UNUSED ( event ) )
{
wmWindow * win = CTX_wm_window ( C ) ;
Scene * scene = CTX_data_scene ( C ) ;
2017-01-18 00:48:15 +13:00
bGPdata * gpd = CTX_data_gpencil_data ( C ) ;
2017-01-03 23:29:21 +13:00
bGPDlayer * gpl = CTX_data_active_gpencil_layer ( C ) ;
2017-01-18 01:29:40 +13:00
bGPDframe * actframe = gpl - > actframe ;
2017-01-03 23:29:21 +13:00
tGPDinterpolate * tgpi = NULL ;
/* cannot interpolate if not between 2 frames */
2017-01-18 01:29:40 +13:00
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 " ) ;
2017-01-03 23:29:21 +13:00
return OPERATOR_CANCELLED ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* cannot interpolate in extremes */
2017-01-18 01:29:40 +13:00
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 " ) ;
2017-01-03 23:29:21 +13:00
return OPERATOR_CANCELLED ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* need editable strokes */
if ( ! gp_interpolate_check_todo ( C , gpd ) ) {
2017-01-18 01:29:40 +13:00
BKE_report ( op - > reports , RPT_ERROR , " Interpolation requires some editable strokes " ) ;
2017-01-03 23:29:21 +13:00
return OPERATOR_CANCELLED ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* try to initialize context data needed */
if ( ! gpencil_interpolate_init ( C , op ) ) {
if ( op - > customdata )
MEM_freeN ( op - > customdata ) ;
return OPERATOR_CANCELLED ;
}
2017-01-18 00:48:15 +13:00
else {
2017-01-03 23:29:21 +13:00
tgpi = op - > customdata ;
2017-01-18 00:48:15 +13:00
}
/* Enable custom drawing handlers
* It needs 2 handlers because strokes can in 3 d 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 ) ;
2017-01-03 23:29:21 +13:00
/* set cursor to indicate modal */
WM_cursor_modal_set ( win , BC_EW_SCROLLCURSOR ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* update shift indicator in header */
gpencil_interpolate_status_indicators ( tgpi ) ;
WM_event_add_notifier ( C , NC_GPENCIL | NA_EDITED , NULL ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* add a modal handler for this operator */
WM_event_add_modal_handler ( C , op ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
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 ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
switch ( event - > type ) {
case LEFTMOUSE : /* confirm */
case RETKEY :
{
/* return to normal cursor and header status */
ED_area_headerprint ( tgpi - > sa , NULL ) ;
WM_cursor_modal_restore ( win ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ) ;
gps_dst - > triangles = MEM_dupallocN ( gps_src - > triangles ) ;
gps_dst - > flag | = GP_STROKE_RECALC_CACHES ;
BLI_addtail ( & gpf_dst - > strokes , gps_dst ) ;
}
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* clean up temp data */
gpencil_interpolate_exit ( C , op ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* done! */
return OPERATOR_FINISHED ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
case ESCKEY : /* cancel */
case RIGHTMOUSE :
{
/* return to normal cursor and header status */
ED_area_headerprint ( tgpi - > sa , NULL ) ;
WM_cursor_modal_restore ( win ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* clean up temp data */
gpencil_interpolate_exit ( C , op ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* canceled! */
return OPERATOR_CANCELLED ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
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 ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* update screen */
gpencil_interpolate_update ( C , op , tgpi ) ;
}
break ;
}
default :
if ( ( event - > val = = KM_PRESS ) & & handleNumInput ( C , & tgpi - > num , event ) ) {
2017-01-18 00:48:15 +13:00
const float factor = tgpi - > init_factor ;
2017-01-03 23:29:21 +13:00
float value ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* recalculate the shift to get the right value in the frame scale */
tgpi - > shift = tgpi - > shift - factor ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
CLAMP ( tgpi - > shift , tgpi - > low_limit , tgpi - > high_limit ) ;
RNA_float_set ( op - > ptr , " shift " , tgpi - > shift ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* update screen */
gpencil_interpolate_update ( C , op , tgpi ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
break ;
}
else {
/* unhandled event - allow to pass through */
return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH ;
}
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* still running... */
return OPERATOR_RUNNING_MODAL ;
}
2017-01-18 00:48:15 +13:00
/* Cancel handler */
static void gpencil_interpolate_cancel ( bContext * C , wmOperator * op )
{
/* this is just a wrapper around exit() */
gpencil_interpolate_exit ( C , op ) ;
}
2017-01-03 23:29:21 +13:00
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 " ;
2017-01-18 00:48:15 +13:00
/* callbacks */
2017-01-03 23:29:21 +13:00
ot - > invoke = gpencil_interpolate_invoke ;
ot - > modal = gpencil_interpolate_modal ;
ot - > cancel = gpencil_interpolate_cancel ;
ot - > poll = gpencil_interpolate_poll ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* flags */
ot - > flag = OPTYPE_UNDO | OPTYPE_BLOCKING ;
2017-01-18 00:48:15 +13:00
/* 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 ) ;
2017-01-03 23:29:21 +13:00
}
2017-01-18 00:48:15 +13:00
/* ****************** Interpolate Sequence *********************** */
2017-01-18 19:00:17 +13:00
/* 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 BEZT_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 BEZT_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 ;
}
2017-01-03 23:29:21 +13:00
static int gpencil_interpolate_seq_exec ( bContext * C , wmOperator * op )
{
2017-01-18 00:48:15 +13:00
bGPdata * gpd = CTX_data_gpencil_data ( C ) ;
2017-01-03 23:29:21 +13:00
bGPDlayer * active_gpl = CTX_data_active_gpencil_layer ( C ) ;
2017-01-18 01:29:40 +13:00
bGPDframe * actframe = active_gpl - > actframe ;
Scene * scene = CTX_data_scene ( C ) ;
ToolSettings * ts = CTX_data_tool_settings ( C ) ;
2017-01-18 16:43:17 +13:00
GP_Interpolate_Settings * ipo_settings = & ts - > gp_interpolate ;
eGP_Interpolate_SettingsFlag flag = ipo_settings - > flag ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* cannot interpolate if not between 2 frames */
2017-01-18 01:29:40 +13:00
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 " ) ;
2017-01-03 23:29:21 +13:00
return OPERATOR_CANCELLED ;
}
/* cannot interpolate in extremes */
2017-01-18 01:29:40 +13:00
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 " ) ;
2017-01-03 23:29:21 +13:00
return OPERATOR_CANCELLED ;
}
/* loop all layer to check if need interpolation */
2017-01-18 01:29:40 +13:00
for ( bGPDlayer * gpl = gpd - > layers . first ; gpl ; gpl = gpl - > next ) {
bGPDframe * prevFrame , * nextFrame ;
bGPDstroke * gps_from , * gps_to ;
int cframe , fFrame ;
2017-01-03 23:29:21 +13:00
/* all layers or only active */
2017-01-18 16:43:17 +13:00
if ( ( ( flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS ) = = 0 ) & & ( gpl ! = active_gpl ) ) {
2017-01-03 23:29:21 +13:00
continue ;
}
/* only editable and visible layers are considered */
if ( ! gpencil_layer_is_editable ( gpl ) | | ( gpl - > actframe = = NULL ) ) {
continue ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* store extremes */
prevFrame = gpl - > actframe ;
nextFrame = gpl - > actframe - > next ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* Loop over intermediary frames and create the interpolation */
for ( cframe = prevFrame - > framenum + 1 ; cframe < nextFrame - > framenum ; cframe + + ) {
2017-01-18 01:29:40 +13:00
bGPDframe * interFrame = NULL ;
float factor ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* get interpolation factor */
factor = ( float ) ( cframe - prevFrame - > framenum ) / ( nextFrame - > framenum - prevFrame - > framenum + 1 ) ;
2017-01-18 00:48:15 +13:00
2017-01-18 19:00:17 +13:00
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 ) ;
}
2017-01-03 23:29:21 +13:00
/* create new strokes data with interpolated points reading original stroke */
for ( gps_from = prevFrame - > strokes . first ; gps_from ; gps_from = gps_from - > next ) {
2017-01-18 01:29:40 +13:00
bGPDstroke * new_stroke ;
2017-01-03 23:29:21 +13:00
/* only selected */
2017-01-18 16:43:17 +13:00
if ( ( flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED ) & & ( ( gps_from - > flag & GP_STROKE_SELECT ) = = 0 ) ) {
2017-01-03 23:29:21 +13:00
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 ( gpl , gps_from ) = = false ) {
continue ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* get final stroke to interpolate */
fFrame = BLI_findindex ( & prevFrame - > strokes , gps_from ) ;
gps_to = BLI_findlink ( & nextFrame - > strokes , fFrame ) ;
if ( gps_to = = NULL ) {
continue ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* create new stroke */
new_stroke = MEM_dupallocN ( gps_from ) ;
new_stroke - > points = MEM_dupallocN ( gps_from - > points ) ;
new_stroke - > triangles = MEM_dupallocN ( gps_from - > triangles ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* 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 ) ;
new_stroke - > totpoints = gps_to - > totpoints ;
new_stroke - > tot_triangles = 0 ;
new_stroke - > flag | = GP_STROKE_RECALC_CACHES ;
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* update points position */
gp_interpolate_update_points ( gps_from , gps_to , new_stroke , factor ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* add to strokes */
BLI_addtail ( & interFrame - > strokes , new_stroke ) ;
}
}
}
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* notifiers */
WM_event_add_notifier ( C , NC_GPENCIL | ND_DATA | NA_EDITED , NULL ) ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
return OPERATOR_FINISHED ;
}
void GPENCIL_OT_interpolate_sequence ( wmOperatorType * ot )
{
/* identifiers */
2017-01-18 00:48:15 +13:00
ot - > name = " Interpolate Sequence " ;
2017-01-03 23:29:21 +13:00
ot - > idname = " GPENCIL_OT_interpolate_sequence " ;
2017-01-18 00:48:15 +13:00
ot - > description = " Generate 'in-betweens' to smoothly interpolate between Grease Pencil frames " ;
2017-01-03 23:29:21 +13:00
/* api callbacks */
ot - > exec = gpencil_interpolate_seq_exec ;
ot - > poll = gpencil_interpolate_poll ;
2017-01-18 00:48:15 +13:00
2017-01-03 23:29:21 +13:00
/* flags */
ot - > flag = OPTYPE_REGISTER | OPTYPE_UNDO ;
}
2017-01-18 19:40:48 +13:00
/* ******************** Remove Breakdowns ************************ */
/* Same as gpencil_interpolate_poll(),
* except we ALSO need to have an active frame that is a breakdown
*/
static int gpencil_interpolate_reverse_poll ( bContext * C )
{
bGPdata * gpd = CTX_data_gpencil_data ( C ) ;
bGPDlayer * gpl = CTX_data_active_gpencil_layer ( C ) ;
/* only 3D view */
if ( CTX_wm_area ( C ) - > spacetype ! = SPACE_VIEW3D ) {
return 0 ;
}
/* need data to interpolate */
if ( ELEM ( NULL , gpd , gpl ) ) {
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 ;
}
2017-01-18 12:20:53 +01:00
static int gpencil_interpolate_reverse_exec ( bContext * C , wmOperator * UNUSED ( op ) )
2017-01-18 19:40:48 +13:00
{
/* 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 */
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 ;
}
2017-01-18 00:48:15 +13:00
/* *************************************************************** */