Blender V2.61 - r43446

poseSlide.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung
00019  * This is a new part of Blender
00020  *
00021  * Contributor(s): Joshua Leung
00022  *
00023  * ***** END GPL LICENSE BLOCK *****
00024  */
00025 
00031 #include <stdlib.h>
00032 #include <stdio.h>
00033 #include <stddef.h>
00034 #include <string.h>
00035 #include <math.h>
00036 #include <float.h>
00037 
00038 #include "MEM_guardedalloc.h"
00039 
00040 #include "BLI_math.h"
00041 #include "BLI_blenlib.h"
00042 #include "BLI_dynstr.h"
00043 #include "BLI_dlrbTree.h"
00044 #include "BLI_utildefines.h"
00045 
00046 #include "DNA_anim_types.h"
00047 #include "DNA_armature_types.h"
00048 #include "DNA_object_types.h"
00049 #include "DNA_scene_types.h"
00050 
00051 #include "BKE_fcurve.h"
00052 
00053 #include "BKE_context.h"
00054 #include "BKE_object.h"
00055 #include "BKE_report.h"
00056 
00057 #include "RNA_access.h"
00058 #include "RNA_define.h"
00059 
00060 #include "WM_api.h"
00061 #include "WM_types.h"
00062 
00063 #include "ED_armature.h"
00064 #include "ED_keyframes_draw.h"
00065 #include "ED_markers.h"
00066 #include "ED_screen.h"
00067 
00068 #include "armature_intern.h"
00069 
00070 /* **************************************************** */
00071 /* == POSE 'SLIDING' TOOLS == 
00072  *
00073  * A) Push & Relax, Breakdowner
00074  * These tools provide the animator with various capabilities
00075  * for interactively controlling the spacing of poses, but also
00076  * for 'pushing' and/or 'relaxing' extremes as they see fit.
00077  *
00078  * B) Propagate
00079  * This tool copies elements of the selected pose to successive
00080  * keyframes, allowing the animator to go back and modify the poses
00081  * for some "static" pose controls, without having to repeatedly
00082  * doing a "next paste" dance.
00083  *
00084  * C) Pose Sculpting
00085  * This is yet to be implemented, but the idea here is to use
00086  * sculpting techniques to make it easier to pose rigs by allowing
00087  * rigs to be manipulated using a familiar paint-based interface. 
00088  */
00089 /* **************************************************** */
00090 /* A) Push & Relax, Breakdowner */
00091 
00092 /* Temporary data shared between these operators */
00093 typedef struct tPoseSlideOp {
00094     Scene *scene;       /* current scene */
00095     ScrArea *sa;        /* area that we're operating in (needed for modal()) */
00096     ARegion *ar;        /* region that we're operating in (needed for modal()) */
00097     Object *ob;         /* active object that Pose Info comes from */
00098     bArmature *arm;     /* armature for pose */
00099     
00100     ListBase pfLinks;   /* links between posechannels and f-curves  */
00101     DLRBT_Tree keys;    /* binary tree for quicker searching for keyframes (when applicable) */
00102     
00103     int cframe;         /* current frame number */
00104     int prevFrame;      /* frame before current frame (blend-from) */
00105     int nextFrame;      /* frame after current frame (blend-to) */
00106     
00107     int mode;           /* sliding mode (ePoseSlide_Modes) */
00108     int flag;           // unused for now, but can later get used for storing runtime settings....
00109     
00110     float percentage;   /* 0-1 value for determining the influence of whatever is relevant */
00111 } tPoseSlideOp;
00112 
00113 /* Pose Sliding Modes */
00114 typedef enum ePoseSlide_Modes {
00115     POSESLIDE_PUSH  = 0,        /* exaggerate the pose... */
00116     POSESLIDE_RELAX,            /* soften the pose... */
00117     POSESLIDE_BREAKDOWN,        /* slide between the endpoint poses, finding a 'soft' spot */
00118 } ePoseSlide_Modes;
00119 
00120 /* ------------------------------------ */
00121 
00122 /* operator init */
00123 static int pose_slide_init (bContext *C, wmOperator *op, short mode)
00124 {
00125     tPoseSlideOp *pso;
00126     bAction *act= NULL;
00127     
00128     /* init slide-op data */
00129     pso= op->customdata= MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp");
00130     
00131     /* get info from context */
00132     pso->scene= CTX_data_scene(C);
00133     pso->ob= object_pose_armature_get(CTX_data_active_object(C));
00134     pso->arm= (pso->ob)? pso->ob->data : NULL;
00135     pso->sa= CTX_wm_area(C); /* only really needed when doing modal() */
00136     pso->ar= CTX_wm_region(C); /* only really needed when doing modal() */
00137     
00138     pso->cframe= pso->scene->r.cfra;
00139     pso->mode= mode;
00140     
00141     /* set range info from property values - these may get overridden for the invoke() */
00142     pso->percentage= RNA_float_get(op->ptr, "percentage");
00143     pso->prevFrame= RNA_int_get(op->ptr, "prev_frame");
00144     pso->nextFrame= RNA_int_get(op->ptr, "next_frame");
00145     
00146     /* check the settings from the context */
00147     if (ELEM4(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action))
00148         return 0;
00149     else
00150         act= pso->ob->adt->action;
00151     
00152     /* for each Pose-Channel which gets affected, get the F-Curves for that channel 
00153      * and set the relevant transform flags...
00154      */
00155     poseAnim_mapping_get(C, &pso->pfLinks, pso->ob, act);
00156     
00157     /* set depsgraph flags */
00158         /* make sure the lock is set OK, unlock can be accidentally saved? */
00159     pso->ob->pose->flag |= POSE_LOCKED;
00160     pso->ob->pose->flag &= ~POSE_DO_UNLOCK;
00161     
00162     /* do basic initialise of RB-BST used for finding keyframes, but leave the filling of it up 
00163      * to the caller of this (usually only invoke() will do it, to make things more efficient).
00164      */
00165     BLI_dlrbTree_init(&pso->keys);
00166     
00167     /* return status is whether we've got all the data we were requested to get */
00168     return 1;
00169 }
00170 
00171 /* exiting the operator - free data */
00172 static void pose_slide_exit(wmOperator *op)
00173 {
00174     tPoseSlideOp *pso= op->customdata;
00175     
00176     /* if data exists, clear its data and exit */
00177     if (pso) {
00178         /* free the temp pchan links and their data */
00179         poseAnim_mapping_free(&pso->pfLinks);
00180         
00181         /* free RB-BST for keyframes (if it contained data) */
00182         BLI_dlrbTree_free(&pso->keys);
00183         
00184         /* free data itself */
00185         MEM_freeN(pso);
00186     }
00187     
00188     /* cleanup */
00189     op->customdata= NULL;
00190 }
00191 
00192 /* ------------------------------------ */
00193 
00194 /* helper for apply() / reset() - refresh the data */
00195 static void pose_slide_refresh (bContext *C, tPoseSlideOp *pso)
00196 {
00197     /* wrapper around the generic version, allowing us to add some custom stuff later still */
00198     poseAnim_mapping_refresh(C, pso->scene, pso->ob);
00199 }
00200 
00201 /* helper for apply() - perform sliding for some value */
00202 static void pose_slide_apply_val (tPoseSlideOp *pso, FCurve *fcu, float *val)
00203 {
00204     float cframe = (float)pso->cframe;
00205     float sVal, eVal;
00206     float w1, w2;
00207     
00208     /* get keyframe values for endpoint poses to blend with */
00209         /* previous/start */
00210     sVal= evaluate_fcurve(fcu, (float)pso->prevFrame);
00211         /* next/end */
00212     eVal= evaluate_fcurve(fcu, (float)pso->nextFrame);
00213     
00214     /* calculate the relative weights of the endpoints */
00215     if (pso->mode == POSESLIDE_BREAKDOWN) {
00216         /* get weights from the percentage control */
00217         w1= pso->percentage;    /* this must come second */
00218         w2= 1.0f - w1;          /* this must come first */
00219     }
00220     else {
00221         /*  - these weights are derived from the relative distance of these 
00222          *    poses from the current frame
00223          *  - they then get normalised so that they only sum up to 1
00224          */
00225         float wtot; 
00226         
00227         w1 = cframe - (float)pso->prevFrame;
00228         w2 = (float)pso->nextFrame - cframe;
00229         
00230         wtot = w1 + w2;
00231         w1 = (w1/wtot);
00232         w2 = (w2/wtot);
00233     }
00234     
00235     /* depending on the mode, calculate the new value
00236      *  - in all of these, the start+end values are multiplied by w2 and w1 (respectively),
00237      *    since multiplication in another order would decrease the value the current frame is closer to
00238      */
00239     switch (pso->mode) {
00240         case POSESLIDE_PUSH: /* make the current pose more pronounced */
00241         {
00242             /* perform a weighted average here, favouring the middle pose 
00243              *  - numerator should be larger than denominator to 'expand' the result
00244              *  - perform this weighting a number of times given by the percentage...
00245              */
00246             int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
00247             
00248             while (iters-- > 0) {
00249                 (*val)= ( -((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f) ) / 5.0f; 
00250             }
00251         }
00252             break;
00253             
00254         case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */
00255         {
00256             /* perform a weighted average here, favouring the middle pose 
00257              *  - numerator should be smaller than denominator to 'relax' the result
00258              *  - perform this weighting a number of times given by the percentage...
00259              */
00260             int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
00261             
00262             while (iters-- > 0) {
00263                 (*val)= ( ((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f) ) / 6.0f;
00264             }
00265         }
00266             break;
00267             
00268         case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */
00269         {
00270             /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */
00271             // TODO: make this use some kind of spline interpolation instead?
00272             (*val)= ((sVal * w2) + (eVal * w1));
00273         }
00274             break;
00275     }
00276 }
00277 
00278 /* helper for apply() - perform sliding for some 3-element vector */
00279 static void pose_slide_apply_vec3 (tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], const char propName[])
00280 {
00281     LinkData *ld=NULL;
00282     char *path=NULL;
00283     
00284     /* get the path to use... */
00285     path= BLI_sprintfN("%s.%s", pfl->pchan_path, propName);
00286     
00287     /* using this path, find each matching F-Curve for the variables we're interested in */
00288     while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
00289         FCurve *fcu= (FCurve *)ld->data;
00290         
00291         /* just work on these channels one by one... there's no interaction between values */
00292         pose_slide_apply_val(pso, fcu, &vec[fcu->array_index]);
00293     }
00294     
00295     /* free the temp path we got */
00296     MEM_freeN(path);
00297 }
00298 
00299 /* helper for apply() - perform sliding for custom properties */
00300 static void pose_slide_apply_props (tPoseSlideOp *pso, tPChanFCurveLink *pfl)
00301 {
00302     PointerRNA ptr = {{NULL}};
00303     LinkData *ld;
00304     int len = strlen(pfl->pchan_path);
00305     
00306     /* setup pointer RNA for resolving paths */
00307     RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr);
00308     
00309     /* custom properties are just denoted using ["..."][etc.] after the end of the base path, 
00310      * so just check for opening pair after the end of the path
00311      */
00312     for (ld = pfl->fcurves.first; ld; ld = ld->next) {
00313         FCurve *fcu = (FCurve *)ld->data;
00314         char *bPtr, *pPtr;
00315         
00316         if (fcu->rna_path == NULL)
00317             continue;
00318         
00319         /* do we have a match? 
00320          *  - bPtr is the RNA Path with the standard part chopped off
00321          *  - pPtr is the chunk of the path which is left over
00322          */
00323         bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len;
00324         pPtr = strstr(bPtr, "[\"");   /* dummy " for texteditor bugs */
00325         
00326         if (pPtr) {
00327             /* use RNA to try and get a handle on this property, then, assuming that it is just
00328              * numerical, try and grab the value as a float for temp editing before setting back
00329              */
00330             PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr);
00331             
00332             if (prop) {
00333                 switch (RNA_property_type(prop)) {
00334                     case PROP_FLOAT:
00335                     {
00336                         float tval = RNA_property_float_get(&ptr, prop);
00337                         pose_slide_apply_val(pso, fcu, &tval);
00338                         RNA_property_float_set(&ptr, prop, tval);
00339                     }
00340                         break;
00341                     case PROP_BOOLEAN:
00342                     case PROP_ENUM:
00343                     case PROP_INT:
00344                     {
00345                         float tval = (float)RNA_property_int_get(&ptr, prop);
00346                         pose_slide_apply_val(pso, fcu, &tval);
00347                         RNA_property_int_set(&ptr, prop, (int)tval);
00348                     }
00349                         break;
00350                     default:
00351                         /* cannot handle */
00352                         //printf("Cannot Pose Slide non-numerical property\n");
00353                         break;
00354                 }
00355             }
00356         }
00357     }
00358 }
00359 
00360 /* helper for apply() - perform sliding for quaternion rotations (using quat blending) */
00361 static void pose_slide_apply_quat (tPoseSlideOp *pso, tPChanFCurveLink *pfl)
00362 {
00363     FCurve *fcu_w=NULL, *fcu_x=NULL, *fcu_y=NULL, *fcu_z=NULL;
00364     bPoseChannel *pchan= pfl->pchan;
00365     LinkData *ld=NULL;
00366     char *path=NULL;
00367     float cframe;
00368     
00369     /* get the path to use - this should be quaternion rotations only (needs care) */
00370     path= BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion");
00371     
00372     /* get the current frame number */
00373     cframe= (float)pso->cframe;
00374     
00375     /* using this path, find each matching F-Curve for the variables we're interested in */
00376     while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
00377         FCurve *fcu= (FCurve *)ld->data;
00378         
00379         /* assign this F-Curve to one of the relevant pointers... */
00380         switch (fcu->array_index) {
00381             case 3: /* z */
00382                 fcu_z= fcu;
00383                 break;
00384             case 2: /* y */
00385                 fcu_y= fcu;
00386                 break;
00387             case 1: /* x */
00388                 fcu_x= fcu;
00389                 break;
00390             case 0: /* w */
00391                 fcu_w= fcu;
00392                 break;
00393         }
00394     }
00395     
00396     /* only if all channels exist, proceed */
00397     if (fcu_w && fcu_x && fcu_y && fcu_z) {
00398         float quat_prev[4], quat_next[4];
00399         
00400         /* get 2 quats */
00401         quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrame);
00402         quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrame);
00403         quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrame);
00404         quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrame);
00405         
00406         quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrame);
00407         quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrame);
00408         quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrame);
00409         quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrame);
00410         
00411         /* perform blending */
00412         if (pso->mode == POSESLIDE_BREAKDOWN) {
00413             /* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */
00414             interp_qt_qtqt(pchan->quat, quat_prev, quat_next, pso->percentage);
00415         }
00416         else if (pso->mode == POSESLIDE_PUSH) {
00417             float quat_diff[4], quat_orig[4];
00418             
00419             /* calculate the delta transform from the previous to the current */
00420             // TODO: investigate ways to favour one transform more?
00421             sub_qt_qtqt(quat_diff, pchan->quat, quat_prev);
00422             
00423             /* make a copy of the original rotation */
00424             copy_qt_qt(quat_orig, pchan->quat);
00425             
00426             /* increase the original by the delta transform, by an amount determined by percentage */
00427             add_qt_qtqt(pchan->quat, quat_orig, quat_diff, pso->percentage);
00428         }
00429         else {
00430             float quat_interp[4], quat_orig[4];
00431             int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
00432             
00433             /* perform this blending several times until a satisfactory result is reached */
00434             while (iters-- > 0) {
00435                 /* calculate the interpolation between the endpoints */
00436                 interp_qt_qtqt(quat_interp, quat_prev, quat_next, (cframe-pso->prevFrame) / (pso->nextFrame-pso->prevFrame) );
00437                 
00438                 /* make a copy of the original rotation */
00439                 copy_qt_qt(quat_orig, pchan->quat);
00440                 
00441                 /* tricky interpolations - blending between original and new */
00442                 interp_qt_qtqt(pchan->quat, quat_orig, quat_interp, 1.0f/6.0f);
00443             }
00444         }
00445     }
00446     
00447     /* free the path now */
00448     MEM_freeN(path);
00449 }
00450 
00451 /* apply() - perform the pose sliding based on weighting various poses */
00452 static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
00453 {
00454     tPChanFCurveLink *pfl;
00455     
00456     /* sanitise the frame ranges */
00457     if (pso->prevFrame == pso->nextFrame) {
00458         /* move out one step either side */
00459         pso->prevFrame--;
00460         pso->nextFrame++;
00461     }
00462     
00463     /* for each link, handle each set of transforms */
00464     for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
00465         /* valid transforms for each PoseChannel should have been noted already 
00466          *  - sliding the pose should be a straightforward exercise for location+rotation, 
00467          *    but rotations get more complicated since we may want to use quaternion blending 
00468          *    for quaternions instead...
00469          */
00470         bPoseChannel *pchan= pfl->pchan;
00471          
00472         if (pchan->flag & POSE_LOC) {
00473             /* calculate these for the 'location' vector, and use location curves */
00474             pose_slide_apply_vec3(pso, pfl, pchan->loc, "location");
00475         }
00476         
00477         if (pchan->flag & POSE_SIZE) {
00478             /* calculate these for the 'scale' vector, and use scale curves */
00479             pose_slide_apply_vec3(pso, pfl, pchan->size, "scale");
00480         }
00481         
00482         if (pchan->flag & POSE_ROT) {
00483             /* everything depends on the rotation mode */
00484             if (pchan->rotmode > 0) {
00485                 /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
00486                 pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler");
00487             }
00488             else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
00489                 // TODO: need to figure out how to do this!
00490             }
00491             else {
00492                 /* quaternions - use quaternion blending */
00493                 pose_slide_apply_quat(pso, pfl);
00494             }
00495         }
00496         
00497         if (pfl->oldprops) {
00498             /* not strictly a transform, but contributes to the pose produced in many rigs */
00499             pose_slide_apply_props(pso, pfl);
00500         }
00501     }
00502     
00503     /* depsgraph updates + redraws */
00504     pose_slide_refresh(C, pso);
00505 }
00506 
00507 /* perform autokeyframing after changes were made + confirmed */
00508 static void pose_slide_autoKeyframe (bContext *C, tPoseSlideOp *pso)
00509 {
00510     /* wrapper around the generic call */
00511     poseAnim_mapping_autoKeyframe(C, pso->scene, pso->ob, &pso->pfLinks, (float)pso->cframe);
00512 }
00513 
00514 /* reset changes made to current pose */
00515 static void pose_slide_reset (tPoseSlideOp *pso)
00516 {
00517     /* wrapper around the generic call, so that custom stuff can be added later */
00518     poseAnim_mapping_reset(&pso->pfLinks);
00519 }
00520 
00521 /* ------------------------------------ */
00522 
00523 /* draw percentage indicator in header */
00524 static void pose_slide_draw_status (tPoseSlideOp *pso)
00525 {
00526     char status_str[32];
00527     char mode_str[32];
00528     
00529     switch (pso->mode) {
00530         case POSESLIDE_PUSH:
00531             strcpy(mode_str, "Push Pose");
00532             break;
00533         case POSESLIDE_RELAX:
00534             strcpy(mode_str, "Relax Pose");
00535             break;
00536         case POSESLIDE_BREAKDOWN:
00537             strcpy(mode_str, "Breakdown");
00538             break;
00539         
00540         default:
00541             // unknown
00542             strcpy(mode_str, "Sliding-Tool");
00543             break;
00544     }
00545     
00546     BLI_snprintf(status_str, sizeof(status_str), "%s: %d %%", mode_str, (int)(pso->percentage*100.0f));
00547     ED_area_headerprint(pso->sa, status_str);
00548 }
00549 
00550 /* common code for invoke() methods */
00551 static int pose_slide_invoke_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
00552 {
00553     tPChanFCurveLink *pfl;
00554     AnimData *adt= pso->ob->adt;
00555     wmWindow *win= CTX_wm_window(C);
00556     
00557     /* for each link, add all its keyframes to the search tree */
00558     for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
00559         LinkData *ld;
00560         
00561         /* do this for each F-Curve */
00562         for (ld= pfl->fcurves.first; ld; ld= ld->next) {
00563             FCurve *fcu= (FCurve *)ld->data;
00564             fcurve_to_keylist(adt, fcu, &pso->keys, NULL);
00565         }
00566     }
00567     
00568     /* consolidate these keyframes, and figure out the nearest ones */
00569     BLI_dlrbTree_linkedlist_sync(&pso->keys);
00570     
00571         /* cancel if no keyframes found... */
00572     if (pso->keys.root) {
00573         ActKeyColumn *ak;
00574         float cframe= (float)pso->cframe;
00575         
00576         /* firstly, check if the current frame is a keyframe... */
00577         ak= (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe);
00578         
00579         if (ak == NULL) {
00580             /* current frame is not a keyframe, so search */
00581             ActKeyColumn *pk= (ActKeyColumn *)BLI_dlrbTree_search_prev(&pso->keys, compare_ak_cfraPtr, &cframe);
00582             ActKeyColumn *nk= (ActKeyColumn *)BLI_dlrbTree_search_next(&pso->keys, compare_ak_cfraPtr, &cframe);
00583             
00584             /* new set the frames */
00585                 /* prev frame */
00586             pso->prevFrame= (pk)? (pk->cfra) : (pso->cframe - 1);
00587             RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
00588                 /* next frame */
00589             pso->nextFrame= (nk)? (nk->cfra) : (pso->cframe + 1);
00590             RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
00591         }
00592         else {
00593             /* current frame itself is a keyframe, so just take keyframes on either side */
00594                 /* prev frame */
00595             pso->prevFrame= (ak->prev)? (ak->prev->cfra) : (pso->cframe - 1);
00596             RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
00597                 /* next frame */
00598             pso->nextFrame= (ak->next)? (ak->next->cfra) : (pso->cframe + 1);
00599             RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
00600         }
00601     }
00602     else {
00603         BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between");
00604         pose_slide_exit(op);
00605         return OPERATOR_CANCELLED;
00606     }
00607     
00608     /* initial apply for operator... */
00609     // TODO: need to calculate percentage for initial round too...
00610     pose_slide_apply(C, pso);
00611     
00612     /* depsgraph updates + redraws */
00613     pose_slide_refresh(C, pso);
00614     
00615     /* set cursor to indicate modal */
00616     WM_cursor_modal(win, BC_EW_SCROLLCURSOR);
00617     
00618     /* header print */
00619     pose_slide_draw_status(pso);
00620     
00621     /* add a modal handler for this operator */
00622     WM_event_add_modal_handler(C, op);
00623     return OPERATOR_RUNNING_MODAL;
00624 }
00625 
00626 /* common code for modal() */
00627 static int pose_slide_modal (bContext *C, wmOperator *op, wmEvent *evt)
00628 {
00629     tPoseSlideOp *pso= op->customdata;
00630     wmWindow *win= CTX_wm_window(C);
00631     
00632     switch (evt->type) {
00633         case LEFTMOUSE: /* confirm */
00634         {
00635             /* return to normal cursor and header status */
00636             ED_area_headerprint(pso->sa, NULL);
00637             WM_cursor_restore(win);
00638             
00639             /* insert keyframes as required... */
00640             pose_slide_autoKeyframe(C, pso);
00641             pose_slide_exit(op);
00642             
00643             /* done! */
00644             return OPERATOR_FINISHED;
00645         }
00646         
00647         case ESCKEY:    /* cancel */
00648         case RIGHTMOUSE: 
00649         {
00650             /* return to normal cursor and header status */
00651             ED_area_headerprint(pso->sa, NULL);
00652             WM_cursor_restore(win);
00653             
00654             /* reset transforms back to original state */
00655             pose_slide_reset(pso);
00656             
00657             /* depsgraph updates + redraws */
00658             pose_slide_refresh(C, pso);
00659             
00660             /* clean up temp data */
00661             pose_slide_exit(op);
00662             
00663             /* cancelled! */
00664             return OPERATOR_CANCELLED;
00665         }
00666             
00667         case MOUSEMOVE: /* calculate new position */
00668         {
00669             /* calculate percentage based on position of mouse (we only use x-axis for now.
00670              * since this is more conveninent for users to do), and store new percentage value 
00671              */
00672             pso->percentage= (evt->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx);
00673             RNA_float_set(op->ptr, "percentage", pso->percentage);
00674             
00675             /* update percentage indicator in header */
00676             pose_slide_draw_status(pso);
00677             
00678             /* reset transforms (to avoid accumulation errors) */
00679             pose_slide_reset(pso);
00680             
00681             /* apply... */
00682             pose_slide_apply(C, pso);
00683         }
00684             break;
00685             
00686         default: /* unhandled event (maybe it was some view manip? */
00687             /* allow to pass through */
00688             return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH;
00689     }
00690     
00691     /* still running... */
00692     return OPERATOR_RUNNING_MODAL;
00693 }
00694 
00695 /* common code for cancel() */
00696 static int pose_slide_cancel (bContext *UNUSED(C), wmOperator *op)
00697 {
00698     /* cleanup and done */
00699     pose_slide_exit(op);
00700     return OPERATOR_CANCELLED;
00701 }
00702 
00703 /* common code for exec() methods */
00704 static int pose_slide_exec_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
00705 {
00706     /* settings should have been set up ok for applying, so just apply! */
00707     pose_slide_apply(C, pso);
00708     
00709     /* insert keyframes if needed */
00710     pose_slide_autoKeyframe(C, pso);
00711     
00712     /* cleanup and done */
00713     pose_slide_exit(op);
00714     
00715     return OPERATOR_FINISHED;
00716 }
00717 
00718 /* common code for defining RNA properties */
00719 static void pose_slide_opdef_properties (wmOperatorType *ot)
00720 {
00721     RNA_def_int(ot->srna, "prev_frame", 0, MINAFRAME, MAXFRAME, "Previous Keyframe", "Frame number of keyframe immediately before the current frame", 0, 50);
00722     RNA_def_int(ot->srna, "next_frame", 0, MINAFRAME, MAXFRAME, "Next Keyframe", "Frame number of keyframe immediately after the current frame", 0, 50);
00723     RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for the sliding operation", 0.3, 0.7);
00724 }
00725 
00726 /* ------------------------------------ */
00727 
00728 /* invoke() - for 'push' mode */
00729 static int pose_slide_push_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
00730 {
00731     tPoseSlideOp *pso;
00732     
00733     /* initialise data  */
00734     if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
00735         pose_slide_exit(op);
00736         return OPERATOR_CANCELLED;
00737     }
00738     else
00739         pso= op->customdata;
00740     
00741     /* do common setup work */
00742     return pose_slide_invoke_common(C, op, pso);
00743 }
00744 
00745 /* exec() - for push */
00746 static int pose_slide_push_exec (bContext *C, wmOperator *op)
00747 {
00748     tPoseSlideOp *pso;
00749     
00750     /* initialise data (from RNA-props) */
00751     if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
00752         pose_slide_exit(op);
00753         return OPERATOR_CANCELLED;
00754     }
00755     else
00756         pso= op->customdata;
00757         
00758     /* do common exec work */
00759     return pose_slide_exec_common(C, op, pso);
00760 }
00761 
00762 void POSE_OT_push (wmOperatorType *ot)
00763 {
00764     /* identifiers */
00765     ot->name= "Push Pose";
00766     ot->idname= "POSE_OT_push";
00767     ot->description= "Exaggerate the current pose";
00768     
00769     /* callbacks */
00770     ot->exec= pose_slide_push_exec;
00771     ot->invoke= pose_slide_push_invoke;
00772     ot->modal= pose_slide_modal;
00773     ot->cancel= pose_slide_cancel;
00774     ot->poll= ED_operator_posemode;
00775     
00776     /* flags */
00777     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
00778     
00779     /* Properties */
00780     pose_slide_opdef_properties(ot);
00781 }
00782 
00783 /* ........................ */
00784 
00785 /* invoke() - for 'relax' mode */
00786 static int pose_slide_relax_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
00787 {
00788     tPoseSlideOp *pso;
00789     
00790     /* initialise data  */
00791     if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
00792         pose_slide_exit(op);
00793         return OPERATOR_CANCELLED;
00794     }
00795     else
00796         pso= op->customdata;
00797     
00798     /* do common setup work */
00799     return pose_slide_invoke_common(C, op, pso);
00800 }
00801 
00802 /* exec() - for relax */
00803 static int pose_slide_relax_exec (bContext *C, wmOperator *op)
00804 {
00805     tPoseSlideOp *pso;
00806     
00807     /* initialise data (from RNA-props) */
00808     if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
00809         pose_slide_exit(op);
00810         return OPERATOR_CANCELLED;
00811     }
00812     else
00813         pso= op->customdata;
00814         
00815     /* do common exec work */
00816     return pose_slide_exec_common(C, op, pso);
00817 }
00818 
00819 void POSE_OT_relax (wmOperatorType *ot)
00820 {
00821     /* identifiers */
00822     ot->name= "Relax Pose";
00823     ot->idname= "POSE_OT_relax";
00824     ot->description= "Make the current pose more similar to its surrounding ones";
00825     
00826     /* callbacks */
00827     ot->exec= pose_slide_relax_exec;
00828     ot->invoke= pose_slide_relax_invoke;
00829     ot->modal= pose_slide_modal;
00830     ot->cancel= pose_slide_cancel;
00831     ot->poll= ED_operator_posemode;
00832     
00833     /* flags */
00834     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
00835     
00836     /* Properties */
00837     pose_slide_opdef_properties(ot);
00838 }
00839 
00840 /* ........................ */
00841 
00842 /* invoke() - for 'breakdown' mode */
00843 static int pose_slide_breakdown_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
00844 {
00845     tPoseSlideOp *pso;
00846     
00847     /* initialise data  */
00848     if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
00849         pose_slide_exit(op);
00850         return OPERATOR_CANCELLED;
00851     }
00852     else
00853         pso= op->customdata;
00854     
00855     /* do common setup work */
00856     return pose_slide_invoke_common(C, op, pso);
00857 }
00858 
00859 /* exec() - for breakdown */
00860 static int pose_slide_breakdown_exec (bContext *C, wmOperator *op)
00861 {
00862     tPoseSlideOp *pso;
00863     
00864     /* initialise data (from RNA-props) */
00865     if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
00866         pose_slide_exit(op);
00867         return OPERATOR_CANCELLED;
00868     }
00869     else
00870         pso= op->customdata;
00871         
00872     /* do common exec work */
00873     return pose_slide_exec_common(C, op, pso);
00874 }
00875 
00876 void POSE_OT_breakdown (wmOperatorType *ot)
00877 {
00878     /* identifiers */
00879     ot->name= "Pose Breakdowner";
00880     ot->idname= "POSE_OT_breakdown";
00881     ot->description= "Create a suitable breakdown pose on the current frame";
00882     
00883     /* callbacks */
00884     ot->exec= pose_slide_breakdown_exec;
00885     ot->invoke= pose_slide_breakdown_invoke;
00886     ot->modal= pose_slide_modal;
00887     ot->cancel= pose_slide_cancel;
00888     ot->poll= ED_operator_posemode;
00889     
00890     /* flags */
00891     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
00892     
00893     /* Properties */
00894     pose_slide_opdef_properties(ot);
00895 }
00896 
00897 /* **************************************************** */
00898 /* B) Pose Propagate */
00899 
00900 /* "termination conditions" - i.e. when we stop */
00901 typedef enum ePosePropagate_Termination {
00902         /* stop after the current hold ends */
00903     POSE_PROPAGATE_SMART_HOLDS = 0,
00904         /* only do on the last keyframe */
00905     POSE_PROPAGATE_LAST_KEY,
00906         /* stop after the next keyframe */
00907     POSE_PROPAGATE_NEXT_KEY,
00908         /* stop after the specified frame */
00909     POSE_PROPAGATE_BEFORE_FRAME,
00910         /* stop when we run out of keyframes */
00911     POSE_PROPAGATE_BEFORE_END,
00912     
00913         /* only do on the frames where markers are selected */
00914     POSE_PROPAGATE_SELECTED_MARKERS
00915 } ePosePropagate_Termination;
00916 
00917 /* termination data needed for some modes - assumes only one of these entries will be needed at a time */
00918 typedef union tPosePropagate_ModeData {
00919     /* smart holds + before frame: frame number to stop on */
00920     float end_frame;
00921     
00922     /* selected markers: listbase for CfraElem's marking these frames */
00923     ListBase sel_markers;
00924 } tPosePropagate_ModeData;
00925 
00926 /* --------------------------------- */
00927 
00928 /* get frame on which the "hold" for the bone ends 
00929  * XXX: this may not really work that well if a bone moves on some channels and not others
00930  *      if this happens to be a major issue, scrap this, and just make this happen 
00931  *      independently per F-Curve
00932  */
00933 static float pose_propagate_get_boneHoldEndFrame (Object *ob, tPChanFCurveLink *pfl, float startFrame)
00934 {
00935     DLRBT_Tree keys, blocks;
00936     ActKeyBlock *ab;
00937     
00938     AnimData *adt= ob->adt;
00939     LinkData *ld;
00940     float endFrame = startFrame;
00941     
00942     /* set up optimised data-structures for searching for relevant keyframes + holds */
00943     BLI_dlrbTree_init(&keys);
00944     BLI_dlrbTree_init(&blocks);
00945     
00946     for (ld = pfl->fcurves.first; ld; ld = ld->next) {
00947         FCurve *fcu = (FCurve *)ld->data;
00948         fcurve_to_keylist(adt, fcu, &keys, &blocks);
00949     }
00950     
00951     BLI_dlrbTree_linkedlist_sync(&keys);
00952     BLI_dlrbTree_linkedlist_sync(&blocks);
00953     
00954     /* find the long keyframe (i.e. hold), and hence obtain the endFrame value 
00955      *  - the best case would be one that starts on the frame itself
00956      */
00957     ab = (ActKeyBlock *)BLI_dlrbTree_search_exact(&blocks, compare_ab_cfraPtr, &startFrame);
00958     
00959     if (actkeyblock_is_valid(ab, &keys) == 0) {
00960         /* There are only two cases for no-exact match:
00961          *  1) the current frame is just before another key but not on a key itself
00962          *  2) the current frame is on a key, but that key doesn't link to the next
00963          *
00964          * If we've got the first case, then we can search for another block, 
00965          * otherwise forget it, as we'd be overwriting some valid data.
00966          */
00967         if (BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &startFrame) == NULL) {
00968             /* we've got case 1, so try the one after */
00969             ab = (ActKeyBlock *)BLI_dlrbTree_search_next(&blocks, compare_ab_cfraPtr, &startFrame);
00970             
00971             if (actkeyblock_is_valid(ab, &keys) == 0) {
00972                 /* try the block before this frame then as last resort */
00973                 ab = (ActKeyBlock *)BLI_dlrbTree_search_prev(&blocks, compare_ab_cfraPtr, &startFrame);
00974                 
00975                 /* whatever happens, stop searching now... */
00976                 if (actkeyblock_is_valid(ab, &keys) == 0) {
00977                     /* restrict range to just the frame itself 
00978                      * i.e. everything is in motion, so no holds to safely overwrite
00979                      */
00980                     ab = NULL;
00981                 }
00982             }
00983         }
00984         else {
00985             /* we've got case 2 - set ab to NULL just in case, since we shouldn't do anything in this case */
00986             ab = NULL;
00987         }
00988     }
00989     
00990     /* check if we can go any further than we've already gone */
00991     if (ab) {
00992         /* go to next if it is also valid and meets "extension" criteria */
00993         while (ab->next) {
00994             ActKeyBlock *abn = (ActKeyBlock *)ab->next;
00995             
00996             /* must be valid */
00997             if (actkeyblock_is_valid(abn, &keys) == 0)
00998                 break;
00999             /* should start on the same frame that the last ended on */
01000             if (ab->end != abn->start)
01001                 break;
01002             /* should have the same number of curves */
01003             if (ab->totcurve != abn->totcurve)
01004                 break;
01005             /* should have the same value 
01006              * XXX: this may be a bit fuzzy on larger data sets, so be careful
01007              */
01008             if (ab->val != abn->val)
01009                 break;
01010                 
01011             /* we can extend the bounds to the end of this "next" block now */
01012             ab = abn;
01013         }
01014         
01015         /* end frame can now take the value of the end of the block */
01016         endFrame = ab->end;
01017     }
01018     
01019     /* free temp memory */
01020     BLI_dlrbTree_free(&keys);
01021     BLI_dlrbTree_free(&blocks);
01022     
01023     /* return the end frame we've found */
01024     return endFrame;
01025 }
01026 
01027 /* get reference value from F-Curve using RNA */
01028 static short pose_propagate_get_refVal (Object *ob, FCurve *fcu, float *value)
01029 {
01030     PointerRNA id_ptr, ptr;
01031     PropertyRNA *prop;
01032     short found= FALSE;
01033     
01034     /* base pointer is always the object -> id_ptr */
01035     RNA_id_pointer_create(&ob->id, &id_ptr);
01036     
01037     /* resolve the property... */
01038     if (RNA_path_resolve(&id_ptr, fcu->rna_path, &ptr, &prop)) {
01039         if (RNA_property_array_check(prop)) {
01040             /* array */
01041             if (fcu->array_index < RNA_property_array_length(&ptr, prop)) {
01042                 found= TRUE;
01043                 switch (RNA_property_type(prop)) {
01044                     case PROP_BOOLEAN:
01045                         *value= (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index);
01046                         break;
01047                     case PROP_INT:
01048                         *value= (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index);
01049                         break;
01050                     case PROP_FLOAT:
01051                         *value= RNA_property_float_get_index(&ptr, prop, fcu->array_index);
01052                         break;
01053                     default:
01054                         found= FALSE;
01055                         break;
01056                 }
01057             }
01058         }
01059         else {
01060             /* not an array */
01061             found= TRUE;
01062             switch (RNA_property_type(prop)) {
01063                 case PROP_BOOLEAN:
01064                     *value= (float)RNA_property_boolean_get(&ptr, prop);
01065                     break;
01066                 case PROP_INT:
01067                     *value= (float)RNA_property_int_get(&ptr, prop);
01068                     break;
01069                 case PROP_ENUM:
01070                     *value= (float)RNA_property_enum_get(&ptr, prop);
01071                     break;
01072                 case PROP_FLOAT:
01073                     *value= RNA_property_float_get(&ptr, prop);
01074                     break;
01075                 default:
01076                     found= FALSE;
01077                     break;
01078             }
01079         }
01080     }
01081     
01082     return found;
01083 }
01084 
01085 /* propagate just works along each F-Curve in turn */
01086 static void pose_propagate_fcurve (wmOperator *op, Object *ob, FCurve *fcu, 
01087                 float startFrame, tPosePropagate_ModeData modeData)
01088 {
01089     const int mode = RNA_enum_get(op->ptr, "mode");
01090     
01091     BezTriple *bezt;
01092     float refVal = 0.0f;
01093     short keyExists;
01094     int i, match;
01095     short first=1;
01096     
01097     /* skip if no keyframes to edit */
01098     if ((fcu->bezt == NULL) || (fcu->totvert < 2))
01099         return;
01100         
01101     /* find the reference value from bones directly, which means that the user
01102      * doesn't need to firstly keyframe the pose (though this doesn't mean that 
01103      * they can't either)
01104      */
01105     if( !pose_propagate_get_refVal(ob, fcu, &refVal))
01106         return;
01107     
01108     /* find the first keyframe to start propagating from 
01109      *  - if there's a keyframe on the current frame, we probably want to save this value there too
01110      *    since it may be as of yet unkeyed
01111      *  - if starting before the starting frame, don't touch the key, as it may have had some valid 
01112      *    values
01113      */
01114     match = binarysearch_bezt_index(fcu->bezt, startFrame, fcu->totvert, &keyExists);
01115     
01116     if (fcu->bezt[match].vec[1][0] < startFrame)
01117         i = match + 1;
01118     else
01119         i = match;
01120     
01121     for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) {
01122         /* additional termination conditions based on the operator 'mode' property go here... */
01123         if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) {
01124             /* stop if keyframe is outside the accepted range */
01125             if (bezt->vec[1][0] > modeData.end_frame)
01126                 break; 
01127         }
01128         else if (mode == POSE_PROPAGATE_NEXT_KEY) {
01129             /* stop after the first keyframe has been processed */
01130             if (first == 0)
01131                 break;
01132         }
01133         else if (mode == POSE_PROPAGATE_LAST_KEY) {
01134             /* only affect this frame if it will be the last one */
01135             if (i != (fcu->totvert-1))
01136                 continue;
01137         }
01138         else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
01139             /* only allow if there's a marker on this frame */
01140             CfraElem *ce = NULL;
01141             
01142             /* stop on matching marker if there is one */
01143             for (ce = modeData.sel_markers.first; ce; ce = ce->next) {
01144                 if (ce->cfra == (int)(floor(bezt->vec[1][0] + 0.5f)))
01145                     break;
01146             }
01147             
01148             /* skip this keyframe if no marker */
01149             if (ce == NULL)
01150                 continue;
01151         }
01152         
01153         /* just flatten handles, since values will now be the same either side... */
01154         // TODO: perhaps a fade-out modulation of the value is required here (optional once again)?
01155         bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal;
01156         
01157         /* select keyframe to indicate that it's been changed */
01158         bezt->f2 |= SELECT;
01159         first = 0;
01160     }
01161 }
01162 
01163 /* --------------------------------- */
01164 
01165 static int pose_propagate_exec (bContext *C, wmOperator *op)
01166 {
01167     Scene *scene = CTX_data_scene(C);
01168     Object *ob= object_pose_armature_get(CTX_data_active_object(C));
01169     bAction *act= (ob && ob->adt)? ob->adt->action : NULL;
01170     
01171     ListBase pflinks = {NULL, NULL};
01172     tPChanFCurveLink *pfl;
01173     
01174     tPosePropagate_ModeData modeData;
01175     const int mode = RNA_enum_get(op->ptr, "mode");
01176     
01177     /* sanity checks */
01178     if (ob == NULL) {
01179         BKE_report(op->reports, RPT_ERROR, "No object to propagate poses for");
01180         return OPERATOR_CANCELLED;
01181     }
01182     if (act == NULL) {
01183         BKE_report(op->reports, RPT_ERROR, "No keyframed poses to propagate to");
01184         return OPERATOR_CANCELLED;
01185     }
01186     
01187     /* isolate F-Curves related to the selected bones */
01188     poseAnim_mapping_get(C, &pflinks, ob, act);
01189     
01190     /* mode-specific data preprocessing (requiring no access to curves) */
01191     if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
01192         /* get a list of selected markers */
01193         ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT);
01194     }
01195     else {
01196         /* assume everything else wants endFrame */
01197         modeData.end_frame = RNA_float_get(op->ptr, "end_frame");
01198     }
01199     
01200     /* for each bone, perform the copying required */
01201     for (pfl = pflinks.first; pfl; pfl = pfl->next) {
01202         LinkData *ld;
01203         
01204         /* mode-specific data preprocessing (requiring access to all curves) */
01205         if (mode == POSE_PROPAGATE_SMART_HOLDS) {
01206             /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
01207              * from the keyframe that occurs after the current frame
01208              */
01209             modeData.end_frame = pose_propagate_get_boneHoldEndFrame(ob, pfl, (float)CFRA);
01210         }
01211         
01212         /* go through propagating pose to keyframes, curve by curve */
01213         for (ld = pfl->fcurves.first; ld; ld= ld->next)
01214             pose_propagate_fcurve(op, ob, (FCurve *)ld->data, (float)CFRA, modeData);
01215     }
01216     
01217     /* free temp data */
01218     poseAnim_mapping_free(&pflinks);
01219     
01220     if (mode == POSE_PROPAGATE_SELECTED_MARKERS)
01221         BLI_freelistN(&modeData.sel_markers);
01222     
01223     /* updates + notifiers */
01224     poseAnim_mapping_refresh(C, scene, ob);
01225     
01226     return OPERATOR_FINISHED;
01227 }
01228 
01229 /* --------------------------------- */
01230 
01231 void POSE_OT_propagate (wmOperatorType *ot)
01232 {
01233     static EnumPropertyItem terminate_items[]= {
01234         {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", "Propagate pose to all keyframes after current frame that don't change (Default behaviour)"},
01235         {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe", "Propagate pose to first keyframe following the current frame only"},
01236         {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "To Last Keyframe", "Propagate pose to the last keyframe only (i.e. making action cyclic)"},
01237         {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame", "Propagate pose to all keyframes between current frame and 'Frame' property"},
01238         {POSE_PROPAGATE_BEFORE_END, "BEFORE_END", 0, "Before Last Keyframe", "Propagate pose to all keyframes from current frame until no more are found"},
01239         {POSE_PROPAGATE_SELECTED_MARKERS, "SELECTED_MARKERS", 0, "On Selected Markers", "Propagate pose to all keyframes occurring on frames with Scene Markers after the current frame"},
01240         {0, NULL, 0, NULL, NULL}};
01241         
01242     /* identifiers */
01243     ot->name= "Propagate Pose";
01244     ot->idname= "POSE_OT_propagate";
01245     ot->description= "Copy selected aspects of the current pose to subsequent poses already keyframed";
01246     
01247     /* callbacks */
01248     ot->exec= pose_propagate_exec;
01249     ot->poll= ED_operator_posemode; // XXX: needs selected bones!
01250     
01251     /* flag */
01252     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01253     
01254     /* properties */
01255     // TODO: add "fade out" control for tapering off amount of propagation as time goes by?
01256     ot->prop= RNA_def_enum(ot->srna, "mode", terminate_items, POSE_PROPAGATE_SMART_HOLDS, "Terminate Mode", "Method used to determine when to stop propagating pose to keyframes");
01257     RNA_def_float(ot->srna, "end_frame", 250.0, FLT_MIN, FLT_MAX, "End Frame", "Frame to stop propagating frames to (for 'Before Frame' mode)", 1.0, 250.0);
01258 }
01259 
01260 /* **************************************************** */