Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/transform/transform_conversions_action.c
- This file was added.
| /* | |||||
| * 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_listbase.h" | |||||
| #include "BLI_rect.h" | |||||
| #include "BKE_action.h" | |||||
| #include "BKE_fcurve.h" | |||||
| #include "BKE_nla.h" | |||||
| #include "BKE_context.h" | |||||
| #include "BKE_report.h" | |||||
| #include "ED_anim_api.h" | |||||
| #include "UI_view2d.h" | |||||
| #include "transform.h" | |||||
| #include "transform_conversions.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->ar->v2d.mask; | |||||
| rctf *datamask = &t->ar->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->ar->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 mouse is gets transformed */ | |||||
| float xmouse, ymouse; | |||||
| UI_view2d_region_to_view(&ac.ar->v2d, t->mouse.imval[0], t->mouse.imval[1], &xmouse, &ymouse); | |||||
| t->frame_side = (xmouse > CFRA) ? 'R' : 'L'; // XXX use t->frame_side | |||||
| } | |||||
| 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); | |||||
| } | |||||
| } | |||||
| /** \} */ | |||||