mirror of
https://github.com/brl/mutter.git
synced 2024-11-23 08:30:42 -05:00
ff92cc9766
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.
611 lines
14 KiB
C
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);
|
|
}
|
|
|