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