Hello Synfig devs,

I'd like to share a train of thought I had the other day.   It seems to
me that Synfig layers are essentially ''functions with arguments that
change over time''.  For example, a circle can be represented this way,
sliding down over five seconds:

        SolidColor(
                color = rgb(0.0, 0.0, 0.0)
        );
        Circle(
                x = 200.0,
                y = tween {0s:0.0  ->  5s:500.0},
                radius = 5.0,
                color = rgb(1.0, 0.0, 0.0)
        );

That's hard to represent in a language like C or C++, because tweening
isn't a built-in feature.   Synfig works around this by wrapping basic
types (int, double, char*) with tweenable classes (Real, Integer,
String).  It works, but it's like an onion's skin:

        ValueNode_Real > ValueBase > Real

Class upon class upon class.  Arithmetic becomes verbose, because we
aren't dealing with C++'s basic type system.  ValueBase currently looks
like this, if I understand correctly:

        float sum;
        ValueBase value(Real(5.0));
        ValueBase other(Real(7.0));
        sum = value.get(Real()) + other.get(Real());
        value.set(sum);

In plain C++, this would look like:

        float sum;
        float value(5.0);
        float other(7.0);
        sum = value + other;
        value = sum;

We ought to have that clean syntax, while keeping the ability to tween
values.  What if we had "Tweener" objects that update pointers to
values every frame?  Layers would never have to touch tweening code,
and we could use basic C++ types in drawing functions.

Please see the attached code.
I hope to hear your opinions! :-)

-Charlie Murphy

/* tweener.c - Public domain, CC-0, or WTFPL */
/* See main(). */

#include <stdio.h>
#include <stdlib.h>

typedef struct Keyframe Keyframe;
typedef struct Tweener Tweener;

struct Keyframe {
	int frame;
	float value;
};

struct Tweener {
	float *ptr;
	int count;
	Keyframe *kf;
};

int
compare_kf(const void *a, const void *b)
{
	/* for sorting keyframes */
	Keyframe *ak = (Keyframe *)a;
	Keyframe *bk = (Keyframe *)b;
	return ak->frame - bk->frame;
}

static int addkey(Tweener *t, int frame);
static float ease(float now, float start_val, float val_diff, float duration);
static int setkey(Tweener *t, int frame);
static Tweener *tween(float *ptr);
static void update(Tweener *t, int frame);

static int
addkey(Tweener *t, int frame)
{
	void *tmem;

	/* Try setting an existing keyframe first. */
	if (setkey(t, frame) == 0)
		return 0;

	/* Failed, so let's make a new keyframe. */
	tmem = realloc(t, sizeof(Tweener) + ((t->count+1) * sizeof(Keyframe)));
	if (tmem) {
		t = (Tweener *)tmem;

		t->kf[t->count].frame = frame;
		t->kf[t->count].value = *t->ptr;
		t->count++;

		/* Sort all keyframes. */
		qsort(t->kf, t->count, sizeof(Keyframe), compare_kf);
	} else {
		perror("realloc");
		return -1;
	}

	return 0;
}

static float
ease(float now, float start_val, float val_diff, float duration)
{
	return ((val_diff * now) / duration) + start_val;
}

static int
setkey(Tweener *t, int frame)
{
	int i;
	for (i=0; i<t->count; i++) {
		if (frame == t->kf[i].frame) {
			t->kf[i].value = *t->ptr;
			return 0;
		}
	}
	return -1;
}

static Tweener *
tween(float *ptr)
{
	Tweener *t = (Tweener *)malloc(sizeof(Tweener));
	if (!t) {
		perror("malloc");
		return NULL;
	}

	t->ptr = ptr;
	t->count = 0;
	t->kf = (Keyframe *)(t+1);

	return t;
}

static void
update(Tweener *t, int frame)
{
	int i;
	Keyframe *start = NULL;
	Keyframe *end = NULL;

	/* Find the keyframe just below 'frame'. */
	for (i = t->count-1; i >= 0; i--) {
		if (t->kf[i].frame <= frame) {
			start = &t->kf[i];
			break;
		}
	}

	/* Update the tweened data. */
	if (start) {
		if (i+1 < t->count) {
			end = start+1;
			*t->ptr = ease((float)(frame - start->frame),
			               start->value,
			               end->value - start->value,
			               (float)(end->frame - start->frame));
		} else
			*t->ptr = start->value;
	} else if (t->count)
		*t->ptr = t->kf[0].value;
}

int
main(int argc, char *argv[])
{
	int i;
	Tweener *t;
	float x;

	t = tween(&x);

	x = 1.0f;
	addkey(t, 1);

	x = 10.0f;
	addkey(t, 20);

	for (i=1; i<=20; i++) {
		update(t, i);
		printf("frame %d: %f\n", i, x);
	}

	free(t);

	return 0;
}

------------------------------------------------------------------------------
"Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE
Instantly run your Selenium tests across 300+ browser/OS combos.
Get unparalleled scalability from the best Selenium testing platform available
Simple to use. Nothing to install. Get started now for free."
http://p.sf.net/sfu/SauceLabs
_______________________________________________
Synfig-devl mailing list
Synfig-devl@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/synfig-devl

Reply via email to