2.5 - Pose Relax/Push improvements
[blender.git] / source / blender / editors / armature / poseSlide.c
1 /**
2  * $Id: poseSlide.c 23179 2009-09-13 12:34:00Z aligorith $
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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_arithb.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         KeyingSet *ks_loc;      /* builtin KeyingSet for keyframing locations */
112         KeyingSet *ks_rot;      /* builtin KeyingSet for keyframing rotations */
113         KeyingSet *ks_scale;/* builtin KeyingSet for keyframing scale */
114         
115         int cframe;                     /* current frame number */
116         int prevFrame;          /* frame before current frame (blend-from) */
117         int nextFrame;          /* frame after current frame (blend-to) */
118         
119         int mode;                       /* sliding mode (ePoseSlide_Modes) */
120         int flag;                       // unused for now, but can later get used for storing runtime settings....
121         
122         float percentage;       /* 0-1 value for determining the influence of whatever is relevant */
123 } tPoseSlideOp;
124
125 /* Pose Sliding Modes */
126 typedef enum ePoseSlide_Modes {
127         POSESLIDE_PUSH  = 0,            /* exaggerate the pose... */
128         POSESLIDE_RELAX,                        /* soften the pose... */
129         POSESLIDE_BREAKDOWN,            /* slide between the endpoint poses, finding a 'soft' spot */
130 } ePoseSlide_Modes;
131
132 /* Temporary data linking PoseChannels with the F-Curves they affect */
133 typedef struct tPChanFCurveLink {
134         struct tPChanFCurveLink *next, *prev;
135         
136         ListBase fcurves;               /* F-Curves for this PoseChannel */
137         bPoseChannel *pchan;    /* Pose Channel which data is attached to */
138         
139         char *pchan_path;               /* RNA Path to this Pose Channel (needs to be freed when we're done) */
140         
141         float oldloc[3];                /* transform values at start of operator (to be restored before each modal step) */
142         float oldrot[3];
143         float oldscale[3];
144         float oldquat[4];
145 } tPChanFCurveLink;
146
147 /* ------------------------------------ */
148
149 /* operator init */
150 static int pose_slide_init (bContext *C, wmOperator *op, short mode)
151 {
152         tPoseSlideOp *pso;
153         bAction *act= NULL;
154         
155         /* init slide-op data */
156         pso= op->customdata= MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp");
157         
158         /* get info from context */
159         pso->scene= CTX_data_scene(C);
160         pso->ob= CTX_data_active_object(C);
161         pso->arm= (pso->ob)? pso->ob->data : NULL;
162         pso->ar= CTX_wm_region(C); /* only really needed when doing modal() */
163         
164         pso->cframe= pso->scene->r.cfra;
165         pso->mode= mode;
166         
167         /* set range info from property values - these may get overridden for the invoke() */
168         pso->percentage= RNA_float_get(op->ptr, "percentage");
169         pso->prevFrame= RNA_int_get(op->ptr, "prev_frame");
170         pso->nextFrame= RNA_int_get(op->ptr, "next_frame");
171         
172         /* check the settings from the context */
173         if (ELEM4(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action))
174                 return 0;
175         else
176                 act= pso->ob->adt->action;
177         
178         /* for each Pose-Channel which gets affected, get the F-Curves for that channel 
179          * and set the relevant transform flags...
180          */
181         CTX_DATA_BEGIN(C, bPoseChannel*, pchan, selected_pchans) 
182         {
183                 ListBase curves = {NULL, NULL};
184                 int transFlags = action_get_item_transforms(act, pso->ob, pchan, &curves);
185                 
186                 pchan->flag &= ~(POSE_LOC|POSE_ROT|POSE_SIZE);
187                 
188                 /* check if any transforms found... */
189                 if (transFlags) {
190                         /* make new linkage data */
191                         tPChanFCurveLink *pfl= MEM_callocN(sizeof(tPChanFCurveLink), "tPChanFCurveLink");
192                         PointerRNA ptr;
193                         
194                         pfl->fcurves= curves;
195                         pfl->pchan= pchan;
196                         
197                         /* get the RNA path to this pchan - this needs to be freed! */
198                         RNA_pointer_create((ID *)pso->ob, &RNA_PoseChannel, pchan, &ptr);
199                         pfl->pchan_path= RNA_path_from_ID_to_struct(&ptr);
200                         
201                         /* add linkage data to operator data */
202                         BLI_addtail(&pso->pfLinks, pfl);
203                         
204                         /* set pchan's transform flags */
205                         if (transFlags & ACT_TRANS_LOC)
206                                 pchan->flag |= POSE_LOC;
207                         if (transFlags & ACT_TRANS_ROT)
208                                 pchan->flag |= POSE_ROT;
209                         if (transFlags & ACT_TRANS_SCALE)
210                                 pchan->flag |= POSE_SIZE;
211                                 
212                         /* store current transforms */
213                         VECCOPY(pfl->oldloc, pchan->loc);
214                         VECCOPY(pfl->oldrot, pchan->eul);
215                         VECCOPY(pfl->oldscale, pchan->size);
216                         QUATCOPY(pfl->oldquat, pchan->quat);
217                 }
218         }
219         CTX_DATA_END;
220         
221         /* set depsgraph flags */
222                 /* make sure the lock is set OK, unlock can be accidentally saved? */
223         pso->ob->pose->flag |= POSE_LOCKED;
224         pso->ob->pose->flag &= ~POSE_DO_UNLOCK;
225         
226         /* do basic initialise of RB-BST used for finding keyframes, but leave the filling of it up 
227          * to the caller of this (usually only invoke() will do it, to make things more efficient).
228          */
229         BLI_dlrbTree_init(&pso->keys);
230         
231         /* get builtin KeyingSets */
232         pso->ks_loc= ANIM_builtin_keyingset_get_named(NULL, "Location");
233         pso->ks_rot= ANIM_builtin_keyingset_get_named(NULL, "Rotation");
234         pso->ks_scale= ANIM_builtin_keyingset_get_named(NULL, "Scaling");
235         
236         /* return status is whether we've got all the data we were requested to get */
237         return 1;
238 }
239
240 /* exiting the operator - free data */
241 static void pose_slide_exit (bContext *C, wmOperator *op)
242 {
243         tPoseSlideOp *pso= op->customdata;
244         
245         /* if data exists, clear its data and exit */
246         if (pso) {
247                 tPChanFCurveLink *pfl, *pfln=NULL;
248                 
249                 /* free the temp pchan links and their data */
250                 for (pfl= pso->pfLinks.first; pfl; pfl= pfln) {
251                         pfln= pfl->next;
252                         
253                         /* free list of F-Curve reference links */
254                         BLI_freelistN(&pfl->fcurves);
255                         
256                         /* free pchan RNA Path */
257                         MEM_freeN(pfl->pchan_path);
258                         
259                         /* free link itself */
260                         BLI_freelinkN(&pso->pfLinks, pfl);
261                 }
262                 
263                 /* free RB-BST for keyframes (if it contained data) */
264                 BLI_dlrbTree_free(&pso->keys);
265                 
266                 /* free data itself */
267                 MEM_freeN(pso);
268         }
269         
270         /* cleanup */
271         op->customdata= NULL;
272 }
273
274 /* ------------------------------------ */
275
276 /* helper for apply() / reset() - refresh the data */
277 static void pose_slide_refresh (bContext *C, tPoseSlideOp *pso)
278 {
279         /* old optimize trick... this enforces to bypass the depgraph 
280          *      - note: code copied from transform_generics.c -> recalcData()
281          */
282         // FIXME: shouldn't this use the builtin stuff?
283         if ((pso->arm->flag & ARM_DELAYDEFORM)==0)
284                 DAG_id_flush_update(&pso->ob->id, OB_RECALC_DATA);  /* sets recalc flags */
285         else
286                 where_is_pose(pso->scene, pso->ob);
287         
288         /* note, notifier might evolve */
289         WM_event_add_notifier(C, NC_OBJECT|ND_POSE, pso->ob);
290         WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); 
291 }
292
293 /* helper for apply() callabcks - find the next F-Curve with matching path... */
294 static LinkData *find_next_fcurve_link (ListBase *fcuLinks, LinkData *prev, char *path)
295 {
296         LinkData *first= (prev)? prev->next : (fcuLinks)? fcuLinks->first : NULL;
297         LinkData *ld;
298         
299         /* check each link to see if the linked F-Curve has a matching path */
300         for (ld= first; ld; ld= ld->next) {
301                 FCurve *fcu= (FCurve *)ld->data;
302                 
303                 /* check if paths match */
304                 if (strcmp(path, fcu->rna_path) == 0)
305                         return ld;
306         }       
307         
308         /* none found */
309         return NULL;
310 }
311
312 /* helper for apply() - perform sliding for some 3-element vector */
313 static void pose_slide_apply_vec3 (tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], char *propName)
314 {
315         LinkData *ld=NULL;
316         char *path=NULL;
317         float cframe;
318         
319         /* get the path to use... */
320         path= BLI_sprintfN("%s.%s", pfl->pchan_path, propName);
321         
322         /* get the current frame number */
323         cframe= (float)pso->cframe;
324         
325         /* using this path, find each matching F-Curve for the variables we're interested in */
326         while ( (ld= find_next_fcurve_link(&pfl->fcurves, ld, path)) ) {
327                 FCurve *fcu= (FCurve *)ld->data;
328                 float sVal, eVal;
329                 float w1, w2;
330                 int ch;
331                 
332                 /* get keyframe values for endpoint poses to blend with */
333                         /* previous/start */
334                 sVal= evaluate_fcurve(fcu, (float)pso->prevFrame);
335                         /* next/end */
336                 eVal= evaluate_fcurve(fcu, (float)pso->nextFrame);
337                 
338                 /* get channel index */
339                 ch= fcu->array_index;
340                 
341                 /* calculate the relative weights of the endpoints */
342                 if (pso->mode == POSESLIDE_BREAKDOWN) {
343                         /* get weights from the percentage control */
344                         w1= pso->percentage;    /* this must come second */
345                         w2= 1.0f - w1;                  /* this must come first */
346                 }
347                 else {
348                         /*      - these weights are derived from the relative distance of these 
349                          *        poses from the current frame
350                          *      - they then get normalised so that they only sum up to 1
351                          */
352                         float wtot; 
353                         
354                         w1 = cframe - (float)pso->prevFrame;
355                         w2 = (float)pso->nextFrame - cframe;
356                         
357                         wtot = w1 + w2;
358                         w1 = (w1/wtot);
359                         w2 = (w2/wtot);
360                 }
361                 
362                 /* depending on the mode, calculate the new value
363                  *      - in all of these, the start+end values are multiplied by w2 and w1 (respectively),
364                  *        since multiplication in another order would decrease the value the current frame is closer to
365                  */
366                 switch (pso->mode) {
367                         case POSESLIDE_PUSH: /* make the current pose more pronounced */
368                         {
369                                 /* perform a weighted average here, favouring the middle pose 
370                                  *      - numerator should be larger than denominator to 'expand' the result
371                                  *      - perform this weighting a number of times given by the percentage...
372                                  */
373                                 int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
374                                 
375                                 while (iters-- > 0) {
376                                         vec[ch]= ( -((sVal * w2) + (eVal * w1)) + (vec[ch] * 6.0f) ) / 5.0f; 
377                                 }
378                         }
379                                 break;
380                                 
381                         case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */
382                         {
383                                 /* perform a weighted average here, favouring the middle pose 
384                                  *      - numerator should be smaller than denominator to 'relax' the result
385                                  *      - perform this weighting a number of times given by the percentage...
386                                  */
387                                 int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
388                                 
389                                 while (iters-- > 0) {
390                                         vec[ch]= ( ((sVal * w2) + (eVal * w1)) + (vec[ch] * 5.0f) ) / 6.0f;
391                                 }
392                         }
393                                 break;
394                                 
395                         case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */
396                         {
397                                 /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */
398                                 // TODO: make this use some kind of spline interpolation instead?
399                                 vec[ch]= ((sVal * w2) + (eVal * w1));
400                         }
401                                 break;
402                 }
403                 
404         }
405         
406         /* free the temp path we got */
407         MEM_freeN(path);
408 }
409
410 /* helper for apply() - perform sliding for quaternion rotations (using quat blending) */
411 static void pose_slide_apply_quat (tPoseSlideOp *pso, tPChanFCurveLink *pfl)
412 {
413         // TODO: this is quite evil stuff...
414 #if 0 // XXX port...
415         /* get 2 quats */
416         quat_prev[0] = eval_icu(icu_w, frame_prev);
417         quat_prev[1] = eval_icu(icu_x, frame_prev);
418         quat_prev[2] = eval_icu(icu_y, frame_prev);
419         quat_prev[3] = eval_icu(icu_z, frame_prev);
420         
421         quat_next[0] = eval_icu(icu_w, frame_next);
422         quat_next[1] = eval_icu(icu_x, frame_next);
423         quat_next[2] = eval_icu(icu_y, frame_next);
424         quat_next[3] = eval_icu(icu_z, frame_next);
425         
426 #if 0
427         /* apply the setting, completely smooth */
428         QuatInterpol(pchan->quat, quat_prev, quat_next, (framef-frame_prev) / (frame_next-frame_prev) );
429 #else
430         /* tricky interpolation */
431         QuatInterpol(quat_interp, quat_prev, quat_next, (framef-frame_prev) / (frame_next-frame_prev) );
432         QUATCOPY(quat_orig, pchan->quat);
433         QuatInterpol(pchan->quat, quat_orig, quat_interp, 1.0f/6.0f);
434         /* done */
435 #endif
436 #endif
437 }
438
439 /* apply() - perform the pose sliding based on weighting various poses */
440 static void pose_slide_apply (bContext *C, wmOperator *op, tPoseSlideOp *pso)
441 {
442         tPChanFCurveLink *pfl;
443         
444         /* sanitise the frame ranges */
445         if (pso->prevFrame == pso->nextFrame) {
446                 /* move out one step either side */
447                 pso->prevFrame--;
448                 pso->nextFrame++;
449         }
450         
451         /* for each link, handle each set of transforms */
452         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
453                 /* valid transforms for each PoseChannel should have been noted already 
454                  *      - sliding the pose should be a straightforward exercise for location+rotation, 
455                  *        but rotations get more complicated since we may want to use quaternion blending 
456                  *        for quaternions instead...
457                  */
458                 bPoseChannel *pchan= pfl->pchan;
459                  
460                 if (pchan->flag & POSE_LOC) {
461                         /* calculate these for the 'location' vector, and use location curves */
462                         pose_slide_apply_vec3(pso, pfl, pchan->loc, "location");
463                 }
464                 
465                 if (pchan->flag & POSE_SIZE) {
466                         /* calculate these for the 'scale' vector, and use scale curves */
467                         pose_slide_apply_vec3(pso, pfl, pchan->size, "scale");
468                 }
469                 
470                 if (pchan->flag & POSE_ROT) {
471                         /* everything depends on the rotation mode */
472                         if (pchan->rotmode > 0) {
473                                 /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
474                                 pose_slide_apply_vec3(pso, pfl, pchan->eul, "euler_rotation");
475                         }
476                         else if (pchan->rotmode == PCHAN_ROT_AXISANGLE) {
477                                 // TODO: need to figure out how to do this!
478                         }
479                         else {
480                                 /* quaternions - use quaternion blending */
481                                 pose_slide_apply_quat(pso, pfl);
482                         }
483                 }
484         }
485         
486         /* depsgraph updates + redraws */
487         pose_slide_refresh(C, pso);
488 }
489
490 /* perform autokeyframing after changes were made + confirmed */
491 static void pose_slide_autoKeyframe (bContext *C, tPoseSlideOp *pso)
492 {
493         /* insert keyframes as necessary if autokeyframing */
494         if (autokeyframe_cfra_can_key(pso->scene, &pso->ob->id)) {
495                 bCommonKeySrc cks;
496                 ListBase dsources = {&cks, &cks};
497                 tPChanFCurveLink *pfl;
498                 
499                 /* init common-key-source for use by KeyingSets */
500                 memset(&cks, 0, sizeof(bCommonKeySrc));
501                 cks.id= &pso->ob->id;
502                 
503                 /* iterate over each pose-channel affected, applying the changes */
504                 for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
505                         bPoseChannel *pchan= pfl->pchan;
506                         /* init cks for this PoseChannel, then use the relative KeyingSets to keyframe it */
507                         cks.pchan= pchan;
508                         
509                         /* insert keyframes */
510                         if (pchan->flag & POSE_LOC)
511                                 modify_keyframes(C, &dsources, NULL, pso->ks_loc, MODIFYKEY_MODE_INSERT, (float)pso->cframe);
512                         if (pchan->flag & POSE_ROT)
513                                 modify_keyframes(C, &dsources, NULL, pso->ks_rot, MODIFYKEY_MODE_INSERT, (float)pso->cframe);
514                         if (pchan->flag & POSE_SIZE)
515                                 modify_keyframes(C, &dsources, NULL, pso->ks_scale, MODIFYKEY_MODE_INSERT, (float)pso->cframe);
516                 }
517         }
518 }
519
520 /* reset changes made to current pose */
521 static void pose_slide_reset (bContext *C, tPoseSlideOp *pso)
522 {
523         tPChanFCurveLink *pfl;
524         
525         /* iterate over each pose-channel affected, restoring all channels to their original values */
526         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
527                 bPoseChannel *pchan= pfl->pchan;
528                 
529                 /* just copy all the values over regardless of whether they changed or not */
530                 VECCOPY(pchan->loc, pfl->oldloc);
531                 VECCOPY(pchan->eul, pfl->oldrot);
532                 VECCOPY(pchan->size, pfl->oldscale);
533                 QUATCOPY(pchan->quat, pfl->oldquat);
534         }
535 }
536
537 /* ------------------------------------ */
538
539 /* common code for invoke() methods */
540 static int pose_slide_invoke_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
541 {
542         tPChanFCurveLink *pfl;
543         AnimData *adt= pso->ob->adt;
544         wmWindow *win= CTX_wm_window(C);
545         
546         /* for each link, add all its keyframes to the search tree */
547         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
548                 LinkData *ld;
549                 
550                 /* do this for each F-Curve */
551                 for (ld= pfl->fcurves.first; ld; ld= ld->next) {
552                         FCurve *fcu= (FCurve *)ld->data;
553                         fcurve_to_keylist(adt, fcu, &pso->keys, NULL);
554                 }
555         }
556         
557         /* consolidate these keyframes, and figure out the nearest ones */
558         BLI_dlrbTree_linkedlist_sync(&pso->keys);
559         
560                 /* cancel if no keyframes found... */
561         if (pso->keys.root) {
562                 ActKeyColumn *ak;
563                 
564                 /* firstly, check if the current frame is a keyframe... */
565                 ak= cfra_find_actkeycolumn(pso->keys.root, pso->cframe);
566                 
567                 if (ak == NULL) {
568                         /* current frame is not a keyframe, so search */
569                         ActKeyColumn *pk= cfra_find_nearest_next_ak(pso->keys.root, pso->cframe, 0);
570                         ActKeyColumn *nk= cfra_find_nearest_next_ak(pso->keys.root, pso->cframe, 1);
571                         
572                         /* check if we found good keyframes */
573                         if ((pk == nk) && (pk != NULL)) {
574                                 if (pk->cfra < pso->cframe)
575                                         nk= nk->next;
576                                 else if (nk->cfra > pso->cframe)
577                                         pk= pk->prev;
578                         }
579                         
580                         /* new set the frames */
581                                 /* prev frame */
582                         pso->prevFrame= (pk)? (pk->cfra) : (pso->cframe - 1);
583                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
584                                 /* next frame */
585                         pso->nextFrame= (nk)? (nk->cfra) : (pso->cframe + 1);
586                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
587                 }
588                 else {
589                         /* current frame itself is a keyframe, so just take keyframes on either side */
590                                 /* prev frame */
591                         pso->prevFrame= (ak->prev)? (ak->prev->cfra) : (pso->cframe - 1);
592                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
593                                 /* next frame */
594                         pso->nextFrame= (ak->next)? (ak->next->cfra) : (pso->cframe + 1);
595                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
596                 }
597         }
598         else {
599                 BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between.");
600                 return OPERATOR_CANCELLED;
601         }
602         
603         /* initial apply for operator... */
604         // TODO: need to calculate percentage for initial round too...
605         pose_slide_apply(C, op, pso);
606         
607         /* depsgraph updates + redraws */
608         pose_slide_refresh(C, pso);
609         
610         /* set cursor to indicate modal */
611         WM_cursor_modal(win, BC_EW_SCROLLCURSOR);
612         
613         /* add a modal handler for this operator */
614         WM_event_add_modal_handler(C, op);
615         return OPERATOR_RUNNING_MODAL;
616 }
617
618 /* common code for modal() */
619 static int pose_slide_modal (bContext *C, wmOperator *op, wmEvent *evt)
620 {
621         tPoseSlideOp *pso= op->customdata;
622         wmWindow *win= CTX_wm_window(C);
623         
624         switch (evt->type) {
625                 case LEFTMOUSE: /* confirm */
626                 {
627                         /* return to normal cursor */
628                         WM_cursor_restore(win);
629                         
630                         /* insert keyframes as required... */
631                         pose_slide_autoKeyframe(C, pso);
632                         pose_slide_exit(C, op);
633                         
634                         /* done! */
635                         return OPERATOR_FINISHED;
636                 }
637                 
638                 case ESCKEY:    /* cancel */
639                 case RIGHTMOUSE: 
640                 {
641                         /* return to normal cursor */
642                         WM_cursor_restore(win);
643                         
644                         /* reset transforms back to original state */
645                         pose_slide_reset(C, pso);
646                         
647                         /* depsgraph updates + redraws */
648                         pose_slide_refresh(C, pso);
649                         
650                         /* clean up temp data */
651                         pose_slide_exit(C, op);
652                         
653                         /* cancelled! */
654                         return OPERATOR_CANCELLED;
655                 }
656                         
657                 case MOUSEMOVE: /* calculate new position */
658                 {
659                         /* calculate percentage based on position of mouse (we only use x-axis for now.
660                          * since this is more conveninent for users to do), and store new percentage value 
661                          */
662                         pso->percentage= (evt->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx);
663                         RNA_float_set(op->ptr, "percentage", pso->percentage);
664                         
665                         /* reset transforms (to avoid accumulation errors) */
666                         pose_slide_reset(C, pso);
667                         
668                         /* apply... */
669                         pose_slide_apply(C, op, pso);
670                 }
671                         break;
672         }
673         
674         /* still running... */
675         return OPERATOR_RUNNING_MODAL;
676 }
677
678 /* common code for cancel() */
679 static int pose_slide_cancel (bContext *C, wmOperator *op)
680 {
681         /* cleanup and done */
682         pose_slide_exit(C, op);
683         return OPERATOR_CANCELLED;
684 }
685
686 /* common code for exec() methods */
687 static int pose_slide_exec_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
688 {
689         /* settings should have been set up ok for applying, so just apply! */
690         pose_slide_apply(C, op, pso);
691         
692         /* insert keyframes if needed */
693         pose_slide_autoKeyframe(C, pso);
694         
695         /* cleanup and done */
696         pose_slide_exit(C, op);
697         
698         return OPERATOR_FINISHED;
699 }
700
701 /* common code for defining RNA properties */
702 static void pose_slide_opdef_properties (wmOperatorType *ot)
703 {
704         RNA_def_int(ot->srna, "prev_frame", 0, MINAFRAME, MAXFRAME, "Previous Keyframe", "Frame number of keyframe immediately before the current frame.", 0, 50);
705         RNA_def_int(ot->srna, "next_frame", 0, MINAFRAME, MAXFRAME, "Next Keyframe", "Frame number of keyframe immediately after the current frame.", 0, 50);
706         RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for the sliding operation", 0.3, 0.7);
707 }
708
709 /* ------------------------------------ */
710
711 /* invoke() - for 'push' mode */
712 static int pose_slide_push_invoke (bContext *C, wmOperator *op, wmEvent *evt)
713 {
714         tPoseSlideOp *pso;
715         
716         /* initialise data  */
717         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
718                 pose_slide_exit(C, op);
719                 return OPERATOR_CANCELLED;
720         }
721         else
722                 pso= op->customdata;
723         
724         /* do common setup work */
725         return pose_slide_invoke_common(C, op, pso);
726 }
727
728 /* exec() - for push */
729 static int pose_slide_push_exec (bContext *C, wmOperator *op)
730 {
731         tPoseSlideOp *pso;
732         
733         /* initialise data (from RNA-props) */
734         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
735                 pose_slide_exit(C, op);
736                 return OPERATOR_CANCELLED;
737         }
738         else
739                 pso= op->customdata;
740                 
741         /* do common exec work */
742         return pose_slide_exec_common(C, op, pso);
743 }
744
745 void POSE_OT_push (wmOperatorType *ot)
746 {
747         /* identifiers */
748         ot->name= "Push Pose";
749         ot->idname= "POSE_OT_push";
750         ot->description= "Exaggerate the current pose";
751         
752         /* callbacks */
753         ot->exec= pose_slide_push_exec;
754         ot->invoke= pose_slide_push_invoke;
755         ot->modal= pose_slide_modal;
756         ot->cancel= pose_slide_cancel;
757         ot->poll= ED_operator_posemode;
758         
759         /* flags */
760         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
761         
762         /* Properties */
763         pose_slide_opdef_properties(ot);
764 }
765
766 /* ........................ */
767
768 /* invoke() - for 'relax' mode */
769 static int pose_slide_relax_invoke (bContext *C, wmOperator *op, wmEvent *evt)
770 {
771         tPoseSlideOp *pso;
772         
773         /* initialise data  */
774         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
775                 pose_slide_exit(C, op);
776                 return OPERATOR_CANCELLED;
777         }
778         else
779                 pso= op->customdata;
780         
781         /* do common setup work */
782         return pose_slide_invoke_common(C, op, pso);
783 }
784
785 /* exec() - for relax */
786 static int pose_slide_relax_exec (bContext *C, wmOperator *op)
787 {
788         tPoseSlideOp *pso;
789         
790         /* initialise data (from RNA-props) */
791         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
792                 pose_slide_exit(C, op);
793                 return OPERATOR_CANCELLED;
794         }
795         else
796                 pso= op->customdata;
797                 
798         /* do common exec work */
799         return pose_slide_exec_common(C, op, pso);
800 }
801
802 void POSE_OT_relax (wmOperatorType *ot)
803 {
804         /* identifiers */
805         ot->name= "Relax Pose";
806         ot->idname= "POSE_OT_relax";
807         ot->description= "Make the current pose more similar to its surrounding ones.";
808         
809         /* callbacks */
810         ot->exec= pose_slide_relax_exec;
811         ot->invoke= pose_slide_relax_invoke;
812         ot->modal= pose_slide_modal;
813         ot->cancel= pose_slide_cancel;
814         ot->poll= ED_operator_posemode;
815         
816         /* flags */
817         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
818         
819         /* Properties */
820         pose_slide_opdef_properties(ot);
821 }
822
823 /* ........................ */
824
825 /* invoke() - for 'breakdown' mode */
826 static int pose_slide_breakdown_invoke (bContext *C, wmOperator *op, wmEvent *evt)
827 {
828         tPoseSlideOp *pso;
829         
830         /* initialise data  */
831         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
832                 pose_slide_exit(C, op);
833                 return OPERATOR_CANCELLED;
834         }
835         else
836                 pso= op->customdata;
837         
838         /* do common setup work */
839         return pose_slide_invoke_common(C, op, pso);
840 }
841
842 /* exec() - for breakdown */
843 static int pose_slide_breakdown_exec (bContext *C, wmOperator *op)
844 {
845         tPoseSlideOp *pso;
846         
847         /* initialise data (from RNA-props) */
848         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
849                 pose_slide_exit(C, op);
850                 return OPERATOR_CANCELLED;
851         }
852         else
853                 pso= op->customdata;
854                 
855         /* do common exec work */
856         return pose_slide_exec_common(C, op, pso);
857 }
858
859 void POSE_OT_breakdown (wmOperatorType *ot)
860 {
861         /* identifiers */
862         ot->name= "Pose Breakdowner";
863         ot->idname= "POSE_OT_breakdown";
864         ot->description= "Create a suitable breakdown pose on the current frame.";
865         
866         /* callbacks */
867         ot->exec= pose_slide_breakdown_exec;
868         ot->invoke= pose_slide_breakdown_invoke;
869         ot->modal= pose_slide_modal;
870         ot->cancel= pose_slide_cancel;
871         ot->poll= ED_operator_posemode;
872         
873         /* flags */
874         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
875         
876         /* Properties */
877         pose_slide_opdef_properties(ot);
878 }
879
880 /* **************************************************** */