2.5 - Warning cleanups (for mingw+scons)
[blender-staging.git] / source / blender / editors / animation / keyframes_general.c
index 30d05b915827e3cbb106e74e1fdf564067bf85d8..ced3c1177005e7447e265c27b7a6792b27487e69 100644 (file)
 #include "DNA_anim_types.h"
 #include "DNA_action_types.h"
 #include "DNA_curve_types.h"
-#include "DNA_ipo_types.h" // XXX to be removed
 #include "DNA_key_types.h"
 #include "DNA_object_types.h"
 #include "DNA_space_types.h"
 #include "DNA_scene_types.h"
 
+#include "RNA_access.h"
+
 #include "BKE_action.h"
 #include "BKE_fcurve.h"
 #include "BKE_key.h"
@@ -103,6 +104,13 @@ 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 0 // XXX for now, we don't get rid of empty curves...
        /* Only delete if there isn't an ipo-driver still hanging around on an empty curve */
        if ((icu->totvert==0) && (icu->driver==NULL)) {
@@ -119,11 +127,11 @@ void duplicate_fcurve_keys(FCurve *fcu)
 {
        BezTriple *newbezt;
        int i;
-
-       if (fcu == NULL)
+       
+       /* this can only work when there is an F-Curve, and also when there are some BezTriples */
+       if ELEM(NULL, fcu, fcu->bezt)
                return;
        
-       // XXX this does not take into account sample data...
        for (i=0; i < fcu->totvert; i++) {
                /* If a key is selected */
                if (fcu->bezt[i].f2 & SELECT) {
@@ -152,7 +160,7 @@ void duplicate_fcurve_keys(FCurve *fcu)
 /* **************************************************** */
 /* Various Tools */
 
-/* Basic IPO-Curve 'cleanup' function that removes 'double points' and unnecessary keyframes on linear-segments only */
+/* Basic F-Curve 'cleanup' function that removes 'double points' and unnecessary keyframes on linear-segments only */
 void clean_fcurve(FCurve *fcu, float thresh)
 {
        BezTriple *old_bezts, *bezt, *beztn;
@@ -277,76 +285,265 @@ void smooth_fcurve (FCurve *fcu)
                }
        }
        
-               /* if any points were selected, allocate tSmooth_Bezt points to work on */
-               if (totSel >= 3) {
-                       tSmooth_Bezt *tarray, *tsb;
-                       
-                       /* allocate memory in one go */
-                       tsb= tarray= MEM_callocN(totSel*sizeof(tSmooth_Bezt), "tSmooth_Bezt Array");
+       /* if any points were selected, allocate tSmooth_Bezt points to work on */
+       if (totSel >= 3) {
+               tSmooth_Bezt *tarray, *tsb;
+               
+               /* allocate memory in one go */
+               tsb= tarray= MEM_callocN(totSel*sizeof(tSmooth_Bezt), "tSmooth_Bezt Array");
+               
+               /* populate tarray with data of selected points */
+               bezt= fcu->bezt;
+               for (i=0, x=0; (i < fcu->totvert) && (x < totSel); i++, bezt++) {
+                       if (BEZSELECTED(bezt)) {
+                               /* tsb simply needs pointer to vec, and index */
+                               tsb->h1 = &bezt->vec[0][1];
+                               tsb->h2 = &bezt->vec[1][1];
+                               tsb->h3 = &bezt->vec[2][1];
+                               
+                               /* advance to the next tsb to populate */
+                               if (x < totSel- 1) 
+                                       tsb++;
+                               else
+                                       break;
+                       }
+               }
                        
-                       /* populate tarray with data of selected points */
-                       bezt= fcu->bezt;
-                       for (i=0, x=0; (i < fcu->totvert) && (x < totSel); i++, bezt++) {
-                               if (BEZSELECTED(bezt)) {
-                                       /* tsb simply needs pointer to vec, and index */
-                                       tsb->h1 = &bezt->vec[0][1];
-                                       tsb->h2 = &bezt->vec[1][1];
-                                       tsb->h3 = &bezt->vec[2][1];
-                                       
-                                       /* advance to the next tsb to populate */
-                                       if (x < totSel- 1) 
-                                               tsb++;
-                                       else
-                                               break;
-                               }
+               /* calculate the new smoothed F-Curve's with weighted averages:
+                *      - this is done with two passes
+                *      - uses 5 points for each operation (which stores in the relevant handles)
+                *      -       previous: w/a ratio = 3:5:2:1:1
+                *      -       next: w/a ratio = 1:1:2:5:3
+                */
+               
+               /* round 1: calculate previous and next */ 
+               tsb= tarray;
+               for (i=0; i < totSel; i++, tsb++) {
+                       /* don't touch end points (otherwise, curves slowly explode) */
+                       if (ELEM(i, 0, (totSel-1)) == 0) {
+                               const tSmooth_Bezt *tP1 = tsb - 1;
+                               const tSmooth_Bezt *tP2 = (i-2 > 0) ? (tsb - 2) : (NULL);
+                               const tSmooth_Bezt *tN1 = tsb + 1;
+                               const tSmooth_Bezt *tN2 = (i+2 < totSel) ? (tsb + 2) : (NULL);
+                               
+                               const float p1 = *tP1->h2;
+                               const float p2 = (tP2) ? (*tP2->h2) : (*tP1->h2);
+                               const float c1 = *tsb->h2;
+                               const float n1 = *tN1->h2;
+                               const float n2 = (tN2) ? (*tN2->h2) : (*tN1->h2);
+                               
+                               /* calculate previous and next */
+                               *tsb->h1= (3*p2 + 5*p1 + 2*c1 + n1 + n2) / 12;
+                               *tsb->h3= (p2 + p1 + 2*c1 + 5*n1 + 3*n2) / 12;
                        }
+               }
+               
+               /* round 2: calculate new values and reset handles */
+               tsb= tarray;
+               for (i=0; i < totSel; i++, tsb++) {
+                       /* calculate new position by averaging handles */
+                       *tsb->h2 = (*tsb->h1 + *tsb->h3) / 2;
                        
-       /* calculate the new smoothed F-Curve's with weighted averages:
-        *      - this is done with two passes
-        *      - uses 5 points for each operation (which stores in the relevant handles)
-        *      -       previous: w/a ratio = 3:5:2:1:1
-        *      -       next: w/a ratio = 1:1:2:5:3
-        */
+                       /* reset handles now */
+                       *tsb->h1 = *tsb->h2;
+                       *tsb->h3 = *tsb->h2;
+               }
+               
+               /* free memory required for tarray */
+               MEM_freeN(tarray);
+       }
+       
+       /* recalculate handles */
+       calchandles_fcurve(fcu);
+}
+
+/* **************************************************** */
+/* Copy/Paste Tools */
+/* - The copy/paste buffer currently stores a set of temporary F-Curves containing only the keyframes 
+ *   that were selected in each of the original F-Curves
+ * - All pasted frames are offset by the same amount. This is calculated as the difference in the times of
+ *     the current frame and the 'first keyframe' (i.e. the earliest one in all channels).
+ * - The earliest frame is calculated per copy operation.
+ */
+
+/* globals for copy/paste data (like for other copy/paste buffers) */
+ListBase animcopybuf = {NULL, NULL};
+static float animcopy_firstframe= 999999999.0f;
+
+/* datatype for use in copy/paste buffer */
+typedef struct tAnimCopybufItem {
+       struct tAnimCopybufItem *next, *prev;
        
-       /* round 1: calculate previous and next */ 
-       tsb= tarray;
-       for (i=0; i < totSel; i++, tsb++) {
-               /* don't touch end points (otherwise, curves slowly explode) */
-               if (ELEM(i, 0, (totSel-1)) == 0) {
-                       const tSmooth_Bezt *tP1 = tsb - 1;
-                       const tSmooth_Bezt *tP2 = (i-2 > 0) ? (tsb - 2) : (NULL);
-                       const tSmooth_Bezt *tN1 = tsb + 1;
-                       const tSmooth_Bezt *tN2 = (i+2 < totSel) ? (tsb + 2) : (NULL);
+       ID *id;                         /* ID which owns the curve */
+       bActionGroup *grp;      /* Action Group */
+       char *rna_path;         /* RNA-Path */
+       int array_index;        /* array index */
+       
+       int totvert;            /* number of keyframes stored for this channel */
+       BezTriple *bezt;        /* keyframes in buffer */
+} tAnimCopybufItem;
+
+
+/* This function frees any MEM_calloc'ed copy/paste buffer data */
+// XXX find some header to put this in!
+void free_anim_copybuf (void)
+{
+       tAnimCopybufItem *aci, *acn;
+       
+       /* free each buffer element */
+       for (aci= animcopybuf.first; aci; aci= acn) {
+               acn= aci->next;
+               
+               /* free keyframes */
+               if (aci->bezt) 
+                       MEM_freeN(aci->bezt);
                        
-                       const float p1 = *tP1->h2;
-                       const float p2 = (tP2) ? (*tP2->h2) : (*tP1->h2);
-                       const float c1 = *tsb->h2;
-                       const float n1 = *tN1->h2;
-                       const float n2 = (tN2) ? (*tN2->h2) : (*tN1->h2);
+               /* free RNA-path */
+               if (aci->rna_path)
+                       MEM_freeN(aci->rna_path);
                        
-                       /* calculate previous and next */
-                       *tsb->h1= (3*p2 + 5*p1 + 2*c1 + n1 + n2) / 12;
-                       *tsb->h3= (p2 + p1 + 2*c1 + 5*n1 + 3*n2) / 12;
-               }
+               /* free ourself */
+               BLI_freelinkN(&animcopybuf, aci);
        }
        
-       /* round 2: calculate new values and reset handles */
-       tsb= tarray;
-       for (i=0; i < totSel; i++, tsb++) {
-               /* calculate new position by averaging handles */
-               *tsb->h2 = (*tsb->h1 + *tsb->h3) / 2;
+       /* restore initial state */
+       animcopybuf.first= animcopybuf.last= NULL;
+       animcopy_firstframe= 999999999.0f;
+}
+
+/* ------------------- */
+
+/* This function adds data to the keyframes copy/paste buffer, freeing existing data first */
+short copy_animedit_keys (bAnimContext *ac, ListBase *anim_data)
+{      
+       bAnimListElem *ale;
+       
+       /* clear buffer first */
+       free_anim_copybuf();
+       
+       /* assume that each of these is an F-Curve */
+       for (ale= anim_data->first; ale; ale= ale->next) {
+               FCurve *fcu= (FCurve *)ale->key_data;
+               tAnimCopybufItem *aci;
+               BezTriple *bezt, *newbuf;
+               int i;
+               
+               /* firstly, check if F-Curve has any selected keyframes
+                *      - skip if no selected keyframes found (so no need to create unnecessary copy-buffer data)
+                *      - this check should also eliminate any problems associated with using sample-data
+                */
+               if (ANIM_fcurve_keys_bezier_loop(NULL, fcu, NULL, ANIM_editkeyframes_ok(BEZT_OK_SELECTED), NULL) == 0)
+                       continue;
+               
+               /* init copybuf item info */
+               aci= MEM_callocN(sizeof(tAnimCopybufItem), "AnimCopybufItem");
+               aci->id= ale->id;
+               aci->grp= fcu->grp;
+               aci->rna_path= MEM_dupallocN(fcu->rna_path);
+               aci->array_index= fcu->array_index;
+               BLI_addtail(&animcopybuf, aci);
+               
+               /* add selected keyframes to buffer */
+               // TODO: currently, we resize array everytime we add a new vert - this works ok as long as it is assumed only a few keys are copied
+               for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, bezt++) {
+                       if (BEZSELECTED(bezt)) {
+                               /* add to buffer */
+                               newbuf= MEM_callocN(sizeof(BezTriple)*(aci->totvert+1), "copybuf beztriple");
+                               
+                               /* assume that since we are just resizing the array, just copy all existing data across */
+                               if (aci->bezt)
+                                       memcpy(newbuf, aci->bezt, sizeof(BezTriple)*(aci->totvert));
+                               
+                               /* copy current beztriple across too */
+                               *(newbuf + aci->totvert)= *bezt; 
+                               
+                               /* free old array and set the new */
+                               if (aci->bezt) MEM_freeN(aci->bezt);
+                               aci->bezt= newbuf;
+                               aci->totvert++;
+                               
+                               /* check if this is the earliest frame encountered so far */
+                               if (bezt->vec[1][0] < animcopy_firstframe)
+                                       animcopy_firstframe= bezt->vec[1][0];
+                       }
+               }
                
-               /* reset handles now */
-               *tsb->h1 = *tsb->h2;
-               *tsb->h3 = *tsb->h2;
        }
        
-       /* free memory required for tarray */
-       MEM_freeN(tarray);
+       /* check if anything ended up in the buffer */
+       if (ELEM(NULL, animcopybuf.first, animcopybuf.last))
+               return -1;
+       
+       /* everything went fine */
+       return 0;
 }
+
+/* This function pastes data from the keyframes copy/paste buffer */
+short paste_animedit_keys (bAnimContext *ac, ListBase *anim_data)
+{
+       bAnimListElem *ale;
+       const Scene *scene= (ac->scene);
+       const float offset = (float)(CFRA - animcopy_firstframe);
+       short no_name= 0;
        
-       /* recalculate handles */
-       calchandles_fcurve(fcu);
+       /* check if buffer is empty */
+       if (ELEM(NULL, animcopybuf.first, animcopybuf.last)) {
+               //error("No data in buffer to paste");
+               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;
+               
+               /* 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)
+                */
+               // 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) {
+                               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)) 
+                                               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 */
+                               insert_bezt_fcurve(fcu, bezt);
+                               
+                               /* 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);
+               }
+       }
+       
+       return 0;
 }
 
 /* **************************************************** */