mutter/tests/conform/test-path.c
Neil Roberts ff92cc9766 Convert clutter_path_get_position to take a gdouble instead of an alpha value
The gdouble value represents an interval along the path from 0.0 to
1.0. This makes more sense than using an alpha value because paths are
not directly related to ClutterAlphas and the rest of the Clutter API
tends to expose gdouble arguments.
2008-12-16 15:39:53 +00:00

611 lines
14 KiB
C

#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],
&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);
}