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