0e066b112b067855aed3a0994aa1458da4d8e593
[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., 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_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         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_pose_bones) 
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_PoseBone, 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         FCurve *fcu_w=NULL, *fcu_x=NULL, *fcu_y=NULL, *fcu_z=NULL;
414         bPoseChannel *pchan= pfl->pchan;
415         LinkData *ld=NULL;
416         char *path=NULL;
417         float cframe;
418         
419         /* get the path to use - this should be quaternion rotations only (needs care) */
420         path= BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion");
421         
422         /* get the current frame number */
423         cframe= (float)pso->cframe;
424         
425         /* using this path, find each matching F-Curve for the variables we're interested in */
426         while ( (ld= find_next_fcurve_link(&pfl->fcurves, ld, path)) ) {
427                 FCurve *fcu= (FCurve *)ld->data;
428                 
429                 /* assign this F-Curve to one of the relevant pointers... */
430                 switch (fcu->array_index) {
431                         case 3: /* z */
432                                 fcu_z= fcu;
433                                 break;
434                         case 2: /* y */
435                                 fcu_y= fcu;
436                                 break;
437                         case 1: /* x */
438                                 fcu_x= fcu;
439                                 break;
440                         case 0: /* w */
441                                 fcu_w= fcu;
442                                 break;
443                 }
444         }
445         
446         /* only if all channels exist, proceed */
447         if (fcu_w && fcu_x && fcu_y && fcu_z) {
448                 float quat_prev[4], quat_next[4];
449                 
450                 /* get 2 quats */
451                 quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrame);
452                 quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrame);
453                 quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrame);
454                 quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrame);
455                 
456                 quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrame);
457                 quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrame);
458                 quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrame);
459                 quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrame);
460                 
461                 /* perform blending */
462                 if (pso->mode == POSESLIDE_BREAKDOWN) {
463                         /* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */
464                         interp_qt_qtqt(pchan->quat, quat_prev, quat_next, pso->percentage);
465                 }
466                 else if (pso->mode == POSESLIDE_PUSH) {
467                         float quat_diff[4], quat_orig[4];
468                         
469                         /* calculate the delta transform from the previous to the current */
470                         // TODO: investigate ways to favour one transform more?
471                         sub_qt_qtqt(quat_diff, pchan->quat, quat_prev);
472                         
473                         /* make a copy of the original rotation */
474                         QUATCOPY(quat_orig, pchan->quat);
475                         
476                         /* increase the original by the delta transform, by an amount determined by percentage */
477                         add_qt_qtqt(pchan->quat, quat_orig, quat_diff, pso->percentage);
478                 }
479                 else {
480                         float quat_interp[4], quat_orig[4];
481                         int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
482                         
483                         /* perform this blending several times until a satisfactory result is reached */
484                         while (iters-- > 0) {
485                                 /* calculate the interpolation between the endpoints */
486                                 interp_qt_qtqt(quat_interp, quat_prev, quat_next, (cframe-pso->prevFrame) / (pso->nextFrame-pso->prevFrame) );
487                                 
488                                 /* make a copy of the original rotation */
489                                 QUATCOPY(quat_orig, pchan->quat);
490                                 
491                                 /* tricky interpolations - blending between original and new */
492                                 interp_qt_qtqt(pchan->quat, quat_orig, quat_interp, 1.0f/6.0f);
493                         }
494                 }
495         }
496         
497         /* free the path now */
498         MEM_freeN(path);
499 }
500
501 /* apply() - perform the pose sliding based on weighting various poses */
502 static void pose_slide_apply (bContext *C, wmOperator *op, tPoseSlideOp *pso)
503 {
504         tPChanFCurveLink *pfl;
505         
506         /* sanitise the frame ranges */
507         if (pso->prevFrame == pso->nextFrame) {
508                 /* move out one step either side */
509                 pso->prevFrame--;
510                 pso->nextFrame++;
511         }
512         
513         /* for each link, handle each set of transforms */
514         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
515                 /* valid transforms for each PoseChannel should have been noted already 
516                  *      - sliding the pose should be a straightforward exercise for location+rotation, 
517                  *        but rotations get more complicated since we may want to use quaternion blending 
518                  *        for quaternions instead...
519                  */
520                 bPoseChannel *pchan= pfl->pchan;
521                  
522                 if (pchan->flag & POSE_LOC) {
523                         /* calculate these for the 'location' vector, and use location curves */
524                         pose_slide_apply_vec3(pso, pfl, pchan->loc, "location");
525                 }
526                 
527                 if (pchan->flag & POSE_SIZE) {
528                         /* calculate these for the 'scale' vector, and use scale curves */
529                         pose_slide_apply_vec3(pso, pfl, pchan->size, "scale");
530                 }
531                 
532                 if (pchan->flag & POSE_ROT) {
533                         /* everything depends on the rotation mode */
534                         if (pchan->rotmode > 0) {
535                                 /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
536                                 pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler");
537                         }
538                         else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
539                                 // TODO: need to figure out how to do this!
540                         }
541                         else {
542                                 /* quaternions - use quaternion blending */
543                                 pose_slide_apply_quat(pso, pfl);
544                         }
545                 }
546         }
547         
548         /* depsgraph updates + redraws */
549         pose_slide_refresh(C, pso);
550 }
551
552 /* perform autokeyframing after changes were made + confirmed */
553 static void pose_slide_autoKeyframe (bContext *C, tPoseSlideOp *pso)
554 {
555         /* insert keyframes as necessary if autokeyframing */
556         if (autokeyframe_cfra_can_key(pso->scene, &pso->ob->id)) {
557                 bCommonKeySrc cks;
558                 ListBase dsources = {&cks, &cks};
559                 tPChanFCurveLink *pfl;
560                 
561                 /* init common-key-source for use by KeyingSets */
562                 memset(&cks, 0, sizeof(bCommonKeySrc));
563                 cks.id= &pso->ob->id;
564                 
565                 /* iterate over each pose-channel affected, applying the changes */
566                 for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
567                         bPoseChannel *pchan= pfl->pchan;
568                         /* init cks for this PoseChannel, then use the relative KeyingSets to keyframe it */
569                         cks.pchan= pchan;
570                         
571                         /* insert keyframes */
572                         if (pchan->flag & POSE_LOC)
573                                 modify_keyframes(pso->scene, &dsources, NULL, pso->ks_loc, MODIFYKEY_MODE_INSERT, (float)pso->cframe);
574                         if (pchan->flag & POSE_ROT)
575                                 modify_keyframes(pso->scene, &dsources, NULL, pso->ks_rot, MODIFYKEY_MODE_INSERT, (float)pso->cframe);
576                         if (pchan->flag & POSE_SIZE)
577                                 modify_keyframes(pso->scene, &dsources, NULL, pso->ks_scale, MODIFYKEY_MODE_INSERT, (float)pso->cframe);
578                 }
579         }
580 }
581
582 /* reset changes made to current pose */
583 static void pose_slide_reset (bContext *C, tPoseSlideOp *pso)
584 {
585         tPChanFCurveLink *pfl;
586         
587         /* iterate over each pose-channel affected, restoring all channels to their original values */
588         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
589                 bPoseChannel *pchan= pfl->pchan;
590                 
591                 /* just copy all the values over regardless of whether they changed or not */
592                 VECCOPY(pchan->loc, pfl->oldloc);
593                 VECCOPY(pchan->eul, pfl->oldrot);
594                 VECCOPY(pchan->size, pfl->oldscale);
595                 QUATCOPY(pchan->quat, pfl->oldquat);
596         }
597 }
598
599 /* ------------------------------------ */
600
601 /* common code for invoke() methods */
602 static int pose_slide_invoke_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
603 {
604         tPChanFCurveLink *pfl;
605         AnimData *adt= pso->ob->adt;
606         wmWindow *win= CTX_wm_window(C);
607         
608         /* for each link, add all its keyframes to the search tree */
609         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
610                 LinkData *ld;
611                 
612                 /* do this for each F-Curve */
613                 for (ld= pfl->fcurves.first; ld; ld= ld->next) {
614                         FCurve *fcu= (FCurve *)ld->data;
615                         fcurve_to_keylist(adt, fcu, &pso->keys, NULL);
616                 }
617         }
618         
619         /* consolidate these keyframes, and figure out the nearest ones */
620         BLI_dlrbTree_linkedlist_sync(&pso->keys);
621         
622                 /* cancel if no keyframes found... */
623         if (pso->keys.root) {
624                 ActKeyColumn *ak;
625                 float cframe= (float)pso->cframe;
626                 
627                 /* firstly, check if the current frame is a keyframe... */
628                 ak= (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe);
629                 
630                 if (ak == NULL) {
631                         /* current frame is not a keyframe, so search */
632                         ActKeyColumn *pk= (ActKeyColumn *)BLI_dlrbTree_search_prev(&pso->keys, compare_ak_cfraPtr, &cframe);
633                         ActKeyColumn *nk= (ActKeyColumn *)BLI_dlrbTree_search_next(&pso->keys, compare_ak_cfraPtr, &cframe);
634                         
635                         /* new set the frames */
636                                 /* prev frame */
637                         pso->prevFrame= (pk)? (pk->cfra) : (pso->cframe - 1);
638                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
639                                 /* next frame */
640                         pso->nextFrame= (nk)? (nk->cfra) : (pso->cframe + 1);
641                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
642                 }
643                 else {
644                         /* current frame itself is a keyframe, so just take keyframes on either side */
645                                 /* prev frame */
646                         pso->prevFrame= (ak->prev)? (ak->prev->cfra) : (pso->cframe - 1);
647                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
648                                 /* next frame */
649                         pso->nextFrame= (ak->next)? (ak->next->cfra) : (pso->cframe + 1);
650                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
651                 }
652         }
653         else {
654                 BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between.");
655                 return OPERATOR_CANCELLED;
656         }
657         
658         /* initial apply for operator... */
659         // TODO: need to calculate percentage for initial round too...
660         pose_slide_apply(C, op, pso);
661         
662         /* depsgraph updates + redraws */
663         pose_slide_refresh(C, pso);
664         
665         /* set cursor to indicate modal */
666         WM_cursor_modal(win, BC_EW_SCROLLCURSOR);
667         
668         /* add a modal handler for this operator */
669         WM_event_add_modal_handler(C, op);
670         return OPERATOR_RUNNING_MODAL;
671 }
672
673 /* common code for modal() */
674 static int pose_slide_modal (bContext *C, wmOperator *op, wmEvent *evt)
675 {
676         tPoseSlideOp *pso= op->customdata;
677         wmWindow *win= CTX_wm_window(C);
678         
679         switch (evt->type) {
680                 case LEFTMOUSE: /* confirm */
681                 {
682                         /* return to normal cursor */
683                         WM_cursor_restore(win);
684                         
685                         /* insert keyframes as required... */
686                         pose_slide_autoKeyframe(C, pso);
687                         pose_slide_exit(C, op);
688                         
689                         /* done! */
690                         return OPERATOR_FINISHED;
691                 }
692                 
693                 case ESCKEY:    /* cancel */
694                 case RIGHTMOUSE: 
695                 {
696                         /* return to normal cursor */
697                         WM_cursor_restore(win);
698                         
699                         /* reset transforms back to original state */
700                         pose_slide_reset(C, pso);
701                         
702                         /* depsgraph updates + redraws */
703                         pose_slide_refresh(C, pso);
704                         
705                         /* clean up temp data */
706                         pose_slide_exit(C, op);
707                         
708                         /* cancelled! */
709                         return OPERATOR_CANCELLED;
710                 }
711                         
712                 case MOUSEMOVE: /* calculate new position */
713                 {
714                         /* calculate percentage based on position of mouse (we only use x-axis for now.
715                          * since this is more conveninent for users to do), and store new percentage value 
716                          */
717                         pso->percentage= (evt->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx);
718                         RNA_float_set(op->ptr, "percentage", pso->percentage);
719                         
720                         /* reset transforms (to avoid accumulation errors) */
721                         pose_slide_reset(C, pso);
722                         
723                         /* apply... */
724                         pose_slide_apply(C, op, pso);
725                 }
726                         break;
727                         
728                 default: /* unhandled event (maybe it was some view manip? */
729                         /* allow to pass through */
730                         return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH;
731         }
732         
733         /* still running... */
734         return OPERATOR_RUNNING_MODAL;
735 }
736
737 /* common code for cancel() */
738 static int pose_slide_cancel (bContext *C, wmOperator *op)
739 {
740         /* cleanup and done */
741         pose_slide_exit(C, op);
742         return OPERATOR_CANCELLED;
743 }
744
745 /* common code for exec() methods */
746 static int pose_slide_exec_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
747 {
748         /* settings should have been set up ok for applying, so just apply! */
749         pose_slide_apply(C, op, pso);
750         
751         /* insert keyframes if needed */
752         pose_slide_autoKeyframe(C, pso);
753         
754         /* cleanup and done */
755         pose_slide_exit(C, op);
756         
757         return OPERATOR_FINISHED;
758 }
759
760 /* common code for defining RNA properties */
761 static void pose_slide_opdef_properties (wmOperatorType *ot)
762 {
763         RNA_def_int(ot->srna, "prev_frame", 0, MINAFRAME, MAXFRAME, "Previous Keyframe", "Frame number of keyframe immediately before the current frame.", 0, 50);
764         RNA_def_int(ot->srna, "next_frame", 0, MINAFRAME, MAXFRAME, "Next Keyframe", "Frame number of keyframe immediately after the current frame.", 0, 50);
765         RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for the sliding operation", 0.3, 0.7);
766 }
767
768 /* ------------------------------------ */
769
770 /* invoke() - for 'push' mode */
771 static int pose_slide_push_invoke (bContext *C, wmOperator *op, wmEvent *evt)
772 {
773         tPoseSlideOp *pso;
774         
775         /* initialise data  */
776         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
777                 pose_slide_exit(C, op);
778                 return OPERATOR_CANCELLED;
779         }
780         else
781                 pso= op->customdata;
782         
783         /* do common setup work */
784         return pose_slide_invoke_common(C, op, pso);
785 }
786
787 /* exec() - for push */
788 static int pose_slide_push_exec (bContext *C, wmOperator *op)
789 {
790         tPoseSlideOp *pso;
791         
792         /* initialise data (from RNA-props) */
793         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
794                 pose_slide_exit(C, op);
795                 return OPERATOR_CANCELLED;
796         }
797         else
798                 pso= op->customdata;
799                 
800         /* do common exec work */
801         return pose_slide_exec_common(C, op, pso);
802 }
803
804 void POSE_OT_push (wmOperatorType *ot)
805 {
806         /* identifiers */
807         ot->name= "Push Pose";
808         ot->idname= "POSE_OT_push";
809         ot->description= "Exaggerate the current pose";
810         
811         /* callbacks */
812         ot->exec= pose_slide_push_exec;
813         ot->invoke= pose_slide_push_invoke;
814         ot->modal= pose_slide_modal;
815         ot->cancel= pose_slide_cancel;
816         ot->poll= ED_operator_posemode;
817         
818         /* flags */
819         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
820         
821         /* Properties */
822         pose_slide_opdef_properties(ot);
823 }
824
825 /* ........................ */
826
827 /* invoke() - for 'relax' mode */
828 static int pose_slide_relax_invoke (bContext *C, wmOperator *op, wmEvent *evt)
829 {
830         tPoseSlideOp *pso;
831         
832         /* initialise data  */
833         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
834                 pose_slide_exit(C, op);
835                 return OPERATOR_CANCELLED;
836         }
837         else
838                 pso= op->customdata;
839         
840         /* do common setup work */
841         return pose_slide_invoke_common(C, op, pso);
842 }
843
844 /* exec() - for relax */
845 static int pose_slide_relax_exec (bContext *C, wmOperator *op)
846 {
847         tPoseSlideOp *pso;
848         
849         /* initialise data (from RNA-props) */
850         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
851                 pose_slide_exit(C, op);
852                 return OPERATOR_CANCELLED;
853         }
854         else
855                 pso= op->customdata;
856                 
857         /* do common exec work */
858         return pose_slide_exec_common(C, op, pso);
859 }
860
861 void POSE_OT_relax (wmOperatorType *ot)
862 {
863         /* identifiers */
864         ot->name= "Relax Pose";
865         ot->idname= "POSE_OT_relax";
866         ot->description= "Make the current pose more similar to its surrounding ones.";
867         
868         /* callbacks */
869         ot->exec= pose_slide_relax_exec;
870         ot->invoke= pose_slide_relax_invoke;
871         ot->modal= pose_slide_modal;
872         ot->cancel= pose_slide_cancel;
873         ot->poll= ED_operator_posemode;
874         
875         /* flags */
876         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
877         
878         /* Properties */
879         pose_slide_opdef_properties(ot);
880 }
881
882 /* ........................ */
883
884 /* invoke() - for 'breakdown' mode */
885 static int pose_slide_breakdown_invoke (bContext *C, wmOperator *op, wmEvent *evt)
886 {
887         tPoseSlideOp *pso;
888         
889         /* initialise data  */
890         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
891                 pose_slide_exit(C, op);
892                 return OPERATOR_CANCELLED;
893         }
894         else
895                 pso= op->customdata;
896         
897         /* do common setup work */
898         return pose_slide_invoke_common(C, op, pso);
899 }
900
901 /* exec() - for breakdown */
902 static int pose_slide_breakdown_exec (bContext *C, wmOperator *op)
903 {
904         tPoseSlideOp *pso;
905         
906         /* initialise data (from RNA-props) */
907         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
908                 pose_slide_exit(C, op);
909                 return OPERATOR_CANCELLED;
910         }
911         else
912                 pso= op->customdata;
913                 
914         /* do common exec work */
915         return pose_slide_exec_common(C, op, pso);
916 }
917
918 void POSE_OT_breakdown (wmOperatorType *ot)
919 {
920         /* identifiers */
921         ot->name= "Pose Breakdowner";
922         ot->idname= "POSE_OT_breakdown";
923         ot->description= "Create a suitable breakdown pose on the current frame.";
924         
925         /* callbacks */
926         ot->exec= pose_slide_breakdown_exec;
927         ot->invoke= pose_slide_breakdown_invoke;
928         ot->modal= pose_slide_modal;
929         ot->cancel= pose_slide_cancel;
930         ot->poll= ED_operator_posemode;
931         
932         /* flags */
933         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
934         
935         /* Properties */
936         pose_slide_opdef_properties(ot);
937 }
938
939 /* **************************************************** */