remove config.h references, was added for automake build system rev around 124-126...
[blender.git] / source / blender / blenkernel / intern / fmodifier.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
21  * All rights reserved.
22  *
23  * Contributor(s): Joshua Leung (full recode)
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28
29 #include <math.h>
30 #include <stdio.h>
31 #include <stddef.h>
32 #include <string.h>
33 #include <float.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "DNA_anim_types.h"
38
39 #include "BLI_blenlib.h"
40 #include "BLI_math.h" /* windows needs for M_PI */
41
42 #include "BKE_fcurve.h"
43 #include "BKE_idprop.h"
44 #include "BKE_utildefines.h"
45
46 #ifndef DISABLE_PYTHON
47 #include "BPY_extern.h" /* for BPY_pydriver_eval() */
48 #endif
49
50 #define SMALL -1.0e-10
51 #define SELECT 1
52
53 /* ******************************** F-Modifiers ********************************* */
54
55 /* Info ------------------------------- */
56
57 /* F-Modifiers are modifiers which operate on F-Curves. However, they can also be defined
58  * on NLA-Strips to affect all of the F-Curves referenced by the NLA-Strip. 
59  */
60
61 /* Template --------------------------- */
62
63 /* Each modifier defines a set of functions, which will be called at the appropriate
64  * times. In addition to this, each modifier should have a type-info struct, where
65  * its functions are attached for use. 
66  */
67  
68 /* Template for type-info data:
69  *      - make a copy of this when creating new modifiers, and just change the functions
70  *        pointed to as necessary
71  *      - although the naming of functions doesn't matter, it would help for code
72  *        readability, to follow the same naming convention as is presented here
73  *      - any functions that a constraint doesn't need to define, don't define
74  *        for such cases, just use NULL 
75  *      - these should be defined after all the functions have been defined, so that
76  *        forward-definitions/prototypes don't need to be used!
77  *      - keep this copy #if-def'd so that future constraints can get based off this
78  */
79 #if 0
80 static FModifierTypeInfo FMI_MODNAME = {
81         FMODIFIER_TYPE_MODNAME, /* type */
82         sizeof(FMod_ModName), /* size */
83         FMI_TYPE_SOME_ACTION, /* action type */
84         FMI_REQUIRES_SOME_REQUIREMENT, /* requirements */
85         "Modifier Name", /* name */
86         "FMod_ModName", /* struct name */
87         fcm_modname_free, /* free data */
88         fcm_modname_relink, /* relink data */
89         fcm_modname_copy, /* copy data */
90         fcm_modname_new_data, /* new data */
91         fcm_modname_verify, /* verify */
92         fcm_modname_time, /* evaluate time */
93         fcm_modname_evaluate /* evaluate */
94 };
95 #endif
96
97 /* Generator F-Curve Modifier --------------------------- */
98
99 /* Generators available:
100  *      1) simple polynomial generator:
101  *              - Exanded form - (y = C[0]*(x^(n)) + C[1]*(x^(n-1)) + ... + C[n])  
102  *              - Factorised form - (y = (C[0][0]*x + C[0][1]) * (C[1][0]*x + C[1][1]) * ... * (C[n][0]*x + C[n][1]))
103  */
104
105 static void fcm_generator_free (FModifier *fcm)
106 {
107         FMod_Generator *data= (FMod_Generator *)fcm->data;
108         
109         /* free polynomial coefficients array */
110         if (data->coefficients)
111                 MEM_freeN(data->coefficients);
112 }
113
114 static void fcm_generator_copy (FModifier *fcm, FModifier *src)
115 {
116         FMod_Generator *gen= (FMod_Generator *)fcm->data;
117         FMod_Generator *ogen= (FMod_Generator *)src->data;
118         
119         /* copy coefficients array? */
120         if (ogen->coefficients)
121                 gen->coefficients= MEM_dupallocN(ogen->coefficients);
122 }
123
124 static void fcm_generator_new_data (void *mdata)
125 {
126         FMod_Generator *data= (FMod_Generator *)mdata;
127         float *cp;
128         
129         /* set default generator to be linear 0-1 (gradient = 1, y-offset = 0) */
130         data->poly_order= 1;
131         data->arraysize= 2;
132         cp= data->coefficients= MEM_callocN(sizeof(float)*2, "FMod_Generator_Coefs");
133         cp[0] = 0; // y-offset 
134         cp[1] = 1; // gradient
135 }
136
137 static void fcm_generator_verify (FModifier *fcm)
138 {
139         FMod_Generator *data= (FMod_Generator *)fcm->data;
140         
141         /* requirements depend on mode */
142         switch (data->mode) {
143                 case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */
144                 {
145                         /* arraysize needs to be order+1, so resize if not */
146                         if (data->arraysize != (data->poly_order+1)) {
147                                 float *nc;
148                                 
149                                 /* make new coefficients array, and copy over as much data as can fit */
150                                 nc= MEM_callocN(sizeof(float)*(data->poly_order+1), "FMod_Generator_Coefs");
151                                 
152                                 if (data->coefficients) {
153                                         if (data->arraysize > (data->poly_order+1))
154                                                 memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order+1));
155                                         else
156                                                 memcpy(nc, data->coefficients, sizeof(float)*data->arraysize);
157                                                 
158                                         /* free the old data */
159                                         MEM_freeN(data->coefficients);
160                                 }       
161                                 
162                                 /* set the new data */
163                                 data->coefficients= nc;
164                                 data->arraysize= data->poly_order+1;
165                         }
166                 }
167                         break;
168                 
169                 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* expanded polynomial expression */
170                 {
171                         /* arraysize needs to be 2*order, so resize if not */
172                         if (data->arraysize != (data->poly_order * 2)) {
173                                 float *nc;
174                                 
175                                 /* make new coefficients array, and copy over as much data as can fit */
176                                 nc= MEM_callocN(sizeof(float)*(data->poly_order*2), "FMod_Generator_Coefs");
177                                 
178                                 if (data->coefficients) {
179                                         if (data->arraysize > (data->poly_order * 2))
180                                                 memcpy(nc, data->coefficients, sizeof(float)*(data->poly_order * 2));
181                                         else
182                                                 memcpy(nc, data->coefficients, sizeof(float)*data->arraysize);
183                                                 
184                                         /* free the old data */
185                                         MEM_freeN(data->coefficients);
186                                 }       
187                                 
188                                 /* set the new data */
189                                 data->coefficients= nc;
190                                 data->arraysize= data->poly_order * 2;
191                         }
192                 }
193                         break;  
194         }
195 }
196
197 static void fcm_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
198 {
199         FMod_Generator *data= (FMod_Generator *)fcm->data;
200         
201         /* behaviour depends on mode 
202          * NOTE: the data in its default state is fine too
203          */
204         switch (data->mode) {
205                 case FCM_GENERATOR_POLYNOMIAL: /* expanded polynomial expression */
206                 {
207                         /* we overwrite cvalue with the sum of the polynomial */
208                         float *powers = MEM_callocN(sizeof(float)*data->arraysize, "Poly Powers");
209                         float value= 0.0f;
210                         unsigned int i;
211                         
212                         /* for each x^n, precalculate value based on previous one first... this should be 
213                          * faster that calling pow() for each entry
214                          */
215                         for (i=0; i < data->arraysize; i++) {
216                                 /* first entry is x^0 = 1, otherwise, calculate based on previous */
217                                 if (i)
218                                         powers[i]= powers[i-1] * evaltime;
219                                 else
220                                         powers[0]= 1;
221                         }
222                         
223                         /* for each coefficient, add to value, which we'll write to *cvalue in one go */
224                         for (i=0; i < data->arraysize; i++)
225                                 value += data->coefficients[i] * powers[i];
226                         
227                         /* only if something changed, write *cvalue in one go */
228                         if (data->poly_order) {
229                                 if (data->flag & FCM_GENERATOR_ADDITIVE)
230                                         *cvalue += value;
231                                 else
232                                         *cvalue= value;
233                         }
234                                 
235                         /* cleanup */
236                         if (powers) 
237                                 MEM_freeN(powers);
238                 }
239                         break;
240                         
241                 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* factorised polynomial */
242                 {
243                         float value= 1.0f, *cp=NULL;
244                         unsigned int i;
245                         
246                         /* for each coefficient pair, solve for that bracket before accumulating in value by multiplying */
247                         for (cp=data->coefficients, i=0; (cp) && (i < data->poly_order); cp+=2, i++) 
248                                 value *= (cp[0]*evaltime + cp[1]);
249                                 
250                         /* only if something changed, write *cvalue in one go */
251                         if (data->poly_order) {
252                                 if (data->flag & FCM_GENERATOR_ADDITIVE)
253                                         *cvalue += value;
254                                 else
255                                         *cvalue= value;
256                         }
257                 }
258                         break;
259         }
260 }
261
262 static FModifierTypeInfo FMI_GENERATOR = {
263         FMODIFIER_TYPE_GENERATOR, /* type */
264         sizeof(FMod_Generator), /* size */
265         FMI_TYPE_GENERATE_CURVE, /* action type */
266         FMI_REQUIRES_NOTHING, /* requirements */
267         "Generator", /* name */
268         "FMod_Generator", /* struct name */
269         fcm_generator_free, /* free data */
270         fcm_generator_copy, /* copy data */
271         fcm_generator_new_data, /* new data */
272         fcm_generator_verify, /* verify */
273         NULL, /* evaluate time */
274         fcm_generator_evaluate /* evaluate */
275 };
276
277 /* Built-In Function Generator F-Curve Modifier --------------------------- */
278
279 /* This uses the general equation for equations:
280  *              y = amplitude * fn(phase_multiplier*x + phase_offset) + y_offset
281  *
282  * where amplitude, phase_multiplier/offset, y_offset are user-defined coefficients,
283  * x is the evaluation 'time', and 'y' is the resultant value
284  *
285  * Functions available are
286  *      sin, cos, tan, sinc (normalised sin), natural log, square root 
287  */
288
289 static void fcm_fn_generator_new_data (void *mdata)
290 {
291         FMod_FunctionGenerator *data= (FMod_FunctionGenerator *)mdata;
292         
293         /* set amplitude and phase multiplier to 1.0f so that something is generated */
294         data->amplitude= 1.0f;
295         data->phase_multiplier= 1.0f;
296 }
297
298 /* Unary 'normalised sine' function
299  *      y = sin(PI + x) / (PI * x),
300  * except for x = 0 when y = 1.
301  */
302 static double sinc (double x)
303 {
304         if (fabs(x) < 0.0001)
305                 return 1.0;
306         else
307                 return sin(M_PI * x) / (M_PI * x);
308 }
309
310 static void fcm_fn_generator_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
311 {
312         FMod_FunctionGenerator *data= (FMod_FunctionGenerator *)fcm->data;
313         double arg= data->phase_multiplier*evaltime + data->phase_offset;
314         double (*fn)(double v) = NULL;
315         
316         /* get function pointer to the func to use:
317          * WARNING: must perform special argument validation hereto guard against crashes  
318          */
319         switch (data->type)
320         {
321                 /* simple ones */                       
322                 case FCM_GENERATOR_FN_SIN: /* sine wave */
323                         fn= sin;
324                         break;
325                 case FCM_GENERATOR_FN_COS: /* cosine wave */
326                         fn= cos;
327                         break;
328                 case FCM_GENERATOR_FN_SINC: /* normalised sine wave */
329                         fn= sinc;
330                         break;
331                         
332                 /* validation required */
333                 case FCM_GENERATOR_FN_TAN: /* tangent wave */
334                 {
335                         /* check that argument is not on one of the discontinuities (i.e. 90deg, 270 deg, etc) */
336                         if IS_EQ(fmod((arg - M_PI_2), M_PI), 0.0) {
337                                 if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
338                                         *cvalue = 0.0f; /* no value possible here */
339                         }
340                         else
341                                 fn= tan;
342                 }
343                         break;
344                 case FCM_GENERATOR_FN_LN: /* natural log */
345                 {
346                         /* check that value is greater than 1? */
347                         if (arg > 1.0f) {
348                                 fn= log;
349                         }
350                         else {
351                                 if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
352                                         *cvalue = 0.0f; /* no value possible here */
353                         }
354                 }
355                         break;
356                 case FCM_GENERATOR_FN_SQRT: /* square root */
357                 {
358                         /* no negative numbers */
359                         if (arg > 0.0f) {
360                                 fn= sqrt;
361                         }
362                         else {
363                                 if ((data->flag & FCM_GENERATOR_ADDITIVE) == 0)
364                                         *cvalue = 0.0f; /* no value possible here */
365                         }
366                 }
367                         break;
368                 
369                 default:
370                         printf("Invalid Function-Generator for F-Modifier - %d \n", data->type);
371         }
372         
373         /* execute function callback to set value if appropriate */
374         if (fn) {
375                 float value= (float)(data->amplitude*fn(arg) + data->value_offset);
376                 
377                 if (data->flag & FCM_GENERATOR_ADDITIVE)
378                         *cvalue += value;
379                 else
380                         *cvalue= value;
381         }
382 }
383
384 static FModifierTypeInfo FMI_FN_GENERATOR = {
385         FMODIFIER_TYPE_FN_GENERATOR, /* type */
386         sizeof(FMod_FunctionGenerator), /* size */
387         FMI_TYPE_GENERATE_CURVE, /* action type */
388         FMI_REQUIRES_NOTHING, /* requirements */
389         "Built-In Function", /* name */
390         "FMod_FunctionGenerator", /* struct name */
391         NULL, /* free data */
392         NULL, /* copy data */
393         fcm_fn_generator_new_data, /* new data */
394         NULL, /* verify */
395         NULL, /* evaluate time */
396         fcm_fn_generator_evaluate /* evaluate */
397 };
398
399 /* Envelope F-Curve Modifier --------------------------- */
400
401 static void fcm_envelope_free (FModifier *fcm)
402 {
403         FMod_Envelope *env= (FMod_Envelope *)fcm->data;
404         
405         /* free envelope data array */
406         if (env->data)
407                 MEM_freeN(env->data);
408 }
409
410 static void fcm_envelope_copy (FModifier *fcm, FModifier *src)
411 {
412         FMod_Envelope *env= (FMod_Envelope *)fcm->data;
413         FMod_Envelope *oenv= (FMod_Envelope *)src->data;
414         
415         /* copy envelope data array */
416         if (oenv->data)
417                 env->data= MEM_dupallocN(oenv->data);
418 }
419
420 static void fcm_envelope_new_data (void *mdata)
421 {
422         FMod_Envelope *env= (FMod_Envelope *)mdata;
423         
424         /* set default min/max ranges */
425         env->min= -1.0f;
426         env->max= 1.0f;
427 }
428
429 static void fcm_envelope_verify (FModifier *fcm)
430 {
431         FMod_Envelope *env= (FMod_Envelope *)fcm->data;
432         
433         /* if the are points, perform bubble-sort on them, as user may have changed the order */
434         if (env->data) {
435                 // XXX todo...
436         }
437 }
438
439 static void fcm_envelope_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
440 {
441         FMod_Envelope *env= (FMod_Envelope *)fcm->data;
442         FCM_EnvelopeData *fed, *prevfed, *lastfed;
443         float min=0.0f, max=0.0f, fac=0.0f;
444         int a;
445         
446         /* get pointers */
447         if (env->data == NULL) return;
448         prevfed= env->data;
449         fed= prevfed + 1;
450         lastfed= prevfed + (env->totvert-1);
451         
452         /* get min/max values for envelope at evaluation time (relative to mid-value) */
453         if (prevfed->time >= evaltime) {
454                 /* before or on first sample, so just extend value */
455                 min= prevfed->min;
456                 max= prevfed->max;
457         }
458         else if (lastfed->time <= evaltime) {
459                 /* after or on last sample, so just extend value */
460                 min= lastfed->min;
461                 max= lastfed->max;
462         }
463         else {
464                 /* evaltime occurs somewhere between segments */
465                 // TODO: implement binary search for this to make it faster?
466                 for (a=0; prevfed && fed && (a < env->totvert-1); a++, prevfed=fed, fed++) {  
467                         /* evaltime occurs within the interval defined by these two envelope points */
468                         if ((prevfed->time <= evaltime) && (fed->time >= evaltime)) {
469                                 float afac, bfac, diff;
470                                 
471                                 diff= fed->time - prevfed->time;
472                                 afac= (evaltime - prevfed->time) / diff;
473                                 bfac= (fed->time - evaltime) / diff;
474                                 
475                                 min= bfac*prevfed->min + afac*fed->min;
476                                 max= bfac*prevfed->max + afac*fed->max;
477                                 
478                                 break;
479                         }
480                 }
481         }
482         
483         /* adjust *cvalue 
484          *      - fac is the ratio of how the current y-value corresponds to the reference range
485          *      - thus, the new value is found by mapping the old range to the new!
486          */
487         fac= (*cvalue - (env->midval + env->min)) / (env->max - env->min);
488         *cvalue= min + fac*(max - min); 
489 }
490
491 static FModifierTypeInfo FMI_ENVELOPE = {
492         FMODIFIER_TYPE_ENVELOPE, /* type */
493         sizeof(FMod_Envelope), /* size */
494         FMI_TYPE_REPLACE_VALUES, /* action type */
495         0, /* requirements */
496         "Envelope", /* name */
497         "FMod_Envelope", /* struct name */
498         fcm_envelope_free, /* free data */
499         fcm_envelope_copy, /* copy data */
500         fcm_envelope_new_data, /* new data */
501         fcm_envelope_verify, /* verify */
502         NULL, /* evaluate time */
503         fcm_envelope_evaluate /* evaluate */
504 };
505
506 /* Cycles F-Curve Modifier  --------------------------- */
507
508 /* This modifier changes evaltime to something that exists within the curve's frame-range, 
509  * then re-evaluates modifier stack up to this point using the new time. This re-entrant behaviour
510  * is very likely to be more time-consuming than the original approach... (which was tighly integrated into 
511  * the calculation code...).
512  *
513  * NOTE: this needs to be at the start of the stack to be of use, as it needs to know the extents of the keyframes/sample-data
514  * Possible TODO - store length of cycle information that can be initialised from the extents of the keyframes/sample-data, and adjusted
515  *                              as appropriate
516  */
517
518 /* temp data used during evaluation */
519 typedef struct tFCMED_Cycles {
520         float cycyofs;          /* y-offset to apply */
521 } tFCMED_Cycles;
522  
523 static void fcm_cycles_new_data (void *mdata)
524 {
525         FMod_Cycles *data= (FMod_Cycles *)mdata;
526         
527         /* turn on cycles by default */
528         data->before_mode= data->after_mode= FCM_EXTRAPOLATE_CYCLIC;
529 }
530
531 static float fcm_cycles_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime)
532 {
533         FMod_Cycles *data= (FMod_Cycles *)fcm->data;
534         float prevkey[2], lastkey[2], cycyofs=0.0f;
535         short side=0, mode=0;
536         int cycles=0;
537         
538         /* check if modifier is first in stack, otherwise disable ourself... */
539         // FIXME...
540         if (fcm->prev) {
541                 fcm->flag |= FMODIFIER_FLAG_DISABLED;
542                 return evaltime;
543         }
544         
545         /* calculate new evaltime due to cyclic interpolation */
546         if (fcu && fcu->bezt) {
547                 BezTriple *prevbezt= fcu->bezt;
548                 BezTriple *lastbezt= prevbezt + fcu->totvert-1;
549                 
550                 prevkey[0]= prevbezt->vec[1][0];
551                 prevkey[1]= prevbezt->vec[1][1];
552                 
553                 lastkey[0]= lastbezt->vec[1][0];
554                 lastkey[1]= lastbezt->vec[1][1];
555         }
556         else if (fcu && fcu->fpt) {
557                 FPoint *prevfpt= fcu->fpt;
558                 FPoint *lastfpt= prevfpt + fcu->totvert-1;
559                 
560                 prevkey[0]= prevfpt->vec[0];
561                 prevkey[1]= prevfpt->vec[1];
562                 
563                 lastkey[0]= lastfpt->vec[0];
564                 lastkey[1]= lastfpt->vec[1];
565         }
566         else
567                 return evaltime;
568                 
569         /* check if modifier will do anything
570          *      1) if in data range, definitely don't do anything
571          *      2) if before first frame or after last frame, make sure some cycling is in use
572          */
573         if (evaltime < prevkey[0]) {
574                 if (data->before_mode)  {
575                         side= -1;
576                         mode= data->before_mode;
577                         cycles= data->before_cycles;
578                 }
579         }
580         else if (evaltime > lastkey[0]) {
581                 if (data->after_mode) {
582                         side= 1;
583                         mode= data->after_mode;
584                         cycles= data->after_cycles;
585                 }
586         }
587         if ELEM(0, side, mode)
588                 return evaltime;
589                 
590         /* find relative place within a cycle */
591         {
592                 float cycdx=0, cycdy=0, ofs=0;
593                 float cycle= 0;
594                 
595                 /* ofs is start frame of cycle */
596                 ofs= prevkey[0];
597                 
598                 /* calculate period and amplitude (total height) of a cycle */
599                 cycdx= lastkey[0] - prevkey[0];
600                 cycdy= lastkey[1] - prevkey[1];
601                 
602                 /* check if cycle is infinitely small, to be point of being impossible to use */
603                 if (cycdx == 0)
604                         return evaltime;
605                         
606                 /* calculate the 'number' of the cycle */
607                 cycle= ((float)side * (evaltime - ofs) / cycdx);
608                 
609                 /* check that cyclic is still enabled for the specified time */
610                 if (cycles == 0) {
611                         /* catch this case so that we don't exit when we have cycles=0
612                          * as this indicates infinite cycles...
613                          */
614                 }
615                 else if (cycle > (cycles+1)) {
616                         /* we are too far away from range to evaluate
617                          * TODO: but we should still hold last value... 
618                          */
619                         return evaltime;
620                 }
621                 
622                 /* check if 'cyclic extrapolation', and thus calculate y-offset for this cycle */
623                 if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) {
624                         cycyofs = (float)floor((evaltime - ofs) / cycdx);
625                         cycyofs *= cycdy;
626                 }
627                 
628                 /* calculate where in the cycle we are (overwrite evaltime to reflect this) */
629                 if ((mode == FCM_EXTRAPOLATE_MIRROR) && ((int)(cycle) % 2)) {
630                         /* when 'mirror' option is used and cycle number is odd, this cycle is played in reverse 
631                          *      - for 'before' extrapolation, we need to flip in a different way, otherwise values past
632                          *        then end of the curve get referenced (result of fmod will be negative, and with different phase)
633                          */
634                         if (side < 0)
635                                 evaltime= (float)(prevkey[0] - fmod(evaltime-ofs, cycdx));
636                         else
637                                 evaltime= (float)(lastkey[0] - fmod(evaltime-ofs, cycdx));
638                 }
639                 else {
640                         /* the cycle is played normally... */
641                         evaltime= (float)(fmod(evaltime-ofs, cycdx) + ofs);
642                 }
643                 if (evaltime < ofs) evaltime += cycdx;
644         }
645         
646         /* store temp data if needed */
647         if (mode == FCM_EXTRAPOLATE_CYCLIC_OFFSET) {
648                 tFCMED_Cycles *edata;
649                 
650                 /* for now, this is just a float, but we could get more stuff... */
651                 fcm->edata= edata= MEM_callocN(sizeof(tFCMED_Cycles), "tFCMED_Cycles");
652                 edata->cycyofs= cycyofs;
653         }
654         
655         /* return the new frame to evaluate */
656         return evaltime;
657 }
658  
659 static void fcm_cycles_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
660 {
661         tFCMED_Cycles *edata= (tFCMED_Cycles *)fcm->edata;
662         
663         /* use temp data */
664         if (edata) {
665                 /* add cyclic offset - no need to check for now, otherwise the data wouldn't exist! */
666                 *cvalue += edata->cycyofs;
667                 
668                 /* free temp data */
669                 MEM_freeN(edata);
670                 fcm->edata= NULL;
671         }
672 }
673
674 static FModifierTypeInfo FMI_CYCLES = {
675         FMODIFIER_TYPE_CYCLES, /* type */
676         sizeof(FMod_Cycles), /* size */
677         FMI_TYPE_EXTRAPOLATION, /* action type */
678         FMI_REQUIRES_ORIGINAL_DATA, /* requirements */
679         "Cycles", /* name */
680         "FMod_Cycles", /* struct name */
681         NULL, /* free data */
682         NULL, /* copy data */
683         fcm_cycles_new_data, /* new data */
684         NULL /*fcm_cycles_verify*/, /* verify */
685         fcm_cycles_time, /* evaluate time */
686         fcm_cycles_evaluate /* evaluate */
687 };
688
689 /* Noise F-Curve Modifier  --------------------------- */
690
691 static void fcm_noise_new_data (void *mdata)
692 {
693         FMod_Noise *data= (FMod_Noise *)mdata;
694         
695         /* defaults */
696         data->size= 1.0f;
697         data->strength= 1.0f;
698         data->phase= 1.0f;
699         data->depth = 0;
700         data->modification = FCM_NOISE_MODIF_REPLACE;
701 }
702  
703 static void fcm_noise_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
704 {
705         FMod_Noise *data= (FMod_Noise *)fcm->data;
706         float noise;
707         
708         /* generate noise using good ol' Blender Noise
709          *      - 0.1 is passed as the 'z' value, otherwise evaluation fails for size = phase = 1
710          *        with evaltime being an integer (which happens when evaluating on frame by frame basis)
711          */
712         noise = BLI_turbulence(data->size, evaltime, data->phase, 0.1f, data->depth);
713         
714         /* combine the noise with existing motion data */
715         switch (data->modification) {
716                 case FCM_NOISE_MODIF_ADD:
717                         *cvalue= *cvalue + noise * data->strength;
718                         break;
719                 case FCM_NOISE_MODIF_SUBTRACT:
720                         *cvalue= *cvalue - noise * data->strength;
721                         break;
722                 case FCM_NOISE_MODIF_MULTIPLY:
723                         *cvalue= *cvalue * noise * data->strength;
724                         break;
725                 case FCM_NOISE_MODIF_REPLACE:
726                 default:
727                         *cvalue= *cvalue + (noise - 0.5f) * data->strength;
728                         break;
729         }
730 }
731
732 static FModifierTypeInfo FMI_NOISE = {
733         FMODIFIER_TYPE_NOISE, /* type */
734         sizeof(FMod_Noise), /* size */
735         FMI_TYPE_REPLACE_VALUES, /* action type */
736         0, /* requirements */
737         "Noise", /* name */
738         "FMod_Noise", /* struct name */
739         NULL, /* free data */
740         NULL, /* copy data */
741         fcm_noise_new_data, /* new data */
742         NULL /*fcm_noise_verify*/, /* verify */
743         NULL, /* evaluate time */
744         fcm_noise_evaluate /* evaluate */
745 };
746
747 /* Filter F-Curve Modifier --------------------------- */
748
749 #if 0 // XXX not yet implemented 
750 static FModifierTypeInfo FMI_FILTER = {
751         FMODIFIER_TYPE_FILTER, /* type */
752         sizeof(FMod_Filter), /* size */
753         FMI_TYPE_REPLACE_VALUES, /* action type */
754         0, /* requirements */
755         "Filter", /* name */
756         "FMod_Filter", /* struct name */
757         NULL, /* free data */
758         NULL, /* copy data */
759         NULL, /* new data */
760         NULL /*fcm_filter_verify*/, /* verify */
761         NULL, /* evlauate time */
762         fcm_filter_evaluate /* evaluate */
763 };
764 #endif // XXX not yet implemented
765
766
767 /* Python F-Curve Modifier --------------------------- */
768
769 static void fcm_python_free (FModifier *fcm)
770 {
771         FMod_Python *data= (FMod_Python *)fcm->data;
772         
773         /* id-properties */
774         IDP_FreeProperty(data->prop);
775         MEM_freeN(data->prop);
776 }
777
778 static void fcm_python_new_data (void *mdata) 
779 {
780         FMod_Python *data= (FMod_Python *)mdata;
781         
782         /* everything should be set correctly by calloc, except for the prop->type constant.*/
783         data->prop = MEM_callocN(sizeof(IDProperty), "PyFModifierProps");
784         data->prop->type = IDP_GROUP;
785 }
786
787 static void fcm_python_copy (FModifier *fcm, FModifier *src)
788 {
789         FMod_Python *pymod = (FMod_Python *)fcm->data;
790         FMod_Python *opymod = (FMod_Python *)src->data;
791         
792         pymod->prop = IDP_CopyProperty(opymod->prop);
793 }
794
795 static void fcm_python_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
796 {
797 #ifndef DISABLE_PYTHON
798         //FMod_Python *data= (FMod_Python *)fcm->data;
799         
800         /* FIXME... need to implement this modifier...
801          *      It will need it execute a script using the custom properties 
802          */
803 #endif /* DISABLE_PYTHON */
804 }
805
806 static FModifierTypeInfo FMI_PYTHON = {
807         FMODIFIER_TYPE_PYTHON, /* type */
808         sizeof(FMod_Python), /* size */
809         FMI_TYPE_GENERATE_CURVE, /* action type */
810         FMI_REQUIRES_RUNTIME_CHECK, /* requirements */
811         "Python", /* name */
812         "FMod_Python", /* struct name */
813         fcm_python_free, /* free data */
814         fcm_python_copy, /* copy data */
815         fcm_python_new_data, /* new data */
816         NULL /*fcm_python_verify*/, /* verify */
817         NULL /*fcm_python_time*/, /* evaluate time */
818         fcm_python_evaluate /* evaluate */
819 };
820
821
822 /* Limits F-Curve Modifier --------------------------- */
823
824 static float fcm_limits_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime)
825 {
826         FMod_Limits *data= (FMod_Limits *)fcm->data;
827         
828         /* check for the time limits */
829         if ((data->flag & FCM_LIMIT_XMIN) && (evaltime < data->rect.xmin))
830                 return data->rect.xmin;
831         if ((data->flag & FCM_LIMIT_XMAX) && (evaltime > data->rect.xmax))
832                 return data->rect.xmax;
833                 
834         /* modifier doesn't change time */
835         return evaltime;
836 }
837
838 static void fcm_limits_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime)
839 {
840         FMod_Limits *data= (FMod_Limits *)fcm->data;
841         
842         /* value limits now */
843         if ((data->flag & FCM_LIMIT_YMIN) && (*cvalue < data->rect.ymin))
844                 *cvalue= data->rect.ymin;
845         if ((data->flag & FCM_LIMIT_YMAX) && (*cvalue > data->rect.ymax))
846                 *cvalue= data->rect.ymax;
847 }
848
849 static FModifierTypeInfo FMI_LIMITS = {
850         FMODIFIER_TYPE_LIMITS, /* type */
851         sizeof(FMod_Limits), /* size */
852         FMI_TYPE_GENERATE_CURVE, /* action type */  /* XXX... err... */   
853         FMI_REQUIRES_RUNTIME_CHECK, /* requirements */
854         "Limits", /* name */
855         "FMod_Limits", /* struct name */
856         NULL, /* free data */
857         NULL, /* copy data */
858         NULL, /* new data */
859         NULL, /* verify */
860         fcm_limits_time, /* evaluate time */
861         fcm_limits_evaluate /* evaluate */
862 };
863
864 /* Stepped F-Curve Modifier --------------------------- */
865
866 static void fcm_stepped_new_data (void *mdata) 
867 {
868         FMod_Stepped *data= (FMod_Stepped *)mdata;
869         
870         /* just need to set the step-size to 2-frames by default */
871         // XXX: or would 5 be more normal?
872         data->step_size = 2.0f;
873 }
874
875 static float fcm_stepped_time (FCurve *fcu, FModifier *fcm, float cvalue, float evaltime)
876 {
877         FMod_Stepped *data= (FMod_Stepped *)fcm->data;
878         int snapblock;
879         
880         /* check range clamping to see if we should alter the timing to achieve the desired results */
881         if (data->flag & FCM_STEPPED_NO_BEFORE) {
882                 if (evaltime < data->start_frame)
883                         return evaltime;
884         }
885         if (data->flag & FCM_STEPPED_NO_AFTER) {
886                 if (evaltime > data->end_frame)
887                         return evaltime;
888         }
889         
890         /* we snap to the start of the previous closest block of 'step_size' frames 
891          * after the start offset has been discarded 
892          *      - i.e. round down
893          */
894         snapblock = (int)((evaltime - data->offset) / data->step_size);
895         
896         /* reapply the offset, and multiple the snapblock by the size of the steps to get 
897          * the new time to evaluate at 
898          */
899         return ((float)snapblock * data->step_size) + data->offset;
900 }
901
902 static FModifierTypeInfo FMI_STEPPED = {
903         FMODIFIER_TYPE_STEPPED, /* type */
904         sizeof(FMod_Limits), /* size */
905         FMI_TYPE_GENERATE_CURVE, /* action type */  /* XXX... err... */   
906         FMI_REQUIRES_RUNTIME_CHECK, /* requirements */
907         "Stepped", /* name */
908         "FMod_Stepped", /* struct name */
909         NULL, /* free data */
910         NULL, /* copy data */
911         fcm_stepped_new_data, /* new data */
912         NULL, /* verify */
913         fcm_stepped_time, /* evaluate time */
914         NULL /* evaluate */
915 };
916
917 /* F-Curve Modifier API --------------------------- */
918 /* All of the F-Curve Modifier api functions use FModifierTypeInfo structs to carry out
919  * and operations that involve F-Curve modifier specific code.
920  */
921
922 /* These globals only ever get directly accessed in this file */
923 static FModifierTypeInfo *fmodifiersTypeInfo[FMODIFIER_NUM_TYPES];
924 static short FMI_INIT= 1; /* when non-zero, the list needs to be updated */
925
926 /* This function only gets called when FMI_INIT is non-zero */
927 static void fmods_init_typeinfo () 
928 {
929         fmodifiersTypeInfo[0]=  NULL;                                   /* 'Null' F-Curve Modifier */
930         fmodifiersTypeInfo[1]=  &FMI_GENERATOR;                 /* Generator F-Curve Modifier */
931         fmodifiersTypeInfo[2]=  &FMI_FN_GENERATOR;              /* Built-In Function Generator F-Curve Modifier */
932         fmodifiersTypeInfo[3]=  &FMI_ENVELOPE;                  /* Envelope F-Curve Modifier */
933         fmodifiersTypeInfo[4]=  &FMI_CYCLES;                    /* Cycles F-Curve Modifier */
934         fmodifiersTypeInfo[5]=  &FMI_NOISE;                             /* Apply-Noise F-Curve Modifier */
935         fmodifiersTypeInfo[6]=  NULL/*&FMI_FILTER*/;                    /* Filter F-Curve Modifier */  // XXX unimplemented
936         fmodifiersTypeInfo[7]=  &FMI_PYTHON;                    /* Custom Python F-Curve Modifier */
937         fmodifiersTypeInfo[8]=  &FMI_LIMITS;                    /* Limits F-Curve Modifier */
938         fmodifiersTypeInfo[9]=  &FMI_STEPPED;                   /* Stepped F-Curve Modifier */
939 }
940
941 /* This function should be used for getting the appropriate type-info when only
942  * a F-Curve modifier type is known
943  */
944 FModifierTypeInfo *get_fmodifier_typeinfo (int type)
945 {
946         /* initialise the type-info list? */
947         if (FMI_INIT) {
948                 fmods_init_typeinfo();
949                 FMI_INIT = 0;
950         }
951         
952         /* only return for valid types */
953         if ( (type >= FMODIFIER_TYPE_NULL) && 
954                  (type <= FMODIFIER_NUM_TYPES ) ) 
955         {
956                 /* there shouldn't be any segfaults here... */
957                 return fmodifiersTypeInfo[type];
958         }
959         else {
960                 printf("No valid F-Curve Modifier type-info data available. Type = %i \n", type);
961         }
962         
963         return NULL;
964
965  
966 /* This function should always be used to get the appropriate type-info, as it
967  * has checks which prevent segfaults in some weird cases.
968  */
969 FModifierTypeInfo *fmodifier_get_typeinfo (FModifier *fcm)
970 {
971         /* only return typeinfo for valid modifiers */
972         if (fcm)
973                 return get_fmodifier_typeinfo(fcm->type);
974         else
975                 return NULL;
976 }
977
978 /* API --------------------------- */
979
980 /* Add a new F-Curve Modifier to the given F-Curve of a certain type */
981 FModifier *add_fmodifier (ListBase *modifiers, int type)
982 {
983         FModifierTypeInfo *fmi= get_fmodifier_typeinfo(type);
984         FModifier *fcm;
985         
986         /* sanity checks */
987         if ELEM(NULL, modifiers, fmi)
988                 return NULL;
989         
990         /* special checks for whether modifier can be added */
991         if ((modifiers->first) && (type == FMODIFIER_TYPE_CYCLES)) {
992                 /* cycles modifier must be first in stack, so for now, don't add if it can't be */
993                 // TODO: perhaps there is some better way, but for now, 
994                 printf("Error: Cannot add 'Cycles' modifier to F-Curve, as 'Cycles' modifier can only be first in stack. \n");
995                 return NULL;
996         }
997         
998         /* add modifier itself */
999         fcm= MEM_callocN(sizeof(FModifier), "F-Curve Modifier");
1000         fcm->type = type;
1001         fcm->flag = FMODIFIER_FLAG_EXPANDED;
1002         BLI_addtail(modifiers, fcm);
1003         
1004         /* add modifier's data */
1005         fcm->data= MEM_callocN(fmi->size, fmi->structName);
1006                 
1007         /* init custom settings if necessary */
1008         if (fmi->new_data)      
1009                 fmi->new_data(fcm->data);
1010                 
1011         /* return modifier for further editing */
1012         return fcm;
1013 }
1014
1015 /* Make a copy of the specified F-Modifier */
1016 FModifier *copy_fmodifier (FModifier *src)
1017 {
1018         FModifierTypeInfo *fmi= fmodifier_get_typeinfo(src);
1019         FModifier *dst;
1020         
1021         /* sanity check */
1022         if (src == NULL)
1023                 return NULL;
1024                 
1025         /* copy the base data, clearing the links */
1026         dst = MEM_dupallocN(src);
1027         dst->next = dst->prev = NULL;
1028         
1029         /* make a new copy of the F-Modifier's data */
1030         dst->data = MEM_dupallocN(src->data);
1031         
1032         /* only do specific constraints if required */
1033         if (fmi && fmi->copy_data)
1034                 fmi->copy_data(dst, src);
1035                 
1036         /* return the new modifier */
1037         return dst;
1038 }
1039
1040 /* Duplicate all of the F-Modifiers in the Modifier stacks */
1041 void copy_fmodifiers (ListBase *dst, ListBase *src)
1042 {
1043         FModifier *fcm, *srcfcm;
1044         
1045         if ELEM(NULL, dst, src)
1046                 return;
1047         
1048         dst->first= dst->last= NULL;
1049         BLI_duplicatelist(dst, src);
1050         
1051         for (fcm=dst->first, srcfcm=src->first; fcm && srcfcm; srcfcm=srcfcm->next, fcm=fcm->next) {
1052                 FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
1053                 
1054                 /* make a new copy of the F-Modifier's data */
1055                 fcm->data = MEM_dupallocN(fcm->data);
1056                 
1057                 /* only do specific constraints if required */
1058                 if (fmi && fmi->copy_data)
1059                         fmi->copy_data(fcm, srcfcm);
1060         }
1061 }
1062
1063 /* Remove and free the given F-Modifier from the given stack  */
1064 int remove_fmodifier (ListBase *modifiers, FModifier *fcm)
1065 {
1066         FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
1067         
1068         /* sanity check */
1069         if (fcm == NULL)
1070                 return 0;
1071         
1072         /* free modifier's special data (stored inside fcm->data) */
1073         if (fcm->data) {
1074                 if (fmi && fmi->free_data)
1075                         fmi->free_data(fcm);
1076                         
1077                 /* free modifier's data (fcm->data) */
1078                 MEM_freeN(fcm->data);
1079         }
1080         
1081         /* remove modifier from stack */
1082         if (modifiers) {
1083                 BLI_freelinkN(modifiers, fcm);
1084                 return 1;
1085         } 
1086         else {
1087                 // XXX this case can probably be removed some day, as it shouldn't happen...
1088                 printf("remove_fmodifier() - no modifier stack given \n");
1089                 MEM_freeN(fcm);
1090                 return 0;
1091         }
1092 }
1093
1094 /* Remove and free the nth F-Modifier from the given stack */
1095 int remove_fmodifier_index (ListBase *modifiers, int index)
1096 {
1097         FModifier *fcm= BLI_findlink(modifiers, index);
1098         return remove_fmodifier(modifiers, fcm);
1099 }
1100
1101 /* Remove all of a given F-Curve's modifiers */
1102 void free_fmodifiers (ListBase *modifiers)
1103 {
1104         FModifier *fcm, *fmn;
1105         
1106         /* sanity check */
1107         if (modifiers == NULL)
1108                 return;
1109         
1110         /* free each modifier in order - modifier is unlinked from list and freed */
1111         for (fcm= modifiers->first; fcm; fcm= fmn) {
1112                 fmn= fcm->next;
1113                 remove_fmodifier(modifiers, fcm);
1114         }
1115 }
1116
1117 /* Find the active F-Modifier */
1118 FModifier *find_active_fmodifier (ListBase *modifiers)
1119 {
1120         FModifier *fcm;
1121         
1122         /* sanity checks */
1123         if ELEM(NULL, modifiers, modifiers->first)
1124                 return NULL;
1125         
1126         /* loop over modifiers until 'active' one is found */
1127         for (fcm= modifiers->first; fcm; fcm= fcm->next) {
1128                 if (fcm->flag & FMODIFIER_FLAG_ACTIVE)
1129                         return fcm;
1130         }
1131         
1132         /* no modifier is active */
1133         return NULL;
1134 }
1135
1136 /* Set the active F-Modifier */
1137 void set_active_fmodifier (ListBase *modifiers, FModifier *fcm)
1138 {
1139         FModifier *fm;
1140         
1141         /* sanity checks */
1142         if ELEM(NULL, modifiers, modifiers->first)
1143                 return;
1144         
1145         /* deactivate all, and set current one active */
1146         for (fm= modifiers->first; fm; fm= fm->next)
1147                 fm->flag &= ~FMODIFIER_FLAG_ACTIVE;
1148         
1149         /* make given modifier active */
1150         if (fcm)
1151                 fcm->flag |= FMODIFIER_FLAG_ACTIVE;
1152 }
1153
1154 /* Do we have any modifiers which match certain criteria 
1155  *      - mtype - type of modifier (if 0, doesn't matter)
1156  *      - acttype - type of action to perform (if -1, doesn't matter)
1157  */
1158 short list_has_suitable_fmodifier (ListBase *modifiers, int mtype, short acttype)
1159 {
1160         FModifier *fcm;
1161         
1162         /* if there are no specific filtering criteria, just skip */
1163         if ((mtype == 0) && (acttype == 0))
1164                 return (modifiers && modifiers->first);
1165                 
1166         /* sanity checks */
1167         if ELEM(NULL, modifiers, modifiers->first)
1168                 return 0;
1169                 
1170         /* find the first mdifier fitting these criteria */
1171         for (fcm= modifiers->first; fcm; fcm= fcm->next) {
1172                 FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
1173                 short mOk=1, aOk=1; /* by default 1, so that when only one test, won't fail */
1174                 
1175                 /* check if applicable ones are fullfilled */
1176                 if (mtype)
1177                         mOk= (fcm->type == mtype);
1178                 if (acttype > -1)
1179                         aOk= (fmi->acttype == acttype);
1180                         
1181                 /* if both are ok, we've found a hit */
1182                 if (mOk && aOk)
1183                         return 1;
1184         }
1185         
1186         /* no matches */
1187         return 0;
1188 }  
1189
1190 /* Evaluation API --------------------------- */
1191
1192 /* evaluate time modifications imposed by some F-Curve Modifiers
1193  *      - this step acts as an optimisation to prevent the F-Curve stack being evaluated 
1194  *        several times by modifiers requesting the time be modified, as the final result
1195  *        would have required using the modified time
1196  *      - modifiers only ever recieve the unmodified time, as subsequent modifiers should be
1197  *        working on the 'global' result of the modified curve, not some localised segment,
1198  *        so nevaltime gets set to whatever the last time-modifying modifier likes...
1199  *      - we start from the end of the stack, as only the last one matters for now
1200  */
1201 float evaluate_time_fmodifiers (ListBase *modifiers, FCurve *fcu, float cvalue, float evaltime)
1202 {
1203         FModifier *fcm;
1204         
1205         /* sanity checks */
1206         if ELEM(NULL, modifiers, modifiers->last)
1207                 return evaltime;
1208                 
1209         /* Starting from the end of the stack, calculate the time effects of various stacked modifiers 
1210          * on the time the F-Curve should be evaluated at. 
1211          *
1212          * This is done in reverse order to standard evaluation, as when this is done in standard
1213          * order, each modifier would cause jumps to other points in the curve, forcing all
1214          * previous ones to be evaluated again for them to be correct. However, if we did in the 
1215          * reverse order as we have here, we can consider them a macro to micro type of waterfall
1216          * effect, which should get us the desired effects when using layered time manipulations
1217          * (such as multiple 'stepped' modifiers in sequence, causing different stepping rates)
1218          */
1219         for (fcm= modifiers->last; fcm; fcm= fcm->prev) {
1220                 FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
1221                 
1222                 /* only evaluate if there's a callback for this */
1223                 // TODO: implement the 'influence' control feature...
1224                 if (fmi && fmi->evaluate_modifier_time) {
1225                         if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
1226                                 evaltime= fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime);
1227                 }
1228         }
1229         
1230         /* return the modified evaltime */
1231         return evaltime;
1232 }
1233
1234 /* Evalautes the given set of F-Curve Modifiers using the given data
1235  * Should only be called after evaluate_time_fmodifiers() has been called...
1236  */
1237 void evaluate_value_fmodifiers (ListBase *modifiers, FCurve *fcu, float *cvalue, float evaltime)
1238 {
1239         FModifier *fcm;
1240         
1241         /* sanity checks */
1242         if ELEM(NULL, modifiers, modifiers->first)
1243                 return;
1244         
1245         /* evaluate modifiers */
1246         for (fcm= modifiers->first; fcm; fcm= fcm->next) {
1247                 FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm);
1248                 
1249                 /* only evaluate if there's a callback for this */
1250                 // TODO: implement the 'influence' control feature...
1251                 if (fmi && fmi->evaluate_modifier) {
1252                         if ((fcm->flag & (FMODIFIER_FLAG_DISABLED|FMODIFIER_FLAG_MUTED)) == 0)
1253                                 fmi->evaluate_modifier(fcu, fcm, cvalue, evaltime);
1254                 }
1255         }
1256
1257
1258 /* ---------- */
1259
1260 /* Bake modifiers for given F-Curve to curve sample data, in the frame range defined
1261  * by start and end (inclusive).
1262  */
1263 void fcurve_bake_modifiers (FCurve *fcu, int start, int end)
1264 {
1265         ChannelDriver *driver;
1266         
1267         /* sanity checks */
1268         // TODO: make these tests report errors using reports not printf's
1269         if ELEM(NULL, fcu, fcu->modifiers.first) {
1270                 printf("Error: No F-Curve with F-Curve Modifiers to Bake\n");
1271                 return;
1272         }
1273         
1274         /* temporarily, disable driver while we sample, so that they don't influence the outcome */
1275         driver= fcu->driver;
1276         fcu->driver= NULL;
1277         
1278         /* bake the modifiers, by sampling the curve at each frame */
1279         fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve);
1280         
1281         /* free the modifiers now */
1282         free_fmodifiers(&fcu->modifiers);
1283         
1284         /* restore driver */
1285         fcu->driver= driver;
1286 }