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