new parameter addspacechars
[swftools.git] / src / swfc-history.c
index 40f694c..d976053 100644 (file)
 #include <memory.h>
 #include "swfc-history.h"
 
-change_t* change_new(U16 frame, int function, float value, interpolation_t* inter)
+enum
 {
-       change_t* newChange = (change_t*)malloc(sizeof(change_t));
-       change_init(newChange);
-       newChange->frame = frame;
-       newChange->function = function;
-       newChange->value = value;
-       newChange->interpolation = inter;
-       return newChange;
+    T_BEFORE,
+    T_AFTER,
+    T_SYMMETRIC
+};
+
+state_t* state_new(U16 frame, int function, float value, interpolation_t* inter)
+{
+    state_t* newState = (state_t*)malloc(sizeof(state_t));
+    state_init(newState);
+    newState->frame = frame;
+    newState->function = function;
+    newState->value = value;
+    newState->interpolation = inter;
+    return newState;
 }
 
-void change_free(change_t *change)
+void state_free(state_t* state)
 {
-       if (change->next)
-               change_free(change->next);
-       free(change);
+    if (state->next)
+       state_free(state->next);
+    free(state);
 }
 
-void change_init(change_t* change)
+void state_init(state_t* state)
 {
-       memset(change, 0, sizeof(change_t));
+    memset(state, 0, sizeof(state_t));
 }
 
-void change_append(change_t* first, change_t* newChange)
+state_t* state_at(state_t* state, U16 frame)
 {
-       while (first->next)
-               first = first->next;
-       first->next = newChange;
+    while (state->next && state->next->frame < frame)
+       state = state->next;
+    return state;
+}
+
+void state_append(state_t* state, state_t* newState)
+{
+    state_t* previous = 0;
+    state_t* start = state;
+    float p0, p1, m0, m1;
+    int previous_frames = 0, state_frames, new_frames;
+
+    while (state->next)
+    {
+       if (previous)
+           previous_frames = state->frame - previous->frame;
+       previous = state;
+       state = state->next;
+    }
+    state->next = newState;
+    new_frames = newState->frame - state->frame;
+    if (state->function == CF_SCHANGE)
+    {
+       state_frames = state->frame - previous->frame;
+       p0 = previous->value;
+       p1 = state->value;
+       if (previous->function == CF_SCHANGE)
+           m0 = (3 * previous->spline.a + 2 * previous->spline.b + previous->spline.c) * state_frames / previous_frames ;
+       else
+           if (previous->function == CF_CHANGE || previous->function == CF_SWEEP)
+               m0 = state_tangent(start, previous->frame, T_BEFORE) * state_frames;
+           else
+               m0 = (state->value - previous->value);
+       if (newState->function == CF_SCHANGE)
+           m1 = /*0.5 * */(newState->value - previous->value) * state_frames / (new_frames + state_frames);
+       else
+           if (newState->function == CF_CHANGE || newState->function == CF_SWEEP)
+               m1 = state_tangent(previous, state->frame, T_AFTER) * state_frames;
+           else
+               m1 = (newState->value - state->value);
+       state->spline.a = 2 * p0 + m0 - 2 * p1 + m1;
+       state->spline.b = -3 * p0 - 2 * m0 + 3 * p1 - m1;
+       state->spline.c = m0;
+       state->spline.d = p0;
+//     printf("p0: %f, p1: %f, m0: %f, m1: %f.\n", p0, p1, m0, m1);
+//     printf("a: %f, b: %f, c: %f, d: %f.\n", state->spline.a, state->spline.b, state->spline.c, state->spline.d);
+    }
+    if (newState->function == CF_SCHANGE)
+    {
+       p0 = state->value;
+       p1 = newState->value;
+       if (state->function == CF_SCHANGE)
+           m0 = m1;
+       else
+           if (state->function == CF_CHANGE || state->function == CF_SWEEP)
+               m0 = state_tangent(start, state->frame, T_BEFORE) * new_frames;
+           else
+               m0 = (newState->value - state->value);
+       m1 = (newState->value - state->value);
+       newState->spline.a = 2 * p0 + m0 - 2 * p1 + m1;
+       newState->spline.b = -3 * p0 - 2 * m0 + 3 * p1 - m1;
+       newState->spline.c = m0;
+       newState->spline.d = p0;
+//     printf("p0: %f, p1: %f, m0: %f, m1: %f.\n", p0, p1, m0, m1);
+//     printf("a: %f, b: %f, c: %f, d: %f.\n", newState->spline.a, newState->spline.b, newState->spline.c, newState->spline.d);
+    }
+    }
+
+void state_insert(state_t* state, state_t* newState)
+{
+    while (state->next && state->next->frame < newState->frame)
+       state = state->next;
+    newState->next = state->next;
+    state->next = newState;
+    // if this is going to be used to insert CF_SCHANGE states it will have to be extended
+    // as in state_append above. I know this is not necessary right now, so I'll be lazy.
+}
+
+float calculateSpline(state_t* modification, float fraction)
+{
+    spline_t s = modification->spline;
+    return (((s.a * fraction) + s.b) * fraction + s.c) * fraction + s.d;
 }
 
-float interpolateParameter(float p1, float p2, float fraction, interpolation_t* inter)
+float interpolateScalar(float p1, float p2, float fraction, interpolation_t* inter)
 {
        if (!inter)
                return linear(fraction, p1, p2 - p1);
        switch (inter->function)
        {
                case IF_LINEAR: return linear(fraction, p1, p2 - p1);
-               case IF_QUAD_IN: return quadIn(fraction, p1, p2 - p1);
-               case IF_QUAD_OUT: return quadOut(fraction, p1, p2 - p1);
-               case IF_QUAD_IN_OUT: return quadInOut(fraction, p1, p2 - p1);
-               case IF_CUBIC_IN: return cubicIn(fraction, p1, p2 - p1);
-               case IF_CUBIC_OUT: return cubicOut(fraction, p1, p2 - p1);
-               case IF_CUBIC_IN_OUT: return cubicInOut(fraction, p1, p2 - p1);
-               case IF_QUART_IN: return quartIn(fraction, p1, p2 - p1);
-               case IF_QUART_OUT: return quartOut(fraction, p1, p2 - p1);
-               case IF_QUART_IN_OUT: return quartInOut(fraction, p1, p2 - p1);
-               case IF_QUINT_IN: return quintIn(fraction, p1, p2 - p1);
-               case IF_QUINT_OUT: return quintOut(fraction, p1, p2 - p1);
-               case IF_QUINT_IN_OUT: return quintInOut(fraction, p1, p2 - p1);
-               case IF_CIRCLE_IN: return circleIn(fraction, p1, p2 - p1);
-               case IF_CIRCLE_OUT: return circleOut(fraction, p1, p2 - p1);
-               case IF_CIRCLE_IN_OUT: return circleInOut(fraction, p1, p2 - p1);
+       case IF_QUAD_IN: return quadIn(fraction, p1, p2 - p1, inter->slope);
+       case IF_QUAD_OUT: return quadOut(fraction, p1, p2 - p1, inter->slope);
+       case IF_QUAD_IN_OUT: return quadInOut(fraction, p1, p2 - p1, inter->slope);
+       case IF_CUBIC_IN: return cubicIn(fraction, p1, p2 - p1, inter->slope);
+       case IF_CUBIC_OUT: return cubicOut(fraction, p1, p2 - p1, inter->slope);
+       case IF_CUBIC_IN_OUT: return cubicInOut(fraction, p1, p2 - p1, inter->slope);
+       case IF_QUART_IN: return quartIn(fraction, p1, p2 - p1, inter->slope);
+       case IF_QUART_OUT: return quartOut(fraction, p1, p2 - p1, inter->slope);
+       case IF_QUART_IN_OUT: return quartInOut(fraction, p1, p2 - p1, inter->slope);
+       case IF_QUINT_IN: return quintIn(fraction, p1, p2 - p1, inter->slope);
+       case IF_QUINT_OUT: return quintOut(fraction, p1, p2 - p1, inter->slope);
+       case IF_QUINT_IN_OUT: return quintInOut(fraction, p1, p2 - p1, inter->slope);
+       case IF_CIRCLE_IN: return circleIn(fraction, p1, p2 - p1, inter->slope);
+       case IF_CIRCLE_OUT: return circleOut(fraction, p1, p2 - p1, inter->slope);
+       case IF_CIRCLE_IN_OUT: return circleInOut(fraction, p1, p2 - p1, inter->slope);
                case IF_EXPONENTIAL_IN: return exponentialIn(fraction, p1, p2 - p1);
                case IF_EXPONENTIAL_OUT: return exponentialOut(fraction, p1, p2 - p1);
                case IF_EXPONENTIAL_IN_OUT: return exponentialInOut(fraction, p1, p2 - p1);
@@ -97,36 +183,87 @@ float interpolateParameter(float p1, float p2, float fraction, interpolation_t*
        }
 }
 
-float change_value(change_t* first, U16 frame)
+float calculateSweep(state_t* modification, float fraction)
+{
+    arc_t* a = &(modification->arc);
+    float angle = a->angle + fraction * a->delta_angle;
+    if (a->X)
+       return a->cX + a->r * cos(angle);
+    else
+       return a->cY + a->r * sin(angle);
+
+}
+
+int state_differs(state_t* modification, U16 frame)
 {
-       change_t* previous = first;
-       while (first && first->frame < frame)
+    state_t* previous = modification;
+    while (modification && modification->frame < frame)
        {
-               previous = first;
-               first = first->next;
+       previous = modification;
+       modification = modification->next;
+    }
+    if (!modification)
+       return 0;
+    if (modification->frame == frame)
+       return 1;
+    return (modification->function != CF_JUMP);
        }
-       if (!first)
+
+float state_tangent(state_t* modification, U16 frame, int tangent)
+{
+    float deltaFrame = 0.1;
+    switch (tangent)
+{
+       case T_BEFORE:
+           return (state_value(modification, frame) - state_value(modification, frame - deltaFrame)) / deltaFrame;
+       case T_AFTER:
+           return (state_value(modification, frame + deltaFrame) - state_value(modification, frame)) / deltaFrame;
+       default:
+           return (state_value(modification, frame + deltaFrame) - state_value(modification, frame - deltaFrame)) / (2 * deltaFrame);
+    }
+}
+
+float state_value(state_t* modification, float frame)
+{
+    state_t* previous = modification;
+    while (modification && modification->frame < frame)
+    {
+       previous = modification;
+       modification = modification->next;
+    }
+    if (!modification)
                return previous->value;
-       if (first->frame == frame)
+    if (modification->frame == frame)
        {
-               float result;
                do 
                {
-                       result = first->value;
-                       first = first->next;
+           previous = modification;
+           modification = modification->next;
                }
-               while (first && first->frame == frame);
-               return result;
+       while (modification && modification->frame == frame);
+       return previous->value;
        }
-       switch (first->function)
+    switch (modification->function)
        {
                case CF_PUT:
-                       return first->value;
+           return modification->value;
                case CF_CHANGE:
                {
-                       float fraction = (frame - previous->frame) / (float)(first->frame - previous->frame);
-                       return interpolateParameter(previous->value, first->value, fraction, first->interpolation);
+           float fraction = (frame - previous->frame) / (float)(modification->frame - previous->frame);
+           return interpolateScalar(previous->value, modification->value, fraction, modification->interpolation);
+       }
+       case CF_SCHANGE:
+       {
+           float fraction = (frame - previous->frame) / (float)(modification->frame - previous->frame);
+           fraction = interpolateScalar(0, 1, fraction, modification->interpolation);
+           return calculateSpline(modification, fraction);
                }
+       case CF_SWEEP:
+       {
+           float fraction = (frame - previous->frame) / (float)(modification->frame - previous->frame);
+           fraction = interpolateScalar(0, 1, fraction, modification->interpolation);
+           return calculateSweep(modification, fraction);
+       }
                case CF_JUMP:
                        return previous->value;
                default:
@@ -134,10 +271,10 @@ float change_value(change_t* first, U16 frame)
        }
 }
 
-changeFilter_t* changeFilter_new(U16 frame, int function, FILTER* value, interpolation_t* inter)
+filterState_t* filterState_new(U16 frame, int function, FILTERLIST* value, interpolation_t* inter)
 {
-       changeFilter_t* newChange = (changeFilter_t*)malloc(sizeof(changeFilter_t));
-       changeFilter_init(newChange);
+    filterState_t* newChange = (filterState_t*)malloc(sizeof(filterState_t));
+    filterState_init(newChange);
        newChange->frame = frame;
        newChange->function = function;
        newChange->value = value;
@@ -145,163 +282,528 @@ changeFilter_t* changeFilter_new(U16 frame, int function, FILTER* value, interpo
        return newChange;
 }
 
-void changeFilter_free(changeFilter_t *change)
+void filterState_free(filterState_t *change)
 {
        if (change->next)
-               changeFilter_free(change->next);
+       filterState_free(change->next);
+    free(change->value);
        free(change);
 }
 
-void changeFilter_init(changeFilter_t* change)
+void filterState_init(filterState_t* change)
 {
-       memset(change, 0, sizeof(changeFilter_t));
+    memset(change, 0, sizeof(filterState_t));
 }
 
-void changeFilter_append(changeFilter_t* first, changeFilter_t* newChange)
+void filterState_append(filterState_t* first, filterState_t* newChange)
 {
        while (first->next)
                first = first->next;
+    if (!first->value || !newChange->value)
        first->next = newChange;
+    else
+    {
+       int i, mergedCount = 0;
+       int common = first->value->num < newChange->value->num ? first->value->num : newChange->value->num;
+       for (i = 0; i < common; i++)
+       {
+           mergedCount++;
+           if (newChange->value->filter[i]->type != first->value->filter[i]->type)
+               mergedCount++;
+       }
+       mergedCount = mergedCount + first->value->num - common + newChange->value->num - common;
+       if (mergedCount > 8)
+       {
+           char* list1;
+           char* list2;
+           char* newList;
+           list1 = (char*)malloc(1);
+           *list1 = '\0';
+           for (i = 0; i < first->value->num; i++)
+           {
+               newList = (char*)malloc(strlen(list1) + strlen(filtername[first->value->filter[i]->type]) + 2);
+               strcpy(newList, strcat(strcat(list1, "+"), filtername[first->value->filter[i]->type]));
+               free(list1);
+               list1 = newList;
+           }
+           list2 = (char*)malloc(1);
+           *list2 = '\0';
+           for (i = 0; i < newChange->value->num; i++)
+           {
+               newList = (char*)malloc(strlen(list1) + strlen(filtername[newChange->value->filter[i]->type]) + 2);
+               strcpy(newList, strcat(strcat(list2, "+"), filtername[newChange->value->filter[i]->type]));
+               free(list2);
+               list2 = newList;
+           }
+           syntaxerror("filterlists %s and %s cannot be interpolated.", list1, list2);
+       }
+       first->next = newChange;
+    }
 }
 
 RGBA interpolateColor(RGBA c1, RGBA c2, float ratio, interpolation_t* inter)
 {
     RGBA c;
-    c.r = c1.r * (1-ratio) + c2.r * ratio;
-    c.g = c1.g * (1-ratio) + c2.g * ratio;
-    c.b = c1.b * (1-ratio) + c2.b * ratio;
-    c.a = c1.a * (1-ratio) + c2.a * ratio;
+    c.r = interpolateScalar(c1.r, c2.r, ratio, inter);
+    c.g = interpolateScalar(c1.g, c2.g, ratio, inter);
+    c.b = interpolateScalar(c1.b, c2.b, ratio, inter);
+    c.a = interpolateScalar(c1.a, c2.a, ratio, inter);
     return c;
 }
 
-FILTER* interpolateFilter(FILTER* filter1,FILTER* filter2, float ratio, interpolation_t* inter)
+GRADIENT* interpolateNodes(GRADIENT* g1, GRADIENT* g2, float fraction, interpolation_t* inter)
 {
-       if(!filter1 && !filter2)
-               return 0;
-       if(!filter1)
-               return interpolateFilter(filter2,filter1,1-ratio, inter);
+    if (g1->num != g2->num)
+       syntaxerror("Internal error: gradients are not equal in size");
 
-       if(filter2 && filter2->type != filter1->type)
-               syntaxerror("can't interpolate between %s and %s filters yet", filtername[filter1->type], filtername[filter2->type]);
+    int i;
+    GRADIENT* g = (GRADIENT*) malloc(sizeof(GRADIENT));
+    g->ratios = rfx_calloc(16*sizeof(U8));
+    g->rgba = rfx_calloc(16*sizeof(RGBA));
+    g->num = g1->num;
+    for (i = 0; i < g->num; i++)
+    {
+       g->ratios[i] = interpolateScalar(g1->ratios[i], g2->ratios[i], fraction, inter);
+       g->rgba[i] = interpolateColor(g1->rgba[i], g2->rgba[i], fraction, inter);
+    }
+    return g;
+}
+
+void copyGradient(GRADIENT* dest, GRADIENT* source)
+{
+    dest->num = source->num;
+    memcpy(dest->ratios, source->ratios, source->num * sizeof(U8));
+    memcpy(dest->rgba, source->rgba, source->num * sizeof(RGBA));
+}
+
+void insertNode(GRADIENT* g, int pos)
+{
+    memmove(&g->ratios[pos + 1], &g->ratios[pos], (g->num - pos) * sizeof(U8));
+    memmove(&g->rgba[pos + 1], &g->rgba[pos], (g->num - pos) * sizeof(RGBA));
+    if (pos == 0)
+    {
+       g->ratios[0] = g->ratios[1] / 2;
+       g->rgba[0] = g->rgba[1];
+    }
+    else
+       if (pos == g->num)
+       {
+           g->ratios[pos] = (255 + g->ratios[g->num - 1]) / 2;
+           g->rgba[pos] = g->rgba[pos - 1];
+       }
+       else
+       {
+           g->ratios[pos] = (g->ratios[pos - 1] + g->ratios[pos + 1]) / 2;
+           g->rgba[pos] = interpolateColor(g->rgba[pos - 1], g->rgba[pos + 1], 0.5, 0);
+       }
+    g->num++;
+}
+
+void insertOptimalNode(GRADIENT* g)
+{
+    int i, next_gap;
+    int pos = 0;
+    int gap = g->ratios[0];
+    for (i = 0; i < g->num - 1; i++)
+    {
+       next_gap = g->ratios[i + 1] - g->ratios[i];
+       if (next_gap > gap)
+       {
+           gap = next_gap;
+           pos = i + 1;
+       }
+    }
+    next_gap = 255 - g->ratios[g->num -1];
+    if (next_gap > gap)
+       pos = g->num;
+    insertNode(g, pos);
+}
    
-       if(filter1->type == FILTERTYPE_BLUR)
+void growGradient(GRADIENT* start, int size)
+{
+    while (start->num < size)
+       insertOptimalNode(start);
+}
+
+GRADIENT* interpolateGradient(GRADIENT* g1, GRADIENT* g2, float fraction, interpolation_t* inter)
+{
+    int i;
+    GRADIENT g;
+    g.ratios = rfx_calloc(16*sizeof(U8));
+    g.rgba = rfx_calloc(16*sizeof(RGBA));
+
+    if (g1->num > g2->num)
+    {
+       copyGradient(&g, g2);
+       growGradient(&g, g1->num);
+       GRADIENT* result = interpolateNodes(g1, &g, fraction, inter);
+       rfx_free(g.rgba);
+       rfx_free(g.ratios);
+       return result;
+    }
+    else
+       if (g1->num < g2->num)
+       {
+           copyGradient(&g, g1);
+           growGradient(&g, g2->num);
+           GRADIENT* result = interpolateNodes(&g, g2, fraction, inter);
+           rfx_free(g.rgba);
+           rfx_free(g.ratios);
+           return result;
+       }
+       else
+           return interpolateNodes(g1, g2, fraction, inter);
+}
+
+FILTER* copyFilter(FILTER* original)
+{
+    if (!original)
+       return original;
+    FILTER* copy = swf_NewFilter(original->type);
+    switch (original->type)
+    {
+       case FILTERTYPE_BLUR:
+           memcpy(copy, original, sizeof(FILTER_BLUR));
+           break;
+       case FILTERTYPE_GRADIENTGLOW:
+       {
+           memcpy(copy, original, sizeof(FILTER_GRADIENTGLOW));
+           FILTER_GRADIENTGLOW* ggcopy = (FILTER_GRADIENTGLOW*)copy;
+           ggcopy->gradient = (GRADIENT*)malloc(sizeof(GRADIENT));
+           ggcopy->gradient->ratios = (U8*)malloc(16 * sizeof(U8));
+           ggcopy->gradient->rgba = (RGBA*)malloc(16 * sizeof(RGBA));
+           copyGradient(ggcopy->gradient, ((FILTER_GRADIENTGLOW*)original)->gradient);
+       }
+           break;
+       case FILTERTYPE_DROPSHADOW:
+           memcpy(copy, original, sizeof(FILTER_DROPSHADOW));
+           break;
+       case FILTERTYPE_BEVEL:
+           memcpy(copy, original, sizeof(FILTER_BEVEL));
+           break;
+       default: syntaxerror("Internal error: unsupported filterype, cannot copy");
+    }
+    return copy;
+}
+
+FILTER* interpolateBlur(FILTER* filter1, FILTER* filter2, float ratio, interpolation_t* inter)
        {
                FILTER_BLUR*f1 = (FILTER_BLUR*)filter1;
                FILTER_BLUR*f2 = (FILTER_BLUR*)filter2;
-               if(f2 && f1->blurx == f2->blurx && f1->blury == f2->blury)
-                       return 0;
+    if (!f1)
+       f1 = noBlur;
+    if (!f2)
+       f2 = noBlur;
+    if(f1->blurx == f2->blurx && f1->blury == f2->blury)
+       return copyFilter(filter1);
                FILTER_BLUR*f = (FILTER_BLUR*)swf_NewFilter(FILTERTYPE_BLUR);
-               f->blurx= (f1->blurx)*(1-ratio) + (f2?f2->blurx:0)*ratio;
-               f->blury= (f1->blury)*(1-ratio) + (f2?f2->blury:0)*ratio;
-               f->passes= (f1->passes)*(1-ratio) + (f2?f2->passes:0)*ratio;
+    f->blurx= interpolateScalar(f1->blurx, (f2->blurx), ratio, inter);
+    f->blury= interpolateScalar(f1->blury, (f2->blury), ratio, inter);
+    f->passes= interpolateScalar(f1->passes, (f2->passes), ratio, inter);
                return (FILTER*)f;
                }
-       else
-               if (filter1->type == FILTERTYPE_DROPSHADOW)
+
+void matchDropshadowFlags(FILTER_DROPSHADOW* unset, FILTER_DROPSHADOW* target)
+{
+    unset->innershadow = target->innershadow;
+    unset->knockout = target->knockout;
+    unset->composite = target->composite;
+}
+
+FILTER* interpolateDropshadow(FILTER* filter1,FILTER* filter2, float ratio, interpolation_t* inter)
                {
                        FILTER_DROPSHADOW*f1 = (FILTER_DROPSHADOW*)filter1;
                        FILTER_DROPSHADOW*f2 = (FILTER_DROPSHADOW*)filter2;
-                       if(f2 && !memcmp(&f1->color,&f2->color,sizeof(RGBA)) && f1->strength == f2->strength && 
+    if (!f1)
+       f1 = noDropshadow;
+    if (!f2)
+       f2 = noDropshadow;
+    if(!memcmp(&f1->color,&f2->color,sizeof(RGBA)) && f1->strength == f2->strength &&
                                f1->blurx == f2->blurx && f1->blury == f2->blury && 
                                f1->angle == f2->angle && f1->distance == f2->distance)
-                               return 0;
+               return copyFilter(filter1);
                        FILTER_DROPSHADOW*f = (FILTER_DROPSHADOW*)swf_NewFilter(FILTERTYPE_DROPSHADOW);
                        memcpy(f, f1, sizeof(FILTER_DROPSHADOW));
                        f->color = interpolateColor(f1->color, f2->color, ratio, inter);
-                       f->blurx= (f1->blurx)*(1-ratio) + (f2?f2->blurx:0)*ratio;
-                       f->blury= (f1->blury)*(1-ratio) + (f2?f2->blury:0)*ratio;
-                       f->passes= (f1->passes)*(1-ratio) + (f2?f2->passes:0)*ratio;
-                       f->angle= (f1->angle)*(1-ratio) + (f2?f2->angle:0)*ratio;
-                       f->distance= (f1->distance)*(1-ratio) + (f2?f2->distance:0)*ratio;
-                       f->strength= (f1->strength)*(1-ratio) + (f2?f2->strength:0)*ratio;
-                       return (FILTER*)f;
+    f->blurx= interpolateScalar(f1->blurx, (f2->blurx), ratio, inter);
+    f->blury= interpolateScalar(f1->blury, (f2->blury), ratio, inter);
+    f->passes= interpolateScalar(f1->passes, (f2->passes), ratio, inter);
+    f->angle= interpolateScalar(f1->angle, (f2->angle), ratio, inter);
+    f->distance= interpolateScalar(f1->distance, (f2->distance), ratio, inter);
+    f->strength= interpolateScalar(f1->strength, (f2->strength), ratio, inter);
+    if (f1 == noDropshadow)
+    {
+       if (f2 != noDropshadow)
+           matchDropshadowFlags(f, f2);
                }
                else
-                       if (filter1->type == FILTERTYPE_BEVEL)
+       if (f2 == noDropshadow)
+           matchDropshadowFlags(f, f1);
+       else
+           if (ratio > 0.5)
+               matchDropshadowFlags(f, f2);
+           else
+               matchDropshadowFlags(f, f1);
+    return (FILTER*)f;
+}
+
+void matchBevelFlags(FILTER_BEVEL* unset, FILTER_BEVEL* target)
+{
+    unset->innershadow = target->innershadow;
+    unset->knockout = target->knockout;
+    unset->composite = target->composite;
+    unset->ontop = target->ontop;
+}
+
+FILTER* interpolateBevel(FILTER* filter1,FILTER* filter2, float ratio, interpolation_t* inter)
                        {
                                FILTER_BEVEL*f1 = (FILTER_BEVEL*)filter1;
                                FILTER_BEVEL*f2 = (FILTER_BEVEL*)filter2;
-                               if(f2 && !memcmp(&f1->shadow,&f2->shadow,sizeof(RGBA)) && 
+    if (!f1)
+       f1 = noBevel;
+    if (!f2)
+       f2 = noBevel;
+    if(!memcmp(&f1->shadow,&f2->shadow,sizeof(RGBA)) &&
                                        !memcmp(&f1->highlight,&f2->highlight,sizeof(RGBA)) && 
                                        f1->blurx == f2->blurx && f1->blury == f2->blury && f1->angle == f2->angle && f1->strength == f2->strength && f1->distance == f2->distance)
-                                       return 0;
+               return copyFilter(filter1);
                                FILTER_BEVEL*f = (FILTER_BEVEL*)swf_NewFilter(FILTERTYPE_BEVEL);
                                memcpy(f, f1, sizeof(FILTER_BEVEL));
                                f->shadow = interpolateColor(f1->shadow, f2->shadow, ratio, inter);
                                f->highlight = interpolateColor(f1->highlight, f2->highlight, ratio, inter);
-                               f->blurx= (f1->blurx)*(1-ratio) + (f2?f2->blurx:0)*ratio;
-                               f->blury= (f1->blury)*(1-ratio) + (f2?f2->blury:0)*ratio;
-                               f->passes= (f1->passes)*(1-ratio) + (f2?f2->passes:0)*ratio;
-                               f->angle= (f1->angle)*(1-ratio) + (f2?f2->angle:0)*ratio;
-                               f->distance= (f1->distance)*(1-ratio) + (f2?f2->distance:0)*ratio;
-                               f->strength= (f1->strength)*(1-ratio) + (f2?f2->strength:0)*ratio;
+    f->blurx= interpolateScalar(f1->blurx, (f2->blurx), ratio, inter);
+    f->blury= interpolateScalar(f1->blury, (f2->blury), ratio, inter);
+    f->passes= interpolateScalar(f1->passes, (f2->passes), ratio, inter);
+    f->angle= interpolateScalar(f1->angle, (f2->angle), ratio, inter);
+    f->distance= interpolateScalar(f1->distance, (f2->distance), ratio, inter);
+    f->strength= interpolateScalar(f1->strength, (f2->strength), ratio, inter);
+    if (f1 == noBevel)
+    {
+       if (f2 != noBevel)
+           matchBevelFlags(f, f2);
+    }
+    else
+       if (f2 == noBevel)
+           matchBevelFlags(f, f1);
+       else
+           if (ratio > 0.5)
+               matchBevelFlags(f, f2);
+           else
+               matchBevelFlags(f, f1);
                                return (FILTER*)f;
-                       } /*else if (filter1->type == FILTERTYPE_GRADIENTGLOW) {
+}
+
+void matchGradientGlowFlags(FILTER_GRADIENTGLOW* unset, FILTER_GRADIENTGLOW* target)
+{
+    unset->innershadow = target->innershadow;
+    unset->knockout = target->knockout;
+    unset->composite = target->composite;
+    unset->ontop = target->ontop;
+}
+
+FILTER* interpolateGradientGlow(FILTER* filter1,FILTER* filter2, float ratio, interpolation_t* inter)
+{
+    FILTER_GRADIENTGLOW*f1 = (FILTER_GRADIENTGLOW*)filter1;
+    FILTER_GRADIENTGLOW*f2 = (FILTER_GRADIENTGLOW*)filter2;
+    if (!f1)
+       f1 = noGradientGlow;
+    if (!f2)
+       f2 = noGradientGlow;
+    if(f1->gradient->num == f2->gradient->num &&
+               !memcmp(&f1->gradient->ratios,&f2->gradient->ratios,f1->gradient->num * sizeof(U8)) &&
+               !memcmp(&f1->gradient->rgba,&f2->gradient->rgba,f1->gradient->num * sizeof(RGBA)) &&
+               f1->blurx == f2->blurx && f1->blury == f2->blury && f1->angle == f2->angle && f1->strength == f2->strength && f1->distance == f2->distance)
+               return copyFilter(filter1);
        FILTER_GRADIENTGLOW*f = (FILTER_GRADIENTGLOW*)swf_NewFilter(FILTERTYPE_GRADIENTGLOW);
-       // can't interpolate gradients
-       memcpy(f, filter1, sizeof(FILTER_GRADIENTGLOW));
+    memcpy(f, f1, sizeof(FILTER_GRADIENTGLOW));
+    f->blurx= interpolateScalar(f1->blurx, (f2->blurx), ratio, inter);
+    f->blury= interpolateScalar(f1->blury, (f2->blury), ratio, inter);
+    f->passes= interpolateScalar(f1->passes, (f2->passes), ratio, inter);
+    f->angle= interpolateScalar(f1->angle, (f2->angle), ratio, inter);
+    f->distance= interpolateScalar(f1->distance, (f2->distance), ratio, inter);
+    f->strength= interpolateScalar(f1->strength, (f2->strength), ratio, inter);
+    f->gradient= interpolateGradient(f1->gradient, f2->gradient, ratio, inter);
+    if (f1 == noGradientGlow)
+    {
+       if (f2 != noGradientGlow)
+           matchGradientGlowFlags(f, f2);
+    }
+    else
+       if (f2 == noGradientGlow)
+           matchGradientGlowFlags(f, f1);
+       else
+           if (ratio > 0.5)
+               matchGradientGlowFlags(f, f2);
+           else
+               matchGradientGlowFlags(f, f1);
        return (FILTER*)f;
-    }*/ else
-                               syntaxerror("can't interpolate %s filters yet", filtername[filter1->type]);
+}
+
+FILTER* interpolateFilter(FILTER* filter1,FILTER* filter2, float ratio, interpolation_t* inter)
+{
+    if(!filter1 && !filter2)
+       return 0;
+
+    int filter_type;
+    if (!filter1)
+       filter_type = filter2->type;
+    else
+       if (!filter2)
+           filter_type = filter1->type;
+       else
+           if(filter2->type != filter1->type)
+               syntaxerror("can't interpolate between %s and %s filters yet", filtername[filter1->type], filtername[filter2->type]);
+           else
+               filter_type = filter1->type;
+
+    switch (filter_type)
+    {
+        case FILTERTYPE_BLUR:
+            return interpolateBlur(filter1, filter2, ratio, inter);
+        case FILTERTYPE_BEVEL:
+            return interpolateBevel(filter1, filter2, ratio, inter);
+        case FILTERTYPE_DROPSHADOW:
+            return interpolateDropshadow(filter1, filter2, ratio, inter);
+        case FILTERTYPE_GRADIENTGLOW:
+            return interpolateGradientGlow(filter1, filter2, ratio, inter);
+        default:
+            syntaxerror("Filtertype %s not supported yet.\n", filtername[filter1->type]);
+    }
        return 0;
 }
 
-FILTER* copyFilter(FILTER* original)
+FILTERLIST* copyFilterList(FILTERLIST* original)
 {
        if (!original)
                return original;
-       FILTER* copy = swf_NewFilter(original->type);
-       switch (original->type)
+    int i;
+    FILTERLIST* copy = (FILTERLIST*)malloc(sizeof(FILTERLIST));
+    copy->num = original->num;
+    for (i = 0; i < copy->num; i++)
+       copy->filter[i] = copyFilter(original->filter[i]);
+    return copy;
+}
+
+FILTER* noFilter(int type)
+{
+    switch (type)
        {
                case FILTERTYPE_BLUR:
-                       memcpy(copy, original, sizeof(FILTER_BLUR));
+           return (FILTER*)noBlur;
                        break;
-               case FILTERTYPE_GRADIENTGLOW:
-                       memcpy(copy, original, sizeof(FILTER_GRADIENTGLOW));
+       case FILTERTYPE_BEVEL:
+           return (FILTER*)noBevel;
                        break;
                case FILTERTYPE_DROPSHADOW:
-                       memcpy(copy, original, sizeof(FILTER_DROPSHADOW)); 
+           return (FILTER*)noDropshadow;
                        break;
-               case FILTERTYPE_BEVEL:
-                       memcpy(copy, original, sizeof(FILTER_BEVEL)); 
+       case FILTERTYPE_GRADIENTGLOW:
+           return (FILTER*)noGradientGlow;
                        break;
-               default: printf("unsupported filterype");
+       default:
+           syntaxerror("Internal error: unsupported filtertype, cannot match filterlists");
        }
-       return copy;
+    return 0;
 }
 
-FILTER* changeFilter_value(changeFilter_t* first, U16 frame)
+FILTERLIST* interpolateFilterList(FILTERLIST* list1, FILTERLIST* list2, float ratio, interpolation_t* inter)
 {
-       changeFilter_t* previous = first;
-       while (first && first->frame < frame)
+    if (!list1 && !list2)
+       return list1;
+    FILTERLIST start, target, dummy;
+    dummy.num = 0;
+    if (!list1)
+       list1 = &dummy;
+    if (!list2)
+       list2 = &dummy;
+    int i, j = 0;
+    int common = list1->num < list2->num ? list1->num : list2->num;
+        for (i = 0; i < common; i++)
+    {
+       start.filter[j] = list1->filter[i];
+       if (list2->filter[i]->type == list1->filter[i]->type)
+       {
+           target.filter[j] = list2->filter[i];
+           j++;
+       }
+       else
+       {
+           target.filter[j] = noFilter(list1->filter[i]->type);
+           j++;
+           start.filter[j] = noFilter(list2->filter[i]->type);
+           target.filter[j] = list2->filter[i];
+           j++;
+       }
+    }
+    if (list1->num > common)
+       for (i = common; i < list1->num; i++)
+       {
+           start.filter[j] = list1->filter[i];
+           target.filter[j] = noFilter(list1->filter[i]->type);
+           j++;
+       }
+    if (list2->num > common)
+       for (i = common; i < list2->num; i++)
+       {
+           start.filter[j] = noFilter(list2->filter[i]->type);
+           target.filter[j] = list2->filter[i];
+           j++;
+       }
+    start.num = j;
+    target.num = j;
+    FILTERLIST* mixedList = (FILTERLIST*)malloc(sizeof(FILTERLIST));
+    mixedList->num = j;
+    for (i = 0; i < j; i++)
+       mixedList->filter[i] = interpolateFilter(start.filter[i], target.filter[i], ratio, inter);
+    return mixedList;
+}
+
+int filterState_differs(filterState_t* modification, U16 frame)
+{
+    filterState_t* previous = modification;
+    while (modification && modification->frame < frame)
        {
-               previous = first;
-               first = first->next;
+       previous = modification;
+       modification = modification->next;
        }
-       if (!first)
-               return copyFilter(previous->value);
-       if (first->frame == frame)
+    if (!modification)
+       return 0;
+    if (modification->frame == frame)
+       return 1;
+    return (modification->function != CF_JUMP);
+}
+
+FILTERLIST* filterState_value(filterState_t* modification, U16 frame)
+{
+    filterState_t* previous = modification;
+    while (modification && modification->frame < frame)
+    {
+       previous = modification;
+       modification = modification->next;
+    }
+    if (!modification)
+       return copyFilterList(previous->value);
+    if (modification->frame == frame)
        {
-               FILTER* result;
                do 
                {
-                       result = first->value;
-                       first = first->next;
+           previous = modification;
+           modification = modification->next;
                }
-               while (first && first->frame == frame);
-               return copyFilter(result);
+       while (modification && modification->frame == frame);
+       return copyFilterList(previous->value);
        }
-       switch (first->function)
+    switch (modification->function)
        {
                case CF_PUT:
-                       return copyFilter(first->value);
+           return copyFilterList(modification->value);
                case CF_CHANGE:
                {
-                       float fraction = (frame - previous->frame) / (float)(first->frame - previous->frame);
-                       return interpolateFilter(previous->value, first->value, fraction, first->interpolation);
+           float fraction = (frame - previous->frame) / (float)(modification->frame - previous->frame);
+           return interpolateFilterList(previous->value, modification->value, fraction, modification->interpolation);
                }
                case CF_JUMP:
-                       return copyFilter(previous->value);
+           return copyFilterList(previous->value);
                default:
                        return 0;
        }
@@ -316,86 +818,345 @@ history_t* history_new()
 
 void history_free(history_t* past)
 {
-       change_free(dictionary_lookup(past->changes, "x"));
-       change_free(dictionary_lookup(past->changes, "y"));
-       change_free(dictionary_lookup(past->changes, "scalex"));
-       change_free(dictionary_lookup(past->changes, "scaley"));
-       change_free(dictionary_lookup(past->changes, "cxform.r0"));
-       change_free(dictionary_lookup(past->changes, "cxform.g0"));
-       change_free(dictionary_lookup(past->changes, "cxform.b0"));
-       change_free(dictionary_lookup(past->changes, "cxform.a0"));
-       change_free(dictionary_lookup(past->changes, "cxform.r1"));
-       change_free(dictionary_lookup(past->changes, "cxform.g1"));
-       change_free(dictionary_lookup(past->changes, "cxform.b1"));
-       change_free(dictionary_lookup(past->changes, "cxform.a1"));
-       change_free(dictionary_lookup(past->changes, "rotate"));
-       change_free(dictionary_lookup(past->changes, "shear"));
-       change_free(dictionary_lookup(past->changes, "pivot.x"));
-       change_free(dictionary_lookup(past->changes, "pivot.y"));
-       change_free(dictionary_lookup(past->changes, "pin.x"));
-       change_free(dictionary_lookup(past->changes, "pin.y"));
-       change_free(dictionary_lookup(past->changes, "blendmode"));
-       changeFilter_free(dictionary_lookup(past->changes, "filter"));
-       dictionary_destroy(past->changes);
-       free(past);
+    state_free(dict_lookup(past->states, "x"));
+    state_free(dict_lookup(past->states, "y"));
+    state_free(dict_lookup(past->states, "scalex"));
+    state_free(dict_lookup(past->states, "scaley"));
+    state_free(dict_lookup(past->states, "cxform.r0"));
+    state_free(dict_lookup(past->states, "cxform.g0"));
+    state_free(dict_lookup(past->states, "cxform.b0"));
+    state_free(dict_lookup(past->states, "cxform.a0"));
+    state_free(dict_lookup(past->states, "cxform.r1"));
+    state_free(dict_lookup(past->states, "cxform.g1"));
+    state_free(dict_lookup(past->states, "cxform.b1"));
+    state_free(dict_lookup(past->states, "cxform.a1"));
+    state_free(dict_lookup(past->states, "rotate"));
+    state_free(dict_lookup(past->states, "shear"));
+    state_free(dict_lookup(past->states, "pivot.x"));
+    state_free(dict_lookup(past->states, "pivot.y"));
+    state_free(dict_lookup(past->states, "pin.x"));
+    state_free(dict_lookup(past->states, "pin.y"));
+    state_free(dict_lookup(past->states, "blendmode"));
+    state_free(dict_lookup(past->states, "flags"));
+    filterState_free(dict_lookup(past->states, "filter"));
+    dict_destroy(past->states);
+    free(past);
 }
 
 void history_init(history_t* past)
 {
-       past->changes = (dictionary_t*)malloc(sizeof(dictionary_t));
-       dictionary_init(past->changes);
+    past->states = (dict_t*)malloc(sizeof(dict_t));
+    dict_init(past->states, 16);
 }
 
 void history_begin(history_t* past, char* parameter, U16 frame, TAG* tag, float value)
 {
-       change_t* first = change_new(frame, CF_PUT, value, 0);
+    state_t* first = state_new(frame, CF_PUT, value, 0);
        past->firstTag = tag;
        past->firstFrame = frame;
-       dictionary_put2(past->changes, parameter, first);
+    dict_put2(past->states, parameter, first);
 }
 
-void history_beginFilter(history_t* past, U16 frame, TAG* tag, FILTER* value)
+void history_beginFilter(history_t* past, U16 frame, TAG* tag, FILTERLIST* value)
 {
-       changeFilter_t* first = changeFilter_new(frame, CF_PUT, value, 0);
+    filterState_t* first = filterState_new(frame, CF_PUT, value, 0);
        past->firstTag = tag;
        past->firstFrame = frame;
-       dictionary_put2(past->changes, "filter", first);
+    dict_put2(past->states, "filter", first);
 }
 
 void history_remember(history_t* past, char* parameter, U16 frame, int function, float value, interpolation_t* inter)
 {
-       change_t* first = dictionary_lookup(past->changes, parameter);
-       if (first) //should always be true
+    past->lastFrame = frame;
+    state_t* state = dict_lookup(past->states, parameter);
+    if (state) //should always be true
+    {
+       state_t* next = state_new(frame, function, value, inter);
+       state_append(state, next);
+    }
+    else
+       syntaxerror("Internal error: changing parameter %s, which is unknown for the instance.", parameter);
+}
+
+static float getAngle(float dX, float dY)
+{
+    float radius = sqrt(dX * dX + dY * dY);
+    if (radius == 0)
+       return 0.0;
+    if (dX >= 0)
+       if (dY > 0)
+           return acos(dX / radius);
+       else
+           return 2 * M_PI - acos(dX / radius);
+    else
+       if (dY > 0)
+           return M_PI - acos(-dX / radius);
+       else
+           return M_PI + acos(-dX / radius);
+}
+
+static float getDeltaAngle(float angle1, float angle2, int clockwise)
        {
-               change_t* next = change_new(frame, function, value, inter);
-               change_append(first, next);
+           if (!clockwise)
+           {
+               if (angle1 > angle2)
+                   return angle2 - angle1;
+               else
+                   return angle2 - angle1 - 2 * M_PI;
+           }
+           else
+           {
+               if (angle1 > angle2)
+                   return 2 * M_PI - angle1 + angle2;
+               else
+                   return angle2 - angle1;
        }
 }
 
-void history_rememberFilter(history_t* past, U16 frame, int function, FILTER* value, interpolation_t* inter)
+void history_rememberSweep(history_t* past, U16 frame, float x, float y, float r, int clockwise, int short_arc, interpolation_t* inter)
 {
-       changeFilter_t* first = dictionary_lookup(past->changes, "filter");
+    float lastX, lastY, dX, dY;
+    U16 lastFrame;
+
+    past->lastFrame = frame;
+    state_t* change = dict_lookup(past->states, "x");
+    if (change) //should always be true
+    {
+       while (change->next)
+           change = change->next;
+       lastFrame = change->frame;
+       lastX = change->value;
+       change = dict_lookup(past->states, "y");
+       if (change) //should always be true
+       {
+           while (change->next)
+               change = change->next;
+           lastY = change->value;
+           dX = x - lastX;
+           dY = y - lastY;
+           if (dX == 0 && dY == 0)
+               syntaxerror("sweep not possible: startpoint and endpoint must not be equal");
+           if ((dX) * (dX) + (dY) * (dY) > 4 * r * r)
+               syntaxerror("sweep not possible: radius is to small");
+           if (change->frame > lastFrame)
+           {
+               lastFrame = change->frame;
+               history_remember(past, "x", lastFrame, CF_JUMP, lastX, 0);
+           }
+           else
+               if (change->frame < lastFrame)
+                   history_remember(past, "y", lastFrame, CF_JUMP, lastY, 0);
+           float c1X, c1Y, c2X, c2Y;
+           if (dX == 0) //vertical
+           {
+               c1Y = c2Y = (lastY + y) / 2;
+               c1X = x + sqrt(r * r - (c1Y - y) * (c1Y - y));
+               c2X = 2 * x -c1X;
+           }
+           else
+               if (dY == 0) //horizontal
+               {
+                   c1X = c2X = (lastX + x) / 2;
+                   c1Y = y +sqrt(r * r - (c1X - x) * (c1X - x));
+                   c2Y = 2 * y -c1Y;
+               }
+               else
+               {
+                   c1X = sqrt((r * r - (dX * dX + dY * dY) / 4) / (1 + dX * dX / dY / dY));
+                   c2X = -c1X;
+                   c1Y = -dX / dY * c1X;
+                   c2Y = -c1Y;
+                   c1X += (x + lastX) / 2;
+                   c2X += (x + lastX) / 2;
+                   c1Y += (y + lastY) / 2;
+                   c2Y += (y + lastY) / 2;
+               }
+           float angle1, angle2, delta_angle, centerX, centerY;
+           angle1 = getAngle(lastX - c1X, lastY - c1Y);
+           angle2 = getAngle(x - c1X, y - c1Y);
+           delta_angle = getDeltaAngle(angle1, angle2, clockwise);
+           if ((short_arc && fabs(delta_angle) <= M_PI) || (! short_arc && fabs(delta_angle) >= M_PI))
+           {
+               centerX = c1X;
+               centerY = c1Y;
+           }
+           else
+           {
+               angle1 = getAngle(lastX - c2X, lastY - c2Y);
+               angle2 = getAngle(x - c2X, y - c2Y);
+               delta_angle = getDeltaAngle(angle1, angle2, clockwise);
+               centerX = c2X;
+               centerY = c2Y;
+           }
+           change = dict_lookup(past->states, "x");
+           state_t* nextX = state_new(frame, CF_SWEEP, x, inter);
+           nextX->arc.r = r;
+           nextX->arc.angle = angle1;
+           nextX->arc.delta_angle = delta_angle;
+           nextX->arc.cX = centerX;
+           nextX->arc.cY = centerY;
+           nextX->arc.X = 1;
+           state_append(change, nextX);
+           change = dict_lookup(past->states, "y");
+           state_t* nextY = state_new(frame, CF_SWEEP, y, inter);
+           nextY->arc.r = r;
+           nextY->arc.angle = angle1;
+           nextY->arc.delta_angle = delta_angle;
+           nextY->arc.cX = centerX;
+           nextY->arc.cY = centerY;
+           nextY->arc.X = 0;
+           state_append(change, nextY);
+       }
+       else
+           syntaxerror("Internal error: changing parameter y in sweep, which is unknown for the instance.");
+    }
+    else
+       syntaxerror("Internal error: changing parameter x in sweep, which is unknown for the instance.");
+}
+
+void history_rememberFilter(history_t* past, U16 frame, int function, FILTERLIST* value, interpolation_t* inter)
+{
+    past->lastFrame = frame;
+    filterState_t* first = dict_lookup(past->states, "filter");
        if (first) //should always be true
        {
-               changeFilter_t* next = changeFilter_new(frame, function, value, inter);
-               changeFilter_append(first, next);
+       filterState_t* next = filterState_new(frame, function, value, inter);
+       filterState_append(first, next);
+    }
+    else
+       syntaxerror("Internal error: changing a filter not set for the instance.");
+}
+
+void history_processFlags(history_t* past)
+// to be called after completely recording this history, before calculating any values.
+{
+    state_t* flagState = dict_lookup(past->states, "flags");
+    state_t* nextState;
+    U16 nextFlags, toggledFlags, currentFlags = (U16)flagState->value;
+    while (flagState->next)
+    {
+       nextState = flagState->next;
+       nextFlags = (U16)nextState->value;
+       toggledFlags = currentFlags ^ nextFlags;
+       if (toggledFlags & IF_FIXED_ALIGNMENT)
+       { // the IF_FIXED_ALIGNMENT bit will change in the next state
+           if (nextFlags & IF_FIXED_ALIGNMENT)
+           { // the IF_FIXED_ALIGNMENT bit will be set
+               int onFrame = nextState->frame;
+               state_t* rotations = dict_lookup(past->states, "rotate");
+               nextState->params.instanceAngle = state_value(rotations, onFrame);
+               state_t* resetRotate = state_new(onFrame, CF_JUMP, 0, 0);
+               state_insert(rotations, resetRotate);
+               if (onFrame == past->firstFrame)
+                   onFrame++;
+               state_t *x, *y;
+               float dx, dy;
+               do
+               {
+                   x = dict_lookup(past->states, "x");
+                   dx = state_tangent(x, onFrame, T_SYMMETRIC);
+                   y = dict_lookup(past->states, "y");
+                   dy = state_tangent(y, onFrame, T_SYMMETRIC);
+                   onFrame++;
+               }
+               while (dx == 0 && dy == 0 && onFrame < past->lastFrame);
+               if (onFrame == past->lastFrame)
+                   nextState->params.pathAngle = 0;
+               else
+                   nextState->params.pathAngle = getAngle(dx, dy) / M_PI * 180;
+           }
+           else // the IF_FIXED_ALIGNMENT bit will be reset
+           {
+               int offFrame = nextState->frame;
+               state_t* rotations = dict_lookup(past->states, "rotate");
+               state_t* setRotate = state_new(offFrame, CF_JUMP, flagState->params.instanceAngle + state_value(rotations, offFrame), 0);
+               state_insert(rotations, setRotate);
+           }
+       }
+       else // the IF_FIXED_ALIGNMENT bit will not change but some processing may be
+            // required just the same
+       {
+           if (nextFlags & IF_FIXED_ALIGNMENT)
+           {
+               nextState->params.instanceAngle = flagState->params.instanceAngle;
+               nextState->params.pathAngle = flagState->params.pathAngle;
+           }
+       }
+// and so on for all the other bits.
+       flagState = nextState;
+       currentFlags = nextFlags;
        }
 }
 
+int history_change(history_t* past, U16 frame, char* parameter)
+{
+    state_t* first = dict_lookup(past->states, parameter);
+    if (first) //should always be true.
+       return state_differs(first, frame);
+    syntaxerror("no history found to predict changes for parameter %s.\n", parameter);
+    return 0;
+}
+
 float history_value(history_t* past, U16 frame, char* parameter)
 {
-       change_t* first = dictionary_lookup(past->changes, parameter);
-       if (first)      //should always be true.
-               return change_value(first, frame);
-       printf("no history found for parameter %s\n", parameter);
+    state_t* state = dict_lookup(past->states, parameter);
+    if (state) //should always be true.
+       return state_value(state, frame);
+    syntaxerror("no history found to get a value for parameter %s.\n", parameter);
+    return 0;
+}
+
+float history_rotateValue(history_t* past, U16 frame)
+{
+    state_t* rotations = dict_lookup(past->states, "rotate");
+    if (rotations)     //should always be true.
+    {
+       float angle = state_value(rotations, frame);
+       state_t* flags = dict_lookup(past->states, "flags");
+       U16 currentflags = state_value(flags, frame);
+       if (currentflags & IF_FIXED_ALIGNMENT)
+       {
+           flags = state_at(flags, frame);
+           if (frame == past->firstFrame)
+               frame++;
+           state_t *x, *y;
+           float dx, dy, pathAngle;
+           do
+           {
+               x = dict_lookup(past->states, "x");
+               dx = state_value(x, frame) - state_value(x, frame - 1);
+               y = dict_lookup(past->states, "y");
+               dy = state_value(y, frame) - state_value(y, frame - 1);
+               frame--;
+           }
+           while (dx == 0 && dy == 0 && frame > past->firstFrame);
+           if (frame == past->firstFrame)
+               pathAngle = 0;
+           else
+               pathAngle = getAngle(dx, dy) / M_PI * 180;
+           return angle + flags->params.instanceAngle + pathAngle - flags->params.pathAngle;
+       }
+       else
+           return angle;
+    }
+    syntaxerror("no history found to get a value for parameter rotate.\n");
        return 0;
 }
 
-FILTER* history_valueFilter(history_t* past, U16 frame)
+int history_changeFilter(history_t* past, U16 frame)
+{
+    filterState_t* first = dict_lookup(past->states, "filter");
+    if (first) //should always be true.
+       return filterState_differs(first, frame);
+    syntaxerror("no history found to predict changes for parameter filter.\n");
+    return 0;
+}
+
+FILTERLIST* history_filterValue(history_t* past, U16 frame)
 {
-       changeFilter_t* first = dictionary_lookup(past->changes, "filter");
+    filterState_t* first = dict_lookup(past->states, "filter");
        if (first)      //should always be true.
-               return changeFilter_value(first, frame);
-       printf("no history found for parameter filter\n");
+       return filterState_value(first, frame);
+    syntaxerror("no history found to get a value for parameter filter.\n");
        return 0;
 }