Bug 1252 - Merge ClutterBehaviourPath and ClutterBehaviourBspline

* clutter/clutter-path.h:
	* clutter/clutter-path.c: Implementation of new ClutterPath object
	to represent a path combining straight line and bezier curve
	elements.

	* clutter/clutter.h: Include clutter-path.h and remove
	clutter-behaviour-bspline.h

	* tests/interactive/test-threads.c (test_threads_main):
	* tests/interactive/test-script.c:
	* tests/interactive/test-behave.c (test_behave_main): Use new path
	API

	* clutter/clutter-effect.c: Use the new ClutterBehaviourPath API.

	* clutter/clutter-bezier.h:
	* clutter/clutter-bezier.c: Moved bezier curve handling code out
	from clutter-behaviour-bspline.c to a separate file.

	* clutter/clutter-behaviour-path.h:
	* clutter/clutter-behaviour-path.c: Reimplemented to work with a
	ClutterPath

	* clutter/clutter-behaviour-bspline.h:
	* clutter/clutter-behaviour-bspline.c: Removed

	* clutter/Makefile.am: Add clutter-path and clutter-bezier, remove
	clutter-behaviour-bspline.

	* tests/conform/test-path.c: New automatic test for ClutterPath
	consistency

	* tests/conform/test-conform-main.c (main): Add test_path

	* tests/conform/Makefile.am (test_conformance_SOURCES): Add
	test-path.c

	* clutter/clutter-sections.txt: Add ClutterPath docs

	* clutter/clutter.types:
	* clutter/clutter-docs.xml:
	* doc/reference/clutter/clutter-animation-tutorial.xml: Remove
	mention of ClutterBehaviourBspline

	* clutter/clutter-marshal.list: Add VOID:UINT
This commit is contained in:
Neil Roberts 2008-12-05 13:13:37 +00:00
parent 98035e4d8a
commit 1a63414966
24 changed files with 3164 additions and 1877 deletions

1
.gitignore vendored
View File

@ -132,6 +132,7 @@ stamp-h1
/tests/conform/test_initial_state
/tests/conform/test_label_cache
/tests/conform/test_mapped
/tests/conform/test_path
/tests/conform/test_pick
/tests/conform/test_realized
/tests/conform/test_rect_set_color

View File

@ -1,3 +1,53 @@
2008-12-05 Neil Roberts <neil@linux.intel.com>
Bug 1252 - Merge ClutterBehaviourPath and ClutterBehaviourBspline
* clutter/clutter-path.h:
* clutter/clutter-path.c: Implementation of new ClutterPath object
to represent a path combining straight line and bezier curve
elements.
* clutter/clutter.h: Include clutter-path.h and remove
clutter-behaviour-bspline.h
* tests/interactive/test-threads.c (test_threads_main):
* tests/interactive/test-script.c:
* tests/interactive/test-behave.c (test_behave_main): Use new path
API
* clutter/clutter-effect.c: Use the new ClutterBehaviourPath API.
* clutter/clutter-bezier.h:
* clutter/clutter-bezier.c: Moved bezier curve handling code out
from clutter-behaviour-bspline.c to a separate file.
* clutter/clutter-behaviour-path.h:
* clutter/clutter-behaviour-path.c: Reimplemented to work with a
ClutterPath
* clutter/clutter-behaviour-bspline.h:
* clutter/clutter-behaviour-bspline.c: Removed
* clutter/Makefile.am: Add clutter-path and clutter-bezier, remove
clutter-behaviour-bspline.
* tests/conform/test-path.c: New automatic test for ClutterPath
consistency
* tests/conform/test-conform-main.c (main): Add test_path
* tests/conform/Makefile.am (test_conformance_SOURCES): Add
test-path.c
* clutter/clutter-sections.txt: Add ClutterPath docs
* clutter/clutter.types:
* clutter/clutter-docs.xml:
* doc/reference/clutter/clutter-animation-tutorial.xml: Remove
mention of ClutterBehaviourBspline
* clutter/clutter-marshal.list: Add VOID:UINT
2008-12-04 Neil Roberts <neil@linux.intel.com>
Bug 1297 - Bring back support for GL_ARB_texture_rectangle

View File

@ -50,7 +50,6 @@ source_h = \
$(srcdir)/clutter-animation.h \
$(srcdir)/clutter-backend.h \
$(srcdir)/clutter-behaviour.h \
$(srcdir)/clutter-behaviour-bspline.h \
$(srcdir)/clutter-behaviour-depth.h \
$(srcdir)/clutter-behaviour-ellipse.h \
$(srcdir)/clutter-behaviour-opacity.h \
@ -76,6 +75,7 @@ source_h = \
$(srcdir)/clutter-main.h \
$(srcdir)/clutter-media.h \
$(srcdir)/clutter-model.h \
$(srcdir)/clutter-path.h \
$(srcdir)/clutter-rectangle.h \
$(srcdir)/clutter-score.h \
$(srcdir)/clutter-script.h \
@ -139,13 +139,13 @@ source_c = \
clutter-animation.c \
clutter-backend.c \
clutter-behaviour.c \
clutter-behaviour-bspline.c \
clutter-behaviour-depth.c \
clutter-behaviour-ellipse.c \
clutter-behaviour-opacity.c \
clutter-behaviour-path.c \
clutter-behaviour-rotate.c \
clutter-behaviour-scale.c \
clutter-bezier.c \
clutter-child-meta.c \
clutter-clone-texture.c \
clutter-color.c \
@ -166,6 +166,7 @@ source_c = \
clutter-marshal.c \
clutter-media.c \
clutter-model.c \
clutter-path.c \
clutter-rectangle.c \
clutter-score.c \
clutter-script.c \
@ -184,6 +185,7 @@ source_c = \
$(NULL)
source_h_priv = \
clutter-bezier.h \
clutter-debug.h \
clutter-keysyms-table.h \
clutter-model-private.h \

File diff suppressed because it is too large Load Diff

View File

@ -1,131 +0,0 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By Tomas Frydrych <tf@openedhand.com>
*
* Copyright (C) 2006, 2007 OpenedHand
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be included directly."
#endif
#ifndef __CLUTTER_BEHAVIOUR_BSPLINE_H__
#define __CLUTTER_BEHAVIOUR_BSPLINE_H__
#include <clutter/clutter-alpha.h>
#include <clutter/clutter-actor.h>
#include <clutter/clutter-behaviour.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_BEHAVIOUR_BSPLINE (clutter_behaviour_bspline_get_type ())
#define CLUTTER_BEHAVIOUR_BSPLINE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
CLUTTER_TYPE_BEHAVIOUR_BSPLINE, ClutterBehaviourBspline))
#define CLUTTER_BEHAVIOUR_BSPLINE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
CLUTTER_TYPE_BEHAVIOUR_BSPLINE, ClutterBehaviourBsplineClass))
#define CLUTTER_IS_BEHAVIOUR_BSPLINE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
CLUTTER_TYPE_BEHAVIOUR_BSPLINE))
#define CLUTTER_IS_BEHAVIOUR_BSPLINE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
CLUTTER_TYPE_BEHAVIOUR_BSPLINE))
#define CLUTTER_BEHAVIOUR_BSPLINE_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
CLUTTER_TYPE_BEHAVIOUR_BSPLINE, ClutterBehaviourBsplineClass))
typedef struct _ClutterBehaviourBspline ClutterBehaviourBspline;
typedef struct _ClutterBehaviourBsplinePrivate ClutterBehaviourBsplinePrivate;
typedef struct _ClutterBehaviourBsplineClass ClutterBehaviourBsplineClass;
/**
* ClutterBehaviourBspline:
*
* #ClutterBehaviourBspline-struct contains only private data and
* should be accessed using the functions below.
*
* Since: 0.2
*/
struct _ClutterBehaviourBspline
{
ClutterBehaviour parent_instance;
ClutterBehaviourBsplinePrivate *priv;
};
/**
* ClutterBehaviourBsplineClass:
* @knot_reached: class handler for the #ClutterBehaviourBspline::knot-reached
* signal
*
* #ClutterBehaviourBsplineClass-struct contains only private data
* and should be accessed using the functions below.
*
* Since: 0.2
*/
struct _ClutterBehaviourBsplineClass
{
/*< private >*/
ClutterBehaviourClass parent_class;
/*< public >*/
void (*knot_reached) (ClutterBehaviourBspline *bsplineb,
const ClutterKnot *knot);
/*< private >*/
/* padding for future expansion */
void (*_clutter_bspline_1) (void);
void (*_clutter_bspline_2) (void);
void (*_clutter_bspline_3) (void);
void (*_clutter_bspline_4) (void);
};
GType clutter_behaviour_bspline_get_type (void) G_GNUC_CONST;
ClutterBehaviour *clutter_behaviour_bspline_new (ClutterAlpha *alpha,
const ClutterKnot *knots,
guint n_knots);
void clutter_behaviour_bspline_append_knot (ClutterBehaviourBspline *bs,
const ClutterKnot *knot);
void clutter_behaviour_bspline_append_knots (ClutterBehaviourBspline *bs,
const ClutterKnot *first_knot,
...) G_GNUC_NULL_TERMINATED;
void clutter_behaviour_bspline_truncate (ClutterBehaviourBspline *bs,
guint offset);
void clutter_behaviour_bspline_join (ClutterBehaviourBspline *bs1,
ClutterBehaviourBspline *bs2);
ClutterBehaviour *clutter_behaviour_bspline_split (ClutterBehaviourBspline *bs,
guint offset);
void clutter_behaviour_bspline_clear (ClutterBehaviourBspline *bs);
void clutter_behaviour_bspline_adjust (ClutterBehaviourBspline *bs,
guint offset,
ClutterKnot *knot);
void clutter_behaviour_bspline_set_origin (ClutterBehaviourBspline *bs,
ClutterKnot *knot);
void clutter_behaviour_bspline_get_origin (ClutterBehaviourBspline *bs,
ClutterKnot *knot);
G_END_DECLS
#endif /* __CLUTTER_BEHAVIOUR_BSPLINE_H__ */

View File

@ -25,20 +25,29 @@
/**
* SECTION:clutter-behaviour-path
* @short_description: A behaviour interpolating position along a path
* @short_description: A behaviour for moving actors along a #ClutterPath
*
* #ClutterBehaviourPath interpolates actors along a defined path.
*
* A path is a set of #ClutterKnots object given when creating a new
* #ClutterBehaviourPath instance. Knots can be also added to the path
* using clutter_behaviour_path_append_knot(). The whole path can be
* cleared using clutter_behaviour_path_clear(). Each time the behaviour
* reaches a knot in the path, the "knot-reached" signal is emitted.
* A path is described by a #ClutterPath object. The path can contain
* straight line parts and bezier curves. If the path contains
* %CLUTTER_PATH_MOVE_TO parts then the actors will jump to those
* coordinates. This can be used make disjoint paths.
*
* This first knot in the path is reached with the lower bound value
* provided by the #ClutterAlpha objectused by the behaviour; the last
* knot in the path is reached with the upper bound value provided by
* the #ClutterAlpha object used by the behaviour.
* When creating a path behaviour in a #ClutterScript, you can specify
* the path property directly as a string. For example:
*
* |[
* {
* "id" : "spline-path",
* "type" : "ClutterBehaviourPath",
* "path" : "M 50 50 L 100 100",
* "alpha" : {
* "timeline" : "main-timeline",
* "function" : "ramp
* }
* }
* ]|
*
* <note>If the alpha function is a periodic function, i.e. it returns to
* 0 after reaching %CLUTTER_ALPHA_MAX_ALPHA, then the actors will walk
@ -54,13 +63,14 @@
#include "clutter-actor.h"
#include "clutter-behaviour.h"
#include "clutter-behaviour-path.h"
#include "clutter-bezier.h"
#include "clutter-debug.h"
#include "clutter-enum-types.h"
#include "clutter-main.h"
#include "clutter-marshal.h"
#include "clutter-private.h"
#include "clutter-scriptable.h"
#include "clutter-script-private.h"
#include "clutter-scriptable.h"
#include <math.h>
@ -74,8 +84,8 @@ G_DEFINE_TYPE_WITH_CODE (ClutterBehaviourPath,
struct _ClutterBehaviourPathPrivate
{
GSList *knots;
ClutterKnot *last_knot_passed;
ClutterPath *path;
guint last_knot_passed;
};
#define CLUTTER_BEHAVIOUR_PATH_GET_PRIVATE(obj) \
@ -96,72 +106,9 @@ enum
{
PROP_0,
PROP_KNOT
PROP_PATH
};
static void
clutter_behaviour_path_finalize (GObject *object)
{
ClutterBehaviourPath *self = CLUTTER_BEHAVIOUR_PATH(object);
g_slist_foreach (self->priv->knots, (GFunc) clutter_knot_free, NULL);
g_slist_free (self->priv->knots);
G_OBJECT_CLASS (clutter_behaviour_path_parent_class)->finalize (object);
}
static inline void
interpolate (const ClutterKnot *start,
const ClutterKnot *end,
ClutterKnot *out,
ClutterFixed t)
{
out->x = start->x + COGL_FIXED_TO_INT (t * (end->x - start->x));
out->y = start->y + COGL_FIXED_TO_INT (t * (end->y - start->y));
}
static gint
node_distance (const ClutterKnot *start,
const ClutterKnot *end)
{
gint t;
g_return_val_if_fail (start != NULL, 0);
g_return_val_if_fail (end != NULL, 0);
if (clutter_knot_equal (start, end))
return 0;
t = (end->x - start->x) * (end->x - start->x) +
(end->y - start->y) * (end->y - start->y);
/*
* If we are using limited precision sqrti implementation, fallback on
* clib sqrt if the precission would be less than 10%
*/
#if INT_MAX > CLUTTER_SQRTI_ARG_10_PERCENT
if (t <= COGL_SQRTI_ARG_10_PERCENT)
return cogl_sqrti (t);
else
return COGL_FLOAT_TO_INT (sqrt(t));
#else
return cogl_sqrti (t);
#endif
}
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 && l->next->data)
len += node_distance (l->data, l->next->data);
return len;
}
static void
actor_apply_knot_foreach (ClutterBehaviour *behaviour,
ClutterActor *actor,
@ -175,105 +122,50 @@ actor_apply_knot_foreach (ClutterBehaviour *behaviour,
}
static void
path_alpha_to_position (ClutterBehaviourPath *behave,
guint32 alpha)
clutter_behaviour_path_alpha_notify (ClutterBehaviour *behave,
guint32 alpha_value)
{
ClutterBehaviourPathPrivate *priv = behave->priv;
ClutterBehaviour *behaviour = CLUTTER_BEHAVIOUR (behave);
GSList *l;
gint total_len, offset, dist = 0;
ClutterBehaviourPath *pathb = CLUTTER_BEHAVIOUR_PATH (behave);
ClutterBehaviourPathPrivate *priv = pathb->priv;
ClutterKnot position;
guint knot_num;
/* 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.
*/
total_len = path_total_length (behave);
offset = (alpha * total_len) / CLUTTER_ALPHA_MAX_ALPHA;
CLUTTER_NOTE (BEHAVIOUR, "alpha %i vs %i, len: %i vs %i",
alpha, CLUTTER_ALPHA_MAX_ALPHA,
offset, total_len);
if (offset == 0)
if (priv->path)
knot_num = clutter_path_get_position (priv->path, alpha_value, &position);
else
{
/* first knot */
clutter_behaviour_actors_foreach (behaviour,
memset (&position, 0, sizeof (position));
knot_num = 0;
}
clutter_behaviour_actors_foreach (behave,
actor_apply_knot_foreach,
priv->knots->data);
&position);
priv->last_knot_passed = (ClutterKnot*)priv->knots->data;
g_signal_emit (behave, path_signals[KNOT_REACHED], 0,
priv->knots->data);
return;
}
if (offset == total_len)
if (knot_num != priv->last_knot_passed)
{
/* Special case for last knot */
ClutterKnot *last_knot = (g_slist_last (priv->knots))->data;
clutter_behaviour_actors_foreach (behaviour,
actor_apply_knot_foreach,
last_knot);
priv->last_knot_passed = (ClutterKnot*)priv->knots->data;
g_signal_emit (behave, path_signals[KNOT_REACHED], 0, last_knot);
return;
}
for (l = priv->knots; l != NULL; l = l->next)
{
gint dist_to_next = 0;
ClutterKnot *knot = l->data;
if (l->next)
{
ClutterKnot *next = l->next->data;
dist_to_next = node_distance (knot, next);
if (offset >= dist && offset < (dist + dist_to_next))
{
ClutterKnot new;
ClutterFixed t;
t = COGL_FIXED_FROM_INT (offset - dist) / dist_to_next;
interpolate (knot, next, &new, t);
clutter_behaviour_actors_foreach (behaviour,
actor_apply_knot_foreach,
&new);
if (knot != priv->last_knot_passed)
{
/* We just passed a new Knot */
priv->last_knot_passed = knot;
g_signal_emit (behave, path_signals[KNOT_REACHED], 0, knot);
}
return;
}
}
dist += dist_to_next;
g_signal_emit (behave, path_signals[KNOT_REACHED], 0, knot_num);
priv->last_knot_passed = knot_num;
}
}
static void
clutter_behaviour_path_alpha_notify (ClutterBehaviour *behave,
guint32 alpha_value)
clutter_behaviour_path_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
path_alpha_to_position (CLUTTER_BEHAVIOUR_PATH (behave), alpha_value);
ClutterBehaviourPath *pathb = CLUTTER_BEHAVIOUR_PATH (gobject);
switch (prop_id)
{
case PROP_PATH:
g_value_set_object (value, clutter_behaviour_path_get_path (pathb));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
@ -286,8 +178,8 @@ clutter_behaviour_path_set_property (GObject *gobject,
switch (prop_id)
{
case PROP_KNOT:
clutter_behaviour_path_append_knot (pathb, g_value_get_boxed (value));
case PROP_PATH:
clutter_behaviour_path_set_path (pathb, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
@ -295,34 +187,39 @@ clutter_behaviour_path_set_property (GObject *gobject,
}
}
static void
clutter_behaviour_path_dispose (GObject *gobject)
{
ClutterBehaviourPath *pathb = CLUTTER_BEHAVIOUR_PATH (gobject);
clutter_behaviour_path_set_path (pathb, NULL);
G_OBJECT_CLASS (clutter_behaviour_path_parent_class)->dispose (gobject);
}
static void
clutter_behaviour_path_class_init (ClutterBehaviourPathClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterBehaviourClass *behave_class = CLUTTER_BEHAVIOUR_CLASS (klass);
GParamSpec *pspec;
gobject_class->get_property = clutter_behaviour_path_get_property;
gobject_class->set_property = clutter_behaviour_path_set_property;
gobject_class->finalize = clutter_behaviour_path_finalize;
gobject_class->dispose = clutter_behaviour_path_dispose;
/**
* ClutterBehaviourPath:knot:
*
* This property can be used to append a new knot to the path.
*
* Since: 0.2
*/
g_object_class_install_property (gobject_class,
PROP_KNOT,
g_param_spec_boxed ("knot",
"Knot",
"Can be used to append a knot to the path",
CLUTTER_TYPE_KNOT,
CLUTTER_PARAM_WRITABLE));
pspec = g_param_spec_object ("path",
"Path",
"The ClutterPath object representing the path "
"to animate along",
CLUTTER_TYPE_PATH,
CLUTTER_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_PATH, pspec);
/**
* ClutterBehaviourPath::knot-reached:
* @pathb: the object which received the signal
* @knot: the #ClutterKnot reached
* @knot_num: the index of the #ClutterPathKnot reached
*
* This signal is emitted each time a node defined inside the path
* is reached.
@ -335,51 +232,16 @@ clutter_behaviour_path_class_init (ClutterBehaviourPathClass *klass)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ClutterBehaviourPathClass, knot_reached),
NULL, NULL,
clutter_marshal_VOID__BOXED,
clutter_marshal_VOID__UINT,
G_TYPE_NONE, 1,
CLUTTER_TYPE_KNOT);
G_TYPE_UINT);
behave_class->alpha_notify = clutter_behaviour_path_alpha_notify;
g_type_class_add_private (klass, sizeof (ClutterBehaviourPathPrivate));
}
static void
clutter_behaviour_path_init (ClutterBehaviourPath *self)
{
ClutterBehaviourPathPrivate *priv;
self->priv = priv = CLUTTER_BEHAVIOUR_PATH_GET_PRIVATE (self);
}
static void
clutter_behaviour_path_set_custom_property (ClutterScriptable *scriptable,
ClutterScript *script,
const gchar *name,
const GValue *value)
{
if (strcmp (name, "knots") == 0)
{
ClutterBehaviourPath *path = CLUTTER_BEHAVIOUR_PATH (scriptable);
GSList *knots, *l;
if (!G_VALUE_HOLDS (value, G_TYPE_POINTER))
return;
knots = g_value_get_pointer (value);
for (l = knots; l != NULL; l = l->next)
{
ClutterKnot *knot = l->data;
clutter_behaviour_path_append_knot (path, knot);
clutter_knot_free (knot);
}
g_slist_free (knots);
}
else
g_object_set_property (G_OBJECT (scriptable), name, value);
}
static ClutterScriptableIface *parent_scriptable_iface = NULL;
static gboolean
clutter_behaviour_path_parse_custom_node (ClutterScriptable *scriptable,
@ -388,53 +250,68 @@ clutter_behaviour_path_parse_custom_node (ClutterScriptable *scriptable,
const gchar *name,
JsonNode *node)
{
if (strcmp (name, "knots") == 0)
if (strcmp ("path", name) == 0)
{
JsonArray *array;
guint knots_len, i;
GSList *knots = NULL;
ClutterPath *path;
GValue node_value = { 0 };
array = json_node_get_array (node);
knots_len = json_array_get_length (array);
path = g_object_ref_sink (clutter_path_new ());
for (i = 0; i < knots_len; i++)
{
JsonNode *val = json_array_get_element (array, i);
ClutterKnot knot = { 0, };
json_node_get_value (node, &node_value);
if (clutter_script_parse_knot (script, val, &knot))
{
CLUTTER_NOTE (SCRIPT, "parsed knot [ x:%d, y:%d ]",
knot.x, knot.y);
if (!G_VALUE_HOLDS (&node_value, G_TYPE_STRING)
|| !clutter_path_set_description (path,
g_value_get_string (&node_value)))
g_warning ("Invalid path description");
knots = g_slist_prepend (knots, clutter_knot_copy (&knot));
}
}
g_value_unset (&node_value);
g_value_init (value, G_TYPE_POINTER);
g_value_set_pointer (value, g_slist_reverse (knots));
g_value_init (value, G_TYPE_OBJECT);
g_value_take_object (value, path);
return TRUE;
}
/* chain up */
else if (parent_scriptable_iface->parse_custom_node)
return parent_scriptable_iface->parse_custom_node (scriptable, script,
value, name, node);
else
return FALSE;
}
static void
clutter_scriptable_iface_init (ClutterScriptableIface *iface)
{
parent_scriptable_iface = g_type_interface_peek_parent (iface);
if (!parent_scriptable_iface)
parent_scriptable_iface
= g_type_default_interface_peek (CLUTTER_TYPE_SCRIPTABLE);
iface->parse_custom_node = clutter_behaviour_path_parse_custom_node;
iface->set_custom_property = clutter_behaviour_path_set_custom_property;
}
static void
clutter_behaviour_path_init (ClutterBehaviourPath *self)
{
ClutterBehaviourPathPrivate *priv;
self->priv = priv = CLUTTER_BEHAVIOUR_PATH_GET_PRIVATE (self);
priv->path = NULL;
priv->last_knot_passed = G_MAXUINT;
}
/**
* clutter_behaviour_path_new:
* @alpha: a #ClutterAlpha, or %NULL
* @knots: a list of #ClutterKnots, or %NULL for an empty path
* @n_knots: the number of nodes in the path
* @path: a #ClutterPath or %NULL for an empty path
*
* Creates a new path behaviour. You can use this behaviour to drive
* actors along the nodes of a path, described by the @knots.
* actors along the nodes of a path, described by @path.
*
* This will claim the floating reference on the #ClutterPath so you
* do not need to unref if it.
*
* Return value: a #ClutterBehaviour
*
@ -442,183 +319,121 @@ clutter_scriptable_iface_init (ClutterScriptableIface *iface)
*/
ClutterBehaviour *
clutter_behaviour_path_new (ClutterAlpha *alpha,
ClutterPath *path)
{
return g_object_new (CLUTTER_TYPE_BEHAVIOUR_PATH,
"alpha", alpha,
"path", path,
NULL);
}
/**
* clutter_behaviour_path_new_with_description:
* @alpha: a #ClutterAlpha
* @desc: a string description of the path
*
* Creates a new path behaviour using the path described by @desc. See
* clutter_path_add_string() for a description of the format.
*
* Return value: a #ClutterBehaviour
*
* Since: 1.0
*/
ClutterBehaviour *
clutter_behaviour_path_new_with_description (ClutterAlpha *alpha,
const gchar *desc)
{
return g_object_new (CLUTTER_TYPE_BEHAVIOUR_PATH,
"alpha", alpha,
"path", clutter_path_new_with_description (desc),
NULL);
}
/**
* clutter_behaviour_path_new_with_knots:
* @alpha: a #ClutterAlpha
* @knots: an array of #ClutterKnot<!-- -->s
* @n_knots: number of entries in @knots
*
* Creates a new path behaviour that will make the actors visit all of
* the given knots in order with straight lines in between.
*
* A path will be created where the first knot is used in a
* %CLUTTER_PATH_MOVE_TO and the subsequent knots are used in
* %CLUTTER_PATH_LINE_TO<!-- -->s.
*
* Return value: a #ClutterBehaviour
*
* Since: 1.0
*/
ClutterBehaviour *
clutter_behaviour_path_new_with_knots (ClutterAlpha *alpha,
const ClutterKnot *knots,
guint n_knots)
{
ClutterBehaviourPath *behave;
gint i;
ClutterPath *path = clutter_path_new ();
guint i;
behave = g_object_new (CLUTTER_TYPE_BEHAVIOUR_PATH,
"alpha", alpha,
NULL);
for (i = 0; i < n_knots; i++)
if (n_knots > 0)
{
ClutterKnot knot = knots[i];
clutter_path_add_move_to (path, knots[0].x, knots[0].y);
clutter_behaviour_path_append_knot (behave, &knot);
for (i = 1; i < n_knots; i++)
clutter_path_add_line_to (path, knots[i].x, knots[i].y);
}
return CLUTTER_BEHAVIOUR (behave);
return g_object_new (CLUTTER_TYPE_BEHAVIOUR_PATH,
"alpha", alpha,
"path", path,
NULL);
}
/**
* clutter_behaviour_path_get_knots:
* @pathb: a #ClutterBehvaiourPath
* clutter_behaviour_path_set_path:
* @pathb: the path behaviour
* @path: the new path to follow
*
* Returns a copy of the list of knots contained by @pathb
* Change the path that the actors will follow. This will take the
* floating reference on the #ClutterPath so you do not need to unref
* it.
*
* Return value: a #GSList of the paths knots.
*
* Since: 0.2
* Since: 1.0
*/
GSList *
clutter_behaviour_path_get_knots (ClutterBehaviourPath *pathb)
void
clutter_behaviour_path_set_path (ClutterBehaviourPath *pathb,
ClutterPath *path)
{
GSList *retval, *l;
ClutterBehaviourPathPrivate *priv;
g_return_if_fail (CLUTTER_IS_BEHAVIOUR_PATH (pathb));
priv = pathb->priv;
if (path)
g_object_ref_sink (path);
if (priv->path)
g_object_unref (priv->path);
priv->path = path;
g_object_notify (G_OBJECT (pathb), "path");
}
/**
* clutter_behaviour_path_get_path:
* @pathb: a #ClutterBehaviourPath instance
*
* Get the current path of the behaviour
*
* Return value: the path
*
* Since 1.0:
*/
ClutterPath *
clutter_behaviour_path_get_path (ClutterBehaviourPath *pathb)
{
g_return_val_if_fail (CLUTTER_IS_BEHAVIOUR_PATH (pathb), NULL);
retval = NULL;
for (l = pathb->priv->knots; l != NULL; l = l->next)
retval = g_slist_prepend (retval, l->data);
return g_slist_reverse (retval);
}
/**
* clutter_behaviour_path_append_knot:
* @pathb: a #ClutterBehvaiourPath
* @knot: a #ClutterKnot to append.
*
* Appends a #ClutterKnot to the path
*
* Since: 0.2
*/
void
clutter_behaviour_path_append_knot (ClutterBehaviourPath *pathb,
const ClutterKnot *knot)
{
ClutterBehaviourPathPrivate *priv;
g_return_if_fail (CLUTTER_IS_BEHAVIOUR_PATH (pathb));
g_return_if_fail (knot != NULL);
priv = pathb->priv;
priv->knots = g_slist_append (priv->knots, clutter_knot_copy (knot));
}
/**
* clutter_behaviour_path_insert_knot:
* @pathb: a #ClutterBehvaiourPath
* @offset: position in path to insert knot.
* @knot: a #ClutterKnot to append.
*
* Inserts a #ClutterKnot in the path at specified position. Values greater
* than total number of knots will append the knot at the end of path.
*
* Since: 0.2
*/
void
clutter_behaviour_path_insert_knot (ClutterBehaviourPath *pathb,
guint offset,
const ClutterKnot *knot)
{
ClutterBehaviourPathPrivate *priv;
g_return_if_fail (CLUTTER_IS_BEHAVIOUR_PATH (pathb));
g_return_if_fail (knot != NULL);
priv = pathb->priv;
priv->knots = g_slist_insert (priv->knots, clutter_knot_copy (knot), offset);
}
/**
* clutter_behaviour_path_remove_knot:
* @pathb: a #ClutterBehvaiourPath
* @offset: position in path to remove knot.
*
* Removes a #ClutterKnot in the path at specified offset.
*
* Since: 0.2
*/
void
clutter_behaviour_path_remove_knot (ClutterBehaviourPath *pathb,
guint offset)
{
ClutterBehaviourPathPrivate *priv;
GSList *togo;
g_return_if_fail (CLUTTER_IS_BEHAVIOUR_PATH (pathb));
priv = pathb->priv;
togo = g_slist_nth (priv->knots, offset);
if (togo)
{
clutter_knot_free ((ClutterKnot*)togo->data);
priv->knots = g_slist_delete_link (priv->knots, togo);
}
}
static void
clutter_behaviour_path_append_knots_valist (ClutterBehaviourPath *pathb,
const ClutterKnot *first_knot,
va_list args)
{
const ClutterKnot * knot;
knot = first_knot;
while (knot)
{
clutter_behaviour_path_append_knot (pathb, knot);
knot = va_arg (args, ClutterKnot*);
}
}
/**
* clutter_behaviour_path_append_knots:
* @pathb: a #ClutterBehvaiourPath
* @first_knot: the #ClutterKnot knot to add to the path
* @Varargs: additional knots to add to the path
*
* Adds a NULL-terminated list of knots to a path. This function is
* equivalent to calling clutter_behaviour_path_append_knot() for each
* member of the list.
*
* Since: 0.2
*/
void
clutter_behaviour_path_append_knots (ClutterBehaviourPath *pathb,
const ClutterKnot *first_knot,
...)
{
va_list args;
g_return_if_fail (CLUTTER_IS_BEHAVIOUR_PATH (pathb));
g_return_if_fail (first_knot != NULL);
va_start (args, first_knot);
clutter_behaviour_path_append_knots_valist (pathb, first_knot, args);
va_end (args);
}
/**
* clutter_behaviour_path_clear:
* @pathb: a #ClutterBehvaiourPath
*
* Removes all knots from a path
*
* Since: 0.2
*/
void
clutter_behaviour_path_clear (ClutterBehaviourPath *pathb)
{
g_return_if_fail (CLUTTER_IS_BEHAVIOUR_PATH (pathb));
g_slist_foreach (pathb->priv->knots, (GFunc) clutter_knot_free, NULL);
g_slist_free (pathb->priv->knots);
pathb->priv->knots = NULL;
return pathb->priv->path;
}

View File

@ -32,6 +32,7 @@
#include <clutter/clutter-alpha.h>
#include <clutter/clutter-behaviour.h>
#include <clutter/clutter-path.h>
G_BEGIN_DECLS
@ -82,7 +83,7 @@ struct _ClutterBehaviourPathClass
/*< public >*/
void (*knot_reached) (ClutterBehaviourPath *pathb,
const ClutterKnot *knot);
guint knot_num);
/*< private >*/
void (*_clutter_path_1) (void);
@ -94,21 +95,21 @@ struct _ClutterBehaviourPathClass
GType clutter_behaviour_path_get_type (void) G_GNUC_CONST;
ClutterBehaviour *clutter_behaviour_path_new (ClutterAlpha *alpha,
ClutterPath *path);
ClutterBehaviour *clutter_behaviour_path_new_with_description
(ClutterAlpha *alpha,
const gchar *desc);
ClutterBehaviour *clutter_behaviour_path_new_with_knots
(ClutterAlpha *alpha,
const ClutterKnot *knots,
guint n_knots);
GSList * clutter_behaviour_path_get_knots (ClutterBehaviourPath *pathb);
void clutter_behaviour_path_append_knot (ClutterBehaviourPath *pathb,
const ClutterKnot *knot);
void clutter_behaviour_path_append_knots (ClutterBehaviourPath *pathb,
const ClutterKnot *first_knot,
...) G_GNUC_NULL_TERMINATED;
void clutter_behaviour_path_insert_knot (ClutterBehaviourPath *pathb,
guint offset,
const ClutterKnot *knot);
void clutter_behaviour_path_remove_knot (ClutterBehaviourPath *pathb,
guint offset);
void clutter_behaviour_path_clear (ClutterBehaviourPath *pathb);
void clutter_behaviour_path_set_path (ClutterBehaviourPath *pathb,
ClutterPath *path);
ClutterPath * clutter_behaviour_path_get_path (ClutterBehaviourPath *pathb);
G_END_DECLS

426
clutter/clutter-bezier.c Normal file
View File

@ -0,0 +1,426 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By Tomas Frydrych <tf@openedhand.com>
*
* Copyright (C) 2007 OpenedHand
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <glib.h>
#include <string.h>
#include "clutter-bezier.h"
#include "clutter-debug.h"
/*
* We have some experimental code here to allow for constant velocity
* movement of actors along the bezier path, this macro enables it.
*/
#undef CBZ_L2T_INTERPOLATION
/****************************************************************************
* ClutterBezier -- represenation of a cubic bezier curve *
* (private; a building block for the public bspline object) *
****************************************************************************/
/*
* The t parameter of the bezier is from interval <0,1>, so we can use
* 14.18 format and special multiplication functions that preserve
* more of the least significant bits but would overflow if the value
* is > 1
*/
#define CBZ_T_Q 18
#define CBZ_T_ONE (1 << CBZ_T_Q)
#define CBZ_T_MUL(x,y) ((((x) >> 3) * ((y) >> 3)) >> 12)
#define CBZ_T_POW2(x) CBZ_T_MUL (x, x)
#define CBZ_T_POW3(x) CBZ_T_MUL (CBZ_T_POW2 (x), x)
#define CBZ_T_DIV(x,y) ((((x) << 9)/(y)) << 9)
/*
* Constants for sampling of the bezier
*/
#define CBZ_T_SAMPLES 128
#define CBZ_T_STEP (CBZ_T_ONE / CBZ_T_SAMPLES)
#define CBZ_L_STEP (CBZ_T_ONE / CBZ_T_SAMPLES)
typedef gint32 _FixedT;
/*
* This is a private type representing a single cubic bezier
*/
struct _ClutterBezier
{
/*
* bezier coefficients -- these are calculated using multiplication and
* addition from integer input, so these are also integers
*/
gint ax;
gint bx;
gint cx;
gint dx;
gint ay;
gint by;
gint cy;
gint dy;
/* length of the bezier */
guint length;
#ifdef CBZ_L2T_INTERPOLATION
/*
* coefficients for the L -> t bezier; these are calculated from fixed
* point input, and more specifically numbers that have been normalised
* to fit <0,1>, so these are also fixed point, and we can used the
* _FixedT type here.
*/
_FixedT La;
_FixedT Lb;
_FixedT Lc;
/* _FixedT Ld; == 0 */
#endif
};
ClutterBezier *
_clutter_bezier_new ()
{
return g_slice_new0 (ClutterBezier);
}
void
_clutter_bezier_free (ClutterBezier * b)
{
if (G_LIKELY (b))
{
g_slice_free (ClutterBezier, b);
}
}
ClutterBezier *
_clutter_bezier_clone_and_move (const ClutterBezier *b, gint x, gint y)
{
ClutterBezier * b2 = _clutter_bezier_new ();
memcpy (b2, b, sizeof (ClutterBezier));
b2->dx += x;
b2->dy += y;
return b2;
}
#ifdef CBZ_L2T_INTERPOLATION
/*
* L is relative advance along the bezier curve from interval <0,1>
*/
static _FixedT
_clutter_bezier_L2t (const ClutterBezier *b, _FixedT L)
{
_FixedT t = CBZ_T_MUL (b->La, CBZ_T_POW3(L))
+ CBZ_T_MUL (b->Lb, CBZ_T_POW2(L))
+ CBZ_T_MUL (b->Lc, L);
if (t > CBZ_T_ONE)
t = CBZ_T_ONE;
else if (t < 0)
t = 0;
return t;
}
#endif
static gint
_clutter_bezier_t2x (const ClutterBezier * b, _FixedT t)
{
/*
* NB -- the int coefficients can be at most 8192 for the multiplication
* to work in this fashion due to the limits of the 14.18 fixed.
*/
return ((b->ax*CBZ_T_POW3(t) + b->bx*CBZ_T_POW2(t) + b->cx*t) >> CBZ_T_Q)
+ b->dx;
}
gint
_clutter_bezier_t2y (const ClutterBezier * b, _FixedT t)
{
/*
* NB -- the int coefficients can be at most 8192 for the multiplication
* to work in this fashion due to the limits of the 14.18 fixed.
*/
return ((b->ay*CBZ_T_POW3(t) + b->by*CBZ_T_POW2(t) + b->cy*t) >> CBZ_T_Q)
+ b->dy;
}
/*
* Advances along the bezier to relative length L and returns the coordinances
* in knot
*/
void
_clutter_bezier_advance (const ClutterBezier *b, gint L, ClutterKnot * knot)
{
#ifdef CBZ_L2T_INTERPOLATION
_FixedT t = clutter_bezier_L2t (b, L);
#else
_FixedT t = L;
#endif
knot->x = _clutter_bezier_t2x (b, t);
knot->y = _clutter_bezier_t2y (b, t);
CLUTTER_NOTE (BEHAVIOUR, "advancing to relative pt %f: t %f, {%d,%d}",
(double) L / (double) CBZ_T_ONE,
(double) t / (double) CBZ_T_ONE,
knot->x, knot->y);
}
void
_clutter_bezier_init (ClutterBezier *b,
gint x_0, gint y_0,
gint x_1, gint y_1,
gint x_2, gint y_2,
gint x_3, gint y_3)
{
_FixedT t;
int i;
int xp = x_0;
int yp = y_0;
_FixedT length [CBZ_T_SAMPLES + 1];
#ifdef CBZ_L2T_INTERPOLATION
int j, k;
_FixedT L;
_FixedT t_equalized [CBZ_T_SAMPLES + 1];
#endif
#if 0
g_debug ("Initializing bezier at {{%d,%d},{%d,%d},{%d,%d},{%d,%d}}",
x0, y0, x1, y1, x2, y2, x3, y3);
#endif
b->dx = x_0;
b->dy = y_0;
b->cx = 3 * (x_1 - x_0);
b->cy = 3 * (y_1 - y_0);
b->bx = 3 * (x_2 - x_1) - b->cx;
b->by = 3 * (y_2 - y_1) - b->cy;
b->ax = x_3 - 3 * x_2 + 3 * x_1 - x_0;
b->ay = y_3 - 3 * y_2 + 3 * y_1 - y_0;
#if 0
g_debug ("Cooeficients {{%d,%d},{%d,%d},{%d,%d},{%d,%d}}",
b->ax, b->ay, b->bx, b->by, b->cx, b->cy, b->dx, b->dy);
#endif
/*
* Because of the way we do the multiplication in bezeir_t2x,y
* these coefficients need to be at most 0x1fff; this should be the case,
* I think, but have added this warning to catch any problems -- if it
* triggers, we need to change those two functions a bit.
*/
if (b->ax > 0x1fff || b->bx > 0x1fff || b->cx > 0x1fff)
g_warning ("Calculated coefficents will result in multiplication "
"overflow in clutter_bezier_t2x and clutter_bezier_t2y.");
/*
* Sample the bezier with CBZ_T_SAMPLES and calculate length at
* each point.
*
* We are working with integers here, so we use the fast sqrti function.
*/
length[0] = 0;
for (t = CBZ_T_STEP, i = 1; i <= CBZ_T_SAMPLES; ++i, t += CBZ_T_STEP)
{
int x = _clutter_bezier_t2x (b, t);
int y = _clutter_bezier_t2y (b, t);
guint l = clutter_sqrti ((y - yp)*(y - yp) + (x - xp)*(x - xp));
l += length[i-1];
length[i] = l;
xp = x;
yp = y;
}
b->length = length[CBZ_T_SAMPLES];
#if 0
g_debug ("length %d", b->length);
#endif
#ifdef CBZ_L2T_INTERPOLATION
/*
* Now normalize the length values, converting them into _FixedT
*/
for (i = 0; i <= CBZ_T_SAMPLES; ++i)
{
length[i] = (length[i] << CBZ_T_Q) / b->length;
}
/*
* Now generate a L -> t table such that the L will equidistant
* over <0,1>
*/
t_equalized[0] = 0;
for (i = 1, j = 1, L = CBZ_L_STEP; i < CBZ_T_SAMPLES; ++i, L += CBZ_L_STEP)
{
_FixedT l1, l2;
_FixedT d1, d2, d;
_FixedT t1, t2;
/* find the band for our L */
for (k = j; k < CBZ_T_SAMPLES; ++k)
{
if (L < length[k])
break;
}
/*
* Now we know that L is from (length[k-1],length[k]>
* We remember k-1 in order not to have to iterate over the
* whole length array in the next iteration of the main loop
*/
j = k - 1;
/*
* Now interpolate equlised t as a weighted average
*/
l1 = length[k-1];
l2 = length[k];
d1 = l2 - L;
d2 = L - l1;
d = l2 - l1;
t1 = (k - 1) * CBZ_T_STEP;
t2 = k * CBZ_T_STEP;
t_equalized[i] = (t1*d1 + t2*d2)/d;
if (t_equalized[i] < t_equalized[i-1])
g_debug ("wrong t: L %f, l1 %f, l2 %f, t1 %f, t2 %f",
(double) (L)/(double)CBZ_T_ONE,
(double) (l1)/(double)CBZ_T_ONE,
(double) (l2)/(double)CBZ_T_ONE,
(double) (t1)/(double)CBZ_T_ONE,
(double) (t2)/(double)CBZ_T_ONE);
}
t_equalized[CBZ_T_SAMPLES] = CBZ_T_ONE;
/* We now fit a bezier -- at this stage, do a single fit through our values
* at 0, 1/3, 2/3 and 1
*
* FIXME -- do we need to use a better fitting approach to choose the best
* beziere. The actual curve we acquire this way is not too bad shapwise,
* but (probably due to rounding errors) the resulting curve no longer
* satisfies the necessary condition that for L2 > L1, t2 > t1, which
* causes oscilation.
*/
#if 0
/*
* These are the control points we use to calculate the curve coefficients
* for bezier t(L); these are not needed directly, but are implied in the
* calculations below.
*
* (p0 is 0,0, and p3 is 1,1)
*/
p1 = (18 * t_equalized[CBZ_T_SAMPLES/3] -
9 * t_equalized[2*CBZ_T_SAMPLES/3] +
2 << CBZ_T_Q) / 6;
p2 = (18 * t_equalized[2*CBZ_T_SAMPLES/3] -
9 * t_equalized[CBZ_T_SAMPLES/3] -
(5 << CBZ_T_Q)) / 6;
#endif
b->Lc = (18 * t_equalized[CBZ_T_SAMPLES/3] -
9 * t_equalized[2*CBZ_T_SAMPLES/3] +
(2 << CBZ_T_Q)) >> 1;
b->Lb = (36 * t_equalized[2*CBZ_T_SAMPLES/3] -
45 * t_equalized[CBZ_T_SAMPLES/3] -
(9 << CBZ_T_Q)) >> 1;
b->La = ((27 * (t_equalized[CBZ_T_SAMPLES/3] -
t_equalized[2*CBZ_T_SAMPLES/3]) +
(7 << CBZ_T_Q)) >> 1) + CBZ_T_ONE;
g_debug ("t(1/3) %f, t(2/3) %f",
(double)t_equalized[CBZ_T_SAMPLES/3]/(double)CBZ_T_ONE,
(double)t_equalized[2*CBZ_T_SAMPLES/3]/(double)CBZ_T_ONE);
g_debug ("L -> t coefficients: %f, %f, %f",
(double)b->La/(double)CBZ_T_ONE,
(double)b->Lb/(double)CBZ_T_ONE,
(double)b->Lc/(double)CBZ_T_ONE);
/*
* For debugging, you can load these values into a spreadsheet and graph
* them to see how well the approximation matches the data
*/
for (i = 0; i < CBZ_T_SAMPLES; ++i)
{
g_print ("%f, %f, %f\n",
(double)(i*CBZ_T_STEP)/(double)CBZ_T_ONE,
(double)(t_equalized[i])/(double)CBZ_T_ONE,
(double)(clutter_bezier_L2t(b,i*CBZ_T_STEP))/(double)CBZ_T_ONE);
}
#endif
}
/*
* Moves a control point at indx to location represented by knot
*/
void
_clutter_bezier_adjust (ClutterBezier * b, ClutterKnot * knot, guint indx)
{
guint x[4], y[4];
g_assert (indx < 4);
x[0] = b->dx;
y[0] = b->dy;
x[1] = b->cx / 3 + x[0];
y[1] = b->cy / 3 + y[0];
x[2] = b->bx / 3 + b->cx + x[1];
y[2] = b->by / 3 + b->cy + y[1];
x[3] = b->ax + x[0] + b->cx + b->bx;
y[3] = b->ay + y[0] + b->cy + b->by;
x[indx] = knot->x;
y[indx] = knot->y;
_clutter_bezier_init (b, x[0], y[0], x[1], y[1], x[2], y[2], x[3], y[3]);
}
guint
_clutter_bezier_get_length (const ClutterBezier *b)
{
return b->length;
}

65
clutter/clutter-bezier.h Normal file
View File

@ -0,0 +1,65 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By Tomas Frydrych <tf@openedhand.com>
*
* Copyright (C) 2006, 2007 OpenedHand
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CLUTTER_BEZIER_H__
#define __CLUTTER_BEZIER_H__
#include <glib.h>
#include "clutter-types.h"
G_BEGIN_DECLS
/* This is used in _clutter_bezier_advance to represent the full
length of the bezier curve. Anything less than that represents a
fraction of the length */
#define CLUTTER_BEZIER_MAX_LENGTH (1 << 18)
typedef struct _ClutterBezier ClutterBezier;
ClutterBezier *_clutter_bezier_new ();
void _clutter_bezier_free (ClutterBezier * b);
ClutterBezier *_clutter_bezier_clone_and_move (const ClutterBezier *b,
gint x,
gint y);
void _clutter_bezier_advance (const ClutterBezier *b,
gint L,
ClutterKnot *knot);
void _clutter_bezier_init (ClutterBezier *b,
gint x_0, gint y_0,
gint x_1, gint y_1,
gint x_2, gint y_2,
gint x_3, gint y_3);
void _clutter_bezier_adjust (ClutterBezier *b,
ClutterKnot *knot,
guint indx);
guint _clutter_bezier_get_length (const ClutterBezier *b);
G_END_DECLS
#endif /* __CLUTTER_BEZIER_H__ */

View File

@ -47,7 +47,6 @@
#include "clutter-marshal.h"
#include "clutter-private.h"
#include "clutter-debug.h"
#include "clutter-behaviour-bspline.h"
#include "clutter-behaviour-depth.h"
#include "clutter-behaviour-ellipse.h"
#include "clutter-behaviour-opacity.h"
@ -658,7 +657,7 @@ clutter_effect_move (ClutterEffectTemplate *template_,
gpointer data)
{
ClutterEffectClosure *c;
ClutterKnot knots[2];
ClutterPath *path;
c = clutter_effect_closure_new (template_,
actor,
@ -667,13 +666,14 @@ clutter_effect_move (ClutterEffectTemplate *template_,
c->completed_func = func;
c->completed_data = data;
knots[0].x = clutter_actor_get_x (actor);
knots[0].y = clutter_actor_get_y (actor);
path = clutter_path_new ();
knots[1].x = x;
knots[1].y = y;
clutter_path_add_move_to (path,
clutter_actor_get_x (actor),
clutter_actor_get_y (actor));
clutter_path_add_line_to (path, x, y);
c->behave = clutter_behaviour_path_new (c->alpha, knots, 2);
c->behave = clutter_behaviour_path_new (c->alpha, path);
clutter_behaviour_apply (c->behave, actor);
clutter_timeline_start (c->timeline);
@ -707,18 +707,30 @@ clutter_effect_path (ClutterEffectTemplate *template_,
gpointer data)
{
ClutterEffectClosure *c;
ClutterPath *path;
guint i;
c = clutter_effect_closure_new (template_,
actor,
G_CALLBACK (on_effect_complete));
path = clutter_path_new ();
c->completed_func = func;
c->completed_data = data;
if (n_knots)
clutter_actor_set_position (actor, knots[0].x, knots[0].y);
path = clutter_path_new ();
c->behave = clutter_behaviour_path_new (c->alpha, knots, n_knots);
if (n_knots)
{
clutter_actor_set_position (actor, knots[0].x, knots[0].y);
clutter_path_add_move_to (path, knots[0].x, knots[0].y);
for (i = 1; i < n_knots; i++)
clutter_path_add_line_to (path, knots[i].x, knots[i].y);
}
c->behave = clutter_behaviour_path_new (c->alpha, path);
clutter_behaviour_apply (c->behave, actor);
clutter_timeline_start (c->timeline);

View File

@ -10,4 +10,5 @@ VOID:OBJECT,OBJECT,PARAM
VOID:OBJECT,POINTER
VOID:STRING,BOOLEAN,BOOLEAN
VOID:STRING,INT
VOID:UINT
VOID:VOID

1414
clutter/clutter-path.c Normal file

File diff suppressed because it is too large Load Diff

245
clutter/clutter-path.h Normal file
View File

@ -0,0 +1,245 @@
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By Matthew Allum <mallum@openedhand.com>
*
* Copyright (C) 2008 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <clutter/clutter.h> can be included directly."
#endif
#ifndef __CLUTTER_PATH_H__
#define __CLUTTER_PATH_H__
#include <glib-object.h>
#include <clutter/clutter-types.h>
G_BEGIN_DECLS
#define CLUTTER_TYPE_PATH \
(clutter_path_get_type())
#define CLUTTER_PATH(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
CLUTTER_TYPE_PATH, \
ClutterPath))
#define CLUTTER_PATH_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
CLUTTER_TYPE_PATH, \
ClutterPathClass))
#define CLUTTER_IS_PATH(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
CLUTTER_TYPE_PATH))
#define CLUTTER_IS_PATH_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
CLUTTER_TYPE_PATH))
#define CLUTTER_PATH_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
CLUTTER_TYPE_PATH, \
ClutterPathClass))
#define CLUTTER_TYPE_PATH_NODE (clutter_path_node_get_type ())
#define CLUTTER_PATH_RELATIVE 32
/**
* ClutterPathNodeType:
* @CLUTTER_PATH_MOVE_TO: jump to the given position
* @CLUTTER_PATH_LINE_TO: create a line from the last node to the
* given position
* @CLUTTER_PATH_CURVE_TO: bezier curve using the last position and
* three control points.
* @CLUTTER_PATH_CLOSE: create a line from the last node to the last
* %CLUTTER_PATH_MOVE_TO node.
* @CLUTTER_PATH_REL_MOVE_TO: same as %CLUTTER_PATH_MOVE_TO but with
* coordinates relative to the last node.
* @CLUTTER_PATH_REL_LINE_TO: same as %CLUTTER_PATH_LINE_TO but with
* coordinates relative to the last node.
* @CLUTTER_PATH_REL_CURVE_TO: same as %CLUTTER_PATH_CURVE_TO but with
* coordinates relative to the last node.
*/
typedef enum {
CLUTTER_PATH_MOVE_TO = 0,
CLUTTER_PATH_LINE_TO = 1,
CLUTTER_PATH_CURVE_TO = 2,
CLUTTER_PATH_CLOSE = 3,
CLUTTER_PATH_REL_MOVE_TO = CLUTTER_PATH_MOVE_TO | CLUTTER_PATH_RELATIVE,
CLUTTER_PATH_REL_LINE_TO = CLUTTER_PATH_LINE_TO | CLUTTER_PATH_RELATIVE,
CLUTTER_PATH_REL_CURVE_TO = CLUTTER_PATH_CURVE_TO | CLUTTER_PATH_RELATIVE
} ClutterPathNodeType;
typedef struct _ClutterPath ClutterPath;
typedef struct _ClutterPathClass ClutterPathClass;
typedef struct _ClutterPathPrivate ClutterPathPrivate;
typedef struct _ClutterPathNode ClutterPathNode;
/**
* ClutterPathCallback:
* @node: the node
* @data: optional data passed to the function
*
* This function is passed to clutter_path_foreach() and will be
* called for each node contained in the path.
*
* Since: 1.0
*/
typedef void (* ClutterPathCallback) (const ClutterPathNode *node,
gpointer data);
/**
* ClutterPathClass:
*
* The #ClutterPathClass struct contains only private data.
*/
struct _ClutterPathClass
{
/*< private >*/
GInitiallyUnownedClass parent_class;
};
/**
* ClutterPath:
*
* The #ClutterPath struct contains only private data and should
* be accessed with the functions below.
*/
struct _ClutterPath
{
/*< private >*/
GInitiallyUnowned parent;
ClutterPathPrivate *priv;
};
/**
* ClutterPathNode:
* @type: the node's type
* @points: the coordinates of the node
*
* Represents a single node of a #ClutterPath.
*
* Some of the coordinates in @points may be unused for some node
* types. %CLUTTER_PATH_MOVE_TO and %CLUTTER_PATH_LINE_TO use only two
* pairs of coordinates, %CLUTTER_PATH_CURVE_TO uses all three and
* %CLUTTER_PATH_CLOSE uses none.
*
* Since: 1.0
*/
struct _ClutterPathNode
{
ClutterPathNodeType type;
ClutterKnot points[3];
};
GType clutter_path_get_type (void) G_GNUC_CONST;
ClutterPath *clutter_path_new (void);
ClutterPath *clutter_path_new_with_description (const gchar *desc);
void clutter_path_add_move_to (ClutterPath *path,
gint x,
gint y);
void clutter_path_add_rel_move_to (ClutterPath *path,
gint x,
gint y);
void clutter_path_add_line_to (ClutterPath *path,
gint x,
gint y);
void clutter_path_add_rel_line_to (ClutterPath *path,
gint x,
gint y);
void clutter_path_add_curve_to (ClutterPath *path,
gint x1,
gint y1,
gint x2,
gint y2,
gint x3,
gint y3);
void clutter_path_add_rel_curve_to (ClutterPath *path,
gint x1,
gint y1,
gint x2,
gint y2,
gint x3,
gint y3);
void clutter_path_add_close (ClutterPath *path);
gboolean clutter_path_add_string (ClutterPath *path,
const gchar *str);
void clutter_path_add_node (ClutterPath *path,
const ClutterPathNode *node);
guint clutter_path_get_n_nodes (ClutterPath *path);
void clutter_path_get_node (ClutterPath *path,
guint index,
ClutterPathNode *node);
GSList *clutter_path_get_nodes (ClutterPath *path);
void clutter_path_foreach (ClutterPath *path,
ClutterPathCallback callback,
gpointer user_data);
void clutter_path_insert_node (ClutterPath *path,
gint index,
const ClutterPathNode *node);
void clutter_path_remove_node (ClutterPath *path,
guint index);
void clutter_path_replace_node (ClutterPath *path,
guint index,
const ClutterPathNode *node);
gchar *clutter_path_get_description (ClutterPath *path);
gboolean clutter_path_set_description (ClutterPath *path,
const gchar *str);
void clutter_path_clear (ClutterPath *path);
guint clutter_path_get_position (ClutterPath *path,
guint alpha,
ClutterKnot *position);
guint clutter_path_get_length (ClutterPath *path);
ClutterPathNode *clutter_path_node_copy (const ClutterPathNode *node);
void clutter_path_node_free (ClutterPathNode *node);
gboolean clutter_path_node_equal (const ClutterPathNode *node_a,
const ClutterPathNode *node_b);
GType clutter_path_node_get_type (void);
G_END_DECLS
#endif /* __CLUTTER_PATH_H__ */

View File

@ -35,7 +35,6 @@
#include "clutter-color.h"
#include "clutter-container.h"
#include "clutter-behaviour.h"
#include "clutter-behaviour-bspline.h"
#include "clutter-behaviour-depth.h"
#include "clutter-behaviour-ellipse.h"
#include "clutter-behaviour-opacity.h"
@ -58,6 +57,7 @@
#include "clutter-main.h"
#include "clutter-media.h"
#include "clutter-model.h"
#include "clutter-path.h"
#include "clutter-stage.h"
#include "clutter-stage-manager.h"
#include "clutter-texture.h"

View File

@ -409,10 +409,6 @@ main (int argc, char *argv[])
<para>
<variablelist>
<varlistentry>
<term>#ClutterBehaviourBspline</term>
<listitem><simpara>Moves actors along a B-spline path</simpara></listitem>
</varlistentry>
<varlistentry>
<term>#ClutterBehaviourDepth</term>
<listitem><simpara>Changes the depth of actors</simpara></listitem>

View File

@ -91,11 +91,11 @@
<chapter>
<title>Behaviours</title>
<xi:include href="xml/clutter-behaviour-bspline.xml"/>
<xi:include href="xml/clutter-behaviour-depth.xml"/>
<xi:include href="xml/clutter-behaviour-ellipse.xml"/>
<xi:include href="xml/clutter-behaviour-opacity.xml"/>
<xi:include href="xml/clutter-behaviour-path.xml"/>
<xi:include href="xml/clutter-path.xml"/>
<xi:include href="xml/clutter-behaviour-rotate.xml"/>
<xi:include href="xml/clutter-behaviour-scale.xml"/>
</chapter>

View File

@ -660,13 +660,10 @@ clutter_timeline_get_type
ClutterBehaviourPath
ClutterBehaviourPathClass
clutter_behaviour_path_new
clutter_behaviour_path_get_knots
clutter_behaviour_path_append_knot
clutter_behaviour_path_append_knots
clutter_behaviour_path_insert_knot
clutter_behaviour_path_remove_knot
clutter_behaviour_path_clear
clutter_behaviour_path_new_with_description
clutter_behaviour_path_new_with_knots
clutter_behaviour_path_set_path
clutter_behaviour_path_get_path
<SUBSECTION>
ClutterKnot
clutter_knot_copy
@ -687,6 +684,44 @@ clutter_knot_get_type
clutter_behaviour_path_get_type
</SECTION>
<SECTION>
<FILE>clutter-path</FILE>
<TITLE>ClutterPath</TITLE>
ClutterPath
ClutterPathClass
ClutterPathCallback
ClutterPathNodeType
clutter_path_new
clutter_path_new_with_description
clutter_path_add_move_to
clutter_path_add_rel_move_to
clutter_path_add_line_to
clutter_path_add_rel_line_to
clutter_path_add_curve_to
clutter_path_add_rel_curve_to
clutter_path_add_close
clutter_path_add_string
clutter_path_add_node
clutter_path_get_n_nodes
clutter_path_get_node
clutter_path_get_nodes
clutter_path_foreach
clutter_path_insert_node
clutter_path_remove_node
clutter_path_replace_node
clutter_path_get_description
clutter_path_set_description
clutter_path_clear
clutter_path_get_position
clutter_path_get_length
<SUBSECTION>
ClutterPathNode
clutter_path_node_copy
clutter_path_node_free
clutter_path_node_equal
</SECTION>
<SECTION>
<FILE>clutter-behaviour-opacity</FILE>
<TITLE>ClutterBehaviourOpacity</TITLE>
@ -762,33 +797,6 @@ ClutterBehaviourScalePrivate
clutter_behaviour_scale_get_type
</SECTION>
<SECTION>
<FILE>clutter-behaviour-bspline</FILE>
<TITLE>ClutterBehaviourBspline</TITLE>
ClutterBehaviourBspline
ClutterBehaviourBsplineClass
clutter_behaviour_bspline_new
clutter_behaviour_bspline_append_knot
clutter_behaviour_bspline_append_knots
clutter_behaviour_bspline_truncate
clutter_behaviour_bspline_join
clutter_behaviour_bspline_split
clutter_behaviour_bspline_clear
clutter_behaviour_bspline_adjust
clutter_behaviour_bspline_set_origin
clutter_behaviour_bspline_get_origin
<SUBSECTION Standard>
CLUTTER_TYPE_BEHAVIOUR_BSPLINE
CLUTTER_BEHAVIOUR_BSPLINE
CLUTTER_BEHAVIOUR_BSPLINE_CLASS
CLUTTER_IS_BEHAVIOUR_BSPLINE
CLUTTER_IS_BEHAVIOUR_BSPLINE_CLASS
CLUTTER_BEHAVIOUR_BSPLINE_GET_CLASS
<SUBSECTION Private>
ClutterBehaviourBsplinePrivate
clutter_behaviour_bspline_get_type
</SECTION>
<SECTION>
<FILE>clutter-behaviour-ellipse</FILE>
<TITLE>ClutterBehaviourEllipse</TITLE>

View File

@ -11,11 +11,11 @@ clutter_timeline_get_type
clutter_media_get_type
clutter_behaviour_get_type
clutter_alpha_get_type
clutter_behaviour_bspline_get_type
clutter_behaviour_depth_get_type
clutter_behaviour_ellipse_get_type
clutter_behaviour_opacity_get_type
clutter_behaviour_path_get_type
clutter_path_get_type
clutter_behaviour_rotate_get_type
clutter_behaviour_scale_get_type
clutter_backend_get_type

View File

@ -13,6 +13,7 @@ test_conformance_SOURCES = \
test-mesh-contiguous.c \
test-mesh-interleved.c \
test-mesh-mutability.c \
test-path.c \
test-pick.c \
test-label-cache.c \
test-clutter-entry.c \

View File

@ -105,5 +105,7 @@ main (int argc, char **argv)
TEST_CONFORM_SIMPLE ("/texture", test_backface_culling);
TEST_CONFORM_SIMPLE ("/path", test_path);
return g_test_run ();
}

609
tests/conform/test-path.c Normal file
View File

@ -0,0 +1,609 @@
#include <clutter/clutter.h>
#include <string.h>
#include <math.h>
#include "test-conform-common.h"
#define MAX_NODES 128
#define FLOAT_FUZZ_AMOUNT 5.0f
typedef struct _CallbackData CallbackData;
typedef gboolean (* PathTestFunc) (CallbackData *data);
static void compare_node (const ClutterPathNode *node, gpointer data_p);
struct _CallbackData
{
ClutterPath *path;
guint n_nodes;
ClutterPathNode nodes[MAX_NODES];
gboolean nodes_different;
guint nodes_found;
};
static const char path_desc[] =
"M 21 22 m 23 24 "
"L 25 26 l 27 28 "
"C 29 30 31 32 33 34 c 35 36 37 38 39 40 "
"z";
static const ClutterPathNode path_nodes[] =
{ { CLUTTER_PATH_MOVE_TO, { { 21, 22 }, { 0, 0 }, { 0, 0 } } },
{ CLUTTER_PATH_REL_MOVE_TO, { { 23, 24 }, { 0, 0 }, { 0, 0 } } },
{ CLUTTER_PATH_LINE_TO, { { 25, 26 }, { 0, 0 }, { 0, 0 } } },
{ CLUTTER_PATH_REL_LINE_TO, { { 27, 28 }, { 0, 0 }, { 0, 0 } } },
{ CLUTTER_PATH_CURVE_TO, { { 29, 30 }, { 31, 32 }, { 33, 34 } } },
{ CLUTTER_PATH_REL_CURVE_TO, { { 35, 36 }, { 37, 38 }, { 39, 40 } } },
{ CLUTTER_PATH_CLOSE, { { 0, 0 }, { 0, 0 }, { 0, 0 } } } };
static gboolean
path_test_add_move_to (CallbackData *data)
{
ClutterPathNode node;
node.type = CLUTTER_PATH_MOVE_TO;
node.points[0].x = 1;
node.points[0].y = 2;
clutter_path_add_move_to (data->path, node.points[0].x, node.points[0].y);
data->nodes[data->n_nodes++] = node;
return TRUE;
}
static gboolean
path_test_add_line_to (CallbackData *data)
{
ClutterPathNode node;
node.type = CLUTTER_PATH_LINE_TO;
node.points[0].x = 3;
node.points[0].y = 4;
clutter_path_add_line_to (data->path, node.points[0].x, node.points[0].y);
data->nodes[data->n_nodes++] = node;
return TRUE;
}
static gboolean
path_test_add_curve_to (CallbackData *data)
{
ClutterPathNode node;
node.type = CLUTTER_PATH_CURVE_TO;
node.points[0].x = 5;
node.points[0].y = 6;
node.points[1].x = 7;
node.points[1].y = 8;
node.points[2].x = 9;
node.points[2].y = 10;
clutter_path_add_curve_to (data->path,
node.points[0].x, node.points[0].y,
node.points[1].x, node.points[1].y,
node.points[2].x, node.points[2].y);
data->nodes[data->n_nodes++] = node;
return TRUE;
}
static gboolean
path_test_add_close (CallbackData *data)
{
ClutterPathNode node;
node.type = CLUTTER_PATH_CLOSE;
clutter_path_add_close (data->path);
data->nodes[data->n_nodes++] = node;
return TRUE;
}
static gboolean
path_test_add_rel_move_to (CallbackData *data)
{
ClutterPathNode node;
node.type = CLUTTER_PATH_REL_MOVE_TO;
node.points[0].x = 11;
node.points[0].y = 12;
clutter_path_add_rel_move_to (data->path, node.points[0].x, node.points[0].y);
data->nodes[data->n_nodes++] = node;
return TRUE;
}
static gboolean
path_test_add_rel_line_to (CallbackData *data)
{
ClutterPathNode node;
node.type = CLUTTER_PATH_REL_LINE_TO;
node.points[0].x = 13;
node.points[0].y = 14;
clutter_path_add_rel_line_to (data->path, node.points[0].x, node.points[0].y);
data->nodes[data->n_nodes++] = node;
return TRUE;
}
static gboolean
path_test_add_rel_curve_to (CallbackData *data)
{
ClutterPathNode node;
node.type = CLUTTER_PATH_REL_CURVE_TO;
node.points[0].x = 15;
node.points[0].y = 16;
node.points[1].x = 17;
node.points[1].y = 18;
node.points[2].x = 19;
node.points[2].y = 20;
clutter_path_add_rel_curve_to (data->path,
node.points[0].x, node.points[0].y,
node.points[1].x, node.points[1].y,
node.points[2].x, node.points[2].y);
data->nodes[data->n_nodes++] = node;
return TRUE;
}
static gboolean
path_test_add_string (CallbackData *data)
{
int i;
for (i = 0; i < G_N_ELEMENTS (path_nodes); i++)
data->nodes[data->n_nodes++] = path_nodes[i];
clutter_path_add_string (data->path, path_desc);
return TRUE;
}
static gboolean
path_test_add_node_by_struct (CallbackData *data)
{
int i;
for (i = 0; i < G_N_ELEMENTS (path_nodes); i++)
{
data->nodes[data->n_nodes++] = path_nodes[i];
clutter_path_add_node (data->path, path_nodes + i);
}
return TRUE;
}
static gboolean
path_test_get_n_nodes (CallbackData *data)
{
return clutter_path_get_n_nodes (data->path) == data->n_nodes;
}
static gboolean
path_test_get_node (CallbackData *data)
{
int i;
data->nodes_found = 0;
data->nodes_different = FALSE;
for (i = 0; i < data->n_nodes; i++)
{
ClutterPathNode node;
clutter_path_get_node (data->path, i, &node);
compare_node (&node, data);
}
return !data->nodes_different;
}
static gboolean
path_test_get_nodes (CallbackData *data)
{
GSList *list, *node;
data->nodes_found = 0;
data->nodes_different = FALSE;
list = clutter_path_get_nodes (data->path);
for (node = list; node; node = node->next)
compare_node (node->data, data);
g_slist_free (list);
return !data->nodes_different && data->nodes_found == data->n_nodes;
}
static gboolean
path_test_insert_beginning (CallbackData *data)
{
ClutterPathNode node;
node.type = CLUTTER_PATH_LINE_TO;
node.points[0].x = 41;
node.points[0].y = 42;
memmove (data->nodes + 1, data->nodes,
data->n_nodes++ * sizeof (ClutterPathNode));
data->nodes[0] = node;
clutter_path_insert_node (data->path, 0, &node);
return TRUE;
}
static gboolean
path_test_insert_end (CallbackData *data)
{
ClutterPathNode node;
node.type = CLUTTER_PATH_LINE_TO;
node.points[0].x = 43;
node.points[0].y = 44;
data->nodes[data->n_nodes++] = node;
clutter_path_insert_node (data->path, -1, &node);
return TRUE;
}
static gboolean
path_test_insert_middle (CallbackData *data)
{
ClutterPathNode node;
int pos = data->n_nodes / 2;
node.type = CLUTTER_PATH_LINE_TO;
node.points[0].x = 45;
node.points[0].y = 46;
memmove (data->nodes + pos + 1, data->nodes + pos,
(data->n_nodes - pos) * sizeof (ClutterPathNode));
data->nodes[pos] = node;
data->n_nodes++;
clutter_path_insert_node (data->path, pos, &node);
return TRUE;
}
static gboolean
path_test_clear (CallbackData *data)
{
clutter_path_clear (data->path);
data->n_nodes = 0;
return TRUE;
}
static gboolean
path_test_clear_insert (CallbackData *data)
{
return path_test_clear (data) && path_test_insert_middle (data);
}
static gboolean
path_test_remove_beginning (CallbackData *data)
{
memmove (data->nodes, data->nodes + 1,
--data->n_nodes * sizeof (ClutterPathNode));
clutter_path_remove_node (data->path, 0);
return TRUE;
}
static gboolean
path_test_remove_end (CallbackData *data)
{
clutter_path_remove_node (data->path, --data->n_nodes);
return TRUE;
}
static gboolean
path_test_remove_middle (CallbackData *data)
{
int pos = data->n_nodes / 2;
memmove (data->nodes + pos, data->nodes + pos + 1,
(--data->n_nodes - pos) * sizeof (ClutterPathNode));
clutter_path_remove_node (data->path, pos);
return TRUE;
}
static gboolean
path_test_remove_only (CallbackData *data)
{
return path_test_clear (data)
&& path_test_add_line_to (data)
&& path_test_remove_beginning (data);
}
static gboolean
path_test_replace (CallbackData *data)
{
ClutterPathNode node;
int pos = data->n_nodes / 2;
node.type = CLUTTER_PATH_LINE_TO;
node.points[0].x = 47;
node.points[0].y = 48;
data->nodes[pos] = node;
clutter_path_replace_node (data->path, pos, &node);
return TRUE;
}
static gboolean
path_test_set_description (CallbackData *data)
{
data->n_nodes = G_N_ELEMENTS (path_nodes);
memcpy (data->nodes, path_nodes, sizeof (path_nodes));
return clutter_path_set_description (data->path, path_desc);
}
static gboolean
path_test_get_description (CallbackData *data)
{
char *desc1, *desc2;
gboolean ret = TRUE;
desc1 = clutter_path_get_description (data->path);
clutter_path_clear (data->path);
if (!clutter_path_set_description (data->path, desc1))
ret = FALSE;
desc2 = clutter_path_get_description (data->path);
if (strcmp (desc1, desc2))
ret = FALSE;
g_free (desc1);
g_free (desc2);
return ret;
}
static gboolean
float_fuzzy_equals (float fa, float fb)
{
return fabs (fa - fb) <= FLOAT_FUZZ_AMOUNT;
}
static void
set_triangle_path (CallbackData *data)
{
/* Triangular shaped path hitting (0,0), (64,64) and (128,0) in four
parts. The two curves are actually straight lines */
static const ClutterPathNode nodes[] =
{ { CLUTTER_PATH_MOVE_TO, { { 0, 0 } } },
{ CLUTTER_PATH_LINE_TO, { { 32, 32 } } },
{ CLUTTER_PATH_CURVE_TO, { { 40, 40 }, { 56, 56 }, { 64, 64 } } },
{ CLUTTER_PATH_REL_CURVE_TO, { { 8, -8 }, { 24, -24 }, { 32, -32 } } },
{ CLUTTER_PATH_REL_LINE_TO, { { 32, -32 } } } };
gint i;
clutter_path_clear (data->path);
for (i = 0; i < G_N_ELEMENTS (nodes); i++)
clutter_path_add_node (data->path, nodes + i);
memcpy (data->nodes, nodes, sizeof (nodes));
data->n_nodes = G_N_ELEMENTS (nodes);
}
static gboolean
path_test_get_position (CallbackData *data)
{
static const float values[] = { 0.125f, 16.0f, 16.0f,
0.375f, 48.0f, 48.0f,
0.625f, 80.0f, 48.0f,
0.875f, 112.0f, 16.0f };
gint i;
set_triangle_path (data);
for (i = 0; i < G_N_ELEMENTS (values); i += 3)
{
ClutterKnot pos;
clutter_path_get_position (data->path,
values[i] * CLUTTER_ALPHA_MAX_ALPHA, &pos);
if (!float_fuzzy_equals (values[i + 1], pos.x)
|| !float_fuzzy_equals (values[i + 2], pos.y))
return FALSE;
}
return TRUE;
}
static gboolean
path_test_get_length (CallbackData *data)
{
const float actual_length /* sqrt(64**2 + 64**2) * 2 */ = 181.019336f;
guint approx_length;
set_triangle_path (data);
g_object_get (data->path, "length", &approx_length, NULL);
/* Allow 15% margin of error */
return fabs (approx_length - actual_length) / actual_length <= 0.15f;
}
static gboolean
path_test_boxed_type (CallbackData *data)
{
gboolean ret = TRUE;
GSList *nodes, *l;
GValue value;
nodes = clutter_path_get_nodes (data->path);
memset (&value, 0, sizeof (value));
for (l = nodes; l; l = l->next)
{
g_value_init (&value, CLUTTER_TYPE_PATH_NODE);
g_value_set_boxed (&value, l->data);
if (!clutter_path_node_equal (l->data,
g_value_get_boxed (&value)))
ret = FALSE;
g_value_unset (&value);
}
g_slist_free (nodes);
return ret;
}
static const struct
{
const char *desc;
PathTestFunc func;
}
path_tests[] =
{
{ "Add line to", path_test_add_line_to },
{ "Add move to", path_test_add_move_to },
{ "Add curve to", path_test_add_curve_to },
{ "Add close", path_test_add_close },
{ "Add relative line to", path_test_add_rel_line_to },
{ "Add relative move to", path_test_add_rel_move_to },
{ "Add relative curve to", path_test_add_rel_curve_to },
{ "Add string", path_test_add_string },
{ "Add node by struct", path_test_add_node_by_struct },
{ "Get number of nodes", path_test_get_n_nodes },
{ "Get a node", path_test_get_node },
{ "Get all nodes", path_test_get_nodes },
{ "Insert at beginning", path_test_insert_beginning },
{ "Insert at end", path_test_insert_end },
{ "Insert at middle", path_test_insert_middle },
{ "Add after insert", path_test_add_line_to },
{ "Clear then insert", path_test_clear_insert },
{ "Add string again", path_test_add_string },
{ "Remove from beginning", path_test_remove_beginning },
{ "Remove from end", path_test_remove_end },
{ "Remove from middle", path_test_remove_middle },
{ "Add after remove", path_test_add_line_to },
{ "Remove only node", path_test_remove_only },
{ "Add after remove again", path_test_add_line_to },
{ "Replace a node", path_test_replace },
{ "Set description", path_test_set_description },
{ "Get description", path_test_get_description },
{ "Clear", path_test_clear },
{ "Get position", path_test_get_position },
{ "Check node boxed type", path_test_boxed_type },
{ "Get length", path_test_get_length }
};
static void
compare_node (const ClutterPathNode *node, gpointer data_p)
{
CallbackData *data = data_p;
if (data->nodes_found >= data->n_nodes)
data->nodes_different = TRUE;
else
{
guint n_points = 0, i;
const ClutterPathNode *onode = data->nodes + data->nodes_found;
if (node->type != onode->type)
data->nodes_different = TRUE;
switch (node->type & ~CLUTTER_PATH_RELATIVE)
{
case CLUTTER_PATH_MOVE_TO: n_points = 1; break;
case CLUTTER_PATH_LINE_TO: n_points = 1; break;
case CLUTTER_PATH_CURVE_TO: n_points = 3; break;
case CLUTTER_PATH_CLOSE: n_points = 0; break;
default:
data->nodes_different = TRUE;
break;
}
for (i = 0; i < n_points; i++)
if (node->points[i].x != onode->points[i].x
|| node->points[i].y != onode->points[i].y)
{
data->nodes_different = TRUE;
break;
}
}
data->nodes_found++;
}
static gboolean
compare_nodes (CallbackData *data)
{
data->nodes_different = FALSE;
data->nodes_found = 0;
clutter_path_foreach (data->path, compare_node, data);
return !data->nodes_different && data->nodes_found == data->n_nodes;
}
void
test_path (TestConformSimpleFixture *fixture,
gconstpointer _data)
{
CallbackData data;
gint i;
memset (&data, 0, sizeof (data));
data.path = clutter_path_new ();
for (i = 0; i < G_N_ELEMENTS (path_tests); i++)
{
gboolean succeeded;
if (g_test_verbose ())
g_print ("%s... ", path_tests[i].desc);
succeeded = path_tests[i].func (&data) && compare_nodes (&data);
if (g_test_verbose ())
g_print ("%s\n", succeeded ? "ok" : "FAIL");
g_assert (succeeded);
}
g_object_unref (data.path);
}

View File

@ -65,9 +65,6 @@ typedef enum {
PATH_BSPLINE
} path_t;
#define MAGIC 0.551784
#define RADIUS 200
G_MODULE_EXPORT int
test_behave_main (int argc, char *argv[])
{
@ -82,24 +79,24 @@ test_behave_main (int argc, char *argv[])
int i;
path_t path_type = PATH_POLY;
ClutterKnot knots_poly[] = {{ 0, 0 }, { 0, 300 }, { 300, 300 },
{ 300, 0 }, {0, 0 }};
const char *knots_poly = ("M 0, 0 L 0, 300 L 300, 300 "
"L 300, 0 L 0, 0");
ClutterKnot origin = { 200, 200 };
ClutterKnot knots_bspline[] = {{ -RADIUS, 0 },
{ -RADIUS, RADIUS*MAGIC },
{ -RADIUS*MAGIC, RADIUS },
{ 0, RADIUS },
{ RADIUS*MAGIC, RADIUS },
{ RADIUS, RADIUS*MAGIC },
{ RADIUS, 0 },
{ RADIUS, -RADIUS*MAGIC },
{ RADIUS*MAGIC, -RADIUS },
{ 0, -RADIUS },
{ -RADIUS*MAGIC, -RADIUS },
{ -RADIUS, -RADIUS*MAGIC },
{ -RADIUS, 0}};
/* A spiral created with inkscake */
const char *knots_bspline =
"M 34.285713,35.219326 "
"C 44.026891,43.384723 28.084874,52.378758 20.714286,51.409804 "
"C 0.7404474,48.783999 -4.6171866,23.967448 1.904757,8.0764719 "
"C 13.570984,-20.348756 49.798303,-26.746504 74.999994,-13.352108 "
"C 111.98449,6.3047056 119.56591,55.259271 99.047626,89.505034 "
"C 71.699974,135.14925 9.6251774,143.91924 -33.571422,116.17172 "
"C -87.929934,81.254291 -97.88804,5.8941057 -62.857155,-46.209236 "
"C -20.430061,-109.31336 68.300385,-120.45954 129.2857,-78.114021 "
"C 201.15479,-28.21129 213.48932,73.938876 163.80954,143.79074 "
"C 106.45226,224.43749 -9.1490153,237.96076 -87.85713,180.93363 "
"C -177.29029,116.13577 -192.00272,-12.937817 -127.61907,-100.49494 "
"C -55.390344,-198.72081 87.170553,-214.62275 183.57141,-142.87593 "
"C 290.59464,-63.223369 307.68641,92.835839 228.57145,198.07645";
for (i = 0; i < argc; ++i)
{
@ -187,7 +184,11 @@ test_behave_main (int argc, char *argv[])
switch (path_type)
{
case PATH_POLY:
p_behave = clutter_behaviour_path_new (alpha, knots_poly, 5);
{
ClutterPath *path = clutter_path_new ();
clutter_path_set_description (path, knots_poly);
p_behave = clutter_behaviour_path_new (alpha, path);
}
break;
case PATH_ELLIPSE:
p_behave =
@ -204,15 +205,11 @@ test_behave_main (int argc, char *argv[])
break;
case PATH_BSPLINE:
origin.x = 0;
origin.y = RADIUS;
p_behave =
clutter_behaviour_bspline_new (alpha, knots_bspline,
sizeof (knots_bspline)/sizeof(ClutterKnot));
clutter_behaviour_bspline_set_origin (
CLUTTER_BEHAVIOUR_BSPLINE (p_behave),
&origin);
{
ClutterPath *path = clutter_path_new ();
clutter_path_set_description (path, knots_bspline);
p_behave = clutter_behaviour_path_new (alpha, path);
}
break;
}

View File

@ -40,7 +40,7 @@ static const gchar *test_behaviour =
" {"
" \"id\" : \"path-behaviour\","
" \"type\" : \"ClutterBehaviourPath\","
" \"knots\" : [ [ 50, 50 ], { \"x\" : 100, \"y\" : 100 } ],"
" \"path\" : \"M 50 50 L 100 100\","
" \"alpha\" : {"
" \"timeline\" : \"main-timeline\","
" \"function\" : \"ramp\""

View File

@ -177,6 +177,7 @@ test_threads_main (int argc, char *argv[])
ClutterColor rect_color = { 0xee, 0x55, 0x55, 0x99 };
ClutterColor progress_color = { 0x55, 0xee, 0x55, 0xbb };
ClutterBehaviour *r_behaviour, *p_behaviour;
ClutterAlpha *alpha;
const ClutterKnot knots[] = {
{ 75, 150 },
{ 400, 150 }
@ -220,9 +221,9 @@ test_threads_main (int argc, char *argv[])
0.0, 360.0);
clutter_behaviour_apply (r_behaviour, rect);
p_behaviour = clutter_behaviour_path_new (clutter_alpha_new_full (timeline,
clutter_ramp_inc_func,
NULL, NULL),
alpha = clutter_alpha_new_full (timeline, clutter_ramp_inc_func,
NULL, NULL);
p_behaviour = clutter_behaviour_path_new_with_knots (alpha,
knots,
G_N_ELEMENTS (knots));
clutter_behaviour_apply (p_behaviour, rect);