Cleanup: comment line length (editors)
[blender.git] / source / blender / editors / armature / pose_slide.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung
19  * This is a new part of Blender
20  *
21  * Contributor(s): Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/armature/pose_slide.c
27  *  \ingroup edarmature
28  */
29
30 #include "MEM_guardedalloc.h"
31
32 #include "BLI_math.h"
33 #include "BLI_blenlib.h"
34 #include "BLI_dlrbTree.h"
35
36 #include "DNA_anim_types.h"
37 #include "DNA_armature_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_scene_types.h"
40
41 #include "BKE_fcurve.h"
42 #include "BKE_nla.h"
43
44 #include "BKE_context.h"
45 #include "BKE_object.h"
46 #include "BKE_report.h"
47 #include "BKE_unit.h"
48
49 #include "RNA_access.h"
50 #include "RNA_define.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54
55 #include "UI_interface.h"
56
57 #include "ED_armature.h"
58 #include "ED_keyframes_draw.h"
59 #include "ED_markers.h"
60 #include "ED_numinput.h"
61 #include "ED_screen.h"
62
63 #include "armature_intern.h"
64
65 /* **************************************************** */
66 /* == POSE 'SLIDING' TOOLS ==
67  *
68  * A) Push & Relax, Breakdowner
69  * These tools provide the animator with various capabilities
70  * for interactively controlling the spacing of poses, but also
71  * for 'pushing' and/or 'relaxing' extremes as they see fit.
72  *
73  * B) Propagate
74  * This tool copies elements of the selected pose to successive
75  * keyframes, allowing the animator to go back and modify the poses
76  * for some "static" pose controls, without having to repeatedly
77  * doing a "next paste" dance.
78  *
79  * C) Pose Sculpting
80  * This is yet to be implemented, but the idea here is to use
81  * sculpting techniques to make it easier to pose rigs by allowing
82  * rigs to be manipulated using a familiar paint-based interface.
83  */
84 /* **************************************************** */
85 /* A) Push & Relax, Breakdowner */
86
87 /* Temporary data shared between these operators */
88 typedef struct tPoseSlideOp {
89         Scene *scene;       /* current scene */
90         ScrArea *sa;        /* area that we're operating in (needed for modal()) */
91         ARegion *ar;        /* region that we're operating in (needed for modal()) */
92         Object *ob;         /* active object that Pose Info comes from */
93         bArmature *arm;     /* armature for pose */
94
95         ListBase pfLinks;   /* links between posechannels and f-curves  */
96         DLRBT_Tree keys;    /* binary tree for quicker searching for keyframes (when applicable) */
97
98         /** current frame number - global time */
99         int cframe;
100
101         /** frame before current frame (blend-from) - global time */
102         int prevFrame;
103         /** frame after current frame (blend-to)    - global time */
104         int nextFrame;
105
106         float prevFrameF;   /* prevFrame, but in local action time (for F-Curve lookups to work) */
107         float nextFrameF;   /* nextFrame, but in local action time (for F-Curve lookups to work) */
108
109         short mode;         /* sliding mode (ePoseSlide_Modes) */
110         short flag;         /* unused for now, but can later get used for storing runtime settings.... */
111
112         /** which transforms/channels are affected (ePoseSlide_Channels) */
113         short channels;
114         /** axis-limits for transforms (ePoseSlide_AxisLock) */
115         short axislock;
116
117         /** 0-1 value for determining the influence of whatever is relevant */
118         float percentage;
119
120         NumInput num;       /* numeric input */
121 } tPoseSlideOp;
122
123 /* Pose Sliding Modes */
124 typedef enum ePoseSlide_Modes {
125         POSESLIDE_PUSH  = 0,        /* exaggerate the pose... */
126         POSESLIDE_RELAX,            /* soften the pose... */
127         POSESLIDE_BREAKDOWN,        /* slide between the endpoint poses, finding a 'soft' spot */
128 } ePoseSlide_Modes;
129
130
131 /* Transforms/Channels to Affect */
132 typedef enum ePoseSlide_Channels {
133         PS_TFM_ALL = 0,              /* All transforms and properties */
134
135         PS_TFM_LOC,                  /* Loc/Rot/Scale */
136         PS_TFM_ROT,
137         PS_TFM_SIZE,
138
139         PS_TFM_BBONE_SHAPE,          /* Bendy Bones */
140
141         PS_TFM_PROPS                 /* Custom Properties */
142 } ePoseSlide_Channels;
143
144 /* Property enum for ePoseSlide_Channels */
145 static const EnumPropertyItem prop_channels_types[] = {
146         {PS_TFM_ALL, "ALL", 0, "All Properties",
147          "All properties, including transforms, bendy bone shape, and custom properties"},
148         {PS_TFM_LOC, "LOC", 0, "Location", "Location only"},
149         {PS_TFM_ROT, "ROT", 0, "Rotation", "Rotation only"},
150         {PS_TFM_SIZE, "SIZE", 0, "Scale", "Scale only"},
151         {PS_TFM_BBONE_SHAPE, "BBONE", 0, "Bendy Bone", "Bendy Bone shape properties"},
152         {PS_TFM_PROPS, "CUSTOM", 0, "Custom Properties", "Custom properties"},
153         {0, NULL, 0, NULL, NULL}
154 };
155
156 /* Axis Locks */
157 typedef enum ePoseSlide_AxisLock {
158         PS_LOCK_X = (1 << 0),
159         PS_LOCK_Y = (1 << 1),
160         PS_LOCK_Z = (1 << 2)
161 } ePoseSlide_AxisLock;
162
163 /* Property enum for ePoseSlide_AxisLock */
164 static const EnumPropertyItem prop_axis_lock_types[] = {
165         {0, "FREE", 0, "Free", "All axes are affected"},
166         {PS_LOCK_X, "X", 0, "X", "Only X-axis transforms are affected"},
167         {PS_LOCK_Y, "Y", 0, "Y", "Only Y-axis transforms are affected"},
168         {PS_LOCK_Z, "Z", 0, "Z", "Only Z-axis transforms are affected"},
169         /* TODO: Combinations? */
170         {0, NULL, 0, NULL, NULL}
171 };
172
173 /* ------------------------------------ */
174
175 /* operator init */
176 static int pose_slide_init(bContext *C, wmOperator *op, short mode)
177 {
178         tPoseSlideOp *pso;
179         bAction *act = NULL;
180
181         /* init slide-op data */
182         pso = op->customdata = MEM_callocN(sizeof(tPoseSlideOp), "tPoseSlideOp");
183
184         /* get info from context */
185         pso->scene = CTX_data_scene(C);
186         pso->ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
187         pso->arm = (pso->ob) ? pso->ob->data : NULL;
188         pso->sa = CTX_wm_area(C); /* only really needed when doing modal() */
189         pso->ar = CTX_wm_region(C); /* only really needed when doing modal() */
190
191         pso->cframe = pso->scene->r.cfra;
192         pso->mode = mode;
193
194         /* set range info from property values - these may get overridden for the invoke() */
195         pso->percentage = RNA_float_get(op->ptr, "percentage");
196         pso->prevFrame = RNA_int_get(op->ptr, "prev_frame");
197         pso->nextFrame = RNA_int_get(op->ptr, "next_frame");
198
199         /* get the set of properties/axes that can be operated on */
200         pso->channels = RNA_enum_get(op->ptr, "channels");
201         pso->axislock = RNA_enum_get(op->ptr, "axis_lock");
202
203         /* ensure validity of the settings from the context */
204         if (ELEM(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action))
205                 return 0;
206
207         act = pso->ob->adt->action;
208
209         /* apply NLA mapping corrections so the frame lookups work */
210         pso->prevFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP);
211         pso->nextFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP);
212
213         /* for each Pose-Channel which gets affected, get the F-Curves for that channel
214          * and set the relevant transform flags...
215          */
216         poseAnim_mapping_get(C, &pso->pfLinks, pso->ob, act);
217
218         /* set depsgraph flags */
219         /* make sure the lock is set OK, unlock can be accidentally saved? */
220         pso->ob->pose->flag |= POSE_LOCKED;
221         pso->ob->pose->flag &= ~POSE_DO_UNLOCK;
222
223         /* do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up
224          * to the caller of this (usually only invoke() will do it, to make things more efficient).
225          */
226         BLI_dlrbTree_init(&pso->keys);
227
228         /* initialise numeric input */
229         initNumInput(&pso->num);
230         pso->num.idx_max = 0; /* one axis */
231         pso->num.val_flag[0] |= NUM_NO_NEGATIVE;
232         pso->num.unit_type[0] = B_UNIT_NONE; /* percentages don't have any units... */
233
234         /* return status is whether we've got all the data we were requested to get */
235         return 1;
236 }
237
238 /* exiting the operator - free data */
239 static void pose_slide_exit(wmOperator *op)
240 {
241         tPoseSlideOp *pso = op->customdata;
242
243         /* if data exists, clear its data and exit */
244         if (pso) {
245                 /* free the temp pchan links and their data */
246                 poseAnim_mapping_free(&pso->pfLinks);
247
248                 /* free RB-BST for keyframes (if it contained data) */
249                 BLI_dlrbTree_free(&pso->keys);
250
251                 /* free data itself */
252                 MEM_freeN(pso);
253         }
254
255         /* cleanup */
256         op->customdata = NULL;
257 }
258
259 /* ------------------------------------ */
260
261 /* helper for apply() / reset() - refresh the data */
262 static void pose_slide_refresh(bContext *C, tPoseSlideOp *pso)
263 {
264         /* wrapper around the generic version, allowing us to add some custom stuff later still */
265         poseAnim_mapping_refresh(C, pso->scene, pso->ob);
266 }
267
268 /* helper for apply() - perform sliding for some value */
269 static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, float *val)
270 {
271         float cframe = (float)pso->cframe;
272         float sVal, eVal;
273         float w1, w2;
274
275         /* get keyframe values for endpoint poses to blend with */
276         /* previous/start */
277         sVal = evaluate_fcurve(fcu, pso->prevFrameF);
278         /* next/end */
279         eVal = evaluate_fcurve(fcu, pso->nextFrameF);
280
281         /* if both values are equal, don't do anything */
282         if (IS_EQF(sVal, eVal)) {
283                 (*val) = sVal;
284                 return;
285         }
286
287         /* calculate the relative weights of the endpoints */
288         if (pso->mode == POSESLIDE_BREAKDOWN) {
289                 /* get weights from the percentage control */
290                 w1 = pso->percentage;    /* this must come second */
291                 w2 = 1.0f - w1;          /* this must come first */
292         }
293         else {
294                 /* - these weights are derived from the relative distance of these
295                  *   poses from the current frame
296                  * - they then get normalized so that they only sum up to 1
297                  */
298                 float wtot;
299
300                 w1 = cframe - (float)pso->prevFrame;
301                 w2 = (float)pso->nextFrame - cframe;
302
303                 wtot = w1 + w2;
304                 w1 = (w1 / wtot);
305                 w2 = (w2 / wtot);
306         }
307
308         /* depending on the mode, calculate the new value
309          * - in all of these, the start+end values are multiplied by w2 and w1 (respectively),
310          *   since multiplication in another order would decrease the value the current frame is closer to
311          */
312         switch (pso->mode) {
313                 case POSESLIDE_PUSH: /* make the current pose more pronounced */
314                 {
315                         /* perform a weighted average here, favoring the middle pose
316                          * - numerator should be larger than denominator to 'expand' the result
317                          * - perform this weighting a number of times given by the percentage...
318                          */
319                         /* TODO: maybe a sensitivity ctrl on top of this is needed */
320                         int iters = (int)ceil(10.0f * pso->percentage);
321
322                         while (iters-- > 0) {
323                                 (*val) = (-((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f) ) / 5.0f;
324                         }
325                         break;
326                 }
327                 case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */
328                 {
329                         /* perform a weighted average here, favoring the middle pose
330                          * - numerator should be smaller than denominator to 'relax' the result
331                          * - perform this weighting a number of times given by the percentage...
332                          */
333                         /* TODO: maybe a sensitivity ctrl on top of this is needed */
334                         int iters = (int)ceil(10.0f * pso->percentage);
335
336                         while (iters-- > 0) {
337                                 (*val) = ( ((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f) ) / 6.0f;
338                         }
339                         break;
340                 }
341                 case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */
342                 {
343                         /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */
344                         /* TODO: make this use some kind of spline interpolation instead? */
345                         (*val) = ((sVal * w2) + (eVal * w1));
346                         break;
347                 }
348         }
349 }
350
351 /* helper for apply() - perform sliding for some 3-element vector */
352 static void pose_slide_apply_vec3(tPoseSlideOp *pso, tPChanFCurveLink *pfl, float vec[3], const char propName[])
353 {
354         LinkData *ld = NULL;
355         char *path = NULL;
356
357         /* get the path to use... */
358         path = BLI_sprintfN("%s.%s", pfl->pchan_path, propName);
359
360         /* using this path, find each matching F-Curve for the variables we're interested in */
361         while ( (ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
362                 FCurve *fcu = (FCurve *)ld->data;
363                 const int idx  = fcu->array_index;
364                 const int lock = pso->axislock;
365
366                 /* check if this F-Curve is ok given the current axis locks */
367                 BLI_assert(fcu->array_index < 3);
368
369                 if ((lock == 0) ||
370                     ((lock & PS_LOCK_X) && (idx == 0)) ||
371                     ((lock & PS_LOCK_Y) && (idx == 1)) ||
372                     ((lock & PS_LOCK_Z) && (idx == 2)))
373                 {
374                         /* just work on these channels one by one... there's no interaction between values */
375                         pose_slide_apply_val(pso, fcu, &vec[fcu->array_index]);
376                 }
377         }
378
379         /* free the temp path we got */
380         MEM_freeN(path);
381 }
382
383 /* helper for apply() - perform sliding for custom properties or bbone properties */
384 static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl, const char prop_prefix[])
385 {
386         PointerRNA ptr = {{NULL}};
387         LinkData *ld;
388         int len = strlen(pfl->pchan_path);
389
390         /* setup pointer RNA for resolving paths */
391         RNA_pointer_create(NULL, &RNA_PoseBone, pfl->pchan, &ptr);
392
393         /* - custom properties are just denoted using ["..."][etc.] after the end of the base path,
394          *   so just check for opening pair after the end of the path
395          * - bbone properties are similar, but they always start with a prefix "bbone_*",
396          *   so a similar method should work here for those too
397          */
398         for (ld = pfl->fcurves.first; ld; ld = ld->next) {
399                 FCurve *fcu = (FCurve *)ld->data;
400                 const char *bPtr, *pPtr;
401
402                 if (fcu->rna_path == NULL)
403                         continue;
404
405                 /* do we have a match?
406                  * - bPtr is the RNA Path with the standard part chopped off
407                  * - pPtr is the chunk of the path which is left over
408                  */
409                 bPtr = strstr(fcu->rna_path, pfl->pchan_path) + len;
410                 pPtr = strstr(bPtr, prop_prefix);
411
412                 if (pPtr) {
413                         /* use RNA to try and get a handle on this property, then, assuming that it is just
414                          * numerical, try and grab the value as a float for temp editing before setting back
415                          */
416                         PropertyRNA *prop = RNA_struct_find_property(&ptr, pPtr);
417
418                         if (prop) {
419                                 switch (RNA_property_type(prop)) {
420                                         /* continuous values that can be smoothly interpolated... */
421                                         case PROP_FLOAT:
422                                         {
423                                                 float tval = RNA_property_float_get(&ptr, prop);
424                                                 pose_slide_apply_val(pso, fcu, &tval);
425                                                 RNA_property_float_set(&ptr, prop, tval);
426                                                 break;
427                                         }
428                                         case PROP_INT:
429                                         {
430                                                 float tval = (float)RNA_property_int_get(&ptr, prop);
431                                                 pose_slide_apply_val(pso, fcu, &tval);
432                                                 RNA_property_int_set(&ptr, prop, (int)tval);
433                                                 break;
434                                         }
435
436                                         /* values which can only take discrete values */
437                                         case PROP_BOOLEAN:
438                                         {
439                                                 float tval = (float)RNA_property_boolean_get(&ptr, prop);
440                                                 pose_slide_apply_val(pso, fcu, &tval);
441                                                 RNA_property_boolean_set(&ptr, prop, (int)tval); // XXX: do we need threshold clamping here?
442                                                 break;
443                                         }
444                                         case PROP_ENUM:
445                                         {
446                                                 /* don't handle this case - these don't usually represent interchangeable
447                                                  * set of values which should be interpolated between
448                                                  */
449                                                 break;
450                                         }
451
452                                         default:
453                                                 /* cannot handle */
454                                                 //printf("Cannot Pose Slide non-numerical property\n");
455                                                 break;
456                                 }
457                         }
458                 }
459         }
460 }
461
462 /* helper for apply() - perform sliding for quaternion rotations (using quat blending) */
463 static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
464 {
465         FCurve *fcu_w = NULL, *fcu_x = NULL, *fcu_y = NULL, *fcu_z = NULL;
466         bPoseChannel *pchan = pfl->pchan;
467         LinkData *ld = NULL;
468         char *path = NULL;
469         float cframe;
470
471         /* get the path to use - this should be quaternion rotations only (needs care) */
472         path = BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion");
473
474         /* get the current frame number */
475         cframe = (float)pso->cframe;
476
477         /* using this path, find each matching F-Curve for the variables we're interested in */
478         while ( (ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) {
479                 FCurve *fcu = (FCurve *)ld->data;
480
481                 /* assign this F-Curve to one of the relevant pointers... */
482                 switch (fcu->array_index) {
483                         case 3: /* z */
484                                 fcu_z = fcu;
485                                 break;
486                         case 2: /* y */
487                                 fcu_y = fcu;
488                                 break;
489                         case 1: /* x */
490                                 fcu_x = fcu;
491                                 break;
492                         case 0: /* w */
493                                 fcu_w = fcu;
494                                 break;
495                 }
496         }
497
498         /* only if all channels exist, proceed */
499         if (fcu_w && fcu_x && fcu_y && fcu_z) {
500                 float quat_prev[4], quat_next[4];
501
502                 /* get 2 quats */
503                 quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrameF);
504                 quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrameF);
505                 quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrameF);
506                 quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrameF);
507
508                 quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrameF);
509                 quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrameF);
510                 quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrameF);
511                 quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrameF);
512
513                 /* perform blending */
514                 if (pso->mode == POSESLIDE_BREAKDOWN) {
515                         /* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */
516                         interp_qt_qtqt(pchan->quat, quat_prev, quat_next, pso->percentage);
517                 }
518                 else if (pso->mode == POSESLIDE_PUSH) {
519                         float quat_diff[4], quat_orig[4];
520
521                         /* calculate the delta transform from the previous to the current */
522                         /* TODO: investigate ways to favour one transform more? */
523                         sub_qt_qtqt(quat_diff, pchan->quat, quat_prev);
524
525                         /* make a copy of the original rotation */
526                         copy_qt_qt(quat_orig, pchan->quat);
527
528                         /* increase the original by the delta transform, by an amount determined by percentage */
529                         add_qt_qtqt(pchan->quat, quat_orig, quat_diff, pso->percentage);
530                 }
531                 else {
532                         float quat_interp[4], quat_orig[4];
533                         /* TODO: maybe a sensitivity ctrl on top of this is needed */
534                         int iters = (int)ceil(10.0f * pso->percentage);
535
536                         /* perform this blending several times until a satisfactory result is reached */
537                         while (iters-- > 0) {
538                                 /* calculate the interpolation between the endpoints */
539                                 interp_qt_qtqt(quat_interp, quat_prev, quat_next, (cframe - pso->prevFrame) / (pso->nextFrame - pso->prevFrame));
540
541                                 /* make a copy of the original rotation */
542                                 copy_qt_qt(quat_orig, pchan->quat);
543
544                                 /* tricky interpolations - blending between original and new */
545                                 interp_qt_qtqt(pchan->quat, quat_orig, quat_interp, 1.0f / 6.0f);
546                         }
547                 }
548         }
549
550         /* free the path now */
551         MEM_freeN(path);
552 }
553
554 /* apply() - perform the pose sliding based on weighting various poses */
555 static void pose_slide_apply(bContext *C, tPoseSlideOp *pso)
556 {
557         tPChanFCurveLink *pfl;
558
559         /* sanitise the frame ranges */
560         if (pso->prevFrame == pso->nextFrame) {
561                 /* move out one step either side */
562                 pso->prevFrame--;
563                 pso->nextFrame++;
564
565                 /* apply NLA mapping corrections so the frame lookups work */
566                 pso->prevFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP);
567                 pso->nextFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP);
568         }
569
570         /* for each link, handle each set of transforms */
571         for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) {
572                 /* valid transforms for each PoseChannel should have been noted already
573                  * - sliding the pose should be a straightforward exercise for location+rotation,
574                  *   but rotations get more complicated since we may want to use quaternion blending
575                  *   for quaternions instead...
576                  */
577                 bPoseChannel *pchan = pfl->pchan;
578
579                 if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_LOC) && (pchan->flag & POSE_LOC)) {
580                         /* calculate these for the 'location' vector, and use location curves */
581                         pose_slide_apply_vec3(pso, pfl, pchan->loc, "location");
582                 }
583
584                 if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_SIZE) && (pchan->flag & POSE_SIZE)) {
585                         /* calculate these for the 'scale' vector, and use scale curves */
586                         pose_slide_apply_vec3(pso, pfl, pchan->size, "scale");
587                 }
588
589                 if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_ROT) && (pchan->flag & POSE_ROT)) {
590                         /* everything depends on the rotation mode */
591                         if (pchan->rotmode > 0) {
592                                 /* eulers - so calculate these for the 'eul' vector, and use euler_rotation curves */
593                                 pose_slide_apply_vec3(pso, pfl, pchan->eul, "rotation_euler");
594                         }
595                         else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
596                                 /* TODO: need to figure out how to do this! */
597                         }
598                         else {
599                                 /* quaternions - use quaternion blending */
600                                 pose_slide_apply_quat(pso, pfl);
601                         }
602                 }
603
604                 if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE) && (pchan->flag & POSE_BBONE_SHAPE)) {
605                         /* bbone properties - they all start a "bbone_" prefix */
606                         pose_slide_apply_props(pso, pfl, "bbone_");
607                 }
608
609                 if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_PROPS) && (pfl->oldprops)) {
610                         /* not strictly a transform, but custom properties contribute to the pose produced in many rigs
611                          * (e.g. the facial rigs used in Sintel)
612                          */
613                         pose_slide_apply_props(pso, pfl, "[\"");  /* dummy " for texteditor bugs */
614                 }
615         }
616
617         /* depsgraph updates + redraws */
618         pose_slide_refresh(C, pso);
619 }
620
621 /* perform autokeyframing after changes were made + confirmed */
622 static void pose_slide_autoKeyframe(bContext *C, tPoseSlideOp *pso)
623 {
624         /* wrapper around the generic call */
625         poseAnim_mapping_autoKeyframe(C, pso->scene, pso->ob, &pso->pfLinks, (float)pso->cframe);
626 }
627
628 /* reset changes made to current pose */
629 static void pose_slide_reset(tPoseSlideOp *pso)
630 {
631         /* wrapper around the generic call, so that custom stuff can be added later */
632         poseAnim_mapping_reset(&pso->pfLinks);
633 }
634
635 /* ------------------------------------ */
636
637 /* draw percentage indicator in header */
638 // TODO: Include hints about locks here...
639 static void pose_slide_draw_status(tPoseSlideOp *pso)
640 {
641         char status_str[UI_MAX_DRAW_STR];
642         char limits_str[UI_MAX_DRAW_STR];
643         char axis_str[50];
644         char mode_str[32];
645
646         switch (pso->mode) {
647                 case POSESLIDE_PUSH:
648                         strcpy(mode_str, "Push Pose");
649                         break;
650                 case POSESLIDE_RELAX:
651                         strcpy(mode_str, "Relax Pose");
652                         break;
653                 case POSESLIDE_BREAKDOWN:
654                         strcpy(mode_str, "Breakdown");
655                         break;
656
657                 default:
658                         /* unknown */
659                         strcpy(mode_str, "Sliding-Tool");
660                         break;
661         }
662
663         switch (pso->axislock) {
664                 case PS_LOCK_X:
665                         BLI_strncpy(axis_str, "[X]/Y/Z axis only (X to clear)", sizeof(axis_str));
666                         break;
667                 case PS_LOCK_Y:
668                         BLI_strncpy(axis_str, "X/[Y]/Z axis only (Y to clear)", sizeof(axis_str));
669                         break;
670                 case PS_LOCK_Z:
671                         BLI_strncpy(axis_str, "X/Y/[Z] axis only (Z to clear)", sizeof(axis_str));
672                         break;
673
674                 default:
675                         if (ELEM(pso->channels, PS_TFM_LOC, PS_TFM_ROT, PS_TFM_SIZE)) {
676                                 BLI_strncpy(axis_str, "X/Y/Z = Axis Constraint", sizeof(axis_str));
677                         }
678                         else {
679                                 axis_str[0] = '\0';
680                         }
681                         break;
682         }
683
684         switch (pso->channels) {
685                 case PS_TFM_LOC:
686                         BLI_snprintf(limits_str, sizeof(limits_str), "[G]/R/S/B/C - Location only (G to clear) | %s", axis_str);
687                         break;
688                 case PS_TFM_ROT:
689                         BLI_snprintf(limits_str, sizeof(limits_str), "G/[R]/S/B/C - Rotation only (R to clear) | %s", axis_str);
690                         break;
691                 case PS_TFM_SIZE:
692                         BLI_snprintf(limits_str, sizeof(limits_str), "G/R/[S]/B/C - Scale only (S to clear) | %s", axis_str);
693                         break;
694                 case PS_TFM_BBONE_SHAPE:
695                         BLI_strncpy(limits_str, "G/R/S/[B]/C - Bendy Bone properties only (B to clear) | %s", sizeof(limits_str));
696                         break;
697                 case PS_TFM_PROPS:
698                         BLI_strncpy(limits_str, "G/R/S/B/[C] - Custom Properties only (C to clear) | %s", sizeof(limits_str));
699                         break;
700                 default:
701                         BLI_strncpy(limits_str, "G/R/S/B/C - Limit to Transform/Property Set", sizeof(limits_str));
702                         break;
703         }
704
705         if (hasNumInput(&pso->num)) {
706                 Scene *scene = pso->scene;
707                 char str_offs[NUM_STR_REP_LEN];
708
709                 outputNumInput(&pso->num, str_offs, &scene->unit);
710
711                 BLI_snprintf(status_str, sizeof(status_str), "%s: %s     |   %s", mode_str, str_offs, limits_str);
712         }
713         else {
714                 BLI_snprintf(status_str, sizeof(status_str), "%s: %d %%     |   %s", mode_str, (int)(pso->percentage * 100.0f), limits_str);
715         }
716
717         ED_area_headerprint(pso->sa, status_str);
718 }
719
720 /* common code for invoke() methods */
721 static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *pso)
722 {
723         tPChanFCurveLink *pfl;
724         AnimData *adt = pso->ob->adt;
725         wmWindow *win = CTX_wm_window(C);
726
727         /* for each link, add all its keyframes to the search tree */
728         for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) {
729                 LinkData *ld;
730
731                 /* do this for each F-Curve */
732                 for (ld = pfl->fcurves.first; ld; ld = ld->next) {
733                         FCurve *fcu = (FCurve *)ld->data;
734                         fcurve_to_keylist(adt, fcu, &pso->keys, NULL);
735                 }
736         }
737
738         /* consolidate these keyframes, and figure out the nearest ones */
739         BLI_dlrbTree_linkedlist_sync(&pso->keys);
740
741         /* cancel if no keyframes found... */
742         if (pso->keys.root) {
743                 ActKeyColumn *ak;
744                 float cframe = (float)pso->cframe;
745
746                 /* firstly, check if the current frame is a keyframe... */
747                 ak = (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe);
748
749                 if (ak == NULL) {
750                         /* current frame is not a keyframe, so search */
751                         ActKeyColumn *pk = (ActKeyColumn *)BLI_dlrbTree_search_prev(&pso->keys, compare_ak_cfraPtr, &cframe);
752                         ActKeyColumn *nk = (ActKeyColumn *)BLI_dlrbTree_search_next(&pso->keys, compare_ak_cfraPtr, &cframe);
753
754                         /* new set the frames */
755                         /* prev frame */
756                         pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1);
757                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
758                         /* next frame */
759                         pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1);
760                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
761                 }
762                 else {
763                         /* current frame itself is a keyframe, so just take keyframes on either side */
764                         /* prev frame */
765                         pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1);
766                         RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
767                         /* next frame */
768                         pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1);
769                         RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
770                 }
771
772                 /* apply NLA mapping corrections so the frame lookups work */
773                 pso->prevFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP);
774                 pso->nextFrameF = BKE_nla_tweakedit_remap(pso->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP);
775         }
776         else {
777                 BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between");
778                 pose_slide_exit(op);
779                 return OPERATOR_CANCELLED;
780         }
781
782         /* initial apply for operator... */
783         /* TODO: need to calculate percentage for initial round too... */
784         pose_slide_apply(C, pso);
785
786         /* depsgraph updates + redraws */
787         pose_slide_refresh(C, pso);
788
789         /* set cursor to indicate modal */
790         WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR);
791
792         /* header print */
793         pose_slide_draw_status(pso);
794
795         /* add a modal handler for this operator */
796         WM_event_add_modal_handler(C, op);
797         return OPERATOR_RUNNING_MODAL;
798 }
799
800 /* calculate percentage based on position of mouse (we only use x-axis for now.
801  * since this is more convenient for users to do), and store new percentage value
802  */
803 static void pose_slide_mouse_update_percentage(tPoseSlideOp *pso, wmOperator *op, const wmEvent *event)
804 {
805         pso->percentage = (event->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx);
806         RNA_float_set(op->ptr, "percentage", pso->percentage);
807 }
808
809 /* handle an event to toggle channels mode */
810 static void pose_slide_toggle_channels_mode(wmOperator *op, tPoseSlideOp *pso, ePoseSlide_Channels channel)
811 {
812         /* Turn channel on or off? */
813         if (pso->channels == channel) {
814                 /* Already limiting to transform only, so pressing this again turns it off */
815                 pso->channels = PS_TFM_ALL;
816         }
817         else {
818                 /* Only this set of channels */
819                 pso->channels = channel;
820         }
821         RNA_enum_set(op->ptr, "channels", pso->channels);
822
823
824         /* Reset axis limits too for good measure */
825         pso->axislock = 0;
826         RNA_enum_set(op->ptr, "axis_lock", pso->axislock);
827 }
828
829 /* handle an event to toggle axis locks - returns whether any change in state is needed */
830 static bool pose_slide_toggle_axis_locks(wmOperator *op, tPoseSlideOp *pso, ePoseSlide_AxisLock axis)
831 {
832         /* Axis can only be set when a transform is set - it doesn't make sense otherwise */
833         if (ELEM(pso->channels, PS_TFM_ALL, PS_TFM_BBONE_SHAPE, PS_TFM_PROPS)) {
834                 pso->axislock = 0;
835                 RNA_enum_set(op->ptr, "axis_lock", pso->axislock);
836                 return false;
837         }
838
839         /* Turn on or off? */
840         if (pso->axislock == axis) {
841                 /* Already limiting on this axis, so turn off */
842                 pso->axislock = 0;
843         }
844         else {
845                 /* Only this axis */
846                 pso->axislock = axis;
847         }
848         RNA_enum_set(op->ptr, "axis_lock", pso->axislock);
849
850         /* Setting changed, so pose update is needed */
851         return true;
852 }
853
854 /* common code for modal() */
855 static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event)
856 {
857         tPoseSlideOp *pso = op->customdata;
858         wmWindow *win = CTX_wm_window(C);
859         bool do_pose_update = false;
860
861         const bool has_numinput = hasNumInput(&pso->num);
862
863         switch (event->type) {
864                 case LEFTMOUSE: /* confirm */
865                 case RETKEY:
866                 case PADENTER:
867                 {
868                         /* return to normal cursor and header status */
869                         ED_area_headerprint(pso->sa, NULL);
870                         WM_cursor_modal_restore(win);
871
872                         /* insert keyframes as required... */
873                         pose_slide_autoKeyframe(C, pso);
874                         pose_slide_exit(op);
875
876                         /* done! */
877                         return OPERATOR_FINISHED;
878                 }
879
880                 case ESCKEY:    /* cancel */
881                 case RIGHTMOUSE:
882                 {
883                         /* return to normal cursor and header status */
884                         ED_area_headerprint(pso->sa, NULL);
885                         WM_cursor_modal_restore(win);
886
887                         /* reset transforms back to original state */
888                         pose_slide_reset(pso);
889
890                         /* depsgraph updates + redraws */
891                         pose_slide_refresh(C, pso);
892
893                         /* clean up temp data */
894                         pose_slide_exit(op);
895
896                         /* canceled! */
897                         return OPERATOR_CANCELLED;
898                 }
899
900                 /* Percentage Chane... */
901                 case MOUSEMOVE: /* calculate new position */
902                 {
903                         /* only handle mousemove if not doing numinput */
904                         if (has_numinput == false) {
905                                 /* update percentage based on position of mouse */
906                                 pose_slide_mouse_update_percentage(pso, op, event);
907
908                                 /* update pose to reflect the new values (see below) */
909                                 do_pose_update = true;
910                         }
911                         break;
912                 }
913                 default:
914                 {
915                         if ((event->val == KM_PRESS) && handleNumInput(C, &pso->num, event)) {
916                                 float value;
917
918                                 /* Grab percentage from numeric input, and store this new value for redo
919                                  * NOTE: users see ints, while internally we use a 0-1 float
920                                  */
921                                 value = pso->percentage * 100.0f;
922                                 applyNumInput(&pso->num, &value);
923
924                                 pso->percentage = value / 100.0f;
925                                 CLAMP(pso->percentage, 0.0f, 1.0f);
926                                 RNA_float_set(op->ptr, "percentage", pso->percentage);
927
928                                 /* Update pose to reflect the new values (see below) */
929                                 do_pose_update = true;
930                                 break;
931                         }
932                         else if (event->val == KM_PRESS) {
933                                 switch (event->type) {
934                                         /* Transform Channel Limits  */
935                                         /* XXX: Replace these hardcoded hotkeys with a modalmap that can be customised */
936                                         case GKEY: /* Location */
937                                         {
938                                                 pose_slide_toggle_channels_mode(op, pso, PS_TFM_LOC);
939                                                 do_pose_update = true;
940                                                 break;
941                                         }
942                                         case RKEY: /* Rotation */
943                                         {
944                                                 pose_slide_toggle_channels_mode(op, pso, PS_TFM_ROT);
945                                                 do_pose_update = true;
946                                                 break;
947                                         }
948                                         case SKEY: /* Scale */
949                                         {
950                                                 pose_slide_toggle_channels_mode(op, pso, PS_TFM_SIZE);
951                                                 do_pose_update = true;
952                                                 break;
953                                         }
954                                         case BKEY: /* Bendy Bones */
955                                         {
956                                                 pose_slide_toggle_channels_mode(op, pso, PS_TFM_BBONE_SHAPE);
957                                                 do_pose_update = true;
958                                                 break;
959                                         }
960                                         case CKEY: /* Custom Properties */
961                                         {
962                                                 pose_slide_toggle_channels_mode(op, pso, PS_TFM_PROPS);
963                                                 do_pose_update = true;
964                                                 break;
965                                         }
966
967
968                                         /* Axis Locks */
969                                         /* XXX: Hardcoded... */
970                                         case XKEY:
971                                         {
972                                                 if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_X)) {
973                                                         do_pose_update = true;
974                                                 }
975                                                 break;
976                                         }
977                                         case YKEY:
978                                         {
979                                                 if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_Y)) {
980                                                         do_pose_update = true;
981                                                 }
982                                                 break;
983                                         }
984                                         case ZKEY:
985                                         {
986                                                 if (pose_slide_toggle_axis_locks(op, pso, PS_LOCK_Z)) {
987                                                         do_pose_update = true;
988                                                 }
989                                                 break;
990                                         }
991
992
993                                         default: /* Some other unhandled key... */
994                                                 break;
995                                 }
996                         }
997                         else {
998                                 /* unhandled event - maybe it was some view manip? */
999                                 /* allow to pass through */
1000                                 return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
1001                         }
1002                 }
1003         }
1004
1005
1006         /* perform pose updates - in response to some user action (e.g. pressing a key or moving the mouse) */
1007         if (do_pose_update) {
1008                 /* update percentage indicator in header */
1009                 pose_slide_draw_status(pso);
1010
1011                 /* reset transforms (to avoid accumulation errors) */
1012                 pose_slide_reset(pso);
1013
1014                 /* apply... */
1015                 pose_slide_apply(C, pso);
1016         }
1017
1018         /* still running... */
1019         return OPERATOR_RUNNING_MODAL;
1020 }
1021
1022 /* common code for cancel() */
1023 static void pose_slide_cancel(bContext *UNUSED(C), wmOperator *op)
1024 {
1025         /* cleanup and done */
1026         pose_slide_exit(op);
1027 }
1028
1029 /* common code for exec() methods */
1030 static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso)
1031 {
1032         /* settings should have been set up ok for applying, so just apply! */
1033         pose_slide_apply(C, pso);
1034
1035         /* insert keyframes if needed */
1036         pose_slide_autoKeyframe(C, pso);
1037
1038         /* cleanup and done */
1039         pose_slide_exit(op);
1040
1041         return OPERATOR_FINISHED;
1042 }
1043
1044 /* common code for defining RNA properties */
1045 /* TODO: Skip save on these? */
1046 static void pose_slide_opdef_properties(wmOperatorType *ot)
1047 {
1048         RNA_def_float_percentage(ot->srna, "percentage", 0.5f, 0.0f, 1.0f, "Percentage", "Weighting factor for which keyframe is favored more", 0.3, 0.7);
1049
1050         RNA_def_int(ot->srna, "prev_frame", 0, MINAFRAME, MAXFRAME, "Previous Keyframe", "Frame number of keyframe immediately before the current frame", 0, 50);
1051         RNA_def_int(ot->srna, "next_frame", 0, MINAFRAME, MAXFRAME, "Next Keyframe", "Frame number of keyframe immediately after the current frame", 0, 50);
1052
1053         RNA_def_enum(ot->srna, "channels", prop_channels_types, PS_TFM_ALL, "Channels", "Set of properties that are affected");
1054         RNA_def_enum(ot->srna, "axis_lock", prop_axis_lock_types, 0, "Axis Lock", "Transform axis to restrict effects to");
1055 }
1056
1057 /* ------------------------------------ */
1058
1059 /* invoke() - for 'push' mode */
1060 static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1061 {
1062         tPoseSlideOp *pso;
1063
1064         /* initialize data  */
1065         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
1066                 pose_slide_exit(op);
1067                 return OPERATOR_CANCELLED;
1068         }
1069         else
1070                 pso = op->customdata;
1071
1072         /* initialise percentage so that it won't pop on first mouse move */
1073         pose_slide_mouse_update_percentage(pso, op, event);
1074
1075         /* do common setup work */
1076         return pose_slide_invoke_common(C, op, pso);
1077 }
1078
1079 /* exec() - for push */
1080 static int pose_slide_push_exec(bContext *C, wmOperator *op)
1081 {
1082         tPoseSlideOp *pso;
1083
1084         /* initialize data (from RNA-props) */
1085         if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) {
1086                 pose_slide_exit(op);
1087                 return OPERATOR_CANCELLED;
1088         }
1089         else
1090                 pso = op->customdata;
1091
1092         /* do common exec work */
1093         return pose_slide_exec_common(C, op, pso);
1094 }
1095
1096 void POSE_OT_push(wmOperatorType *ot)
1097 {
1098         /* identifiers */
1099         ot->name = "Push Pose";
1100         ot->idname = "POSE_OT_push";
1101         ot->description = "Exaggerate the current pose";
1102
1103         /* callbacks */
1104         ot->exec = pose_slide_push_exec;
1105         ot->invoke = pose_slide_push_invoke;
1106         ot->modal = pose_slide_modal;
1107         ot->cancel = pose_slide_cancel;
1108         ot->poll = ED_operator_posemode;
1109
1110         /* flags */
1111         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
1112
1113         /* Properties */
1114         pose_slide_opdef_properties(ot);
1115 }
1116
1117 /* ........................ */
1118
1119 /* invoke() - for 'relax' mode */
1120 static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1121 {
1122         tPoseSlideOp *pso;
1123
1124         /* initialize data  */
1125         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
1126                 pose_slide_exit(op);
1127                 return OPERATOR_CANCELLED;
1128         }
1129         else
1130                 pso = op->customdata;
1131
1132         /* initialise percentage so that it won't pop on first mouse move */
1133         pose_slide_mouse_update_percentage(pso, op, event);
1134
1135         /* do common setup work */
1136         return pose_slide_invoke_common(C, op, pso);
1137 }
1138
1139 /* exec() - for relax */
1140 static int pose_slide_relax_exec(bContext *C, wmOperator *op)
1141 {
1142         tPoseSlideOp *pso;
1143
1144         /* initialize data (from RNA-props) */
1145         if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) {
1146                 pose_slide_exit(op);
1147                 return OPERATOR_CANCELLED;
1148         }
1149         else
1150                 pso = op->customdata;
1151
1152         /* do common exec work */
1153         return pose_slide_exec_common(C, op, pso);
1154 }
1155
1156 void POSE_OT_relax(wmOperatorType *ot)
1157 {
1158         /* identifiers */
1159         ot->name = "Relax Pose";
1160         ot->idname = "POSE_OT_relax";
1161         ot->description = "Make the current pose more similar to its surrounding ones";
1162
1163         /* callbacks */
1164         ot->exec = pose_slide_relax_exec;
1165         ot->invoke = pose_slide_relax_invoke;
1166         ot->modal = pose_slide_modal;
1167         ot->cancel = pose_slide_cancel;
1168         ot->poll = ED_operator_posemode;
1169
1170         /* flags */
1171         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
1172
1173         /* Properties */
1174         pose_slide_opdef_properties(ot);
1175 }
1176
1177 /* ........................ */
1178
1179 /* invoke() - for 'breakdown' mode */
1180 static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1181 {
1182         tPoseSlideOp *pso;
1183
1184         /* initialize data  */
1185         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
1186                 pose_slide_exit(op);
1187                 return OPERATOR_CANCELLED;
1188         }
1189         else
1190                 pso = op->customdata;
1191
1192         /* initialise percentage so that it won't pop on first mouse move */
1193         pose_slide_mouse_update_percentage(pso, op, event);
1194
1195         /* do common setup work */
1196         return pose_slide_invoke_common(C, op, pso);
1197 }
1198
1199 /* exec() - for breakdown */
1200 static int pose_slide_breakdown_exec(bContext *C, wmOperator *op)
1201 {
1202         tPoseSlideOp *pso;
1203
1204         /* initialize data (from RNA-props) */
1205         if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) {
1206                 pose_slide_exit(op);
1207                 return OPERATOR_CANCELLED;
1208         }
1209         else
1210                 pso = op->customdata;
1211
1212         /* do common exec work */
1213         return pose_slide_exec_common(C, op, pso);
1214 }
1215
1216 void POSE_OT_breakdown(wmOperatorType *ot)
1217 {
1218         /* identifiers */
1219         ot->name = "Pose Breakdowner";
1220         ot->idname = "POSE_OT_breakdown";
1221         ot->description = "Create a suitable breakdown pose on the current frame";
1222
1223         /* callbacks */
1224         ot->exec = pose_slide_breakdown_exec;
1225         ot->invoke = pose_slide_breakdown_invoke;
1226         ot->modal = pose_slide_modal;
1227         ot->cancel = pose_slide_cancel;
1228         ot->poll = ED_operator_posemode;
1229
1230         /* flags */
1231         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
1232
1233         /* Properties */
1234         pose_slide_opdef_properties(ot);
1235 }
1236
1237 /* **************************************************** */
1238 /* B) Pose Propagate */
1239
1240 /* "termination conditions" - i.e. when we stop */
1241 typedef enum ePosePropagate_Termination {
1242         /* stop after the current hold ends */
1243         POSE_PROPAGATE_SMART_HOLDS = 0,
1244         /* only do on the last keyframe */
1245         POSE_PROPAGATE_LAST_KEY,
1246         /* stop after the next keyframe */
1247         POSE_PROPAGATE_NEXT_KEY,
1248         /* stop after the specified frame */
1249         POSE_PROPAGATE_BEFORE_FRAME,
1250         /* stop when we run out of keyframes */
1251         POSE_PROPAGATE_BEFORE_END,
1252
1253         /* only do on keyframes that are selected */
1254         POSE_PROPAGATE_SELECTED_KEYS,
1255         /* only do on the frames where markers are selected */
1256         POSE_PROPAGATE_SELECTED_MARKERS
1257 } ePosePropagate_Termination;
1258
1259 /* termination data needed for some modes - assumes only one of these entries will be needed at a time */
1260 typedef union tPosePropagate_ModeData {
1261         /* smart holds + before frame: frame number to stop on */
1262         float end_frame;
1263
1264         /* selected markers: listbase for CfraElem's marking these frames */
1265         ListBase sel_markers;
1266 } tPosePropagate_ModeData;
1267
1268 /* --------------------------------- */
1269
1270 /* get frame on which the "hold" for the bone ends
1271  * XXX: this may not really work that well if a bone moves on some channels and not others
1272  *      if this happens to be a major issue, scrap this, and just make this happen
1273  *      independently per F-Curve
1274  */
1275 static float pose_propagate_get_boneHoldEndFrame(Object *ob, tPChanFCurveLink *pfl, float startFrame)
1276 {
1277         DLRBT_Tree keys, blocks;
1278         ActKeyBlock *ab;
1279
1280         AnimData *adt = ob->adt;
1281         LinkData *ld;
1282         float endFrame = startFrame;
1283
1284         /* set up optimized data-structures for searching for relevant keyframes + holds */
1285         BLI_dlrbTree_init(&keys);
1286         BLI_dlrbTree_init(&blocks);
1287
1288         for (ld = pfl->fcurves.first; ld; ld = ld->next) {
1289                 FCurve *fcu = (FCurve *)ld->data;
1290                 fcurve_to_keylist(adt, fcu, &keys, &blocks);
1291         }
1292
1293         BLI_dlrbTree_linkedlist_sync(&keys);
1294         BLI_dlrbTree_linkedlist_sync(&blocks);
1295
1296         /* find the long keyframe (i.e. hold), and hence obtain the endFrame value
1297          * - the best case would be one that starts on the frame itself
1298          */
1299         ab = (ActKeyBlock *)BLI_dlrbTree_search_exact(&blocks, compare_ab_cfraPtr, &startFrame);
1300
1301         if (actkeyblock_is_valid(ab, &keys) == 0) {
1302                 /* There are only two cases for no-exact match:
1303                  *  1) the current frame is just before another key but not on a key itself
1304                  *  2) the current frame is on a key, but that key doesn't link to the next
1305                  *
1306                  * If we've got the first case, then we can search for another block,
1307                  * otherwise forget it, as we'd be overwriting some valid data.
1308                  */
1309                 if (BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &startFrame) == NULL) {
1310                         /* we've got case 1, so try the one after */
1311                         ab = (ActKeyBlock *)BLI_dlrbTree_search_next(&blocks, compare_ab_cfraPtr, &startFrame);
1312
1313                         if (actkeyblock_is_valid(ab, &keys) == 0) {
1314                                 /* try the block before this frame then as last resort */
1315                                 ab = (ActKeyBlock *)BLI_dlrbTree_search_prev(&blocks, compare_ab_cfraPtr, &startFrame);
1316
1317                                 /* whatever happens, stop searching now... */
1318                                 if (actkeyblock_is_valid(ab, &keys) == 0) {
1319                                         /* restrict range to just the frame itself
1320                                          * i.e. everything is in motion, so no holds to safely overwrite
1321                                          */
1322                                         ab = NULL;
1323                                 }
1324                         }
1325                 }
1326                 else {
1327                         /* we've got case 2 - set ab to NULL just in case, since we shouldn't do anything in this case */
1328                         ab = NULL;
1329                 }
1330         }
1331
1332         /* check if we can go any further than we've already gone */
1333         if (ab) {
1334                 /* go to next if it is also valid and meets "extension" criteria */
1335                 while (ab->next) {
1336                         ActKeyBlock *abn = (ActKeyBlock *)ab->next;
1337
1338                         /* must be valid */
1339                         if (actkeyblock_is_valid(abn, &keys) == 0)
1340                                 break;
1341                         /* should start on the same frame that the last ended on */
1342                         if (ab->end != abn->start)
1343                                 break;
1344                         /* should have the same number of curves */
1345                         if (ab->totcurve != abn->totcurve)
1346                                 break;
1347                         /* should have the same value
1348                          * XXX: this may be a bit fuzzy on larger data sets, so be careful
1349                          */
1350                         if (ab->val != abn->val)
1351                                 break;
1352
1353                         /* we can extend the bounds to the end of this "next" block now */
1354                         ab = abn;
1355                 }
1356
1357                 /* end frame can now take the value of the end of the block */
1358                 endFrame = ab->end;
1359         }
1360
1361         /* free temp memory */
1362         BLI_dlrbTree_free(&keys);
1363         BLI_dlrbTree_free(&blocks);
1364
1365         /* return the end frame we've found */
1366         return endFrame;
1367 }
1368
1369 /* get reference value from F-Curve using RNA */
1370 static bool pose_propagate_get_refVal(Object *ob, FCurve *fcu, float *value)
1371 {
1372         PointerRNA id_ptr, ptr;
1373         PropertyRNA *prop;
1374         bool found = false;
1375
1376         /* base pointer is always the object -> id_ptr */
1377         RNA_id_pointer_create(&ob->id, &id_ptr);
1378
1379         /* resolve the property... */
1380         if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
1381                 if (RNA_property_array_check(prop)) {
1382                         /* array */
1383                         if (fcu->array_index < RNA_property_array_length(&ptr, prop)) {
1384                                 found = true;
1385                                 switch (RNA_property_type(prop)) {
1386                                         case PROP_BOOLEAN:
1387                                                 *value = (float)RNA_property_boolean_get_index(&ptr, prop, fcu->array_index);
1388                                                 break;
1389                                         case PROP_INT:
1390                                                 *value = (float)RNA_property_int_get_index(&ptr, prop, fcu->array_index);
1391                                                 break;
1392                                         case PROP_FLOAT:
1393                                                 *value = RNA_property_float_get_index(&ptr, prop, fcu->array_index);
1394                                                 break;
1395                                         default:
1396                                                 found = false;
1397                                                 break;
1398                                 }
1399                         }
1400                 }
1401                 else {
1402                         /* not an array */
1403                         found = true;
1404                         switch (RNA_property_type(prop)) {
1405                                 case PROP_BOOLEAN:
1406                                         *value = (float)RNA_property_boolean_get(&ptr, prop);
1407                                         break;
1408                                 case PROP_INT:
1409                                         *value = (float)RNA_property_int_get(&ptr, prop);
1410                                         break;
1411                                 case PROP_ENUM:
1412                                         *value = (float)RNA_property_enum_get(&ptr, prop);
1413                                         break;
1414                                 case PROP_FLOAT:
1415                                         *value = RNA_property_float_get(&ptr, prop);
1416                                         break;
1417                                 default:
1418                                         found = false;
1419                                         break;
1420                         }
1421                 }
1422         }
1423
1424         return found;
1425 }
1426
1427 /* propagate just works along each F-Curve in turn */
1428 static void pose_propagate_fcurve(wmOperator *op, Object *ob, FCurve *fcu,
1429                                   float startFrame, tPosePropagate_ModeData modeData)
1430 {
1431         const int mode = RNA_enum_get(op->ptr, "mode");
1432
1433         BezTriple *bezt;
1434         float refVal = 0.0f;
1435         bool keyExists;
1436         int i, match;
1437         short first = 1;
1438
1439         /* skip if no keyframes to edit */
1440         if ((fcu->bezt == NULL) || (fcu->totvert < 2))
1441                 return;
1442
1443         /* find the reference value from bones directly, which means that the user
1444          * doesn't need to firstly keyframe the pose (though this doesn't mean that
1445          * they can't either)
1446          */
1447         if (!pose_propagate_get_refVal(ob, fcu, &refVal))
1448                 return;
1449
1450         /* find the first keyframe to start propagating from
1451          * - if there's a keyframe on the current frame, we probably want to save this value there too
1452          *   since it may be as of yet unkeyed
1453          * - if starting before the starting frame, don't touch the key, as it may have had some valid
1454          *   values
1455          * - if only doing selected keyframes, start from the first one
1456          */
1457         if (mode != POSE_PROPAGATE_SELECTED_KEYS) {
1458                 match = binarysearch_bezt_index(fcu->bezt, startFrame, fcu->totvert, &keyExists);
1459
1460                 if (fcu->bezt[match].vec[1][0] < startFrame)
1461                         i = match + 1;
1462                 else
1463                         i = match;
1464         }
1465         else {
1466                 /* selected - start from first keyframe */
1467                 i = 0;
1468         }
1469
1470         for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) {
1471                 /* additional termination conditions based on the operator 'mode' property go here... */
1472                 if (ELEM(mode, POSE_PROPAGATE_BEFORE_FRAME, POSE_PROPAGATE_SMART_HOLDS)) {
1473                         /* stop if keyframe is outside the accepted range */
1474                         if (bezt->vec[1][0] > modeData.end_frame)
1475                                 break;
1476                 }
1477                 else if (mode == POSE_PROPAGATE_NEXT_KEY) {
1478                         /* stop after the first keyframe has been processed */
1479                         if (first == 0)
1480                                 break;
1481                 }
1482                 else if (mode == POSE_PROPAGATE_LAST_KEY) {
1483                         /* only affect this frame if it will be the last one */
1484                         if (i != (fcu->totvert - 1))
1485                                 continue;
1486                 }
1487                 else if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
1488                         /* only allow if there's a marker on this frame */
1489                         CfraElem *ce = NULL;
1490
1491                         /* stop on matching marker if there is one */
1492                         for (ce = modeData.sel_markers.first; ce; ce = ce->next) {
1493                                 if (ce->cfra == round_fl_to_int(bezt->vec[1][0]))
1494                                         break;
1495                         }
1496
1497                         /* skip this keyframe if no marker */
1498                         if (ce == NULL)
1499                                 continue;
1500                 }
1501                 else if (mode == POSE_PROPAGATE_SELECTED_KEYS) {
1502                         /* only allow if this keyframe is already selected - skip otherwise */
1503                         if (BEZT_ISSEL_ANY(bezt) == 0)
1504                                 continue;
1505                 }
1506
1507                 /* just flatten handles, since values will now be the same either side... */
1508                 /* TODO: perhaps a fade-out modulation of the value is required here (optional once again)? */
1509                 bezt->vec[0][1] = bezt->vec[1][1] = bezt->vec[2][1] = refVal;
1510
1511                 /* select keyframe to indicate that it's been changed */
1512                 bezt->f2 |= SELECT;
1513                 first = 0;
1514         }
1515 }
1516
1517 /* --------------------------------- */
1518
1519 static int pose_propagate_exec(bContext *C, wmOperator *op)
1520 {
1521         Scene *scene = CTX_data_scene(C);
1522         Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
1523         bAction *act = (ob && ob->adt) ? ob->adt->action : NULL;
1524
1525         ListBase pflinks = {NULL, NULL};
1526         tPChanFCurveLink *pfl;
1527
1528         tPosePropagate_ModeData modeData;
1529         const int mode = RNA_enum_get(op->ptr, "mode");
1530
1531         /* sanity checks */
1532         if (ob == NULL) {
1533                 BKE_report(op->reports, RPT_ERROR, "No object to propagate poses for");
1534                 return OPERATOR_CANCELLED;
1535         }
1536         if (act == NULL) {
1537                 BKE_report(op->reports, RPT_ERROR, "No keyframed poses to propagate to");
1538                 return OPERATOR_CANCELLED;
1539         }
1540
1541         /* isolate F-Curves related to the selected bones */
1542         poseAnim_mapping_get(C, &pflinks, ob, act);
1543
1544         /* mode-specific data preprocessing (requiring no access to curves) */
1545         if (mode == POSE_PROPAGATE_SELECTED_MARKERS) {
1546                 /* get a list of selected markers */
1547                 ED_markers_make_cfra_list(&scene->markers, &modeData.sel_markers, SELECT);
1548         }
1549         else {
1550                 /* assume everything else wants endFrame */
1551                 modeData.end_frame = RNA_float_get(op->ptr, "end_frame");
1552         }
1553
1554         /* for each bone, perform the copying required */
1555         for (pfl = pflinks.first; pfl; pfl = pfl->next) {
1556                 LinkData *ld;
1557
1558                 /* mode-specific data preprocessing (requiring access to all curves) */
1559                 if (mode == POSE_PROPAGATE_SMART_HOLDS) {
1560                         /* we store in endFrame the end frame of the "long keyframe" (i.e. a held value) starting
1561                          * from the keyframe that occurs after the current frame
1562                          */
1563                         modeData.end_frame = pose_propagate_get_boneHoldEndFrame(ob, pfl, (float)CFRA);
1564                 }
1565
1566                 /* go through propagating pose to keyframes, curve by curve */
1567                 for (ld = pfl->fcurves.first; ld; ld = ld->next)
1568                         pose_propagate_fcurve(op, ob, (FCurve *)ld->data, (float)CFRA, modeData);
1569         }
1570
1571         /* free temp data */
1572         poseAnim_mapping_free(&pflinks);
1573
1574         if (mode == POSE_PROPAGATE_SELECTED_MARKERS)
1575                 BLI_freelistN(&modeData.sel_markers);
1576
1577         /* updates + notifiers */
1578         poseAnim_mapping_refresh(C, scene, ob);
1579
1580         return OPERATOR_FINISHED;
1581 }
1582
1583 /* --------------------------------- */
1584
1585 void POSE_OT_propagate(wmOperatorType *ot)
1586 {
1587         static const EnumPropertyItem terminate_items[] = {
1588                 {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held",
1589              "Propagate pose to all keyframes after current frame that don't change (Default behavior)"},
1590                 {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe",
1591              "Propagate pose to first keyframe following the current frame only"},
1592                 {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "To Last Keyframe",
1593              "Propagate pose to the last keyframe only (i.e. making action cyclic)"},
1594                 {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame",
1595              "Propagate pose to all keyframes between current frame and 'Frame' property"},
1596                 {POSE_PROPAGATE_BEFORE_END, "BEFORE_END", 0, "Before Last Keyframe",
1597              "Propagate pose to all keyframes from current frame until no more are found"},
1598                 {POSE_PROPAGATE_SELECTED_KEYS, "SELECTED_KEYS", 0, "On Selected Keyframes",
1599              "Propagate pose to all selected keyframes"},
1600                 {POSE_PROPAGATE_SELECTED_MARKERS, "SELECTED_MARKERS", 0, "On Selected Markers",
1601              "Propagate pose to all keyframes occurring on frames with Scene Markers after the current frame"},
1602                 {0, NULL, 0, NULL, NULL}};
1603
1604         /* identifiers */
1605         ot->name = "Propagate Pose";
1606         ot->idname = "POSE_OT_propagate";
1607         ot->description = "Copy selected aspects of the current pose to subsequent poses already keyframed";
1608
1609         /* callbacks */
1610         ot->exec = pose_propagate_exec;
1611         ot->poll = ED_operator_posemode;  /* XXX: needs selected bones! */
1612
1613         /* flag */
1614         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1615
1616         /* properties */
1617         /* TODO: add "fade out" control for tapering off amount of propagation as time goes by? */
1618         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");
1619         RNA_def_float(ot->srna, "end_frame", 250.0, FLT_MIN, FLT_MAX, "End Frame", "Frame to stop propagating frames to (for 'Before Frame' mode)", 1.0, 250.0);
1620 }
1621
1622 /* **************************************************** */