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