4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * The Original Code is Copyright (C) 2008 Blender Foundation
22 * Contributor(s): Joshua Leung
24 * ***** END GPL LICENSE BLOCK *****
32 #include "MEM_guardedalloc.h"
34 #include "BLI_blenlib.h"
35 #include "BLI_arithb.h"
37 #include "DNA_anim_types.h"
38 #include "DNA_action_types.h"
39 #include "DNA_constraint_types.h"
40 #include "DNA_curve_types.h"
41 #include "DNA_key_types.h"
42 #include "DNA_object_types.h"
43 #include "DNA_space_types.h"
44 #include "DNA_scene_types.h"
46 #include "BKE_action.h"
47 #include "BKE_fcurve.h"
49 #include "BKE_utildefines.h"
51 #include "ED_anim_api.h"
52 #include "ED_keyframes_edit.h"
53 #include "ED_markers.h"
55 /* This file defines an API and set of callback-operators for non-destructive editing of keyframe data.
57 * Two API functions are defined for actually performing the operations on the data:
58 * ANIM_fcurve_keys_bezier_loop()
59 * which take the data they operate on, a few callbacks defining what operations to perform.
61 * As operators which work on keyframes usually apply the same operation on all BezTriples in
62 * every channel, the code has been optimised providing a set of functions which will get the
63 * appropriate bezier-modify function to set. These functions (ANIM_editkeyframes_*) will need
64 * to be called before getting any channels.
66 * A set of 'validation' callbacks are provided for checking if a BezTriple should be operated on.
67 * These should only be used when using a 'general' BezTriple editor (i.e. selection setters which
68 * don't check existing selection status).
70 * - Joshua Leung, Dec 2008
73 /* ************************************************************************** */
74 /* IPO Editing Loops - Exposed API */
76 /* --------------------------- Base Functions ------------------------------------ */
78 /* This function is used to loop over BezTriples in the given F-Curve, applying a given
79 * operation on them, and optionally applies an F-Curve validation function afterwards.
81 short ANIM_fcurve_keys_bezier_loop(BeztEditData *bed, FCurve *fcu, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb)
87 if (ELEM(NULL, fcu, fcu->bezt))
90 /* if function to apply to bezier curves is set, then loop through executing it on beztriples */
92 /* if there's a validation func, include that check in the loop
93 * (this is should be more efficient than checking for it in every loop)
96 for (b=0, bezt=fcu->bezt; b < fcu->totvert; b++, bezt++) {
97 /* Only operate on this BezTriple if it fullfills the criteria of the validation func */
98 if (bezt_ok(bed, bezt)) {
99 /* Exit with return-code '1' if function returns positive
100 * This is useful if finding if some BezTriple satisfies a condition.
102 if (bezt_cb(bed, bezt)) return 1;
107 for (b=0, bezt=fcu->bezt; b < fcu->totvert; b++, bezt++) {
108 /* Exit with return-code '1' if function returns positive
109 * This is useful if finding if some BezTriple satisfies a condition.
111 if (bezt_cb(bed, bezt)) return 1;
116 /* if fcu_cb (F-Curve post-editing callback) has been specified then execute it */
124 /* -------------------------------- Further Abstracted ----------------------------- */
126 /* This function is used to loop over the keyframe data in an Action Group */
127 static short agrp_keys_bezier_loop(BeztEditData *bed, bActionGroup *agrp, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb)
131 /* only iterate over the action-channels and their sub-channels that are in this group */
132 for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next) {
133 if (ANIM_fcurve_keys_bezier_loop(bed, fcu, bezt_ok, bezt_cb, fcu_cb))
140 /* This function is used to loop over the keyframe data in an Action */
141 static short act_keys_bezier_loop(BeztEditData *bed, bAction *act, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb)
145 /* just loop through all F-Curves */
146 for (fcu= act->curves.first; fcu; fcu= fcu->next) {
147 ANIM_fcurve_keys_bezier_loop(bed, fcu, bezt_ok, bezt_cb, fcu_cb);
156 /* This function is used to apply operation to all keyframes, regardless of the type */
157 short ANIM_animchannel_keys_bezier_loop(BeztEditData *bed, bAnimListElem *ale, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb)
163 /* method to use depends on the type of keyframe data */
164 switch (ale->datatype) {
165 /* direct keyframe data (these loops are exposed) */
166 case ALE_FCURVE: /* F-Curve */
167 return ANIM_fcurve_keys_bezier_loop(bed, ale->key_data, bezt_ok, bezt_cb, fcu_cb);
169 /* indirect 'summaries' (these are not exposed directly)
170 * NOTE: must keep this code in sync with the drawing code and also the filtering code!
172 case ALE_GROUP: /* action group */
173 return agrp_keys_bezier_loop(bed, (bActionGroup *)ale->data, bezt_ok, bezt_cb, fcu_cb);
174 case ALE_ACT: /* action */
175 return act_keys_bezier_loop(bed, (bAction *)ale->data, bezt_ok, bezt_cb, fcu_cb);
181 /* ************************************************************************** */
182 /* Keyframe Integrity Tools */
184 /* Rearrange keyframes if some are out of order */
185 // used to be recalc_*_ipos() where * was object or action
186 void ANIM_editkeyframes_refresh(bAnimContext *ac)
188 ListBase anim_data = {NULL, NULL};
192 /* filter animation data */
193 filter= ANIMFILTER_CURVESONLY;
194 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
196 /* loop over ipo-curves that are likely to have been edited, and check them */
197 for (ale= anim_data.first; ale; ale= ale->next) {
198 FCurve *fcu= ale->key_data;
200 /* make sure keyframes in F-curve are all in order, and handles are in valid positions */
201 sort_time_fcurve(fcu);
202 testhandles_fcurve(fcu);
206 BLI_freelistN(&anim_data);
209 /* ************************************************************************** */
210 /* BezTriple Validation Callbacks */
212 static short ok_bezier_frame(BeztEditData *bed, BezTriple *bezt)
214 /* frame is stored in f1 property (this float accuracy check may need to be dropped?) */
215 return IS_EQ(bezt->vec[1][0], bed->f1);
218 static short ok_bezier_framerange(BeztEditData *bed, BezTriple *bezt)
220 /* frame range is stored in float properties */
221 return ((bezt->vec[1][0] > bed->f1) && (bezt->vec[1][0] < bed->f2));
224 static short ok_bezier_selected(BeztEditData *bed, BezTriple *bezt)
226 /* this macro checks all beztriple handles for selection... */
227 return BEZSELECTED(bezt);
230 static short ok_bezier_value(BeztEditData *bed, BezTriple *bezt)
232 /* value is stored in f1 property
233 * - this float accuracy check may need to be dropped?
234 * - should value be stored in f2 instead so that we won't have conflicts when using f1 for frames too?
236 return IS_EQ(bezt->vec[1][1], bed->f1);
239 static short ok_bezier_valuerange(BeztEditData *bed, BezTriple *bezt)
241 /* value range is stored in float properties */
242 return ((bezt->vec[1][1] > bed->f1) && (bezt->vec[1][1] < bed->f2));
245 static short ok_bezier_region(BeztEditData *bed, BezTriple *bezt)
247 /* rect is stored in data property (it's of type rectf, but may not be set) */
249 return BLI_in_rctf(bed->data, bezt->vec[1][0], bezt->vec[1][1]);
255 BeztEditFunc ANIM_editkeyframes_ok(short mode)
257 /* eEditKeyframes_Validate */
259 case BEZT_OK_FRAME: /* only if bezt falls on the right frame (float) */
260 return ok_bezier_frame;
261 case BEZT_OK_FRAMERANGE: /* only if bezt falls within the specified frame range (floats) */
262 return ok_bezier_framerange;
263 case BEZT_OK_SELECTED: /* only if bezt is selected (self) */
264 return ok_bezier_selected;
265 case BEZT_OK_VALUE: /* only if bezt value matches (float) */
266 return ok_bezier_value;
267 case BEZT_OK_VALUERANGE: /* only if bezier falls within the specified value range (floats) */
268 return ok_bezier_valuerange;
269 case BEZT_OK_REGION: /* only if bezier falls within the specified rect (data -> rectf) */
270 return ok_bezier_region;
271 default: /* nothing was ok */
276 /* ******************************************* */
279 static short snap_bezier_nearest(BeztEditData *bed, BezTriple *bezt)
281 if (bezt->f2 & SELECT)
282 bezt->vec[1][0]= (float)(floor(bezt->vec[1][0]+0.5));
286 static short snap_bezier_nearestsec(BeztEditData *bed, BezTriple *bezt)
288 const Scene *scene= bed->scene;
289 const float secf = (float)FPS;
291 if (bezt->f2 & SELECT)
292 bezt->vec[1][0]= ((float)floor(bezt->vec[1][0]/secf + 0.5f) * secf);
296 static short snap_bezier_cframe(BeztEditData *bed, BezTriple *bezt)
298 const Scene *scene= bed->scene;
299 if (bezt->f2 & SELECT)
300 bezt->vec[1][0]= (float)CFRA;
304 static short snap_bezier_nearmarker(BeztEditData *bed, BezTriple *bezt)
306 //if (bezt->f2 & SELECT)
307 // bezt->vec[1][0]= (float)find_nearest_marker_time(bezt->vec[1][0]); // XXX missing function!
311 static short snap_bezier_horizontal(BeztEditData *bed, BezTriple *bezt)
313 if (bezt->f2 & SELECT) {
314 bezt->vec[0][1]= bezt->vec[2][1]= bezt->vec[1][1];
315 if ((bezt->h1==HD_AUTO) || (bezt->h1==HD_VECT)) bezt->h1= HD_ALIGN;
316 if ((bezt->h2==HD_AUTO) || (bezt->h2==HD_VECT)) bezt->h2= HD_ALIGN;
321 // calchandles_ipocurve
322 BeztEditFunc ANIM_editkeyframes_snap(short type)
324 /* eEditKeyframes_Snap */
326 case SNAP_KEYS_NEARFRAME: /* snap to nearest frame */
327 return snap_bezier_nearest;
328 case SNAP_KEYS_CURFRAME: /* snap to current frame */
329 return snap_bezier_cframe;
330 case SNAP_KEYS_NEARMARKER: /* snap to nearest marker */
331 return snap_bezier_nearmarker;
332 case SNAP_KEYS_NEARSEC: /* snap to nearest second */
333 return snap_bezier_nearestsec;
334 case SNAP_KEYS_HORIZONTAL: /* snap handles to same value */
335 return snap_bezier_horizontal;
336 default: /* just in case */
337 return snap_bezier_nearest;
343 static short mirror_bezier_cframe(BeztEditData *bed, BezTriple *bezt)
345 const Scene *scene= bed->scene;
348 if (bezt->f2 & SELECT) {
349 diff= ((float)CFRA - bezt->vec[1][0]);
350 bezt->vec[1][0]= ((float)CFRA + diff);
356 static short mirror_bezier_yaxis(BeztEditData *bed, BezTriple *bezt)
360 if (bezt->f2 & SELECT) {
361 diff= (0.0f - bezt->vec[1][0]);
362 bezt->vec[1][0]= (0.0f + diff);
368 static short mirror_bezier_xaxis(BeztEditData *bed, BezTriple *bezt)
372 if (bezt->f2 & SELECT) {
373 diff= (0.0f - bezt->vec[1][1]);
374 bezt->vec[1][1]= (0.0f + diff);
380 static short mirror_bezier_marker(BeztEditData *bed, BezTriple *bezt)
382 /* mirroring time stored in f1 */
383 if (bezt->f2 & SELECT) {
384 const float diff= (bed->f1 - bezt->vec[1][0]);
385 bezt->vec[1][0]= (bed->f1 + diff);
391 /* Note: for markers case, need to set global vars (eww...) */
392 // calchandles_fcurve
393 BeztEditFunc ANIM_editkeyframes_mirror(short type)
396 case MIRROR_KEYS_CURFRAME: /* mirror over current frame */
397 return mirror_bezier_cframe;
398 case MIRROR_KEYS_YAXIS: /* mirror over frame 0 */
399 return mirror_bezier_yaxis;
400 case MIRROR_KEYS_XAXIS: /* mirror over value 0 */
401 return mirror_bezier_xaxis;
402 case MIRROR_KEYS_MARKER: /* mirror over marker */
403 return mirror_bezier_marker;
404 default: /* just in case */
405 return mirror_bezier_yaxis;
410 /* ******************************************* */
413 /* Sets the selected bezier handles to type 'auto' */
414 static short set_bezier_auto(BeztEditData *bed, BezTriple *bezt)
416 if((bezt->f1 & SELECT) || (bezt->f3 & SELECT)) {
417 if (bezt->f1 & SELECT) bezt->h1= 1; /* the secret code for auto */
418 if (bezt->f3 & SELECT) bezt->h2= 1;
420 /* if the handles are not of the same type, set them
423 if (bezt->h1 != bezt->h2) {
424 if ELEM(bezt->h1, HD_ALIGN, HD_AUTO) bezt->h1= HD_FREE;
425 if ELEM(bezt->h2, HD_ALIGN, HD_AUTO) bezt->h2= HD_FREE;
431 /* Sets the selected bezier handles to type 'vector' */
432 static short set_bezier_vector(BeztEditData *bed, BezTriple *bezt)
434 if ((bezt->f1 & SELECT) || (bezt->f3 & SELECT)) {
435 if (bezt->f1 & SELECT) bezt->h1= HD_VECT;
436 if (bezt->f3 & SELECT) bezt->h2= HD_VECT;
438 /* if the handles are not of the same type, set them
441 if (bezt->h1 != bezt->h2) {
442 if ELEM(bezt->h1, HD_ALIGN, HD_AUTO) bezt->h1= HD_FREE;
443 if ELEM(bezt->h2, HD_ALIGN, HD_AUTO) bezt->h2= HD_FREE;
449 /* Queries if the handle should be set to 'free' or 'align' */
450 static short bezier_isfree(BeztEditData *bed, BezTriple *bezt)
452 if ((bezt->f1 & SELECT) && (bezt->h1)) return 1;
453 if ((bezt->f3 & SELECT) && (bezt->h2)) return 1;
457 /* Sets selected bezier handles to type 'align' */
458 static short set_bezier_align(BeztEditData *bed, BezTriple *bezt)
460 if (bezt->f1 & SELECT) bezt->h1= HD_ALIGN;
461 if (bezt->f3 & SELECT) bezt->h2= HD_ALIGN;
465 /* Sets selected bezier handles to type 'free' */
466 static short set_bezier_free(BeztEditData *bed, BezTriple *bezt)
468 if (bezt->f1 & SELECT) bezt->h1= HD_FREE;
469 if (bezt->f3 & SELECT) bezt->h2= HD_FREE;
473 /* Set all Bezier Handles to a single type */
474 // calchandles_fcurve
475 BeztEditFunc ANIM_editkeyframes_handles(short code)
478 case HD_AUTO: /* auto */
479 return set_bezier_auto;
480 case HD_VECT: /* vector */
481 return set_bezier_vector;
482 case HD_FREE: /* free */
483 return set_bezier_free;
484 case HD_ALIGN: /* align */
485 return set_bezier_align;
487 default: /* free or align? */
488 return bezier_isfree;
494 static short set_bezt_constant(BeztEditData *bed, BezTriple *bezt)
496 if (bezt->f2 & SELECT)
497 bezt->ipo= BEZT_IPO_CONST;
501 static short set_bezt_linear(BeztEditData *bed, BezTriple *bezt)
503 if (bezt->f2 & SELECT)
504 bezt->ipo= BEZT_IPO_LIN;
508 static short set_bezt_bezier(BeztEditData *bed, BezTriple *bezt)
510 if (bezt->f2 & SELECT)
511 bezt->ipo= BEZT_IPO_BEZ;
515 /* Set the interpolation type of the selected BezTriples in each IPO curve to the specified one */
516 // ANIM_editkeyframes_ipocurve_ipotype() !
517 BeztEditFunc ANIM_editkeyframes_ipo(short code)
520 case BEZT_IPO_CONST: /* constant */
521 return set_bezt_constant;
522 case BEZT_IPO_LIN: /* linear */
523 return set_bezt_linear;
524 default: /* bezier */
525 return set_bezt_bezier;
529 /* ******************************************* */
532 static short select_bezier_add(BeztEditData *bed, BezTriple *bezt)
534 /* Select the bezier triple */
539 static short select_bezier_subtract(BeztEditData *bed, BezTriple *bezt)
541 /* Deselect the bezier triple */
546 static short select_bezier_invert(BeztEditData *bed, BezTriple *bezt)
548 /* Invert the selection for the bezier triple */
550 if (bezt->f2 & SELECT) {
562 BeztEditFunc ANIM_editkeyframes_select(short selectmode)
564 switch (selectmode) {
565 case SELECT_ADD: /* add */
566 return select_bezier_add;
567 case SELECT_SUBTRACT: /* subtract */
568 return select_bezier_subtract;
569 case SELECT_INVERT: /* invert */
570 return select_bezier_invert;
571 default: /* replace (need to clear all, then add) */
572 return select_bezier_add;