Reshuffled utility function to keyframe drawing API, removing some
[blender.git] / source / blender / editors / armature / poseSlide.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung
21  * This is a new part of Blender
22  *
23  * Contributor(s): Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/armature/poseSlide.c
29  *  \ingroup edarmature
30  */
31
32  
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <stddef.h>
36 #include <string.h>
37 #include <math.h>
38 #include <float.h>
39
40 #include "MEM_guardedalloc.h"
41
42 #include "BLI_math.h"
43 #include "BLI_blenlib.h"
44 #include "BLI_dynstr.h"
45 #include "BLI_dlrbTree.h"
46 #include "BLI_utildefines.h"
47
48 #include "DNA_anim_types.h"
49 #include "DNA_armature_types.h"
50 #include "DNA_object_types.h"
51 #include "DNA_scene_types.h"
52
53 #include "BKE_fcurve.h"
54
55 #include "BKE_context.h"
56 #include "BKE_report.h"
57
58 #include "RNA_access.h"
59 #include "RNA_define.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63
64 #include "ED_armature.h"
65 #include "ED_keyframes_draw.h"
66 #include "ED_screen.h"
67
68 #include "armature_intern.h"
69
70 /* **************************************************** */
71 /* == POSE 'SLIDING' TOOLS == 
72  *
73  * A) Push & Relax, Breakdowner
74  * These tools provide the animator with various capabilities
75  * for interactively controlling the spacing of poses, but also
76  * for 'pushing' and/or 'relaxing' extremes as they see fit.
77  *
78  * B) Propagate
79  * This tool copies elements of the selected pose to successive
80  * keyframes, allowing the animator to go back and modify the poses
81  * for some "static" pose controls, without having to repeatedly
82  * doing a "next paste" dance.
83  *
84  * C) Pose Sculpting
85  * This is yet to be implemented, but the idea here is to use
86  * sculpting techniques to make it easier to pose rigs by allowing
87  * rigs to be manipulated using a familiar paint-based interface. 
88  */
89 /* **************************************************** */
90 /* A) Push & Relax, Breakdowner */
91
92 /* Temporary data shared between these operators */
93 typedef struct tPoseSlideOp {
94         Scene *scene;           /* current scene */
95         ARegion *ar;            /* region that we're operating in (needed for  */
96         Object *ob;                     /* active object that Pose Info comes from */
97         bArmature *arm;         /* armature for pose */
98         
99         ListBase pfLinks;       /* links between posechannels and f-curves  */
100         DLRBT_Tree keys;        /* binary tree for quicker searching for keyframes (when applicable) */
101         
102         int cframe;                     /* current frame number */
103         int prevFrame;          /* frame before current frame (blend-from) */
104         int nextFrame;          /* frame after current frame (blend-to) */
105         
106         int mode;                       /* sliding mode (ePoseSlide_Modes) */
107         int flag;                       // unused for now, but can later get used for storing runtime settings....
108         
109         float percentage;       /* 0-1 value for determining the influence of whatever is relevant */
110 } tPoseSlideOp;
111
112 /* Pose Sliding Modes */
113 typedef enum ePoseSlide_Modes {
114         POSESLIDE_PUSH  = 0,            /* exaggerate the pose... */
115         POSESLIDE_RELAX,                        /* soften the pose... */
116         POSESLIDE_BREAKDOWN,            /* slide between the endpoint poses, finding a 'soft' spot */
117 } ePoseSlide_Modes;
118
119 /* ------------------------------------ */
120
121 /* operator init */
122 static int pose_slide_init (bContext *C, wmOperator *op, short mode)
123 {
124         tPoseSlideOp *pso;
125         bAction *act= NULL;
126         
127         /* init slide-op data */
128         pso= op->customdata= MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp");
129         
130         /* get info from context */
131         pso->scene= CTX_data_scene(C);
132         pso->ob= ED_object_pose_armature(CTX_data_active_object(C));
133         pso->arm= (pso->ob)? pso->ob->data : NULL;
134         pso->ar= CTX_wm_region(C); /* only really needed when doing modal() */
135         
136         pso->cframe= pso->scene->r.cfra;
137         pso->mode= mode;
138         
139         /* set range info from property values - these may get overridden for the invoke() */
140         pso->percentage= RNA_float_get(op->ptr, "percentage");
141         pso->prevFrame= RNA_int_get(op->ptr, "prev_frame");
142         pso->nextFrame= RNA_int_get(op->ptr, "next_frame");
143         
144         /* check the settings from the context */
145         if (ELEM4(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action))
146                 return 0;
147         else
148                 act= pso->ob->adt->action;
149         
150         /* for each Pose-Channel which gets affected, get the F-Curves for that channel 
151          * and set the relevant transform flags...
152          */
153         poseAnim_mapping_get(C, &pso->pfLinks, pso->ob, act);
154         
155         /* set depsgraph flags */
156                 /* make sure the lock is set OK, unlock can be accidentally saved? */
157         pso->ob->pose->flag |= POSE_LOCKED;
158         pso->ob->pose->flag &= ~POSE_DO_UNLOCK;
159         
160         /* do basic initialise of RB-BST used for finding keyframes, but leave the filling of it up 
161          * to the caller of this (usually only invoke() will do it, to make things more efficient).
162          */
163         BLI_dlrbTree_init(&pso->keys);
164         
165         /* return status is whether we've got all the data we were requested to get */
166         return 1;
167 }
168
169 /* exiting the operator - free data */
170 static void pose_slide_exit(wmOperator *op)
171 {
172         tPoseSlideOp *pso= op->customdata;
173         
174         /* if data exists, clear its data and exit */
175         if (pso) {
176                 /* free the temp pchan links and their data */
177                 poseAnim_mapping_free(&pso->pfLinks);
178                 
179                 /* free RB-BST for keyframes (if it contained data) */
180                 BLI_dlrbTree_free(&pso->keys);
181                 
182                 /* free data itself */
183                 MEM_freeN(pso);
184         }
185         
186         /* cleanup */
187         op->customdata= NULL;
188 }
189
190 /* ------------------------------------ */
191
192 /* helper for apply() / reset() - refresh the data */
193 static void pose_slide_refresh (bContext *C, tPoseSlideOp *pso)
194 {
195         /* wrapper around the generic version, allowing us to add some custom stuff later still */
196         poseAnim_mapping_refresh(C, pso->scene, pso->ob);
197 }
198
199 /* helper for apply() - perform sliding for some value */
200 static void pose_slide_apply_val (tPoseSlideOp *pso, FCurve *fcu, float *val)
201 {
202         float cframe = (float)pso->cframe;
203         float sVal, eVal;
204         float w1, w2;
205         
206         /* get keyframe values for endpoint poses to blend with */
207                 /* previous/start */
208         sVal= evaluate_fcurve(fcu, (float)pso->prevFrame);
209                 /* next/end */
210         eVal= evaluate_fcurve(fcu, (float)pso->nextFrame);
211         
212         /* calculate the relative weights of the endpoints */
213         if (pso->mode == POSESLIDE_BREAKDOWN) {
214                 /* get weights from the percentage control */
215                 w1= pso->percentage;    /* this must come second */
216                 w2= 1.0f - w1;                  /* this must come first */
217         }
218         else {
219                 /*      - these weights are derived from the relative distance of these 
220                  *        poses from the current frame
221                  *      - they then get normalised so that they only sum up to 1
222                  */
223                 float wtot; 
224                 
225                 w1 = cframe - (float)pso->prevFrame;
226                 w2 = (float)pso->nextFrame - cframe;
227                 
228                 wtot = w1 + w2;
229                 w1 = (w1/wtot);
230                 w2 = (w2/wtot);
231         }
232         
233         /* depending on the mode, calculate the new value
234          *      - in all of these, the start+end values are multiplied by w2 and w1 (respectively),
235          *        since multiplication in another order would decrease the value the current frame is closer to
236          */
237         switch (pso->mode) {
238                 case POSESLIDE_PUSH: /* make the current pose more pronounced */
239                 {
240                         /* perform a weighted average here, favouring the middle pose 
241                          *      - numerator should be larger than denominator to 'expand' the result
242                          *      - perform this weighting a number of times given by the percentage...
243                          */
244                         int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
245                         
246                         while (iters-- > 0) {
247                                 (*val)= ( -((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f) ) / 5.0f; 
248                         }
249                 }
250                         break;
251                         
252                 case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */
253                 {
254                         /* perform a weighted average here, favouring the middle pose 
255                          *      - numerator should be smaller than denominator to 'relax' the result
256                          *      - perform this weighting a number of times given by the percentage...
257                          */
258                         int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
259                         
260                         while (iters-- > 0) {
261                                 (*val)= ( ((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f) ) / 6.0f;
262                         }
263                 }
264                         break;
265                         
266                 case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */
267                 {
268                         /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */
269                         // TODO: make this use some kind of spline interpolation instead?
270                         (*val)= ((sVal * w2) + (eVal * w1));
271                 }
272                         break;
273         }
274 }
275
276 /* helper for apply() - perform sliding for some 3-element vector */
277 static void pose_slide_apply_vec3 (tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], const char propName[])
278 {
279         LinkData *ld=NULL;
280         char *path=NULL;
281         
282         /* get the path to use... */
283         path= BLI_sprintfN("%s.%s", pfl->pchan_path, propName);
284         
285         /* using this path, find each matching F-Curve for the variables we're interested in */
286         while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
287                 FCurve *fcu= (FCurve *)ld->data;
288                 
289                 /* just work on these channels one by one... there's no interaction between values */
290                 pose_slide_apply_val(pso, fcu, &vec[fcu->array_index]);
291         }
292         
293         /* free the temp path we got */
294         MEM_freeN(path);
295 }
296
297 /* helper for apply() - perform sliding for custom properties */
298 static void pose_slide_apply_props (tPoseSlideOp *pso, tPChanFCurveLink *pfl)
299 {
300         PointerRNA ptr = {{NULL}};
301         LinkData *ld;
302         int len = strlen(pfl->pchan_path);
303         
304         /* setup pointer RNA for resolving paths */
305         RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr);
306         
307         /* custom properties are just denoted using ["..."][etc.] after the end of the base path, 
308          * so just check for opening pair after the end of the path
309          */
310         for (ld = pfl->fcurves.first; ld; ld = ld->next) {
311                 FCurve *fcu = (FCurve *)ld->data;
312                 char *bPtr, *pPtr;
313                 
314                 if (fcu->rna_path == NULL)
315                         continue;
316                 
317                 /* do we have a match? 
318                  *      - bPtr is the RNA Path with the standard part chopped off
319                  *      - pPtr is the chunk of the path which is left over
320                  */
321                 bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len;
322                 pPtr = strstr(bPtr, "[\"");   /* dummy " for texteditor bugs */
323                 
324                 if (pPtr) {
325                         /* use RNA to try and get a handle on this property, then, assuming that it is just
326                          * numerical, try and grab the value as a float for temp editing before setting back
327                          */
328                         PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr);
329                         
330                         if (prop) {
331                                 switch (RNA_property_type(prop)) {
332                                         case PROP_FLOAT:
333                                         {
334                                                 float tval = RNA_property_float_get(&ptr, prop);
335                                                 pose_slide_apply_val(pso, fcu, &tval);
336                                                 RNA_property_float_set(&ptr, prop, tval);
337                                         }
338                                                 break;
339                                         case PROP_BOOLEAN:
340                                         case PROP_ENUM:
341                                         case PROP_INT:
342                                         {
343                                                 float tval = (float)RNA_property_int_get(&ptr, prop);
344                                                 pose_slide_apply_val(pso, fcu, &tval);
345                                                 RNA_property_int_set(&ptr, prop, (int)tval);
346                                         }
347                                                 break;
348                                         default:
349                                                 /* cannot handle */
350                                                 //printf("Cannot Pose Slide non-numerical property\n");
351                                                 break;
352                                 }
353                         }
354                 }
355         }
356 }
357
358 /* helper for apply() - perform sliding for quaternion rotations (using quat blending) */
359 static void pose_slide_apply_quat (tPoseSlideOp *pso, tPChanFCurveLink *pfl)
360 {
361         FCurve *fcu_w=NULL, *fcu_x=NULL, *fcu_y=NULL, *fcu_z=NULL;
362         bPoseChannel *pchan= pfl->pchan;
363         LinkData *ld=NULL;
364         char *path=NULL;
365         float cframe;
366         
367         /* get the path to use - this should be quaternion rotations only (needs care) */
368         path= BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion");
369         
370         /* get the current frame number */
371         cframe= (float)pso->cframe;
372         
373         /* using this path, find each matching F-Curve for the variables we're interested in */
374         while ( (ld= poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
375                 FCurve *fcu= (FCurve *)ld->data;
376                 
377                 /* assign this F-Curve to one of the relevant pointers... */
378                 switch (fcu->array_index) {
379                         case 3: /* z */
380                                 fcu_z= fcu;
381                                 break;
382                         case 2: /* y */
383                                 fcu_y= fcu;
384                                 break;
385                         case 1: /* x */
386                                 fcu_x= fcu;
387                                 break;
388                         case 0: /* w */
389                                 fcu_w= fcu;
390                                 break;
391                 }
392         }
393         
394         /* only if all channels exist, proceed */
395         if (fcu_w && fcu_x && fcu_y && fcu_z) {
396                 float quat_prev[4], quat_next[4];
397                 
398                 /* get 2 quats */
399                 quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrame);
400                 quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrame);
401                 quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrame);
402                 quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrame);
403                 
404                 quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrame);
405                 quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrame);
406                 quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrame);
407                 quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrame);
408                 
409                 /* perform blending */
410                 if (pso->mode == POSESLIDE_BREAKDOWN) {
411                         /* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */
412                         interp_qt_qtqt(pchan->quat, quat_prev, quat_next, pso->percentage);
413                 }
414                 else if (pso->mode == POSESLIDE_PUSH) {
415                         float quat_diff[4], quat_orig[4];
416                         
417                         /* calculate the delta transform from the previous to the current */
418                         // TODO: investigate ways to favour one transform more?
419                         sub_qt_qtqt(quat_diff, pchan->quat, quat_prev);
420                         
421                         /* make a copy of the original rotation */
422                         QUATCOPY(quat_orig, pchan->quat);
423                         
424                         /* increase the original by the delta transform, by an amount determined by percentage */
425                         add_qt_qtqt(pchan->quat, quat_orig, quat_diff, pso->percentage);
426                 }
427                 else {
428                         float quat_interp[4], quat_orig[4];
429                         int iters= (int)ceil(10.0f*pso->percentage); // TODO: maybe a sensitivity ctrl on top of this is needed
430                         
431                         /* perform this blending several times until a satisfactory result is reached */
432                         while (iters-- > 0) {
433                                 /* calculate the interpolation between the endpoints */
434                                 interp_qt_qtqt(quat_interp, quat_prev, quat_next, (cframe-pso->prevFrame) / (pso->nextFrame-pso->prevFrame) );
435                                 
436                                 /* make a copy of the original rotation */
437                                 QUATCOPY(quat_orig, pchan->quat);
438                                 
439                                 /* tricky interpolations - blending between original and new */
440                                 interp_qt_qtqt(pchan->quat, quat_orig, quat_interp, 1.0f/6.0f);
441                         }
442                 }
443         }
444         
445         /* free the path now */
446         MEM_freeN(path);
447 }
448
449 /* apply() - perform the pose sliding based on weighting various poses */
450 static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
451 {
452         tPChanFCurveLink *pfl;
453         
454         /* sanitise the frame ranges */
455         if (pso->prevFrame == pso->nextFrame) {
456                 /* move out one step either side */
457                 pso->prevFrame--;
458                 pso->nextFrame++;
459         }
460         
461         /* for each link, handle each set of transforms */
462         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
463                 /* valid transforms for each PoseChannel should have been noted already 
464                  *      - sliding the pose should be a straightforward exercise for location+rotation, 
465                  *        but rotations get more complicated since we may want to use quaternion blending 
466                  *        for quaternions instead...
467                  */
468                 bPoseChannel *pchan= pfl->pchan;
469                  
470                 if (pchan->flag & POSE_LOC) {
471                         /* calculate these for the 'location' vector, and use location curves */
472                         pose_slide_apply_vec3(pso, pfl, pchan->loc, "location");
473                 }
474                 
475                 if (pchan->flag & POSE_SIZE) {
476                         /* calculate these for the 'scale' vector, and use scale curves */
477                         pose_slide_apply_vec3(pso, pfl, pchan->size, "scale");
478                 }
479                 
480                 if (pchan->flag & POSE_ROT) {
481                         /* everything depends on the rotation mode */
482                         if (pchan->rotmode > 0) {
483                                 /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
484                                 pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler");
485                         }
486                         else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
487                                 // TODO: need to figure out how to do this!
488                         }
489                         else {
490                                 /* quaternions - use quaternion blending */
491                                 pose_slide_apply_quat(pso, pfl);
492                         }
493                 }
494                 
495                 if (pfl->oldprops) {
496                         /* not strictly a transform, but contributes to the pose produced in many rigs */
497                         pose_slide_apply_props(pso, pfl);
498                 }
499         }
500         
501         /* depsgraph updates + redraws */
502         pose_slide_refresh(C, pso);
503 }
504
505 /* perform autokeyframing after changes were made + confirmed */
506 static void pose_slide_autoKeyframe (bContext *C, tPoseSlideOp *pso)
507 {
508         /* wrapper around the generic call */
509         poseAnim_mapping_autoKeyframe(C, pso->scene, pso->ob, &pso->pfLinks, (float)pso->cframe);
510 }
511
512 /* reset changes made to current pose */
513 static void pose_slide_reset (tPoseSlideOp *pso)
514 {
515         /* wrapper around the generic call, so that custom stuff can be added later */
516         poseAnim_mapping_reset(&pso->pfLinks);
517 }
518
519 /* ------------------------------------ */
520
521 /* common code for invoke() methods */
522 static int pose_slide_invoke_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
523 {
524         tPChanFCurveLink *pfl;
525         AnimData *adt= pso->ob->adt;
526         wmWindow *win= CTX_wm_window(C);
527         
528         /* for each link, add all its keyframes to the search tree */
529         for (pfl= pso->pfLinks.first; pfl; pfl= pfl->next) {
530                 LinkData *ld;
531                 
532                 /* do this for each F-Curve */
533                 for (ld= pfl->fcurves.first; ld; ld= ld->next) {
534                         FCurve *fcu= (FCurve *)ld->data;
535                         fcurve_to_keylist(adt, fcu, &pso->keys, NULL);
536                 }
537         }
538         
539         /* consolidate these keyframes, and figure out the nearest ones */
540         BLI_dlrbTree_linkedlist_sync(&pso->keys);
541         
542                 /* cancel if no keyframes found... */
543         if (pso->keys.root) {
544                 ActKeyColumn *ak;
545                 float cframe= (float)pso->cframe;
546                 
547                 /* firstly, check if the current frame is a keyframe... */
548                 ak= (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe);
549                 
550                 if (ak == NULL) {
551                         /* current frame is not a keyframe, so search */
552                         ActKeyColumn *pk= (ActKeyColumn *)BLI_dlrbTree_search_prev(&pso->keys, compare_ak_cfraPtr, &cframe);
553                         ActKeyColumn *nk= (ActKeyColumn *)BLI_dlrbTree_search_next(&pso->keys, compare_ak_cfraPtr, &cframe);
554                         
555                         /* new set the frames */
556                                 /* prev frame */
557                         pso->prevFrame= (pk)? (pk->cfra) : (pso->cframe - 1);
558                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
559                                 /* next frame */
560                         pso->nextFrame= (nk)? (nk->cfra) : (pso->cframe + 1);
561                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
562                 }
563                 else {
564                         /* current frame itself is a keyframe, so just take keyframes on either side */
565                                 /* prev frame */
566                         pso->prevFrame= (ak->prev)? (ak->prev->cfra) : (pso->cframe - 1);
567                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
568                                 /* next frame */
569                         pso->nextFrame= (ak->next)? (ak->next->cfra) : (pso->cframe + 1);
570                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
571                 }
572         }
573         else {
574                 BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between.");
575                 pose_slide_exit(op);
576                 return OPERATOR_CANCELLED;
577         }
578         
579         /* initial apply for operator... */
580         // TODO: need to calculate percentage for initial round too...
581         pose_slide_apply(C, pso);
582         
583         /* depsgraph updates + redraws */
584         pose_slide_refresh(C, pso);
585         
586         /* set cursor to indicate modal */
587         WM_cursor_modal(win, BC_EW_SCROLLCURSOR);
588         
589         /* add a modal handler for this operator */
590         WM_event_add_modal_handler(C, op);
591         return OPERATOR_RUNNING_MODAL;
592 }
593
594 /* common code for modal() */
595 static int pose_slide_modal (bContext *C, wmOperator *op, wmEvent *evt)
596 {
597         tPoseSlideOp *pso= op->customdata;
598         wmWindow *win= CTX_wm_window(C);
599         
600         switch (evt->type) {
601                 case LEFTMOUSE: /* confirm */
602                 {
603                         /* return to normal cursor */
604                         WM_cursor_restore(win);
605                         
606                         /* insert keyframes as required... */
607                         pose_slide_autoKeyframe(C, pso);
608                         pose_slide_exit(op);
609                         
610                         /* done! */
611                         return OPERATOR_FINISHED;
612                 }
613                 
614                 case ESCKEY:    /* cancel */
615                 case RIGHTMOUSE: 
616                 {
617                         /* return to normal cursor */
618                         WM_cursor_restore(win);
619                         
620                         /* reset transforms back to original state */
621                         pose_slide_reset(pso);
622                         
623                         /* depsgraph updates + redraws */
624                         pose_slide_refresh(C, pso);
625                         
626                         /* clean up temp data */
627                         pose_slide_exit(op);
628                         
629                         /* cancelled! */
630                         return OPERATOR_CANCELLED;
631                 }
632                         
633                 case MOUSEMOVE: /* calculate new position */
634                 {
635                         /* calculate percentage based on position of mouse (we only use x-axis for now.
636                          * since this is more conveninent for users to do), and store new percentage value 
637                          */
638                         pso->percentage= (evt->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx);
639                         RNA_float_set(op->ptr, "percentage", pso->percentage);
640                         
641                         /* reset transforms (to avoid accumulation errors) */
642                         pose_slide_reset(pso);
643                         
644                         /* apply... */
645                         pose_slide_apply(C, pso);
646                 }
647                         break;
648                         
649                 default: /* unhandled event (maybe it was some view manip? */
650                         /* allow to pass through */
651                         return OPERATOR_RUNNING_MODAL|OPERATOR_PASS_THROUGH;
652         }
653         
654         /* still running... */
655         return OPERATOR_RUNNING_MODAL;
656 }
657
658 /* common code for cancel() */
659 static int pose_slide_cancel (bContext *UNUSED(C), wmOperator *op)
660 {
661         /* cleanup and done */
662         pose_slide_exit(op);
663         return OPERATOR_CANCELLED;
664 }
665
666 /* common code for exec() methods */
667 static int pose_slide_exec_common (bContext *C, wmOperator *op, tPoseSlideOp *pso)
668 {
669         /* settings should have been set up ok for applying, so just apply! */
670         pose_slide_apply(C, pso);
671         
672         /* insert keyframes if needed */
673         pose_slide_autoKeyframe(C, pso);
674         
675         /* cleanup and done */
676         pose_slide_exit(op);
677         
678         return OPERATOR_FINISHED;
679 }
680
681 /* common code for defining RNA properties */
682 static void pose_slide_opdef_properties (wmOperatorType *ot)
683 {
684         RNA_def_int(ot->srna, "prev_frame", 0, MINAFRAME, MAXFRAME, "Previous Keyframe", "Frame number of keyframe immediately before the current frame.", 0, 50);
685         RNA_def_int(ot->srna, "next_frame", 0, MINAFRAME, MAXFRAME, "Next Keyframe", "Frame number of keyframe immediately after the current frame.", 0, 50);
686         RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for the sliding operation", 0.3, 0.7);
687 }
688
689 /* ------------------------------------ */
690
691 /* invoke() - for 'push' mode */
692 static int pose_slide_push_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
693 {
694         tPoseSlideOp *pso;
695         
696         /* initialise data  */
697         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
698                 pose_slide_exit(op);
699                 return OPERATOR_CANCELLED;
700         }
701         else
702                 pso= op->customdata;
703         
704         /* do common setup work */
705         return pose_slide_invoke_common(C, op, pso);
706 }
707
708 /* exec() - for push */
709 static int pose_slide_push_exec (bContext *C, wmOperator *op)
710 {
711         tPoseSlideOp *pso;
712         
713         /* initialise data (from RNA-props) */
714         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
715                 pose_slide_exit(op);
716                 return OPERATOR_CANCELLED;
717         }
718         else
719                 pso= op->customdata;
720                 
721         /* do common exec work */
722         return pose_slide_exec_common(C, op, pso);
723 }
724
725 void POSE_OT_push (wmOperatorType *ot)
726 {
727         /* identifiers */
728         ot->name= "Push Pose";
729         ot->idname= "POSE_OT_push";
730         ot->description= "Exaggerate the current pose";
731         
732         /* callbacks */
733         ot->exec= pose_slide_push_exec;
734         ot->invoke= pose_slide_push_invoke;
735         ot->modal= pose_slide_modal;
736         ot->cancel= pose_slide_cancel;
737         ot->poll= ED_operator_posemode;
738         
739         /* flags */
740         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
741         
742         /* Properties */
743         pose_slide_opdef_properties(ot);
744 }
745
746 /* ........................ */
747
748 /* invoke() - for 'relax' mode */
749 static int pose_slide_relax_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
750 {
751         tPoseSlideOp *pso;
752         
753         /* initialise data  */
754         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
755                 pose_slide_exit(op);
756                 return OPERATOR_CANCELLED;
757         }
758         else
759                 pso= op->customdata;
760         
761         /* do common setup work */
762         return pose_slide_invoke_common(C, op, pso);
763 }
764
765 /* exec() - for relax */
766 static int pose_slide_relax_exec (bContext *C, wmOperator *op)
767 {
768         tPoseSlideOp *pso;
769         
770         /* initialise data (from RNA-props) */
771         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
772                 pose_slide_exit(op);
773                 return OPERATOR_CANCELLED;
774         }
775         else
776                 pso= op->customdata;
777                 
778         /* do common exec work */
779         return pose_slide_exec_common(C, op, pso);
780 }
781
782 void POSE_OT_relax (wmOperatorType *ot)
783 {
784         /* identifiers */
785         ot->name= "Relax Pose";
786         ot->idname= "POSE_OT_relax";
787         ot->description= "Make the current pose more similar to its surrounding ones";
788         
789         /* callbacks */
790         ot->exec= pose_slide_relax_exec;
791         ot->invoke= pose_slide_relax_invoke;
792         ot->modal= pose_slide_modal;
793         ot->cancel= pose_slide_cancel;
794         ot->poll= ED_operator_posemode;
795         
796         /* flags */
797         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
798         
799         /* Properties */
800         pose_slide_opdef_properties(ot);
801 }
802
803 /* ........................ */
804
805 /* invoke() - for 'breakdown' mode */
806 static int pose_slide_breakdown_invoke (bContext *C, wmOperator *op, wmEvent *UNUSED(evt))
807 {
808         tPoseSlideOp *pso;
809         
810         /* initialise data  */
811         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
812                 pose_slide_exit(op);
813                 return OPERATOR_CANCELLED;
814         }
815         else
816                 pso= op->customdata;
817         
818         /* do common setup work */
819         return pose_slide_invoke_common(C, op, pso);
820 }
821
822 /* exec() - for breakdown */
823 static int pose_slide_breakdown_exec (bContext *C, wmOperator *op)
824 {
825         tPoseSlideOp *pso;
826         
827         /* initialise data (from RNA-props) */
828         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
829                 pose_slide_exit(op);
830                 return OPERATOR_CANCELLED;
831         }
832         else
833                 pso= op->customdata;
834                 
835         /* do common exec work */
836         return pose_slide_exec_common(C, op, pso);
837 }
838
839 void POSE_OT_breakdown (wmOperatorType *ot)
840 {
841         /* identifiers */
842         ot->name= "Pose Breakdowner";
843         ot->idname= "POSE_OT_breakdown";
844         ot->description= "Create a suitable breakdown pose on the current frame";
845         
846         /* callbacks */
847         ot->exec= pose_slide_breakdown_exec;
848         ot->invoke= pose_slide_breakdown_invoke;
849         ot->modal= pose_slide_modal;
850         ot->cancel= pose_slide_cancel;
851         ot->poll= ED_operator_posemode;
852         
853         /* flags */
854         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
855         
856         /* Properties */
857         pose_slide_opdef_properties(ot);
858 }
859
860 /* **************************************************** */
861 /* B) Pose Propagate */
862
863 /* "termination conditions" - i.e. when we stop */
864 typedef enum ePosePropagate_Termination {
865                 /* stop when we run out of keyframes */
866         POSE_PROPAGATE_LAST_KEY = 0,
867                 /* stop after the next keyframe */
868         POSE_PROPAGATE_NEXT_KEY,
869                 /* stop after the specified frame */
870         POSE_PROPAGATE_BEFORE_FRAME,
871                 /* stop after */
872         POSE_PROPAGATE_SMART_HOLDS
873 } ePosePropagate_Termination;
874
875 /* --------------------------------- */
876
877 /* get frame on which the "hold" for the bone ends 
878  * XXX: this may not really work that well if a bone moves on some channels and not others
879  *              if this happens to be a major issue, scrap this, and just make this happen 
880  *              independently per F-Curve
881  */
882 static float pose_propagate_get_boneHoldEndFrame (Object *ob, tPChanFCurveLink *pfl, float startFrame)
883 {
884         DLRBT_Tree keys, blocks;
885         ActKeyBlock *ab;
886         
887         AnimData *adt= ob->adt;
888         LinkData *ld;
889         float endFrame = startFrame;
890         
891         /* set up optimised data-structures for searching for relevant keyframes + holds */
892         BLI_dlrbTree_init(&keys);
893         BLI_dlrbTree_init(&blocks);
894         
895         for (ld = pfl->fcurves.first; ld; ld = ld->next) {
896                 FCurve *fcu = (FCurve *)ld->data;
897                 fcurve_to_keylist(adt, fcu, &keys, &blocks);
898         }
899         
900         BLI_dlrbTree_linkedlist_sync(&keys);
901         BLI_dlrbTree_linkedlist_sync(&blocks);
902         
903         /* find the long keyframe (i.e. hold), and hence obtain the endFrame value 
904          *      - the best case would be one that starts on the frame itself
905          */
906         ab = (ActKeyBlock *)BLI_dlrbTree_search_exact(&blocks, compare_ab_cfraPtr, &startFrame);
907         
908         if (actkeyblock_is_valid(ab, &keys) == 0) {
909                 /* There are only two cases for no-exact match:
910                  *      1) the current frame is just before another key but not on a key itself
911                  *      2) the current frame is on a key, but that key doesn't link to the next
912                  *
913                  * If we've got the first case, then we can search for another block, 
914                  * otherwise forget it, as we'd be overwriting some valid data.
915                  */
916                 if (BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &startFrame) == NULL) {
917                         /* we've got case 1, so try the one after */
918                         ab = (ActKeyBlock *)BLI_dlrbTree_search_next(&blocks, compare_ab_cfraPtr, &startFrame);
919                         
920                         if (actkeyblock_is_valid(ab, &keys) == 0) {
921                                 /* try the block before this frame then as last resort */
922                                 ab = (ActKeyBlock *)BLI_dlrbTree_search_prev(&blocks, compare_ab_cfraPtr, &startFrame);
923                                 
924                                 /* whatever happens, stop searching now... */
925                                 if (actkeyblock_is_valid(ab, &keys) == 0) {
926                                         /* restrict range to just the frame itself 
927                                          * i.e. everything is in motion, so no holds to safely overwrite
928                                          */
929                                         ab = NULL;
930                                 }
931                         }
932                 }
933                 else {
934                         /* we've got case 2 - set ab to NULL just in case, since we shouldn't do anything in this case */
935                         ab = NULL;
936                 }
937         }
938         
939         /* check if we can go any further than we've already gone */
940         if (ab) {
941                 /* go to next if it is also valid and meets "extension" criteria */
942                 while (ab->next) {
943                         ActKeyBlock *abn = (ActKeyBlock *)ab->next;
944                         
945                         /* must be valid */
946                         if (actkeyblock_is_valid(abn, &keys) == 0)
947                                 break;
948                         /* should start on the same frame that the last ended on */
949                         if (ab->end != abn->start)
950                                 break;
951                         /* should have the same number of curves */
952                         if (ab->totcurve != abn->totcurve)
953                                 break;
954                         /* should have the same value 
955                          * XXX: this may be a bit fuzzy on larger data sets, so be careful
956                          */
957                         if (ab->val != abn->val)
958                                 break;
959                                 
960                         /* we can extend the bounds to the end of this "next" block now */
961                         ab = abn;
962                 }
963                 
964                 /* end frame can now take the value of the end of the block */
965                 endFrame = ab->end;
966         }
967         
968         /* free temp memory */
969         BLI_dlrbTree_free(&keys);
970         BLI_dlrbTree_free(&blocks);
971         
972         /* return the end frame we've found */
973         return endFrame;
974 }
975
976 /* get reference value from F-Curve using RNA */
977 static float pose_propagate_get_refVal (Object *ob, FCurve *fcu)
978 {
979         PointerRNA id_ptr, ptr;
980         PropertyRNA *prop;
981         float value;
982         
983         /* base pointer is always the object -> id_ptr */
984         RNA_id_pointer_create(&ob->id, &id_ptr);
985         
986         /* resolve the property... */
987         if (RNA_path_resolve(&id_ptr, fcu->rna_path, &ptr, &prop)) {
988                 if (RNA_property_array_check(&ptr, prop)) {
989                         /* array */
990                         if (fcu->array_index < RNA_property_array_length(&ptr, prop)) { 
991                                 switch (RNA_property_type(prop)) {
992                                         case PROP_BOOLEAN:
993                                                 value= (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index);
994                                                 break;
995                                         case PROP_INT:
996                                                 value= (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index);
997                                                 break;
998                                         case PROP_FLOAT:
999                                                 value= RNA_property_float_get_index(&ptr, prop, fcu->array_index);
1000                                                 break;
1001                                         default:
1002                                                 break;
1003                                 }
1004                         }
1005                 }
1006                 else {
1007                         /* not an array */
1008                         switch (RNA_property_type(prop)) {
1009                                 case PROP_BOOLEAN:
1010                                         value= (float)RNA_property_boolean_get(&ptr, prop);
1011                                         break;
1012                                 case PROP_INT:
1013                                         value= (float)RNA_property_int_get(&ptr, prop);
1014                                         break;
1015                                 case PROP_ENUM:
1016                                         value= (float)RNA_property_enum_get(&ptr, prop);
1017                                         break;
1018                                 case PROP_FLOAT:
1019                                         value= RNA_property_float_get(&ptr, prop);
1020                                         break;
1021                                 default:
1022                                         break;
1023                         }
1024                 }
1025         }
1026         
1027         return value;
1028 }
1029
1030 /* propagate just works along each F-Curve in turn */
1031 static void pose_propagate_fcurve (wmOperator *op, Object *ob, tPChanFCurveLink *pfl, FCurve *fcu, float startFrame, float endFrame)
1032 {
1033         const int mode = RNA_enum_get(op->ptr, "mode");
1034         
1035         BezTriple *bezt;
1036         float refVal = 0.0f;
1037         short keyExists;
1038         int i, match;
1039         short first=1;
1040         
1041         /* skip if no keyframes to edit */
1042         if ((fcu->bezt == NULL) || (fcu->totvert < 2))
1043                 return;
1044                 
1045         /* find the reference value from bones directly, which means that the user
1046          * doesn't need to firstly keyframe the pose (though this doesn't mean that 
1047          * they can't either)
1048          */
1049         refVal = pose_propagate_get_refVal(ob, fcu);
1050         
1051         /* find the first keyframe to start propagating from 
1052          *      - if there's a keyframe on the current frame, we probably want to save this value there too
1053          *        since it may be as of yet unkeyed
1054          *      - if starting before the starting frame, don't touch the key, as it may have had some valid 
1055          *        values
1056          */
1057         match = binarysearch_bezt_index(fcu->bezt, startFrame, fcu->totvert, &keyExists);
1058         
1059         if (fcu->bezt[match].vec[1][0] < startFrame)
1060                 i = match + 1;
1061         else
1062                 i = match;
1063         
1064         for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) {
1065                 /* additional termination conditions based on the operator 'mode' property go here... */
1066                 if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) {
1067                         /* stop if keyframe is outside the accepted range */
1068                         if (bezt->vec[1][0] > endFrame)
1069                                 break;
1070                 }
1071                 else if (mode == POSE_PROPAGATE_NEXT_KEY) {
1072                         /* stop after the first keyframe has been processed */
1073                         if (first == 0)
1074                                 break;
1075                 }
1076                 
1077                 /* just flatten handles, since values will now be the same either side... */
1078                 // TODO: perhaps a fade-out modulation of the value is required here (optional once again)?
1079                 bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal;
1080                 
1081                 /* select keyframe to indicate that it's been changed */
1082                 bezt->f2 |= SELECT;
1083                 first = 0;
1084         }
1085 }
1086
1087 /* --------------------------------- */
1088
1089 static int pose_propagate_exec (bContext *C, wmOperator *op)
1090 {
1091         Scene *scene = CTX_data_scene(C);
1092         Object *ob= ED_object_pose_armature(CTX_data_active_object(C));
1093         bAction *act= (ob && ob->adt)? ob->adt->action : NULL;
1094         
1095         ListBase pflinks = {NULL, NULL};
1096         tPChanFCurveLink *pfl;
1097         
1098         float endFrame = RNA_float_get(op->ptr, "end_frame");
1099         const int mode = RNA_enum_get(op->ptr, "mode");
1100         
1101         /* sanity checks */
1102         if (ob == NULL) {
1103                 BKE_report(op->reports, RPT_ERROR, "No object to propagate poses for");
1104                 return OPERATOR_CANCELLED;
1105         }
1106         if (act == NULL) {
1107                 BKE_report(op->reports, RPT_ERROR, "No keyframed poses to propagate to");
1108                 return OPERATOR_CANCELLED;
1109         }
1110         
1111         /* isolate F-Curves related to the selected bones */
1112         poseAnim_mapping_get(C, &pflinks, ob, act);
1113         
1114         /* for each bone, perform the copying required */
1115         for (pfl = pflinks.first; pfl; pfl = pfl->next) {
1116                 LinkData *ld;
1117                 
1118                 /* mode-specific data preprocessing (requiring access to all curves) */
1119                 if (mode == POSE_PROPAGATE_SMART_HOLDS) {
1120                         /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
1121                          * from the keyframe that occurs after the current frame
1122                          */
1123                         endFrame = pose_propagate_get_boneHoldEndFrame(ob, pfl, (float)CFRA);
1124                 }
1125                 
1126                 /* go through propagating pose to keyframes, curve by curve */
1127                 for (ld = pfl->fcurves.first; ld; ld= ld->next)
1128                         pose_propagate_fcurve(op, ob, pfl, (FCurve *)ld->data, (float)CFRA, endFrame);
1129         }
1130         
1131         /* free temp data */
1132         poseAnim_mapping_free(&pflinks);
1133         
1134         /* updates + notifiers */
1135         poseAnim_mapping_refresh(C, scene, ob);
1136         
1137         return OPERATOR_FINISHED;
1138 }
1139
1140 /* --------------------------------- */
1141
1142 void POSE_OT_propagate (wmOperatorType *ot)
1143 {
1144         static EnumPropertyItem terminate_items[]= {
1145                 {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "Last Keyframe", ""},
1146                 {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "Next Keyframe", ""},
1147                 {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame", "Propagate pose to all keyframes between current frame and 'Frame' property"},
1148                 {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", "Propagate pose to all keyframes after current frame that don't change (Default behaviour)"},
1149                 {0, NULL, 0, NULL, NULL}};
1150                 
1151         /* identifiers */
1152         ot->name= "Propagate Pose";
1153         ot->idname= "POSE_OT_propagate";
1154         ot->description= "Copy selected aspects of the current pose to subsequent poses already keyframed";
1155         
1156         /* callbacks */
1157         ot->exec= pose_propagate_exec;
1158         ot->poll= ED_operator_posemode; // XXX: needs selected bones!
1159         
1160         /* flag */
1161         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1162         
1163         /* properties */
1164         // TODO: add "fade out" control for tapering off amount of propagation as time goes by?
1165         ot->prop= RNA_def_enum(ot->srna, "mode", terminate_items, POSE_PROPAGATE_SMART_HOLDS, "Terminate Mode", "Method used to determine when to stop propagating pose to keyframes");
1166         RNA_def_float(ot->srna, "end_frame", 250.0, FLT_MIN, FLT_MAX, "End Frame", "Frame to stop propagating frames to", 1.0, 250.0);
1167 }
1168
1169 /* **************************************************** */