replace VECCOPY and QUATCOPY with inline funcs.
[blender.git] / source / blender / editors / armature / poseSlide.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung
19  * This is a new part of Blender
20  *
21  * Contributor(s): Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/armature/poseSlide.c
27  *  \ingroup edarmature
28  */
29
30  
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <stddef.h>
34 #include <string.h>
35 #include <math.h>
36 #include <float.h>
37
38 #include "MEM_guardedalloc.h"
39
40 #include "BLI_math.h"
41 #include "BLI_blenlib.h"
42 #include "BLI_dynstr.h"
43 #include "BLI_dlrbTree.h"
44 #include "BLI_utildefines.h"
45
46 #include "DNA_anim_types.h"
47 #include "DNA_armature_types.h"
48 #include "DNA_object_types.h"
49 #include "DNA_scene_types.h"
50
51 #include "BKE_fcurve.h"
52
53 #include "BKE_context.h"
54 #include "BKE_object.h"
55 #include "BKE_report.h"
56
57 #include "RNA_access.h"
58 #include "RNA_define.h"
59
60 #include "WM_api.h"
61 #include "WM_types.h"
62
63 #include "ED_armature.h"
64 #include "ED_keyframes_draw.h"
65 #include "ED_markers.h"
66 #include "ED_screen.h"
67
68 #include "armature_intern.h"
69
70 /* **************************************************** */
71 /* == POSE 'SLIDING' TOOLS == 
72  *
73  * A) Push & Relax, Breakdowner
74  * These tools provide the animator with various capabilities
75  * for interactively controlling the spacing of poses, but also
76  * for 'pushing' and/or 'relaxing' extremes as they see fit.
77  *
78  * B) Propagate
79  * This tool copies elements of the selected pose to successive
80  * keyframes, allowing the animator to go back and modify the poses
81  * for some "static" pose controls, without having to repeatedly
82  * doing a "next paste" dance.
83  *
84  * C) Pose Sculpting
85  * This is yet to be implemented, but the idea here is to use
86  * sculpting techniques to make it easier to pose rigs by allowing
87  * rigs to be manipulated using a familiar paint-based interface. 
88  */
89 /* **************************************************** */
90 /* A) Push & Relax, Breakdowner */
91
92 /* Temporary data shared between these operators */
93 typedef struct tPoseSlideOp {
94         Scene *scene;           /* current scene */
95         ScrArea *sa;            /* area that we're operating in (needed for modal()) */
96         ARegion *ar;            /* region that we're operating in (needed for modal()) */
97         Object *ob;                     /* active object that Pose Info comes from */
98         bArmature *arm;         /* armature for pose */
99         
100         ListBase pfLinks;       /* links between posechannels and f-curves  */
101         DLRBT_Tree keys;        /* binary tree for quicker searching for keyframes (when applicable) */
102         
103         int cframe;                     /* current frame number */
104         int prevFrame;          /* frame before current frame (blend-from) */
105         int nextFrame;          /* frame after current frame (blend-to) */
106         
107         int mode;                       /* sliding mode (ePoseSlide_Modes) */
108         int flag;                       // unused for now, but can later get used for storing runtime settings....
109         
110         float percentage;       /* 0-1 value for determining the influence of whatever is relevant */
111 } tPoseSlideOp;
112
113 /* Pose Sliding Modes */
114 typedef enum ePoseSlide_Modes {
115         POSESLIDE_PUSH  = 0,            /* exaggerate the pose... */
116         POSESLIDE_RELAX,                        /* soften the pose... */
117         POSESLIDE_BREAKDOWN,            /* slide between the endpoint poses, finding a 'soft' spot */
118 } ePoseSlide_Modes;
119
120 /* ------------------------------------ */
121
122 /* operator init */
123 static int pose_slide_init (bContext *C, wmOperator *op, short mode)
124 {
125         tPoseSlideOp *pso;
126         bAction *act= NULL;
127         
128         /* init slide-op data */
129         pso= op->customdata= MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp");
130         
131         /* get info from context */
132         pso->scene= CTX_data_scene(C);
133         pso->ob= object_pose_armature_get(CTX_data_active_object(C));
134         pso->arm= (pso->ob)? pso->ob->data : NULL;
135         pso->sa= CTX_wm_area(C); /* only really needed when doing modal() */
136         pso->ar= CTX_wm_region(C); /* only really needed when doing modal() */
137         
138         pso->cframe= pso->scene->r.cfra;
139         pso->mode= mode;
140         
141         /* set range info from property values - these may get overridden for the invoke() */
142         pso->percentage= RNA_float_get(op->ptr, "percentage");
143         pso->prevFrame= RNA_int_get(op->ptr, "prev_frame");
144         pso->nextFrame= RNA_int_get(op->ptr, "next_frame");
145         
146         /* check the settings from the context */
147         if (ELEM4(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action))
148                 return 0;
149         else
150                 act= pso->ob->adt->action;
151         
152         /* for each Pose-Channel which gets affected, get the F-Curves for that channel 
153          * and set the relevant transform flags...
154          */
155         poseAnim_mapping_get(C, &pso->pfLinks, pso->ob, act);
156         
157         /* set depsgraph flags */
158                 /* make sure the lock is set OK, unlock can be accidentally saved? */
159         pso->ob->pose->flag |= POSE_LOCKED;
160         pso->ob->pose->flag &= ~POSE_DO_UNLOCK;
161         
162         /* do basic initialise of RB-BST used for finding keyframes, but leave the filling of it up 
163          * to the caller of this (usually only invoke() will do it, to make things more efficient).
164          */
165         BLI_dlrbTree_init(&pso->keys);
166         
167         /* return status is whether we've got all the data we were requested to get */
168         return 1;
169 }
170
171 /* exiting the operator - free data */
172 static void pose_slide_exit(wmOperator *op)
173 {
174         tPoseSlideOp *pso= op->customdata;
175         
176         /* if data exists, clear its data and exit */
177         if (pso) {
178                 /* free the temp pchan links and their data */
179                 poseAnim_mapping_free(&pso->pfLinks);
180                 
181                 /* free RB-BST for keyframes (if it contained data) */
182                 BLI_dlrbTree_free(&pso->keys);
183                 
184                 /* free data itself */
185                 MEM_freeN(pso);
186         }
187         
188         /* cleanup */
189         op->customdata= NULL;
190 }
191
192 /* ------------------------------------ */
193
194 /* helper for apply() / reset() - refresh the data */
195 static void pose_slide_refresh (bContext *C, tPoseSlideOp *pso)
196 {
197         /* wrapper around the generic version, allowing us to add some custom stuff later still */
198         poseAnim_mapping_refresh(C, pso->scene, pso->ob);
199 }
200
201 /* helper for apply() - perform sliding for some value */
202 static void pose_slide_apply_val (tPoseSlideOp *pso, FCurve *fcu, float *val)
203 {
204         float cframe = (float)pso->cframe;
205         float sVal, eVal;
206         float w1, w2;
207         
208         /* get keyframe values for endpoint poses to blend with */
209                 /* previous/start */
210         sVal= evaluate_fcurve(fcu, (float)pso->prevFrame);
211                 /* next/end */
212         eVal= evaluate_fcurve(fcu, (float)pso->nextFrame);
213         
214         /* calculate the relative weights of the endpoints */
215         if (pso->mode == POSESLIDE_BREAKDOWN) {
216                 /* get weights from the percentage control */
217                 w1= pso->percentage;    /* this must come second */
218                 w2= 1.0f - w1;                  /* this must come first */
219         }
220         else {
221                 /*      - these weights are derived from the relative distance of these 
222                  *        poses from the current frame
223                  *      - they then get normalised so that they only sum up to 1
224                  */
225                 float wtot; 
226                 
227                 w1 = cframe - (float)pso->prevFrame;
228                 w2 = (float)pso->nextFrame - cframe;
229                 
230                 wtot = w1 + w2;
231                 w1 = (w1/wtot);
232                 w2 = (w2/wtot);
233         }
234         
235         /* depending on the mode, calculate the new value
236          *      - in all of these, the start+end values are multiplied by w2 and w1 (respectively),
237          *        since multiplication in another order would decrease the value the current frame is closer to
238          */
239         switch (pso->mode) {
240                 case POSESLIDE_PUSH: /* make the current pose more pronounced */
241                 {
242                         /* perform a weighted average here, favouring the middle pose 
243                          *      - numerator should be larger than denominator to 'expand' the result
244                          *      - perform this weighting a number of times given by the percentage...
245                          */
246                         int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
247                         
248                         while (iters-- > 0) {
249                                 (*val)= ( -((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f) ) / 5.0f; 
250                         }
251                 }
252                         break;
253                         
254                 case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */
255                 {
256                         /* perform a weighted average here, favouring the middle pose 
257                          *      - numerator should be smaller than denominator to 'relax' the result
258                          *      - perform this weighting a number of times given by the percentage...
259                          */
260                         int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
261                         
262                         while (iters-- > 0) {
263                                 (*val)= ( ((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f) ) / 6.0f;
264                         }
265                 }
266                         break;
267                         
268                 case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */
269                 {
270                         /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */
271                         // TODO: make this use some kind of spline interpolation instead?
272                         (*val)= ((sVal * w2) + (eVal * w1));
273                 }
274                         break;
275         }
276 }
277
278 /* helper for apply() - perform sliding for some 3-element vector */
279 static void pose_slide_apply_vec3 (tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], const char propName[])
280 {
281         LinkData *ld=NULL;
282         char *path=NULL;
283         
284         /* get the path to use... */
285         path= BLI_sprintfN("%s.%s", pfl->pchan_path, propName);
286         
287         /* using this path, find each matching F-Curve for the variables we're interested in */
288         while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
289                 FCurve *fcu= (FCurve *)ld->data;
290                 
291                 /* just work on these channels one by one... there's no interaction between values */
292                 pose_slide_apply_val(pso, fcu, &vec[fcu->array_index]);
293         }
294         
295         /* free the temp path we got */
296         MEM_freeN(path);
297 }
298
299 /* helper for apply() - perform sliding for custom properties */
300 static void pose_slide_apply_props (tPoseSlideOp *pso, tPChanFCurveLink *pfl)
301 {
302         PointerRNA ptr = {{NULL}};
303         LinkData *ld;
304         int len = strlen(pfl->pchan_path);
305         
306         /* setup pointer RNA for resolving paths */
307         RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr);
308         
309         /* custom properties are just denoted using ["..."][etc.] after the end of the base path, 
310          * so just check for opening pair after the end of the path
311          */
312         for (ld = pfl->fcurves.first; ld; ld = ld->next) {
313                 FCurve *fcu = (FCurve *)ld->data;
314                 char *bPtr, *pPtr;
315                 
316                 if (fcu->rna_path == NULL)
317                         continue;
318                 
319                 /* do we have a match? 
320                  *      - bPtr is the RNA Path with the standard part chopped off
321                  *      - pPtr is the chunk of the path which is left over
322                  */
323                 bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len;
324                 pPtr = strstr(bPtr, "[\"");   /* dummy " for texteditor bugs */
325                 
326                 if (pPtr) {
327                         /* use RNA to try and get a handle on this property, then, assuming that it is just
328                          * numerical, try and grab the value as a float for temp editing before setting back
329                          */
330                         PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr);
331                         
332                         if (prop) {
333                                 switch (RNA_property_type(prop)) {
334                                         case PROP_FLOAT:
335                                         {
336                                                 float tval = RNA_property_float_get(&ptr, prop);
337                                                 pose_slide_apply_val(pso, fcu, &tval);
338                                                 RNA_property_float_set(&ptr, prop, tval);
339                                         }
340                                                 break;
341                                         case PROP_BOOLEAN:
342                                         case PROP_ENUM:
343                                         case PROP_INT:
344                                         {
345                                                 float tval = (float)RNA_property_int_get(&ptr, prop);
346                                                 pose_slide_apply_val(pso, fcu, &tval);
347                                                 RNA_property_int_set(&ptr, prop, (int)tval);
348                                         }
349                                                 break;
350                                         default:
351                                                 /* cannot handle */
352                                                 //printf("Cannot Pose Slide non-numerical property\n");
353                                                 break;
354                                 }
355                         }
356                 }
357         }
358 }
359
360 /* helper for apply() - perform sliding for quaternion rotations (using quat blending) */
361 static void pose_slide_apply_quat (tPoseSlideOp *pso, tPChanFCurveLink *pfl)
362 {
363         FCurve *fcu_w=NULL, *fcu_x=NULL, *fcu_y=NULL, *fcu_z=NULL;
364         bPoseChannel *pchan= pfl->pchan;
365         LinkData *ld=NULL;
366         char *path=NULL;
367         float cframe;
368         
369         /* get the path to use - this should be quaternion rotations only (needs care) */
370         path= BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion");
371         
372         /* get the current frame number */
373         cframe= (float)pso->cframe;
374         
375         /* using this path, find each matching F-Curve for the variables we're interested in */
376         while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
377                 FCurve *fcu= (FCurve *)ld->data;
378                 
379                 /* assign this F-Curve to one of the relevant pointers... */
380                 switch (fcu->array_index) {
381                         case 3: /* z */
382                                 fcu_z= fcu;
383                                 break;
384                         case 2: /* y */
385                                 fcu_y= fcu;
386                                 break;
387                         case 1: /* x */
388                                 fcu_x= fcu;
389                                 break;
390                         case 0: /* w */
391                                 fcu_w= fcu;
392                                 break;
393                 }
394         }
395         
396         /* only if all channels exist, proceed */
397         if (fcu_w && fcu_x && fcu_y && fcu_z) {
398                 float quat_prev[4], quat_next[4];
399                 
400                 /* get 2 quats */
401                 quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrame);
402                 quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrame);
403                 quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrame);
404                 quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrame);
405                 
406                 quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrame);
407                 quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrame);
408                 quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrame);
409                 quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrame);
410                 
411                 /* perform blending */
412                 if (pso->mode == POSESLIDE_BREAKDOWN) {
413                         /* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */
414                         interp_qt_qtqt(pchan->quat, quat_prev, quat_next, pso->percentage);
415                 }
416                 else if (pso->mode == POSESLIDE_PUSH) {
417                         float quat_diff[4], quat_orig[4];
418                         
419                         /* calculate the delta transform from the previous to the current */
420                         // TODO: investigate ways to favour one transform more?
421                         sub_qt_qtqt(quat_diff, pchan->quat, quat_prev);
422                         
423                         /* make a copy of the original rotation */
424                         copy_qt_qt(quat_orig, pchan->quat);
425                         
426                         /* increase the original by the delta transform, by an amount determined by percentage */
427                         add_qt_qtqt(pchan->quat, quat_orig, quat_diff, pso->percentage);
428                 }
429                 else {
430                         float quat_interp[4], quat_orig[4];
431                         int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
432                         
433                         /* perform this blending several times until a satisfactory result is reached */
434                         while (iters-- > 0) {
435                                 /* calculate the interpolation between the endpoints */
436                                 interp_qt_qtqt(quat_interp, quat_prev, quat_next, (cframe-pso->prevFrame) / (pso->nextFrame-pso->prevFrame) );
437                                 
438                                 /* make a copy of the original rotation */
439                                 copy_qt_qt(quat_orig, pchan->quat);
440                                 
441                                 /* tricky interpolations - blending between original and new */
442                                 interp_qt_qtqt(pchan->quat, quat_orig, quat_interp, 1.0f/6.0f);
443                         }
444                 }
445         }
446         
447         /* free the path now */
448         MEM_freeN(path);
449 }
450
451 /* apply() - perform the pose sliding based on weighting various poses */
452 static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
453 {
454         tPChanFCurveLink *pfl;
455         
456         /* sanitise the frame ranges */
457         if (pso->prevFrame == pso->nextFrame) {
458                 /* move out one step either side */
459                 pso->prevFrame--;
460                 pso->nextFrame++;
461         }
462         
463         /* for each link, handle each set of transforms */
464         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
465                 /* valid transforms for each PoseChannel should have been noted already 
466                  *      - sliding the pose should be a straightforward exercise for location+rotation, 
467                  *        but rotations get more complicated since we may want to use quaternion blending 
468                  *        for quaternions instead...
469                  */
470                 bPoseChannel *pchan= pfl->pchan;
471                  
472                 if (pchan->flag & POSE_LOC) {
473                         /* calculate these for the 'location' vector, and use location curves */
474                         pose_slide_apply_vec3(pso, pfl, pchan->loc, "location");
475                 }
476                 
477                 if (pchan->flag & POSE_SIZE) {
478                         /* calculate these for the 'scale' vector, and use scale curves */
479                         pose_slide_apply_vec3(pso, pfl, pchan->size, "scale");
480                 }
481                 
482                 if (pchan->flag & POSE_ROT) {
483                         /* everything depends on the rotation mode */
484                         if (pchan->rotmode > 0) {
485                                 /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
486                                 pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler");
487                         }
488                         else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
489                                 // TODO: need to figure out how to do this!
490                         }
491                         else {
492                                 /* quaternions - use quaternion blending */
493                                 pose_slide_apply_quat(pso, pfl);
494                         }
495                 }
496                 
497                 if (pfl->oldprops) {
498                         /* not strictly a transform, but contributes to the pose produced in many rigs */
499                         pose_slide_apply_props(pso, pfl);
500                 }
501         }
502         
503         /* depsgraph updates + redraws */
504         pose_slide_refresh(C, pso);
505 }
506
507 /* perform autokeyframing after changes were made + confirmed */
508 static void pose_slide_autoKeyframe (bContext *C, tPoseSlideOp *pso)
509 {
510         /* wrapper around the generic call */
511         poseAnim_mapping_autoKeyframe(C, pso->scene, pso->ob, &pso->pfLinks, (float)pso->cframe);
512 }
513
514 /* reset changes made to current pose */
515 static void pose_slide_reset (tPoseSlideOp *pso)
516 {
517         /* wrapper around the generic call, so that custom stuff can be added later */
518         poseAnim_mapping_reset(&pso->pfLinks);
519 }
520
521 /* ------------------------------------ */
522
523 /* draw percentage indicator in header */
524 static void pose_slide_draw_status (tPoseSlideOp *pso)
525 {
526         char statusStr[32];
527         char mode[32];
528         
529         switch (pso->mode) {
530                 case POSESLIDE_PUSH:
531                         strcpy(mode, "Push Pose");
532                         break;
533                 case POSESLIDE_RELAX:
534                         strcpy(mode, "Relax Pose");
535                         break;
536                 case POSESLIDE_BREAKDOWN:
537                         strcpy(mode, "Breakdown");
538                         break;
539                 
540                 default:
541                         // unknown
542                         strcpy(mode, "Sliding-Tool");
543                         break;
544         }
545         
546         sprintf(statusStr, "%s: %d %%", mode, (int)(pso->percentage*100.0f));
547         ED_area_headerprint(pso->sa, statusStr);
548 }
549
550 /* common code for invoke() methods */
551 static int pose_slide_invoke_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
552 {
553         tPChanFCurveLink *pfl;
554         AnimData *adt= pso->ob->adt;
555         wmWindow *win= CTX_wm_window(C);
556         
557         /* for each link, add all its keyframes to the search tree */
558         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
559                 LinkData *ld;
560                 
561                 /* do this for each F-Curve */
562                 for (ld= pfl->fcurves.first; ld; ld= ld->next) {
563                         FCurve *fcu= (FCurve *)ld->data;
564                         fcurve_to_keylist(adt, fcu, &pso->keys, NULL);
565                 }
566         }
567         
568         /* consolidate these keyframes, and figure out the nearest ones */
569         BLI_dlrbTree_linkedlist_sync(&pso->keys);
570         
571                 /* cancel if no keyframes found... */
572         if (pso->keys.root) {
573                 ActKeyColumn *ak;
574                 float cframe= (float)pso->cframe;
575                 
576                 /* firstly, check if the current frame is a keyframe... */
577                 ak= (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe);
578                 
579                 if (ak == NULL) {
580                         /* current frame is not a keyframe, so search */
581                         ActKeyColumn *pk= (ActKeyColumn *)BLI_dlrbTree_search_prev(&pso->keys, compare_ak_cfraPtr, &cframe);
582                         ActKeyColumn *nk= (ActKeyColumn *)BLI_dlrbTree_search_next(&pso->keys, compare_ak_cfraPtr, &cframe);
583                         
584                         /* new set the frames */
585                                 /* prev frame */
586                         pso->prevFrame= (pk)? (pk->cfra) : (pso->cframe - 1);
587                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
588                                 /* next frame */
589                         pso->nextFrame= (nk)? (nk->cfra) : (pso->cframe + 1);
590                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
591                 }
592                 else {
593                         /* current frame itself is a keyframe, so just take keyframes on either side */
594                                 /* prev frame */
595                         pso->prevFrame= (ak->prev)? (ak->prev->cfra) : (pso->cframe - 1);
596                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
597                                 /* next frame */
598                         pso->nextFrame= (ak->next)? (ak->next->cfra) : (pso->cframe + 1);
599                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
600                 }
601         }
602         else {
603                 BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between");
604                 pose_slide_exit(op);
605                 return OPERATOR_CANCELLED;
606         }
607         
608         /* initial apply for operator... */
609         // TODO: need to calculate percentage for initial round too...
610         pose_slide_apply(C, pso);
611         
612         /* depsgraph updates + redraws */
613         pose_slide_refresh(C, pso);
614         
615         /* set cursor to indicate modal */
616         WM_cursor_modal(win, BC_EW_SCROLLCURSOR);
617         
618         /* header print */
619         pose_slide_draw_status(pso);
620         
621         /* add a modal handler for this operator */
622         WM_event_add_modal_handler(C, op);
623         return OPERATOR_RUNNING_MODAL;
624 }
625
626 /* common code for modal() */
627 static int pose_slide_modal (bContext *C, wmOperator *op, wmEvent *evt)
628 {
629         tPoseSlideOp *pso= op->customdata;
630         wmWindow *win= CTX_wm_window(C);
631         
632         switch (evt->type) {
633                 case LEFTMOUSE: /* confirm */
634                 {
635                         /* return to normal cursor and header status */
636                         ED_area_headerprint(pso->sa, NULL);
637                         WM_cursor_restore(win);
638                         
639                         /* insert keyframes as required... */
640                         pose_slide_autoKeyframe(C, pso);
641                         pose_slide_exit(op);
642                         
643                         /* done! */
644                         return OPERATOR_FINISHED;
645                 }
646                 
647                 case ESCKEY:    /* cancel */
648                 case RIGHTMOUSE: 
649                 {
650                         /* return to normal cursor and header status */
651                         ED_area_headerprint(pso->sa, NULL);
652                         WM_cursor_restore(win);
653                         
654                         /* reset transforms back to original state */
655                         pose_slide_reset(pso);
656                         
657                         /* depsgraph updates + redraws */
658                         pose_slide_refresh(C, pso);
659                         
660                         /* clean up temp data */
661                         pose_slide_exit(op);
662                         
663                         /* cancelled! */
664                         return OPERATOR_CANCELLED;
665                 }
666                         
667                 case MOUSEMOVE: /* calculate new position */
668                 {
669                         /* calculate percentage based on position of mouse (we only use x-axis for now.
670                          * since this is more conveninent for users to do), and store new percentage value 
671                          */
672                         pso->percentage= (evt->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx);
673                         RNA_float_set(op->ptr, "percentage", pso->percentage);
674                         
675                         /* update percentage indicator in header */
676                         pose_slide_draw_status(pso);
677                         
678                         /* reset transforms (to avoid accumulation errors) */
679                         pose_slide_reset(pso);
680                         
681                         /* apply... */
682                         pose_slide_apply(C, pso);
683                 }
684                         break;
685                         
686                 default: /* unhandled event (maybe it was some view manip? */
687                         /* allow to pass through */
688                         return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH;
689         }
690         
691         /* still running... */
692         return OPERATOR_RUNNING_MODAL;
693 }
694
695 /* common code for cancel() */
696 static int pose_slide_cancel (bContext *UNUSED(C), wmOperator *op)
697 {
698         /* cleanup and done */
699         pose_slide_exit(op);
700         return OPERATOR_CANCELLED;
701 }
702
703 /* common code for exec() methods */
704 static int pose_slide_exec_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
705 {
706         /* settings should have been set up ok for applying, so just apply! */
707         pose_slide_apply(C, pso);
708         
709         /* insert keyframes if needed */
710         pose_slide_autoKeyframe(C, pso);
711         
712         /* cleanup and done */
713         pose_slide_exit(op);
714         
715         return OPERATOR_FINISHED;
716 }
717
718 /* common code for defining RNA properties */
719 static void pose_slide_opdef_properties (wmOperatorType *ot)
720 {
721         RNA_def_int(ot->srna, "prev_frame", 0, MINAFRAME, MAXFRAME, "Previous Keyframe", "Frame number of keyframe immediately before the current frame", 0, 50);
722         RNA_def_int(ot->srna, "next_frame", 0, MINAFRAME, MAXFRAME, "Next Keyframe", "Frame number of keyframe immediately after the current frame", 0, 50);
723         RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for the sliding operation", 0.3, 0.7);
724 }
725
726 /* ------------------------------------ */
727
728 /* invoke() - for 'push' mode */
729 static int pose_slide_push_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
730 {
731         tPoseSlideOp *pso;
732         
733         /* initialise data  */
734         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
735                 pose_slide_exit(op);
736                 return OPERATOR_CANCELLED;
737         }
738         else
739                 pso= op->customdata;
740         
741         /* do common setup work */
742         return pose_slide_invoke_common(C, op, pso);
743 }
744
745 /* exec() - for push */
746 static int pose_slide_push_exec (bContext *C, wmOperator *op)
747 {
748         tPoseSlideOp *pso;
749         
750         /* initialise data (from RNA-props) */
751         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
752                 pose_slide_exit(op);
753                 return OPERATOR_CANCELLED;
754         }
755         else
756                 pso= op->customdata;
757                 
758         /* do common exec work */
759         return pose_slide_exec_common(C, op, pso);
760 }
761
762 void POSE_OT_push (wmOperatorType *ot)
763 {
764         /* identifiers */
765         ot->name= "Push Pose";
766         ot->idname= "POSE_OT_push";
767         ot->description= "Exaggerate the current pose";
768         
769         /* callbacks */
770         ot->exec= pose_slide_push_exec;
771         ot->invoke= pose_slide_push_invoke;
772         ot->modal= pose_slide_modal;
773         ot->cancel= pose_slide_cancel;
774         ot->poll= ED_operator_posemode;
775         
776         /* flags */
777         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
778         
779         /* Properties */
780         pose_slide_opdef_properties(ot);
781 }
782
783 /* ........................ */
784
785 /* invoke() - for 'relax' mode */
786 static int pose_slide_relax_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
787 {
788         tPoseSlideOp *pso;
789         
790         /* initialise data  */
791         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
792                 pose_slide_exit(op);
793                 return OPERATOR_CANCELLED;
794         }
795         else
796                 pso= op->customdata;
797         
798         /* do common setup work */
799         return pose_slide_invoke_common(C, op, pso);
800 }
801
802 /* exec() - for relax */
803 static int pose_slide_relax_exec (bContext *C, wmOperator *op)
804 {
805         tPoseSlideOp *pso;
806         
807         /* initialise data (from RNA-props) */
808         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
809                 pose_slide_exit(op);
810                 return OPERATOR_CANCELLED;
811         }
812         else
813                 pso= op->customdata;
814                 
815         /* do common exec work */
816         return pose_slide_exec_common(C, op, pso);
817 }
818
819 void POSE_OT_relax (wmOperatorType *ot)
820 {
821         /* identifiers */
822         ot->name= "Relax Pose";
823         ot->idname= "POSE_OT_relax";
824         ot->description= "Make the current pose more similar to its surrounding ones";
825         
826         /* callbacks */
827         ot->exec= pose_slide_relax_exec;
828         ot->invoke= pose_slide_relax_invoke;
829         ot->modal= pose_slide_modal;
830         ot->cancel= pose_slide_cancel;
831         ot->poll= ED_operator_posemode;
832         
833         /* flags */
834         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
835         
836         /* Properties */
837         pose_slide_opdef_properties(ot);
838 }
839
840 /* ........................ */
841
842 /* invoke() - for 'breakdown' mode */
843 static int pose_slide_breakdown_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
844 {
845         tPoseSlideOp *pso;
846         
847         /* initialise data  */
848         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
849                 pose_slide_exit(op);
850                 return OPERATOR_CANCELLED;
851         }
852         else
853                 pso= op->customdata;
854         
855         /* do common setup work */
856         return pose_slide_invoke_common(C, op, pso);
857 }
858
859 /* exec() - for breakdown */
860 static int pose_slide_breakdown_exec (bContext *C, wmOperator *op)
861 {
862         tPoseSlideOp *pso;
863         
864         /* initialise data (from RNA-props) */
865         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
866                 pose_slide_exit(op);
867                 return OPERATOR_CANCELLED;
868         }
869         else
870                 pso= op->customdata;
871                 
872         /* do common exec work */
873         return pose_slide_exec_common(C, op, pso);
874 }
875
876 void POSE_OT_breakdown (wmOperatorType *ot)
877 {
878         /* identifiers */
879         ot->name= "Pose Breakdowner";
880         ot->idname= "POSE_OT_breakdown";
881         ot->description= "Create a suitable breakdown pose on the current frame";
882         
883         /* callbacks */
884         ot->exec= pose_slide_breakdown_exec;
885         ot->invoke= pose_slide_breakdown_invoke;
886         ot->modal= pose_slide_modal;
887         ot->cancel= pose_slide_cancel;
888         ot->poll= ED_operator_posemode;
889         
890         /* flags */
891         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
892         
893         /* Properties */
894         pose_slide_opdef_properties(ot);
895 }
896
897 /* **************************************************** */
898 /* B) Pose Propagate */
899
900 /* "termination conditions" - i.e. when we stop */
901 typedef enum ePosePropagate_Termination {
902                 /* stop after the current hold ends */
903         POSE_PROPAGATE_SMART_HOLDS = 0,
904                 /* only do on the last keyframe */
905         POSE_PROPAGATE_LAST_KEY,
906                 /* stop after the next keyframe */
907         POSE_PROPAGATE_NEXT_KEY,
908                 /* stop after the specified frame */
909         POSE_PROPAGATE_BEFORE_FRAME,
910                 /* stop when we run out of keyframes */
911         POSE_PROPAGATE_BEFORE_END,
912         
913                 /* only do on the frames where markers are selected */
914         POSE_PROPAGATE_SELECTED_MARKERS
915 } ePosePropagate_Termination;
916
917 /* termination data needed for some modes - assumes only one of these entries will be needed at a time */
918 typedef union tPosePropagate_ModeData {
919         /* smart holds + before frame: frame number to stop on */
920         float end_frame;
921         
922         /* selected markers: listbase for CfraElem's marking these frames */
923         ListBase sel_markers;
924 } tPosePropagate_ModeData;
925
926 /* --------------------------------- */
927
928 /* get frame on which the "hold" for the bone ends 
929  * XXX: this may not really work that well if a bone moves on some channels and not others
930  *              if this happens to be a major issue, scrap this, and just make this happen 
931  *              independently per F-Curve
932  */
933 static float pose_propagate_get_boneHoldEndFrame (Object *ob, tPChanFCurveLink *pfl, float startFrame)
934 {
935         DLRBT_Tree keys, blocks;
936         ActKeyBlock *ab;
937         
938         AnimData *adt= ob->adt;
939         LinkData *ld;
940         float endFrame = startFrame;
941         
942         /* set up optimised data-structures for searching for relevant keyframes + holds */
943         BLI_dlrbTree_init(&keys);
944         BLI_dlrbTree_init(&blocks);
945         
946         for (ld = pfl->fcurves.first; ld; ld = ld->next) {
947                 FCurve *fcu = (FCurve *)ld->data;
948                 fcurve_to_keylist(adt, fcu, &keys, &blocks);
949         }
950         
951         BLI_dlrbTree_linkedlist_sync(&keys);
952         BLI_dlrbTree_linkedlist_sync(&blocks);
953         
954         /* find the long keyframe (i.e. hold), and hence obtain the endFrame value 
955          *      - the best case would be one that starts on the frame itself
956          */
957         ab = (ActKeyBlock *)BLI_dlrbTree_search_exact(&blocks, compare_ab_cfraPtr, &startFrame);
958         
959         if (actkeyblock_is_valid(ab, &keys) == 0) {
960                 /* There are only two cases for no-exact match:
961                  *      1) the current frame is just before another key but not on a key itself
962                  *      2) the current frame is on a key, but that key doesn't link to the next
963                  *
964                  * If we've got the first case, then we can search for another block, 
965                  * otherwise forget it, as we'd be overwriting some valid data.
966                  */
967                 if (BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &startFrame) == NULL) {
968                         /* we've got case 1, so try the one after */
969                         ab = (ActKeyBlock *)BLI_dlrbTree_search_next(&blocks, compare_ab_cfraPtr, &startFrame);
970                         
971                         if (actkeyblock_is_valid(ab, &keys) == 0) {
972                                 /* try the block before this frame then as last resort */
973                                 ab = (ActKeyBlock *)BLI_dlrbTree_search_prev(&blocks, compare_ab_cfraPtr, &startFrame);
974                                 
975                                 /* whatever happens, stop searching now... */
976                                 if (actkeyblock_is_valid(ab, &keys) == 0) {
977                                         /* restrict range to just the frame itself 
978                                          * i.e. everything is in motion, so no holds to safely overwrite
979                                          */
980                                         ab = NULL;
981                                 }
982                         }
983                 }
984                 else {
985                         /* we've got case 2 - set ab to NULL just in case, since we shouldn't do anything in this case */
986                         ab = NULL;
987                 }
988         }
989         
990         /* check if we can go any further than we've already gone */
991         if (ab) {
992                 /* go to next if it is also valid and meets "extension" criteria */
993                 while (ab->next) {
994                         ActKeyBlock *abn = (ActKeyBlock *)ab->next;
995                         
996                         /* must be valid */
997                         if (actkeyblock_is_valid(abn, &keys) == 0)
998                                 break;
999                         /* should start on the same frame that the last ended on */
1000                         if (ab->end != abn->start)
1001                                 break;
1002                         /* should have the same number of curves */
1003                         if (ab->totcurve != abn->totcurve)
1004                                 break;
1005                         /* should have the same value 
1006                          * XXX: this may be a bit fuzzy on larger data sets, so be careful
1007                          */
1008                         if (ab->val != abn->val)
1009                                 break;
1010                                 
1011                         /* we can extend the bounds to the end of this "next" block now */
1012                         ab = abn;
1013                 }
1014                 
1015                 /* end frame can now take the value of the end of the block */
1016                 endFrame = ab->end;
1017         }
1018         
1019         /* free temp memory */
1020         BLI_dlrbTree_free(&keys);
1021         BLI_dlrbTree_free(&blocks);
1022         
1023         /* return the end frame we've found */
1024         return endFrame;
1025 }
1026
1027 /* get reference value from F-Curve using RNA */
1028 static short pose_propagate_get_refVal (Object *ob, FCurve *fcu, float *value)
1029 {
1030         PointerRNA id_ptr, ptr;
1031         PropertyRNA *prop;
1032         short found= FALSE;
1033         
1034         /* base pointer is always the object -> id_ptr */
1035         RNA_id_pointer_create(&ob->id, &id_ptr);
1036         
1037         /* resolve the property... */
1038         if (RNA_path_resolve(&id_ptr, fcu->rna_path, &ptr, &prop)) {
1039                 if (RNA_property_array_check(prop)) {
1040                         /* array */
1041                         if (fcu->array_index < RNA_property_array_length(&ptr, prop)) {
1042                                 found= TRUE;
1043                                 switch (RNA_property_type(prop)) {
1044                                         case PROP_BOOLEAN:
1045                                                 *value= (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index);
1046                                                 break;
1047                                         case PROP_INT:
1048                                                 *value= (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index);
1049                                                 break;
1050                                         case PROP_FLOAT:
1051                                                 *value= RNA_property_float_get_index(&ptr, prop, fcu->array_index);
1052                                                 break;
1053                                         default:
1054                                                 found= FALSE;
1055                                                 break;
1056                                 }
1057                         }
1058                 }
1059                 else {
1060                         /* not an array */
1061                         found= TRUE;
1062                         switch (RNA_property_type(prop)) {
1063                                 case PROP_BOOLEAN:
1064                                         *value= (float)RNA_property_boolean_get(&ptr, prop);
1065                                         break;
1066                                 case PROP_INT:
1067                                         *value= (float)RNA_property_int_get(&ptr, prop);
1068                                         break;
1069                                 case PROP_ENUM:
1070                                         *value= (float)RNA_property_enum_get(&ptr, prop);
1071                                         break;
1072                                 case PROP_FLOAT:
1073                                         *value= RNA_property_float_get(&ptr, prop);
1074                                         break;
1075                                 default:
1076                                         found= FALSE;
1077                                         break;
1078                         }
1079                 }
1080         }
1081         
1082         return found;
1083 }
1084
1085 /* propagate just works along each F-Curve in turn */
1086 static void pose_propagate_fcurve (wmOperator *op, Object *ob, FCurve *fcu, 
1087                                 float startFrame, tPosePropagate_ModeData modeData)
1088 {
1089         const int mode = RNA_enum_get(op->ptr, "mode");
1090         
1091         BezTriple *bezt;
1092         float refVal = 0.0f;
1093         short keyExists;
1094         int i, match;
1095         short first=1;
1096         
1097         /* skip if no keyframes to edit */
1098         if ((fcu->bezt == NULL) || (fcu->totvert < 2))
1099                 return;
1100                 
1101         /* find the reference value from bones directly, which means that the user
1102          * doesn't need to firstly keyframe the pose (though this doesn't mean that 
1103          * they can't either)
1104          */
1105         if( !pose_propagate_get_refVal(ob, fcu, &refVal))
1106                 return;
1107         
1108         /* find the first keyframe to start propagating from 
1109          *      - if there's a keyframe on the current frame, we probably want to save this value there too
1110          *        since it may be as of yet unkeyed
1111          *      - if starting before the starting frame, don't touch the key, as it may have had some valid 
1112          *        values
1113          */
1114         match = binarysearch_bezt_index(fcu->bezt, startFrame, fcu->totvert, &keyExists);
1115         
1116         if (fcu->bezt[match].vec[1][0] < startFrame)
1117                 i = match + 1;
1118         else
1119                 i = match;
1120         
1121         for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) {
1122                 /* additional termination conditions based on the operator 'mode' property go here... */
1123                 if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) {
1124                         /* stop if keyframe is outside the accepted range */
1125                         if (bezt->vec[1][0] > modeData.end_frame)
1126                                 break; 
1127                 }
1128                 else if (mode == POSE_PROPAGATE_NEXT_KEY) {
1129                         /* stop after the first keyframe has been processed */
1130                         if (first == 0)
1131                                 break;
1132                 }
1133                 else if (mode == POSE_PROPAGATE_LAST_KEY) {
1134                         /* only affect this frame if it will be the last one */
1135                         if (i != (fcu->totvert-1))
1136                                 continue;
1137                 }
1138                 else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
1139                         /* only allow if there's a marker on this frame */
1140                         CfraElem *ce = NULL;
1141                         
1142                         /* stop on matching marker if there is one */
1143                         for (ce = modeData.sel_markers.first; ce; ce = ce->next) {
1144                                 if (ce->cfra == (int)(floor(bezt->vec[1][0] + 0.5f)))
1145                                         break;
1146                         }
1147                         
1148                         /* skip this keyframe if no marker */
1149                         if (ce == NULL)
1150                                 continue;
1151                 }
1152                 
1153                 /* just flatten handles, since values will now be the same either side... */
1154                 // TODO: perhaps a fade-out modulation of the value is required here (optional once again)?
1155                 bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal;
1156                 
1157                 /* select keyframe to indicate that it's been changed */
1158                 bezt->f2 |= SELECT;
1159                 first = 0;
1160         }
1161 }
1162
1163 /* --------------------------------- */
1164
1165 static int pose_propagate_exec (bContext *C, wmOperator *op)
1166 {
1167         Scene *scene = CTX_data_scene(C);
1168         Object *ob= object_pose_armature_get(CTX_data_active_object(C));
1169         bAction *act= (ob && ob->adt)? ob->adt->action : NULL;
1170         
1171         ListBase pflinks = {NULL, NULL};
1172         tPChanFCurveLink *pfl;
1173         
1174         tPosePropagate_ModeData modeData;
1175         const int mode = RNA_enum_get(op->ptr, "mode");
1176         
1177         /* sanity checks */
1178         if (ob == NULL) {
1179                 BKE_report(op->reports, RPT_ERROR, "No object to propagate poses for");
1180                 return OPERATOR_CANCELLED;
1181         }
1182         if (act == NULL) {
1183                 BKE_report(op->reports, RPT_ERROR, "No keyframed poses to propagate to");
1184                 return OPERATOR_CANCELLED;
1185         }
1186         
1187         /* isolate F-Curves related to the selected bones */
1188         poseAnim_mapping_get(C, &pflinks, ob, act);
1189         
1190         /* mode-specific data preprocessing (requiring no access to curves) */
1191         if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
1192                 /* get a list of selected markers */
1193                 ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT);
1194         }
1195         else {
1196                 /* assume everything else wants endFrame */
1197                 modeData.end_frame = RNA_float_get(op->ptr, "end_frame");
1198         }
1199         
1200         /* for each bone, perform the copying required */
1201         for (pfl = pflinks.first; pfl; pfl = pfl->next) {
1202                 LinkData *ld;
1203                 
1204                 /* mode-specific data preprocessing (requiring access to all curves) */
1205                 if (mode == POSE_PROPAGATE_SMART_HOLDS) {
1206                         /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
1207                          * from the keyframe that occurs after the current frame
1208                          */
1209                         modeData.end_frame = pose_propagate_get_boneHoldEndFrame(ob, pfl, (float)CFRA);
1210                 }
1211                 
1212                 /* go through propagating pose to keyframes, curve by curve */
1213                 for (ld = pfl->fcurves.first; ld; ld= ld->next)
1214                         pose_propagate_fcurve(op, ob, (FCurve *)ld->data, (float)CFRA, modeData);
1215         }
1216         
1217         /* free temp data */
1218         poseAnim_mapping_free(&pflinks);
1219         
1220         if (mode == POSE_PROPAGATE_SELECTED_MARKERS)
1221                 BLI_freelistN(&modeData.sel_markers);
1222         
1223         /* updates + notifiers */
1224         poseAnim_mapping_refresh(C, scene, ob);
1225         
1226         return OPERATOR_FINISHED;
1227 }
1228
1229 /* --------------------------------- */
1230
1231 void POSE_OT_propagate (wmOperatorType *ot)
1232 {
1233         static EnumPropertyItem terminate_items[]= {
1234                 {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", "Propagate pose to all keyframes after current frame that don't change (Default behaviour)"},
1235                 {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe", "Propagate pose to first keyframe following the current frame only"},
1236                 {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "To Last Keyframe", "Propagate pose to the last keyframe only (i.e. making action cyclic)"},
1237                 {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame", "Propagate pose to all keyframes between current frame and 'Frame' property"},
1238                 {POSE_PROPAGATE_BEFORE_END, "BEFORE_END", 0, "Before Last Keyframe", "Propagate pose to all keyframes from current frame until no more are found"},
1239                 {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"},
1240                 {0, NULL, 0, NULL, NULL}};
1241                 
1242         /* identifiers */
1243         ot->name= "Propagate Pose";
1244         ot->idname= "POSE_OT_propagate";
1245         ot->description= "Copy selected aspects of the current pose to subsequent poses already keyframed";
1246         
1247         /* callbacks */
1248         ot->exec= pose_propagate_exec;
1249         ot->poll= ED_operator_posemode; // XXX: needs selected bones!
1250         
1251         /* flag */
1252         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1253         
1254         /* properties */
1255         // TODO: add "fade out" control for tapering off amount of propagation as time goes by?
1256         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");
1257         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);
1258 }
1259
1260 /* **************************************************** */