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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/animation/keyframes_general.c')
-rw-r--r--source/blender/editors/animation/keyframes_general.c364
1 files changed, 304 insertions, 60 deletions
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c
index 5073cfe08b8..aea8c2407b4 100644
--- a/source/blender/editors/animation/keyframes_general.c
+++ b/source/blender/editors/animation/keyframes_general.c
@@ -1,4 +1,4 @@
-/**
+/*
* $Id$
*
* ***** BEGIN GPL LICENSE BLOCK *****
@@ -34,6 +34,7 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
+#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
#include "DNA_object_types.h"
@@ -42,6 +43,12 @@
#include "BKE_fcurve.h"
#include "BKE_utildefines.h"
+#include "BKE_report.h"
+#include "BKE_library.h"
+#include "BKE_global.h"
+
+#include "RNA_access.h"
+#include "RNA_enum_types.h"
#include "ED_anim_api.h"
#include "ED_keyframing.h"
@@ -82,6 +89,12 @@ void delete_fcurve_key(FCurve *fcu, int index, short do_recalc)
/* Delete this keyframe */
memmove(&fcu->bezt[index], &fcu->bezt[index+1], sizeof(BezTriple)*(fcu->totvert-index-1));
fcu->totvert--;
+
+ if (fcu->totvert == 0) {
+ if (fcu->bezt)
+ MEM_freeN(fcu->bezt);
+ fcu->bezt= NULL;
+ }
/* recalc handles - only if it won't cause problems */
if (do_recalc)
@@ -106,11 +119,18 @@ void delete_fcurve_keys(FCurve *fcu)
}
/* Free the array of BezTriples if there are not keyframes */
- if (fcu->totvert == 0) {
- if (fcu->bezt)
- MEM_freeN(fcu->bezt);
- fcu->bezt= NULL;
- }
+ if(fcu->totvert == 0)
+ clear_fcurve_keys(fcu);
+}
+
+
+void clear_fcurve_keys(FCurve *fcu)
+{
+ if (fcu->bezt)
+ MEM_freeN(fcu->bezt);
+ fcu->bezt= NULL;
+
+ fcu->totvert= 0;
}
/* ---------------- */
@@ -366,7 +386,7 @@ void sample_fcurve (FCurve *fcu)
int sfra, range;
int i, n, nIndex;
- if(fcu->bezt==NULL) /* ignore baked */
+ if (fcu->bezt==NULL) /* ignore baked */
return;
/* find selected keyframes... once pair has been found, add keyframes */
@@ -433,8 +453,10 @@ void sample_fcurve (FCurve *fcu)
*/
/* globals for copy/paste data (like for other copy/paste buffers) */
-ListBase animcopybuf = {NULL, NULL};
+static ListBase animcopybuf = {NULL, NULL};
static float animcopy_firstframe= 999999999.0f;
+static float animcopy_lastframe= -999999999.0f;
+static float animcopy_cfra= 0.0;
/* datatype for use in copy/paste buffer */
typedef struct tAnimCopybufItem {
@@ -447,6 +469,8 @@ typedef struct tAnimCopybufItem {
int totvert; /* number of keyframes stored for this channel */
BezTriple *bezt; /* keyframes in buffer */
+
+ short id_type; /* Result of GS(id->name)*/
} tAnimCopybufItem;
@@ -475,6 +499,7 @@ void free_anim_copybuf (void)
/* restore initial state */
animcopybuf.first= animcopybuf.last= NULL;
animcopy_firstframe= 999999999.0f;
+ animcopy_lastframe= -999999999.0f;
}
/* ------------------- */
@@ -483,6 +508,7 @@ void free_anim_copybuf (void)
short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data)
{
bAnimListElem *ale;
+ Scene *scene= ac->scene;
/* clear buffer first */
free_anim_copybuf();
@@ -491,7 +517,7 @@ short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data)
for (ale= anim_data->first; ale; ale= ale->next) {
FCurve *fcu= (FCurve *)ale->key_data;
tAnimCopybufItem *aci;
- BezTriple *bezt, *newbuf;
+ BezTriple *bezt, *nbezt, *newbuf;
int i;
/* firstly, check if F-Curve has any selected keyframes
@@ -504,6 +530,7 @@ short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data)
/* init copybuf item info */
aci= MEM_callocN(sizeof(tAnimCopybufItem), "AnimCopybufItem");
aci->id= ale->id;
+ aci->id_type= GS(ale->id->name);
aci->grp= fcu->grp;
aci->rna_path= MEM_dupallocN(fcu->rna_path);
aci->array_index= fcu->array_index;
@@ -521,7 +548,11 @@ short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data)
memcpy(newbuf, aci->bezt, sizeof(BezTriple)*(aci->totvert));
/* copy current beztriple across too */
- *(newbuf + aci->totvert)= *bezt;
+ nbezt= &newbuf[aci->totvert];
+ *nbezt= *bezt;
+
+ /* ensure copy buffer is selected so pasted keys are selected */
+ BEZ_SEL(nbezt);
/* free old array and set the new */
if (aci->bezt) MEM_freeN(aci->bezt);
@@ -531,6 +562,8 @@ short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data)
/* check if this is the earliest frame encountered so far */
if (bezt->vec[1][0] < animcopy_firstframe)
animcopy_firstframe= bezt->vec[1][0];
+ if (bezt->vec[1][0] > animcopy_lastframe)
+ animcopy_lastframe= bezt->vec[1][0];
}
}
@@ -539,76 +572,287 @@ short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data)
/* check if anything ended up in the buffer */
if (ELEM(NULL, animcopybuf.first, animcopybuf.last))
return -1;
-
+
+ /* incase 'relative' paste method is used */
+ animcopy_cfra= CFRA;
+
/* everything went fine */
return 0;
}
+/* ------------------- */
+
+/* most strict method: exact matches only */
+static tAnimCopybufItem *pastebuf_match_path_full(FCurve *fcu, const short from_single, const short to_simple)
+{
+ tAnimCopybufItem *aci;
+
+ for (aci= animcopybuf.first; aci; aci= aci->next) {
+ /* check that paths exist */
+ if (to_simple || (aci->rna_path && fcu->rna_path)) {
+ if (to_simple || (strcmp(aci->rna_path, fcu->rna_path) == 0)) {
+ if ((from_single) || (aci->array_index == fcu->array_index))
+ break;
+ }
+ }
+ }
+
+ return aci;
+}
+
+/* medium match strictness: path match only (i.e. ignore ID) */
+static tAnimCopybufItem *pastebuf_match_path_property(FCurve *fcu, const short from_single, const short UNUSED(to_simple))
+{
+ tAnimCopybufItem *aci;
+
+ for (aci= animcopybuf.first; aci; aci= aci->next) {
+ /* check that paths exist */
+ if (aci->rna_path && fcu->rna_path) {
+ /* find the property of the fcurve and compare against the end of the tAnimCopybufItem
+ * more involved since it needs to to path lookups.
+ * This is not 100% reliable since the user could be editing the curves on a path that wont
+ * resolve, or a bone could be renamed after copying for eg. but in normal copy & paste
+ * this should work out ok.
+ */
+ if (BLI_findindex(which_libbase(G.main, aci->id_type), aci->id) == -1) {
+ /* pedantic but the ID could have been removed, and beats crashing! */
+ printf("paste_animedit_keys: error ID has been removed!\n");
+ }
+ else {
+ PointerRNA id_ptr, rptr;
+ PropertyRNA *prop;
+
+ RNA_id_pointer_create(aci->id, &id_ptr);
+ RNA_path_resolve(&id_ptr, aci->rna_path, &rptr, &prop);
+
+ if (prop) {
+ const char *identifier= RNA_property_identifier(prop);
+ int len_id = strlen(identifier);
+ int len_path = strlen(fcu->rna_path);
+ if (len_id <= len_path) {
+ /* note, paths which end with "] will fail with this test - Animated ID Props */
+ if (strcmp(identifier, fcu->rna_path + (len_path-len_id))==0) {
+ if ((from_single) || (aci->array_index == fcu->array_index))
+ break;
+ }
+ }
+ }
+ else {
+ printf("paste_animedit_keys: failed to resolve path id:%s, '%s'!\n", aci->id->name, aci->rna_path);
+ }
+ }
+ }
+ }
+
+ return aci;
+}
+
+/* least strict matching heuristic: indices only */
+static tAnimCopybufItem *pastebuf_match_index_only(FCurve *fcu, const short from_single, const short UNUSED(to_simple))
+{
+ tAnimCopybufItem *aci;
+
+ for (aci= animcopybuf.first; aci; aci= aci->next) {
+ /* check that paths exist */
+ if ((from_single) || (aci->array_index == fcu->array_index)) {
+ break;
+ }
+ }
+
+ return aci;
+}
+
+/* ................ */
+
+/* helper for paste_animedit_keys() - performs the actual pasting */
+static void paste_animedit_keys_fcurve(FCurve *fcu, tAnimCopybufItem *aci, float offset, const eKeyMergeMode merge_mode)
+{
+ BezTriple *bezt;
+ int i;
+
+ /* First de-select existing FCuvre */
+ for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, bezt++) {
+ bezt->f2 &= ~SELECT;
+ }
+
+ /* mix mode with existing data */
+ switch (merge_mode) {
+ case KEYFRAME_PASTE_MERGE_MIX:
+ /* do-nothing */
+ break;
+
+ case KEYFRAME_PASTE_MERGE_OVER:
+ /* remove all keys */
+ clear_fcurve_keys(fcu);
+ break;
+
+ case KEYFRAME_PASTE_MERGE_OVER_RANGE:
+ case KEYFRAME_PASTE_MERGE_OVER_RANGE_ALL:
+ {
+ float f_min;
+ float f_max;
+
+ if (merge_mode==KEYFRAME_PASTE_MERGE_OVER_RANGE) {
+ f_min= aci->bezt[0].vec[1][0] + offset;
+ f_max= aci->bezt[aci->totvert-1].vec[1][0] + offset;
+ }
+ else { /* Entire Range */
+ f_min= animcopy_firstframe + offset;
+ f_max= animcopy_lastframe + offset;
+ }
+
+ /* remove keys in range */
+ if (f_min < f_max) {
+ /* select verts in range for removal */
+ for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, bezt++) {
+ if ((f_min < bezt[0].vec[1][0]) && (bezt[0].vec[1][0] < f_max)) {
+ bezt->f2 |= SELECT;
+ }
+ }
+
+ /* remove frames in the range */
+ delete_fcurve_keys(fcu);
+ }
+ break;
+ }
+ }
+
+ /* just start pasting, with the the first keyframe on the current frame, and so on */
+ for (i=0, bezt=aci->bezt; i < aci->totvert; i++, bezt++) {
+ /* temporarily apply offset to src beztriple while copying */
+ bezt->vec[0][0] += offset;
+ bezt->vec[1][0] += offset;
+ bezt->vec[2][0] += offset;
+
+ /* insert the keyframe
+ * NOTE: no special flags here for now
+ */
+ insert_bezt_fcurve(fcu, bezt, 0);
+
+ /* un-apply offset from src beztriple after copying */
+ bezt->vec[0][0] -= offset;
+ bezt->vec[1][0] -= offset;
+ bezt->vec[2][0] -= offset;
+ }
+
+ /* recalculate F-Curve's handles? */
+ calchandles_fcurve(fcu);
+}
+
+/* ------------------- */
+
+EnumPropertyItem keyframe_paste_offset_items[] = {
+ {KEYFRAME_PASTE_OFFSET_CFRA_START, "START", 0, "Frame Start", "Paste keys starting at current frame"},
+ {KEYFRAME_PASTE_OFFSET_CFRA_END, "END", 0, "Frame End", "Paste keys ending at current frame"},
+ {KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE, "RELATIVE", 0, "Frame Relative", "Paste keys relative to the current frame when copying"},
+ {KEYFRAME_PASTE_OFFSET_NONE, "NONE", 0, "No Offset", "Paste keys from original time"},
+ {0, NULL, 0, NULL, NULL}};
+
+EnumPropertyItem keyframe_paste_merge_items[] = {
+ {KEYFRAME_PASTE_MERGE_MIX, "MIX", 0, "Mix", "Overlay existing with new keys"},
+ {KEYFRAME_PASTE_MERGE_OVER, "OVER_ALL", 0, "Overwrite All", "Replace all keys"},
+ {KEYFRAME_PASTE_MERGE_OVER_RANGE, "OVER_RANGE", 0, "Overwrite Range", "Overwrite keys in pasted range"},
+ {KEYFRAME_PASTE_MERGE_OVER_RANGE_ALL, "OVER_RANGE_ALL", 0, "Overwrite Entire Range", "Overwrite keys in pasted range, using the range of all copied keys."},
+ {0, NULL, 0, NULL, NULL}};
+
+
/* This function pastes data from the keyframes copy/paste buffer */
-short paste_animedit_keys (bAnimContext *ac, ListBase *anim_data)
+short paste_animedit_keys (bAnimContext *ac, ListBase *anim_data,
+ const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode)
{
bAnimListElem *ale;
+
const Scene *scene= (ac->scene);
- const float offset = (float)(CFRA - animcopy_firstframe);
- short no_name= 0;
+ const short from_single= (animcopybuf.first == animcopybuf.last);
+ const short to_simple= (anim_data->first == anim_data->last);
+
+ float offset = 0.0f;
+ int pass;
+
/* check if buffer is empty */
- if (ELEM(NULL, animcopybuf.first, animcopybuf.last)) {
- //error("No data in buffer to paste");
+ if (animcopybuf.first == NULL) {
+ BKE_report(ac->reports, RPT_WARNING, "No data in buffer to paste");
+ return -1;
+ }
+
+ if (anim_data->first == NULL) {
+ BKE_report(ac->reports, RPT_WARNING, "No FCurves to paste into");
return -1;
}
- /* check if single channel in buffer (disregard names if so) */
- if (animcopybuf.first == animcopybuf.last)
- no_name= 1;
- /* from selected channels */
- for (ale= anim_data->first; ale; ale= ale->next) {
- FCurve *fcu = (FCurve *)ale->data; /* destination F-Curve */
- tAnimCopybufItem *aci= NULL;
- BezTriple *bezt;
- int i;
+ /* mathods of offset */
+ switch(offset_mode) {
+ case KEYFRAME_PASTE_OFFSET_CFRA_START:
+ offset= (float)(CFRA - animcopy_firstframe);
+ break;
+ case KEYFRAME_PASTE_OFFSET_CFRA_END:
+ offset= (float)(CFRA - animcopy_lastframe);
+ break;
+ case KEYFRAME_PASTE_OFFSET_CFRA_RELATIVE:
+ offset= (float)(CFRA - animcopy_cfra);
+ break;
+ case KEYFRAME_PASTE_OFFSET_NONE:
+ offset= 0.0f;
+ break;
+ }
+
+ if (from_single && to_simple) {
+ /* 1:1 match, no tricky checking, just paste */
+ FCurve *fcu;
+ tAnimCopybufItem *aci;
+
+ ale= anim_data->first;
+ fcu= (FCurve *)ale->data; /* destination F-Curve */
+ aci= animcopybuf.first;
- /* find buffer item to paste from
- * - if names don't matter (i.e. only 1 channel in buffer), don't check id/group
- * - if names do matter, only check if id-type is ok for now (group check is not that important)
- * - most importantly, rna-paths should match (array indices are unimportant for now)
+ paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode);
+ }
+ else {
+ /* from selected channels
+ * This "passes" system aims to try to find "matching" channels to paste keyframes
+ * into with increasingly loose matching heuristics. The process finishes when at least
+ * one F-Curve has been pasted into.
*/
- // TODO: the matching algorithm here is pathetic!
- for (aci= animcopybuf.first; aci; aci= aci->next) {
- /* check that paths exist */
- if (aci->rna_path && fcu->rna_path) {
- // FIXME: this breaks for bone names!
- if (strcmp(aci->rna_path, fcu->rna_path) == 0) {
- /* should be a match unless there's more than one of these */
- if ((no_name) || (aci->array_index == fcu->array_index))
+ for (pass= 0; pass < 3; pass++) {
+ unsigned int totmatch= 0;
+
+ for (ale= anim_data->first; ale; ale= ale->next) {
+ /* find buffer item to paste from
+ * - if names don't matter (i.e. only 1 channel in buffer), don't check id/group
+ * - if names do matter, only check if id-type is ok for now (group check is not that important)
+ * - most importantly, rna-paths should match (array indices are unimportant for now)
+ */
+ FCurve *fcu = (FCurve *)ale->data; /* destination F-Curve */
+ tAnimCopybufItem *aci= NULL;
+
+ switch (pass) {
+ case 0:
+ /* most strict, must be exact path match data_path & index */
+ aci= pastebuf_match_path_full(fcu, from_single, to_simple);
+ break;
+
+ case 1:
+ /* less strict, just compare property names */
+ aci= pastebuf_match_path_property(fcu, from_single, to_simple);
+ break;
+
+ case 2:
+ /* Comparing properties gave no results, so just do index comparisons */
+ aci= pastebuf_match_index_only(fcu, from_single, to_simple);
break;
}
- }
- }
-
-
- /* copy the relevant data from the matching buffer curve */
- if (aci) {
- /* just start pasting, with the the first keyframe on the current frame, and so on */
- for (i=0, bezt=aci->bezt; i < aci->totvert; i++, bezt++) {
- /* temporarily apply offset to src beztriple while copying */
- bezt->vec[0][0] += offset;
- bezt->vec[1][0] += offset;
- bezt->vec[2][0] += offset;
- /* insert the keyframe
- * NOTE: no special flags here for now
- */
- insert_bezt_fcurve(fcu, bezt, 0);
-
- /* un-apply offset from src beztriple after copying */
- bezt->vec[0][0] -= offset;
- bezt->vec[1][0] -= offset;
- bezt->vec[2][0] -= offset;
+ /* copy the relevant data from the matching buffer curve */
+ if (aci) {
+ totmatch++;
+ paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode);
+ }
}
- /* recalculate F-Curve's handles? */
- calchandles_fcurve(fcu);
+ /* dont continue if some fcurves were pasted */
+ if (totmatch)
+ break;
}
}