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