/* * 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) 2001-2002 by NaN Holding BV. * All rights reserved. */ /** \file * \ingroup edtransform */ #include "DNA_anim_types.h" #include "DNA_gpencil_types.h" #include "DNA_mask_types.h" #include "MEM_guardedalloc.h" #include "BLI_math.h" #include "BLI_rect.h" #include "BKE_nla.h" #include "BKE_context.h" #include "BKE_report.h" #include "ED_anim_api.h" #include "transform.h" #include "transform_convert.h" /* helper struct for gp-frame transforms */ typedef struct tGPFtransdata { float val; /* where transdata writes transform */ int *sdata; /* pointer to gpf->framenum */ } tGPFtransdata; /* -------------------------------------------------------------------- */ /** \name Action Transform Creation * * \{ */ /* fully select selected beztriples, but only include if it's on the right side of cfra */ static int count_fcurve_keys(FCurve *fcu, char side, float cfra, bool is_prop_edit) { BezTriple *bezt; int i, count = 0, count_all = 0; if (ELEM(NULL, fcu, fcu->bezt)) { return count; } /* only include points that occur on the right side of cfra */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { /* no need to adjust the handle selection since they are assumed * selected (like graph editor with SIPO_NOHANDLES) */ if (bezt->f2 & SELECT) { count++; } count_all++; } } if (is_prop_edit && count > 0) { return count_all; } else { return count; } } /* fully select selected beztriples, but only include if it's on the right side of cfra */ static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra, bool is_prop_edit) { bGPDframe *gpf; int count = 0, count_all = 0; if (gpl == NULL) { return count; } /* only include points that occur on the right side of cfra */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) { if (gpf->flag & GP_FRAME_SELECT) { count++; } count_all++; } } if (is_prop_edit && count > 0) { return count_all; } else { return count; } } /* fully select selected beztriples, but only include if it's on the right side of cfra */ static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra, bool is_prop_edit) { MaskLayerShape *masklayer_shape; int count = 0, count_all = 0; if (masklay == NULL) { return count; } /* only include points that occur on the right side of cfra */ for (masklayer_shape = masklay->splines_shapes.first; masklayer_shape; masklayer_shape = masklayer_shape->next) { if (FrameOnMouseSide(side, (float)masklayer_shape->frame, cfra)) { if (masklayer_shape->flag & MASK_SHAPE_SELECT) { count++; } count_all++; } } if (is_prop_edit && count > 0) { return count_all; } else { return count; } } /* This function assigns the information to transdata */ static void TimeToTransData(TransData *td, float *time, AnimData *adt, float ypos) { /* memory is calloc'ed, so that should zero everything nicely for us */ td->val = time; td->ival = *(time); td->center[0] = td->ival; td->center[1] = ypos; /* store the AnimData where this keyframe exists as a keyframe of the * active action as td->extra. */ td->extra = adt; } /* This function advances the address to which td points to, so it must return * the new address so that the next time new transform data is added, it doesn't * overwrite the existing ones... i.e. td = IcuToTransData(td, icu, ob, side, cfra); * * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data * on the named side are used. */ static TransData *ActionFCurveToTransData(TransData *td, TransData2D **td2dv, FCurve *fcu, AnimData *adt, char side, float cfra, bool is_prop_edit, float ypos) { BezTriple *bezt; TransData2D *td2d = *td2dv; int i; if (ELEM(NULL, fcu, fcu->bezt)) { return td; } for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { /* only add selected keyframes (for now, proportional edit is not enabled) */ if (is_prop_edit || (bezt->f2 & SELECT)) { /* note this MUST match count_fcurve_keys(), * so can't use BEZT_ISSEL_ANY() macro */ /* only add if on the right 'side' of the current frame */ if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { TimeToTransData(td, bezt->vec[1], adt, ypos); if (bezt->f2 & SELECT) { td->flag |= TD_SELECTED; } /*set flags to move handles as necessary*/ td->flag |= TD_MOVEHANDLE1 | TD_MOVEHANDLE2; td2d->h1 = bezt->vec[0]; td2d->h2 = bezt->vec[2]; copy_v2_v2(td2d->ih1, td2d->h1); copy_v2_v2(td2d->ih2, td2d->h2); td++; td2d++; } } } *td2dv = td2d; return td; } /* This function advances the address to which td points to, so it must return * the new address so that the next time new transform data is added, it doesn't * overwrite the existing ones... i.e. td = GPLayerToTransData(td, ipo, ob, side, cfra); * * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data * on the named side are used. */ static int GPLayerToTransData(TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, char side, float cfra, bool is_prop_edit, float ypos) { bGPDframe *gpf; int count = 0; /* check for select frames on right side of current frame */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { if (is_prop_edit || (gpf->flag & GP_FRAME_SELECT)) { if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) { /* memory is calloc'ed, so that should zero everything nicely for us */ td->val = &tfd->val; td->ival = (float)gpf->framenum; td->center[0] = td->ival; td->center[1] = ypos; tfd->val = (float)gpf->framenum; tfd->sdata = &gpf->framenum; /* advance td now */ td++; tfd++; count++; } } } return count; } /* refer to comment above #GPLayerToTransData, this is the same but for masks */ static int MaskLayerToTransData(TransData *td, tGPFtransdata *tfd, MaskLayer *masklay, char side, float cfra, bool is_prop_edit, float ypos) { MaskLayerShape *masklay_shape; int count = 0; /* check for select frames on right side of current frame */ for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { if (is_prop_edit || (masklay_shape->flag & MASK_SHAPE_SELECT)) { if (FrameOnMouseSide(side, (float)masklay_shape->frame, cfra)) { /* memory is calloc'ed, so that should zero everything nicely for us */ td->val = &tfd->val; td->ival = (float)masklay_shape->frame; td->center[0] = td->ival; td->center[1] = ypos; tfd->val = (float)masklay_shape->frame; tfd->sdata = &masklay_shape->frame; /* advance td now */ td++; tfd++; count++; } } } return count; } void createTransActionData(bContext *C, TransInfo *t) { Scene *scene = t->scene; TransData *td = NULL; TransData2D *td2d = NULL; tGPFtransdata *tfd = NULL; rcti *mask = &t->region->v2d.mask; rctf *datamask = &t->region->v2d.cur; float xsize = BLI_rctf_size_x(datamask); float ysize = BLI_rctf_size_y(datamask); float xmask = BLI_rcti_size_x(mask); float ymask = BLI_rcti_size_y(mask); bAnimContext ac; ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; int count = 0; float cfra; float ypos = 1.0f / ((ysize / xsize) * (xmask / ymask)) * BLI_rctf_cent_y(&t->region->v2d.cur); /* determine what type of data we are operating on */ if (ANIM_animdata_get_context(C, &ac) == 0) { return; } /* filter data */ if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT); } else { filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/); } ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); /* which side of the current frame should be allowed */ if (t->mode == TFM_TIME_EXTEND) { /* only side on which center is gets transformed */ float center[2]; transform_convert_center_global_v2(t, center); t->frame_side = (center[0] > CFRA) ? 'R' : 'L'; } else { /* normal transform - both sides of current frame are considered */ t->frame_side = 'B'; } /* loop 1: fully select ipo-keys and count how many BezTriples are selected */ for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(&ac, ale); int adt_count = 0; /* convert current-frame to action-time (slightly less accurate, especially under * higher scaling ratios, but is faster than converting all points) */ if (adt) { cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); } else { cfra = (float)CFRA; } if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { adt_count = count_fcurve_keys(ale->key_data, t->frame_side, cfra, is_prop_edit); } else if (ale->type == ANIMTYPE_GPLAYER) { adt_count = count_gplayer_frames(ale->data, t->frame_side, cfra, is_prop_edit); } else if (ale->type == ANIMTYPE_MASKLAYER) { adt_count = count_masklayer_frames(ale->data, t->frame_side, cfra, is_prop_edit); } else { BLI_assert(0); } if (adt_count > 0) { count += adt_count; ale->tag = true; } } /* stop if trying to build list if nothing selected */ if (count == 0) { /* cleanup temp list */ ANIM_animdata_freelist(&anim_data); return; } TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); /* allocate memory for data */ tc->data_len = count; tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(Action Editor)"); tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "transdata2d"); td = tc->data; td2d = tc->data_2d; if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { tc->custom.type.data = tfd = MEM_callocN(sizeof(tGPFtransdata) * count, "tGPFtransdata"); tc->custom.type.use_free = true; } /* loop 2: build transdata array */ for (ale = anim_data.first; ale; ale = ale->next) { if (is_prop_edit && !ale->tag) { continue; } cfra = (float)CFRA; { AnimData *adt; adt = ANIM_nla_mapping_get(&ac, ale); if (adt) { cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP); } } if (ale->type == ANIMTYPE_GPLAYER) { bGPDlayer *gpl = (bGPDlayer *)ale->data; int i; i = GPLayerToTransData(td, tfd, gpl, t->frame_side, cfra, is_prop_edit, ypos); td += i; tfd += i; } else if (ale->type == ANIMTYPE_MASKLAYER) { MaskLayer *masklay = (MaskLayer *)ale->data; int i; i = MaskLayerToTransData(td, tfd, masklay, t->frame_side, cfra, is_prop_edit, ypos); td += i; tfd += i; } else { AnimData *adt = ANIM_nla_mapping_get(&ac, ale); FCurve *fcu = (FCurve *)ale->key_data; td = ActionFCurveToTransData(td, &td2d, fcu, adt, t->frame_side, cfra, is_prop_edit, ypos); } } /* calculate distances for proportional editing */ if (is_prop_edit) { td = tc->data; for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt; /* F-Curve may not have any keyframes */ if (!ale->tag) { continue; } adt = ANIM_nla_mapping_get(&ac, ale); if (adt) { cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); } else { cfra = (float)CFRA; } if (ale->type == ANIMTYPE_GPLAYER) { bGPDlayer *gpl = (bGPDlayer *)ale->data; bGPDframe *gpf; for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { if (gpf->flag & GP_FRAME_SELECT) { td->dist = td->rdist = 0.0f; } else { bGPDframe *gpf_iter; int min = INT_MAX; for (gpf_iter = gpl->frames.first; gpf_iter; gpf_iter = gpf_iter->next) { if (gpf_iter->flag & GP_FRAME_SELECT) { if (FrameOnMouseSide(t->frame_side, (float)gpf_iter->framenum, cfra)) { int val = abs(gpf->framenum - gpf_iter->framenum); if (val < min) { min = val; } } } } td->dist = td->rdist = min; } td++; } } else if (ale->type == ANIMTYPE_MASKLAYER) { MaskLayer *masklay = (MaskLayer *)ale->data; MaskLayerShape *masklay_shape; for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { if (FrameOnMouseSide(t->frame_side, (float)masklay_shape->frame, cfra)) { if (masklay_shape->flag & MASK_SHAPE_SELECT) { td->dist = td->rdist = 0.0f; } else { MaskLayerShape *masklay_iter; int min = INT_MAX; for (masklay_iter = masklay->splines_shapes.first; masklay_iter; masklay_iter = masklay_iter->next) { if (masklay_iter->flag & MASK_SHAPE_SELECT) { if (FrameOnMouseSide(t->frame_side, (float)masklay_iter->frame, cfra)) { int val = abs(masklay_shape->frame - masklay_iter->frame); if (val < min) { min = val; } } } } td->dist = td->rdist = min; } td++; } } } else { FCurve *fcu = (FCurve *)ale->key_data; BezTriple *bezt; int i; for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { if (bezt->f2 & SELECT) { td->dist = td->rdist = 0.0f; } else { BezTriple *bezt_iter; int j; float min = FLT_MAX; for (j = 0, bezt_iter = fcu->bezt; j < fcu->totvert; j++, bezt_iter++) { if (bezt_iter->f2 & SELECT) { if (FrameOnMouseSide(t->frame_side, (float)bezt_iter->vec[1][0], cfra)) { float val = fabs(bezt->vec[1][0] - bezt_iter->vec[1][0]); if (val < min) { min = val; } } } } td->dist = td->rdist = min; } td++; } } } } } /* cleanup temp list */ ANIM_animdata_freelist(&anim_data); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Action Transform Flush * * \{ */ /* This function helps flush transdata written to tempdata into the gp-frames */ void flushTransIntFrameActionData(TransInfo *t) { TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); tGPFtransdata *tfd = tc->custom.type.data; /* flush data! */ for (int i = 0; i < tc->data_len; i++, tfd++) { *(tfd->sdata) = round_fl_to_int(tfd->val); } } /** \} */