+2006-10-23 Matthew Allum <mallum@openedhand.com>
+
+ * clutter/clutter-alpha.h:
+ * clutter/clutter-behaviour.c:
+ * clutter/clutter-behaviour.h:
+ * clutter/clutter-behaviours.c:
+ * clutter/clutter-behaviours.h:
+ * examples/behave.c:
+ Behaviours now only 'driven' by ClutterAlpha, not any object/prop.
+ Add simple Clutter path behaviour.
+
2006-10-03 Matthew Allum <mallum@openedhand.com>
* configure.ac:
void (*_clutter_alpha_5) (void);
};
+
#define CLUTTER_ALPHA_MAX_ALPHA 0xffff
+GType clutter_alpha_get_type (void) G_GNUC_CONST;
+
ClutterAlpha *
clutter_alpha_new (ClutterTimeline *timeline,
ClutterAlphaFunc func);
#include "clutter-actor.h"
#include "clutter-behaviour.h"
-#include "clutter-marshal.h"
G_DEFINE_TYPE (ClutterBehaviour, clutter_behaviour, G_TYPE_OBJECT);
struct ClutterBehaviourPrivate
{
- GObject *object;
- GParamSpec *param_spec;
- guint notify_id;
- GSList *actors;
+ ClutterAlpha *alpha;
+ guint notify_id;
+ GSList *actors;
};
enum
{
PROP_0,
- PROP_OBJECT,
- PROP_PROPERTY
+ PROP_ALPHA
};
enum {
- SIGNAL_PROPERTY_CHANGE,
SIGNAL_LAST
};
-static guint signals[SIGNAL_LAST];
#define CLUTTER_BEHAVIOUR_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
{
/* FIXME: remove all actors */
- clutter_behaviour_set_object (self, NULL);
+ clutter_behaviour_set_alpha (self, NULL);
}
G_OBJECT_CLASS (clutter_behaviour_parent_class)->dispose (object);
switch (prop_id)
{
- case PROP_OBJECT:
- clutter_behaviour_set_object (behaviour, g_value_get_object (value));
- break;
- case PROP_PROPERTY:
- clutter_behaviour_set_property (behaviour, g_value_get_string (value));
+ case PROP_ALPHA:
+ clutter_behaviour_set_alpha (behaviour, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
switch (prop_id)
{
- case PROP_OBJECT:
- g_value_set_object (value, priv->object);
- break;
- case PROP_PROPERTY:
- g_value_set_string (value, priv->param_spec->name);
+ case PROP_ALPHA:
+ g_value_set_object (value, priv->alpha);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
object_class->get_property = _clutter_behaviour_get_property;
g_object_class_install_property
- (object_class, PROP_OBJECT,
- g_param_spec_object ("object",
- "Object",
- "Object whose property to monitor",
- G_TYPE_OBJECT,
- G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_PROPERTY,
- g_param_spec_string ("property",
- "Property",
- "Property to monitor",
- NULL,
+ (object_class, PROP_ALPHA,
+ g_param_spec_object ("alpha",
+ "Alpha",
+ "Alpha Object to drive the behaviour",
+ CLUTTER_TYPE_ALPHA,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
- signals[SIGNAL_PROPERTY_CHANGE] =
- g_signal_new ("property-change",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ClutterBehaviourClass, property_change),
- NULL, NULL,
- clutter_marshal_VOID__OBJECT_POINTER,
- G_TYPE_NONE,
- 2, G_TYPE_OBJECT, G_TYPE_POINTER);
-
g_type_class_add_private (object_class, sizeof (ClutterBehaviourPrivate));
}
g_slist_foreach (behave->priv->actors, func, userdata);
}
-GObject*
-clutter_behaviour_get_object (ClutterBehaviour *behave)
-{
- return behave->priv->object;
-}
-
-void
-clutter_behaviour_set_object (ClutterBehaviour *behave,
- GObject *object)
-{
- ClutterBehaviourPrivate *priv;
- const char *property;
-
- priv = CLUTTER_BEHAVIOUR_GET_PRIVATE(behave);
-
- if (priv->object)
- {
- property = clutter_behaviour_get_property (behave);
- clutter_behaviour_set_property (behave, NULL);
-
- g_object_unref(priv->object);
- priv->object = NULL;
- }
- else
- property = NULL;
-
- if (object)
- {
- priv->object = g_object_ref(object);
-
- if (property)
- clutter_behaviour_set_property (behave, property);
- }
-}
-
-const char *
-clutter_behaviour_get_property (ClutterBehaviour *behave)
-{
- if (behave->priv->param_spec)
- return behave->priv->param_spec->name;
- else
- return NULL;
-}
-
-GParamSpec *
-clutter_behaviour_get_param_spec (ClutterBehaviour *behave)
+ClutterAlpha*
+clutter_behaviour_get_alpha (ClutterBehaviour *behave)
{
- return behave->priv->param_spec;
+ return behave->priv->alpha;
}
static void
GParamSpec *param_spec,
ClutterBehaviour *behave)
{
- g_signal_emit (behave,
- signals[SIGNAL_PROPERTY_CHANGE],
- 0,
- object,
- param_spec);
+ ClutterBehaviourClass *class;
+
+ class = CLUTTER_BEHAVIOUR_GET_CLASS(behave);
+
+ if (class->alpha_notify)
+ class->alpha_notify (behave);
}
void
-clutter_behaviour_set_property (ClutterBehaviour *behave,
- const char *property)
+clutter_behaviour_set_alpha (ClutterBehaviour *behave,
+ ClutterAlpha *alpha)
{
- g_return_if_fail (behave->priv->object);
-
if (behave->priv->notify_id)
{
- g_signal_handler_disconnect (behave->priv->object,
+ g_signal_handler_disconnect (behave->priv->alpha,
behave->priv->notify_id);
behave->priv->notify_id = 0;
}
- behave->priv->param_spec = NULL;
+ if (behave->priv->alpha)
+ {
+ g_object_unref (behave->priv->alpha);
+ behave->priv->alpha = NULL;
+ }
- if (property)
+ if (alpha)
{
- guint signal_id;
- GClosure *closure;
-
- behave->priv->param_spec =
- g_object_class_find_property (G_OBJECT_GET_CLASS (behave->priv->object),
- property);
- g_return_if_fail (behave->priv->param_spec);
-
- signal_id = g_signal_lookup ("notify",
- G_OBJECT_TYPE (behave->priv->object));
- closure = g_cclosure_new ((GCallback) notify_cb, behave, NULL);
-
- behave->priv->notify_id =
- g_signal_connect_closure_by_id (behave->priv->object,
- signal_id,
- g_quark_from_string (property),
- closure,
- FALSE);
+ behave->priv->alpha = alpha;
+ g_object_ref (behave->priv->alpha);
+
+ behave->priv->notify_id = g_signal_connect (behave->priv->alpha,
+ "notify::alpha",
+ G_CALLBACK(notify_cb),
+ behave);
}
}
#define _HAVE_CLUTTER_BEHAVIOUR_H
#include <glib-object.h>
+#include "clutter-alpha.h"
G_BEGIN_DECLS
{
GObjectClass parent_class;
- void (* property_change) (ClutterBehaviour *behave,
- GObject *object,
- GParamSpec *param_spec);
+ void (*alpha_notify) (ClutterBehaviour *behave);
};
GType clutter_behaviour_get_type (void);
GFunc func,
gpointer userdata);
-void
-clutter_behaviour_set_object (ClutterBehaviour *behave,
- GObject *object);
-
-GObject*
-clutter_behaviour_get_object (ClutterBehaviour *behave);
+ClutterAlpha*
+clutter_behaviour_get_alpha (ClutterBehaviour *behave);
void
-clutter_behaviour_set_property (ClutterBehaviour *behave,
- const char *property);
-
-const char*
-clutter_behaviour_get_property (ClutterBehaviour *behave);
-
-GParamSpec*
-clutter_behaviour_get_param_spec (ClutterBehaviour *behave);
+clutter_behaviour_set_alpha (ClutterBehaviour *behave,
+ ClutterAlpha *alpha);
G_END_DECLS
#include "clutter-enum-types.h"
#include "clutter-main.h"
+#include <math.h>
+
+static ClutterKnot *
+clutter_knot_copy (const ClutterKnot *knot)
+{
+ ClutterKnot *copy;
+
+ copy = g_slice_new0 (ClutterKnot);
+
+ *copy = *knot;
+
+ return copy;
+}
+
+static void
+clutter_knot_free (ClutterKnot *knot)
+{
+ if (G_LIKELY (knot))
+ {
+ g_slice_free (ClutterKnot, knot);
+ }
+}
+
+GType
+clutter_knot_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (G_UNLIKELY (!our_type))
+ our_type = g_boxed_type_register_static
+ ("ClutterKnot",
+ (GBoxedCopyFunc) clutter_knot_copy,
+ (GBoxedFreeFunc) clutter_knot_free);
+ return our_type;
+}
+
+
G_DEFINE_TYPE (ClutterBehaviourPath, \
clutter_behaviour_path, \
CLUTTER_TYPE_BEHAVIOUR);
struct ClutterBehaviourPathPrivate
{
- gint x1, y1, x2, y2;
+ GSList *knots;
};
#define CLUTTER_BEHAVIOUR_PATH_GET_PRIVATE(obj) \
CLUTTER_TYPE_BEHAVIOUR_PATH, \
ClutterBehaviourPathPrivate))
+static void
+clutter_behaviour_path_alpha_notify (ClutterBehaviour *behave);
+
static void
clutter_behaviour_path_dispose (GObject *object)
{
if (self->priv)
{
- /* FIXME: remove all actors */
+ /* FIXME: unref knots */
}
G_OBJECT_CLASS (clutter_behaviour_path_parent_class)->dispose (object);
static void
clutter_behaviour_path_class_init (ClutterBehaviourPathClass *klass)
{
- GObjectClass *object_class;
+ GObjectClass *object_class;
+ ClutterBehaviourClass *behave_class;
object_class = (GObjectClass*) klass;
+ behave_class = (ClutterBehaviourClass*) klass;
object_class->finalize = clutter_behaviour_path_finalize;
object_class->dispose = clutter_behaviour_path_dispose;
+ behave_class->alpha_notify = clutter_behaviour_path_alpha_notify;
+
g_type_class_add_private (object_class, sizeof (ClutterBehaviourPathPrivate));
}
self->priv = priv = CLUTTER_BEHAVIOUR_PATH_GET_PRIVATE (self);
}
-/*
-
-function line(x0, x1, y0, y1)
- boolean steep := abs(y1 - y0) > abs(x1 - x0)
- if steep then
- swap(x0, y0)
- swap(x1, y1)
- if x0 > x1 then
- swap(x0, x1)
- swap(y0, y1)
- int deltax := x1 - x0
- int deltay := abs(y1 - y0)
- int error := 0
- int ystep
- int y := y0
- if y0 < y1 then ystep := 1 else ystep := -1
- for x from x0 to x1
- if steep then plot(y,x) else plot(x,y)
- error := error + deltay
- if 2
+static void
+interpolate (const ClutterKnot *begin,
+ const ClutterKnot *end,
+ ClutterKnot *out,
+ double t)
+{
+ /* FIXME: fixed point */
+ out->x = begin->x + t * (end->x - begin->x);
+ out->y = begin->y + t * (end->y - begin->y);
+}
+
+static gint
+node_distance (const ClutterKnot *begin, const ClutterKnot *end)
+{
+ g_return_val_if_fail (begin != NULL, 0);
+ g_return_val_if_fail (end != NULL, 0);
- */
+ /* FIXME: need fixed point here */
+ return sqrt ((end->x - begin->x) * (end->x - begin->x) +
+ (end->y - begin->y) * (end->y - begin->y));
+}
+
+static gint
+path_total_length (ClutterBehaviourPath *behave)
+{
+ GSList *l;
+ gint len = 0;
+
+ for (l = behave->priv->knots; l != NULL; l = l->next)
+ if (l->next)
+ len += node_distance (l->data, l->next->data);
+
+ return len;
+}
+
+static void
+actor_apply_knot_foreach (ClutterActor *actor,
+ ClutterKnot *knot)
+{
+ clutter_actor_set_position (actor, knot->x, knot->y);
+}
+
+static void
+path_alpha_to_position (ClutterBehaviourPath *behave)
+{
+ guint32 alpha;
+ GSList *l;
+ gint total_len, offset, dist_to_next, dist = 0;
+
+ /* FIXME: Optimise. Much of the data used here can be pre-generated
+ * ( total_len, dist between knots ) when knots are added/removed.
+ */
+
+ /* Calculation as follows:
+ * o Get total length of path
+ * o Find the offset on path where alpha val corresponds to
+ * o Figure out between which knots this offset lies.
+ * o Interpolate new co-ords via dist between these knots
+ * o Apply to actors.
+ */
+
+ alpha = clutter_alpha_get_alpha
+ (clutter_behaviour_get_alpha (CLUTTER_BEHAVIOUR(behave)));
+
+ total_len = path_total_length (behave);
+
+ offset = (alpha * total_len) / CLUTTER_ALPHA_MAX_ALPHA;
+
+ if (offset == 0)
+ {
+ clutter_behaviour_actors_foreach (CLUTTER_BEHAVIOUR(behave),
+ (GFunc)actor_apply_knot_foreach,
+ behave->priv->knots->data);
+ return;
+ }
+
+ for (l = behave->priv->knots; l != NULL; l = l->next)
+ if (l->next)
+ {
+ dist_to_next = node_distance (l->data, l->next->data);
+
+ if (offset >= dist && offset < (dist + dist_to_next))
+ {
+ ClutterKnot new;
+ double t;
+
+ /* FIXME: Use fixed */
+ t = (double)(offset - dist) / dist_to_next ;
+
+ interpolate (l->data, l->next->data, &new, t);
+
+ clutter_behaviour_actors_foreach (CLUTTER_BEHAVIOUR(behave),
+ (GFunc)actor_apply_knot_foreach,
+ &new);
+ return;
+ }
+
+ dist += dist_to_next;
+ }
+}
+
+static void
+clutter_behaviour_path_alpha_notify (ClutterBehaviour *behave)
+{
+ path_alpha_to_position (CLUTTER_BEHAVIOUR_PATH(behave));
+}
ClutterBehaviour*
-clutter_behaviour_path_new (GObject *object,
- const char *property,
- gint x1,
- gint y1,
- gint x2,
- gint y2)
+clutter_behaviour_path_new (ClutterAlpha *alpha,
+ const ClutterKnot *knots,
+ guint n_knots)
{
ClutterBehaviourPath *behave;
-
+ gint i;
+
behave = g_object_new (CLUTTER_TYPE_BEHAVIOUR_PATH,
- "object", object,
- "property", property,
+ "alpha", alpha,
NULL);
+ for (i = 0; i < n_knots; i++)
+ {
+ ClutterKnot knot = knots[i];
+ clutter_path_behaviour_append_knot (behave, &knot);
+ }
+
return CLUTTER_BEHAVIOUR(behave);
}
-/* opacity */
+GSList*
+clutter_path_behaviour_get_knots (ClutterBehaviourPath *behave)
+{
+ GSList *retval, *l;
+
+ g_return_val_if_fail (CLUTTER_IS_BEHAVIOUR_PATH (behave), NULL);
+
+ retval = NULL;
+ for (l = behave->priv->knots; l != NULL; l = l->next)
+ retval = g_slist_prepend (retval, l->data);
+
+ return g_slist_reverse (retval);
+}
+
+void
+clutter_path_behaviour_append_knot (ClutterBehaviourPath *pathb,
+ const ClutterKnot *knot)
+{
+ ClutterBehaviourPathPrivate *priv;
+
+ g_return_if_fail (knot != NULL);
+
+ priv = pathb->priv;
+
+ priv->knots = g_slist_append (priv->knots,
+ clutter_knot_copy (knot));
+}
+
+void
+clutter_path_behaviour_append_knots_valist (ClutterBehaviourPath *pathb,
+ const ClutterKnot *first_knot,
+ va_list args)
+{
+ const ClutterKnot * knot;
+
+ knot = first_knot;
+ while (knot)
+ {
+ clutter_path_behaviour_append_knot (pathb, knot);
+ knot = va_arg (args, ClutterKnot*);
+ }
+}
+
+void
+clutter_path_behavior_append_knots (ClutterBehaviourPath *pathb,
+ const ClutterKnot *first_knot,
+ ...)
+{
+ va_list args;
+
+ g_return_if_fail (first_knot != NULL);
+
+ va_start (args, first_knot);
+ clutter_path_behaviour_append_knots_valist (pathb, first_knot, args);
+ va_end (args);
+}
+
+void
+clutter_path_behavior_remove_knot (ClutterBehaviourPath *behave,
+ guint index)
+{
+ /* FIXME: implement */
+}
+
+ClutterKnot*
+clutter_path_behavior_get_knot (ClutterBehaviourPath *behave,
+ guint index)
+{
+ /* FIXME: implement */
+}
+
+void
+clutter_path_behavior_insert_knot (ClutterBehaviourPath *behave,
+ ClutterKnot *knot,
+ guint index)
+{
+ /* FIXME: implement */
+}
+
+
+/*
+ * ====================== Opacity ============================
+ */
+
G_DEFINE_TYPE (ClutterBehaviourOpacity, \
clutter_behaviour_opacity, \
guint8 opacity;
ClutterBehaviourOpacityPrivate *priv;
ClutterBehaviour *_behave;
- GParamSpec *pspec;
priv = CLUTTER_BEHAVIOUR_OPACITY_GET_PRIVATE (behave);
_behave = CLUTTER_BEHAVIOUR (behave);
- pspec = clutter_behaviour_get_param_spec (_behave);
-
- g_object_get (clutter_behaviour_get_object (_behave),
- pspec->name,
- &alpha,
- NULL);
+ alpha = clutter_alpha_get_alpha (clutter_behaviour_get_alpha (_behave));
opacity = (alpha * (priv->opacity_end - priv->opacity_start))
- / ((GParamSpecUInt *) pspec)->maximum;
+ / CLUTTER_ALPHA_MAX_ALPHA;
opacity += priv->opacity_start;
}
static void
-clutter_behaviour_property_change (ClutterBehaviour *behave,
- GObject *object,
- GParamSpec *param_spec)
+clutter_behaviour_alpha_notify (ClutterBehaviour *behave)
{
- g_return_if_fail (param_spec->value_type == G_TYPE_UINT);
-
clutter_behaviour_actors_foreach
(behave,
(GFunc)clutter_behaviour_opacity_frame_foreach,
behave_class = (ClutterBehaviourClass*) klass;
- behave_class->property_change = clutter_behaviour_property_change;
+ behave_class->alpha_notify = clutter_behaviour_alpha_notify;
g_type_class_add_private (object_class, sizeof (ClutterBehaviourOpacityPrivate));
}
}
ClutterBehaviour*
-clutter_behaviour_opacity_new (GObject *object,
- const char *property,
- guint8 opacity_start,
- guint8 opacity_end)
+clutter_behaviour_opacity_new (ClutterAlpha *alpha,
+ guint8 opacity_start,
+ guint8 opacity_end)
{
ClutterBehaviourOpacity *behave;
behave = g_object_new (CLUTTER_TYPE_BEHAVIOUR_OPACITY,
- "object", object,
- "property", property,
+ "alpha", alpha,
NULL);
behave->priv->opacity_start = opacity_start;
return CLUTTER_BEHAVIOUR(behave);
}
-ClutterBehaviour*
-clutter_behaviour_opacity_new_from_alpha (ClutterAlpha *alpha,
- guint8 opacity_start,
- guint8 opacity_end)
-{
- return clutter_behaviour_opacity_new (G_OBJECT (alpha),
- "alpha",
- opacity_start,
- opacity_end);
-}
G_BEGIN_DECLS
+typedef struct _ClutterKnot ClutterKnot;
+
+struct _ClutterKnot
+{
+ gint x,y;
+ /* FIXME: optionally include bezier control points also ? */
+};
+
#define CLUTTER_TYPE_BEHAVIOUR_PATH clutter_behaviour_path_get_type()
#define CLUTTER_BEHAVIOUR_PATH(obj) \
GType clutter_behaviour_path_get_type (void);
ClutterBehaviour*
-clutter_behaviour_path_new (GObject *object,
- const char *property,
- gint x1,
- gint y1,
- gint x2,
- gint y2);
+clutter_behaviour_path_new (ClutterAlpha *alpha,
+ const ClutterKnot *knots,
+ guint n_knots);
+
+GSList*
+clutter_path_behaviour_get_knots (ClutterBehaviourPath *behave);
+
+void
+clutter_path_behaviour_append_knot (ClutterBehaviourPath *pathb,
+ const ClutterKnot *knot);
+
+void
+clutter_path_behaviour_append_knots_valist (ClutterBehaviourPath *pathb,
+ const ClutterKnot *first_knot,
+ va_list args);
+
+void
+clutter_path_behavior_append_knots (ClutterBehaviourPath *pathb,
+ const ClutterKnot *first_knot,
+ ...);
/* opacity */
GType clutter_behaviour_opacity_get_type (void);
ClutterBehaviour*
-clutter_behaviour_opacity_new (GObject *object,
- const char *property,
- guint8 opacity_start,
- guint8 opacity_end);
-
-ClutterBehaviour*
-clutter_behaviour_opacity_new_from_alpha (ClutterAlpha *alpha,
- guint8 opacity_start,
- guint8 opacity_end);
+clutter_behaviour_opacity_new (ClutterAlpha *alpha,
+ guint8 opacity_start,
+ guint8 opacity_end);
G_END_DECLS
ClutterColor stage_color = { 0xcc, 0xcc, 0xcc, 0xff };
GdkPixbuf *pixbuf;
+ ClutterKnot knots[] = {{ 100, 100 }, { 100, 200 }, { 200, 200 },
+ { 200, 100 }, {100, 100 }};
clutter_init (&argc, &argv);
clutter_group_add (CLUTTER_GROUP (stage), hand);
/* Make a timeline */
- timeline = clutter_timeline_new (100, 30); /* num frames, fps */
+ timeline = clutter_timeline_new (100, 60); /* num frames, fps */
g_object_set(timeline, "loop", TRUE, 0);
/* Set an alpha func to power behaviour - ramp is constant rise/fall */
alpha = clutter_alpha_new (timeline, CLUTTER_ALPHA_RAMP);
- /* Create a behaviour for that time line */
- behave = clutter_behaviour_opacity_new_from_alpha (alpha, 0X33, 0xff);
+ /* Create a behaviour for that alpha */
+ behave = clutter_behaviour_opacity_new (alpha, 0X33, 0xff);
/* Apply it to our actor */
clutter_behaviour_apply (behave, hand);
- /* start the timeline */
+ /* Make a path behaviour and apply that too */
+ behave = clutter_behaviour_path_new (alpha, knots, 5);
+ clutter_behaviour_apply (behave, hand);
+
+ /* start the timeline and thus the animations */
clutter_timeline_start (timeline);
clutter_group_show_all (CLUTTER_GROUP (stage));