doxygen: blender/editors tagged.
[blender-staging.git] / source / blender / editors / armature / poseSlide.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung
21  * This is a new part of Blender
22  *
23  * Contributor(s): Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/armature/poseSlide.c
29  *  \ingroup edarmature
30  */
31
32  
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <stddef.h>
36 #include <string.h>
37 #include <math.h>
38 #include <float.h>
39
40 #include "MEM_guardedalloc.h"
41
42 #include "BLI_math.h"
43 #include "BLI_blenlib.h"
44 #include "BLI_dynstr.h"
45 #include "BLI_dlrbTree.h"
46 #include "BLI_utildefines.h"
47
48 #include "DNA_anim_types.h"
49 #include "DNA_armature_types.h"
50 #include "DNA_object_types.h"
51 #include "DNA_scene_types.h"
52
53 #include "BKE_fcurve.h"
54
55 #include "BKE_context.h"
56 #include "BKE_report.h"
57
58 #include "RNA_access.h"
59 #include "RNA_define.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63
64
65
66 #include "ED_armature.h"
67 #include "ED_keyframes_draw.h"
68 #include "ED_screen.h"
69
70 #include "armature_intern.h"
71
72 /* **************************************************** */
73 /* == POSE 'SLIDING' TOOLS == 
74  *
75  * A) Push & Relax, Breakdowner
76  * These tools provide the animator with various capabilities
77  * for interactively controlling the spacing of poses, but also
78  * for 'pushing' and/or 'relaxing' extremes as they see fit.
79  *
80  * B) Pose Sculpting
81  * This is yet to be implemented, but the idea here is to use
82  * sculpting techniques to make it easier to pose rigs by allowing
83  * rigs to be manipulated using a familiar paint-based interface. 
84  */
85 /* **************************************************** */
86 /* A) Push & Relax, Breakdowner */
87
88 /* Temporary data shared between these operators */
89 typedef struct tPoseSlideOp {
90         Scene *scene;           /* current scene */
91         ARegion *ar;            /* region that we're operating in (needed for  */
92         Object *ob;                     /* active object that Pose Info comes from */
93         bArmature *arm;         /* armature for pose */
94         
95         ListBase pfLinks;       /* links between posechannels and f-curves  */
96         DLRBT_Tree keys;        /* binary tree for quicker searching for keyframes (when applicable) */
97         
98         int cframe;                     /* current frame number */
99         int prevFrame;          /* frame before current frame (blend-from) */
100         int nextFrame;          /* frame after current frame (blend-to) */
101         
102         int mode;                       /* sliding mode (ePoseSlide_Modes) */
103         int flag;                       // unused for now, but can later get used for storing runtime settings....
104         
105         float percentage;       /* 0-1 value for determining the influence of whatever is relevant */
106 } tPoseSlideOp;
107
108 /* Pose Sliding Modes */
109 typedef enum ePoseSlide_Modes {
110         POSESLIDE_PUSH  = 0,            /* exaggerate the pose... */
111         POSESLIDE_RELAX,                        /* soften the pose... */
112         POSESLIDE_BREAKDOWN,            /* slide between the endpoint poses, finding a 'soft' spot */
113 } ePoseSlide_Modes;
114
115 /* ------------------------------------ */
116
117 /* operator init */
118 static int pose_slide_init (bContext *C, wmOperator *op, short mode)
119 {
120         tPoseSlideOp *pso;
121         bAction *act= NULL;
122         
123         /* init slide-op data */
124         pso= op->customdata= MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp");
125         
126         /* get info from context */
127         pso->scene= CTX_data_scene(C);
128         pso->ob= ED_object_pose_armature(CTX_data_active_object(C));
129         pso->arm= (pso->ob)? pso->ob->data : NULL;
130         pso->ar= CTX_wm_region(C); /* only really needed when doing modal() */
131         
132         pso->cframe= pso->scene->r.cfra;
133         pso->mode= mode;
134         
135         /* set range info from property values - these may get overridden for the invoke() */
136         pso->percentage= RNA_float_get(op->ptr, "percentage");
137         pso->prevFrame= RNA_int_get(op->ptr, "prev_frame");
138         pso->nextFrame= RNA_int_get(op->ptr, "next_frame");
139         
140         /* check the settings from the context */
141         if (ELEM4(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action))
142                 return 0;
143         else
144                 act= pso->ob->adt->action;
145         
146         /* for each Pose-Channel which gets affected, get the F-Curves for that channel 
147          * and set the relevant transform flags...
148          */
149         poseAnim_mapping_get(C, &pso->pfLinks, pso->ob, act);
150         
151         /* set depsgraph flags */
152                 /* make sure the lock is set OK, unlock can be accidentally saved? */
153         pso->ob->pose->flag |= POSE_LOCKED;
154         pso->ob->pose->flag &= ~POSE_DO_UNLOCK;
155         
156         /* do basic initialise of RB-BST used for finding keyframes, but leave the filling of it up 
157          * to the caller of this (usually only invoke() will do it, to make things more efficient).
158          */
159         BLI_dlrbTree_init(&pso->keys);
160         
161         /* return status is whether we've got all the data we were requested to get */
162         return 1;
163 }
164
165 /* exiting the operator - free data */
166 static void pose_slide_exit(wmOperator *op)
167 {
168         tPoseSlideOp *pso= op->customdata;
169         
170         /* if data exists, clear its data and exit */
171         if (pso) {
172                 /* free the temp pchan links and their data */
173                 poseAnim_mapping_free(&pso->pfLinks);
174                 
175                 /* free RB-BST for keyframes (if it contained data) */
176                 BLI_dlrbTree_free(&pso->keys);
177                 
178                 /* free data itself */
179                 MEM_freeN(pso);
180         }
181         
182         /* cleanup */
183         op->customdata= NULL;
184 }
185
186 /* ------------------------------------ */
187
188 /* helper for apply() / reset() - refresh the data */
189 static void pose_slide_refresh (bContext *C, tPoseSlideOp *pso)
190 {
191         /* wrapper around the generic version, allowing us to add some custom stuff later still */
192         poseAnim_mapping_refresh(C, pso->scene, pso->ob);
193 }
194
195 /* helper for apply() - perform sliding for some 3-element vector */
196 static void pose_slide_apply_vec3 (tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], const char *propName)
197 {
198         LinkData *ld=NULL;
199         char *path=NULL;
200         float cframe;
201         
202         /* get the path to use... */
203         path= BLI_sprintfN("%s.%s", pfl->pchan_path, propName);
204         
205         /* get the current frame number */
206         cframe= (float)pso->cframe;
207         
208         /* using this path, find each matching F-Curve for the variables we're interested in */
209         while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
210                 FCurve *fcu= (FCurve *)ld->data;
211                 float sVal, eVal;
212                 float w1, w2;
213                 int ch;
214                 
215                 /* get keyframe values for endpoint poses to blend with */
216                         /* previous/start */
217                 sVal= evaluate_fcurve(fcu, (float)pso->prevFrame);
218                         /* next/end */
219                 eVal= evaluate_fcurve(fcu, (float)pso->nextFrame);
220                 
221                 /* get channel index */
222                 ch= fcu->array_index;
223                 
224                 /* calculate the relative weights of the endpoints */
225                 if (pso->mode == POSESLIDE_BREAKDOWN) {
226                         /* get weights from the percentage control */
227                         w1= pso->percentage;    /* this must come second */
228                         w2= 1.0f - w1;                  /* this must come first */
229                 }
230                 else {
231                         /*      - these weights are derived from the relative distance of these 
232                          *        poses from the current frame
233                          *      - they then get normalised so that they only sum up to 1
234                          */
235                         float wtot; 
236                         
237                         w1 = cframe - (float)pso->prevFrame;
238                         w2 = (float)pso->nextFrame - cframe;
239                         
240                         wtot = w1 + w2;
241                         w1 = (w1/wtot);
242                         w2 = (w2/wtot);
243                 }
244                 
245                 /* depending on the mode, calculate the new value
246                  *      - in all of these, the start+end values are multiplied by w2 and w1 (respectively),
247                  *        since multiplication in another order would decrease the value the current frame is closer to
248                  */
249                 switch (pso->mode) {
250                         case POSESLIDE_PUSH: /* make the current pose more pronounced */
251                         {
252                                 /* perform a weighted average here, favouring the middle pose 
253                                  *      - numerator should be larger than denominator to 'expand' the result
254                                  *      - perform this weighting a number of times given by the percentage...
255                                  */
256                                 int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
257                                 
258                                 while (iters-- > 0) {
259                                         vec[ch]= ( -((sVal * w2) + (eVal * w1)) + (vec[ch] * 6.0f) ) / 5.0f; 
260                                 }
261                         }
262                                 break;
263                                 
264                         case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */
265                         {
266                                 /* perform a weighted average here, favouring the middle pose 
267                                  *      - numerator should be smaller than denominator to 'relax' the result
268                                  *      - perform this weighting a number of times given by the percentage...
269                                  */
270                                 int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
271                                 
272                                 while (iters-- > 0) {
273                                         vec[ch]= ( ((sVal * w2) + (eVal * w1)) + (vec[ch] * 5.0f) ) / 6.0f;
274                                 }
275                         }
276                                 break;
277                                 
278                         case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */
279                         {
280                                 /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */
281                                 // TODO: make this use some kind of spline interpolation instead?
282                                 vec[ch]= ((sVal * w2) + (eVal * w1));
283                         }
284                                 break;
285                 }
286                 
287         }
288         
289         /* free the temp path we got */
290         MEM_freeN(path);
291 }
292
293 /* helper for apply() - perform sliding for quaternion rotations (using quat blending) */
294 static void pose_slide_apply_quat (tPoseSlideOp *pso, tPChanFCurveLink *pfl)
295 {
296         FCurve *fcu_w=NULL, *fcu_x=NULL, *fcu_y=NULL, *fcu_z=NULL;
297         bPoseChannel *pchan= pfl->pchan;
298         LinkData *ld=NULL;
299         char *path=NULL;
300         float cframe;
301         
302         /* get the path to use - this should be quaternion rotations only (needs care) */
303         path= BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion");
304         
305         /* get the current frame number */
306         cframe= (float)pso->cframe;
307         
308         /* using this path, find each matching F-Curve for the variables we're interested in */
309         while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
310                 FCurve *fcu= (FCurve *)ld->data;
311                 
312                 /* assign this F-Curve to one of the relevant pointers... */
313                 switch (fcu->array_index) {
314                         case 3: /* z */
315                                 fcu_z= fcu;
316                                 break;
317                         case 2: /* y */
318                                 fcu_y= fcu;
319                                 break;
320                         case 1: /* x */
321                                 fcu_x= fcu;
322                                 break;
323                         case 0: /* w */
324                                 fcu_w= fcu;
325                                 break;
326                 }
327         }
328         
329         /* only if all channels exist, proceed */
330         if (fcu_w && fcu_x && fcu_y && fcu_z) {
331                 float quat_prev[4], quat_next[4];
332                 
333                 /* get 2 quats */
334                 quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrame);
335                 quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrame);
336                 quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrame);
337                 quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrame);
338                 
339                 quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrame);
340                 quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrame);
341                 quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrame);
342                 quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrame);
343                 
344                 /* perform blending */
345                 if (pso->mode == POSESLIDE_BREAKDOWN) {
346                         /* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */
347                         interp_qt_qtqt(pchan->quat, quat_prev, quat_next, pso->percentage);
348                 }
349                 else if (pso->mode == POSESLIDE_PUSH) {
350                         float quat_diff[4], quat_orig[4];
351                         
352                         /* calculate the delta transform from the previous to the current */
353                         // TODO: investigate ways to favour one transform more?
354                         sub_qt_qtqt(quat_diff, pchan->quat, quat_prev);
355                         
356                         /* make a copy of the original rotation */
357                         QUATCOPY(quat_orig, pchan->quat);
358                         
359                         /* increase the original by the delta transform, by an amount determined by percentage */
360                         add_qt_qtqt(pchan->quat, quat_orig, quat_diff, pso->percentage);
361                 }
362                 else {
363                         float quat_interp[4], quat_orig[4];
364                         int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
365                         
366                         /* perform this blending several times until a satisfactory result is reached */
367                         while (iters-- > 0) {
368                                 /* calculate the interpolation between the endpoints */
369                                 interp_qt_qtqt(quat_interp, quat_prev, quat_next, (cframe-pso->prevFrame) / (pso->nextFrame-pso->prevFrame) );
370                                 
371                                 /* make a copy of the original rotation */
372                                 QUATCOPY(quat_orig, pchan->quat);
373                                 
374                                 /* tricky interpolations - blending between original and new */
375                                 interp_qt_qtqt(pchan->quat, quat_orig, quat_interp, 1.0f/6.0f);
376                         }
377                 }
378         }
379         
380         /* free the path now */
381         MEM_freeN(path);
382 }
383
384 /* apply() - perform the pose sliding based on weighting various poses */
385 static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
386 {
387         tPChanFCurveLink *pfl;
388         
389         /* sanitise the frame ranges */
390         if (pso->prevFrame == pso->nextFrame) {
391                 /* move out one step either side */
392                 pso->prevFrame--;
393                 pso->nextFrame++;
394         }
395         
396         /* for each link, handle each set of transforms */
397         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
398                 /* valid transforms for each PoseChannel should have been noted already 
399                  *      - sliding the pose should be a straightforward exercise for location+rotation, 
400                  *        but rotations get more complicated since we may want to use quaternion blending 
401                  *        for quaternions instead...
402                  */
403                 bPoseChannel *pchan= pfl->pchan;
404                  
405                 if (pchan->flag & POSE_LOC) {
406                         /* calculate these for the 'location' vector, and use location curves */
407                         pose_slide_apply_vec3(pso, pfl, pchan->loc, "location");
408                 }
409                 
410                 if (pchan->flag & POSE_SIZE) {
411                         /* calculate these for the 'scale' vector, and use scale curves */
412                         pose_slide_apply_vec3(pso, pfl, pchan->size, "scale");
413                 }
414                 
415                 if (pchan->flag & POSE_ROT) {
416                         /* everything depends on the rotation mode */
417                         if (pchan->rotmode > 0) {
418                                 /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
419                                 pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler");
420                         }
421                         else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
422                                 // TODO: need to figure out how to do this!
423                         }
424                         else {
425                                 /* quaternions - use quaternion blending */
426                                 pose_slide_apply_quat(pso, pfl);
427                         }
428                 }
429         }
430         
431         /* depsgraph updates + redraws */
432         pose_slide_refresh(C, pso);
433 }
434
435 /* perform autokeyframing after changes were made + confirmed */
436 static void pose_slide_autoKeyframe (bContext *C, tPoseSlideOp *pso)
437 {
438         /* wrapper around the generic call */
439         poseAnim_mapping_autoKeyframe(C, pso->scene, pso->ob, &pso->pfLinks, (float)pso->cframe);
440 }
441
442 /* reset changes made to current pose */
443 static void pose_slide_reset (tPoseSlideOp *pso)
444 {
445         /* wrapper around the generic call, so that custom stuff can be added later */
446         poseAnim_mapping_reset(&pso->pfLinks);
447 }
448
449 /* ------------------------------------ */
450
451 /* common code for invoke() methods */
452 static int pose_slide_invoke_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
453 {
454         tPChanFCurveLink *pfl;
455         AnimData *adt= pso->ob->adt;
456         wmWindow *win= CTX_wm_window(C);
457         
458         /* for each link, add all its keyframes to the search tree */
459         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
460                 LinkData *ld;
461                 
462                 /* do this for each F-Curve */
463                 for (ld= pfl->fcurves.first; ld; ld= ld->next) {
464                         FCurve *fcu= (FCurve *)ld->data;
465                         fcurve_to_keylist(adt, fcu, &pso->keys, NULL);
466                 }
467         }
468         
469         /* consolidate these keyframes, and figure out the nearest ones */
470         BLI_dlrbTree_linkedlist_sync(&pso->keys);
471         
472                 /* cancel if no keyframes found... */
473         if (pso->keys.root) {
474                 ActKeyColumn *ak;
475                 float cframe= (float)pso->cframe;
476                 
477                 /* firstly, check if the current frame is a keyframe... */
478                 ak= (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe);
479                 
480                 if (ak == NULL) {
481                         /* current frame is not a keyframe, so search */
482                         ActKeyColumn *pk= (ActKeyColumn *)BLI_dlrbTree_search_prev(&pso->keys, compare_ak_cfraPtr, &cframe);
483                         ActKeyColumn *nk= (ActKeyColumn *)BLI_dlrbTree_search_next(&pso->keys, compare_ak_cfraPtr, &cframe);
484                         
485                         /* new set the frames */
486                                 /* prev frame */
487                         pso->prevFrame= (pk)? (pk->cfra) : (pso->cframe - 1);
488                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
489                                 /* next frame */
490                         pso->nextFrame= (nk)? (nk->cfra) : (pso->cframe + 1);
491                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
492                 }
493                 else {
494                         /* current frame itself is a keyframe, so just take keyframes on either side */
495                                 /* prev frame */
496                         pso->prevFrame= (ak->prev)? (ak->prev->cfra) : (pso->cframe - 1);
497                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
498                                 /* next frame */
499                         pso->nextFrame= (ak->next)? (ak->next->cfra) : (pso->cframe + 1);
500                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
501                 }
502         }
503         else {
504                 BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between.");
505                 return OPERATOR_CANCELLED;
506         }
507         
508         /* initial apply for operator... */
509         // TODO: need to calculate percentage for initial round too...
510         pose_slide_apply(C, pso);
511         
512         /* depsgraph updates + redraws */
513         pose_slide_refresh(C, pso);
514         
515         /* set cursor to indicate modal */
516         WM_cursor_modal(win, BC_EW_SCROLLCURSOR);
517         
518         /* add a modal handler for this operator */
519         WM_event_add_modal_handler(C, op);
520         return OPERATOR_RUNNING_MODAL;
521 }
522
523 /* common code for modal() */
524 static int pose_slide_modal (bContext *C, wmOperator *op, wmEvent *evt)
525 {
526         tPoseSlideOp *pso= op->customdata;
527         wmWindow *win= CTX_wm_window(C);
528         
529         switch (evt->type) {
530                 case LEFTMOUSE: /* confirm */
531                 {
532                         /* return to normal cursor */
533                         WM_cursor_restore(win);
534                         
535                         /* insert keyframes as required... */
536                         pose_slide_autoKeyframe(C, pso);
537                         pose_slide_exit(op);
538                         
539                         /* done! */
540                         return OPERATOR_FINISHED;
541                 }
542                 
543                 case ESCKEY:    /* cancel */
544                 case RIGHTMOUSE: 
545                 {
546                         /* return to normal cursor */
547                         WM_cursor_restore(win);
548                         
549                         /* reset transforms back to original state */
550                         pose_slide_reset(pso);
551                         
552                         /* depsgraph updates + redraws */
553                         pose_slide_refresh(C, pso);
554                         
555                         /* clean up temp data */
556                         pose_slide_exit(op);
557                         
558                         /* cancelled! */
559                         return OPERATOR_CANCELLED;
560                 }
561                         
562                 case MOUSEMOVE: /* calculate new position */
563                 {
564                         /* calculate percentage based on position of mouse (we only use x-axis for now.
565                          * since this is more conveninent for users to do), and store new percentage value 
566                          */
567                         pso->percentage= (evt->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx);
568                         RNA_float_set(op->ptr, "percentage", pso->percentage);
569                         
570                         /* reset transforms (to avoid accumulation errors) */
571                         pose_slide_reset(pso);
572                         
573                         /* apply... */
574                         pose_slide_apply(C, pso);
575                 }
576                         break;
577                         
578                 default: /* unhandled event (maybe it was some view manip? */
579                         /* allow to pass through */
580                         return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH;
581         }
582         
583         /* still running... */
584         return OPERATOR_RUNNING_MODAL;
585 }
586
587 /* common code for cancel() */
588 static int pose_slide_cancel (bContext *UNUSED(C), wmOperator *op)
589 {
590         /* cleanup and done */
591         pose_slide_exit(op);
592         return OPERATOR_CANCELLED;
593 }
594
595 /* common code for exec() methods */
596 static int pose_slide_exec_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
597 {
598         /* settings should have been set up ok for applying, so just apply! */
599         pose_slide_apply(C, pso);
600         
601         /* insert keyframes if needed */
602         pose_slide_autoKeyframe(C, pso);
603         
604         /* cleanup and done */
605         pose_slide_exit(op);
606         
607         return OPERATOR_FINISHED;
608 }
609
610 /* common code for defining RNA properties */
611 static void pose_slide_opdef_properties (wmOperatorType *ot)
612 {
613         RNA_def_int(ot->srna, "prev_frame", 0, MINAFRAME, MAXFRAME, "Previous Keyframe", "Frame number of keyframe immediately before the current frame.", 0, 50);
614         RNA_def_int(ot->srna, "next_frame", 0, MINAFRAME, MAXFRAME, "Next Keyframe", "Frame number of keyframe immediately after the current frame.", 0, 50);
615         RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for the sliding operation", 0.3, 0.7);
616 }
617
618 /* ------------------------------------ */
619
620 /* invoke() - for 'push' mode */
621 static int pose_slide_push_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
622 {
623         tPoseSlideOp *pso;
624         
625         /* initialise data  */
626         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
627                 pose_slide_exit(op);
628                 return OPERATOR_CANCELLED;
629         }
630         else
631                 pso= op->customdata;
632         
633         /* do common setup work */
634         return pose_slide_invoke_common(C, op, pso);
635 }
636
637 /* exec() - for push */
638 static int pose_slide_push_exec (bContext *C, wmOperator *op)
639 {
640         tPoseSlideOp *pso;
641         
642         /* initialise data (from RNA-props) */
643         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
644                 pose_slide_exit(op);
645                 return OPERATOR_CANCELLED;
646         }
647         else
648                 pso= op->customdata;
649                 
650         /* do common exec work */
651         return pose_slide_exec_common(C, op, pso);
652 }
653
654 void POSE_OT_push (wmOperatorType *ot)
655 {
656         /* identifiers */
657         ot->name= "Push Pose";
658         ot->idname= "POSE_OT_push";
659         ot->description= "Exaggerate the current pose";
660         
661         /* callbacks */
662         ot->exec= pose_slide_push_exec;
663         ot->invoke= pose_slide_push_invoke;
664         ot->modal= pose_slide_modal;
665         ot->cancel= pose_slide_cancel;
666         ot->poll= ED_operator_posemode;
667         
668         /* flags */
669         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
670         
671         /* Properties */
672         pose_slide_opdef_properties(ot);
673 }
674
675 /* ........................ */
676
677 /* invoke() - for 'relax' mode */
678 static int pose_slide_relax_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
679 {
680         tPoseSlideOp *pso;
681         
682         /* initialise data  */
683         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
684                 pose_slide_exit(op);
685                 return OPERATOR_CANCELLED;
686         }
687         else
688                 pso= op->customdata;
689         
690         /* do common setup work */
691         return pose_slide_invoke_common(C, op, pso);
692 }
693
694 /* exec() - for relax */
695 static int pose_slide_relax_exec (bContext *C, wmOperator *op)
696 {
697         tPoseSlideOp *pso;
698         
699         /* initialise data (from RNA-props) */
700         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
701                 pose_slide_exit(op);
702                 return OPERATOR_CANCELLED;
703         }
704         else
705                 pso= op->customdata;
706                 
707         /* do common exec work */
708         return pose_slide_exec_common(C, op, pso);
709 }
710
711 void POSE_OT_relax (wmOperatorType *ot)
712 {
713         /* identifiers */
714         ot->name= "Relax Pose";
715         ot->idname= "POSE_OT_relax";
716         ot->description= "Make the current pose more similar to its surrounding ones";
717         
718         /* callbacks */
719         ot->exec= pose_slide_relax_exec;
720         ot->invoke= pose_slide_relax_invoke;
721         ot->modal= pose_slide_modal;
722         ot->cancel= pose_slide_cancel;
723         ot->poll= ED_operator_posemode;
724         
725         /* flags */
726         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
727         
728         /* Properties */
729         pose_slide_opdef_properties(ot);
730 }
731
732 /* ........................ */
733
734 /* invoke() - for 'breakdown' mode */
735 static int pose_slide_breakdown_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
736 {
737         tPoseSlideOp *pso;
738         
739         /* initialise data  */
740         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
741                 pose_slide_exit(op);
742                 return OPERATOR_CANCELLED;
743         }
744         else
745                 pso= op->customdata;
746         
747         /* do common setup work */
748         return pose_slide_invoke_common(C, op, pso);
749 }
750
751 /* exec() - for breakdown */
752 static int pose_slide_breakdown_exec (bContext *C, wmOperator *op)
753 {
754         tPoseSlideOp *pso;
755         
756         /* initialise data (from RNA-props) */
757         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
758                 pose_slide_exit(op);
759                 return OPERATOR_CANCELLED;
760         }
761         else
762                 pso= op->customdata;
763                 
764         /* do common exec work */
765         return pose_slide_exec_common(C, op, pso);
766 }
767
768 void POSE_OT_breakdown (wmOperatorType *ot)
769 {
770         /* identifiers */
771         ot->name= "Pose Breakdowner";
772         ot->idname= "POSE_OT_breakdown";
773         ot->description= "Create a suitable breakdown pose on the current frame";
774         
775         /* callbacks */
776         ot->exec= pose_slide_breakdown_exec;
777         ot->invoke= pose_slide_breakdown_invoke;
778         ot->modal= pose_slide_modal;
779         ot->cancel= pose_slide_cancel;
780         ot->poll= ED_operator_posemode;
781         
782         /* flags */
783         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
784         
785         /* Properties */
786         pose_slide_opdef_properties(ot);
787 }
788
789 /* **************************************************** */