From a24b9a32e56b76ab04727bb1f9e29ec3cca8dd11 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 22 Jan 2009 13:07:33 +0000 Subject: [PATCH 1/8] Store when the anchor point is set from a gravity This makes it so when the anchor point is set using a gravity enum then the anchor point moves when the actor changes size. A new property is added for the anchor point gravity. If the anchor point is set from gravity then the position in units can also be retreived with the regular API. A new union type is used to store the anchor point with helper accessor functions. The hope is these can be reused for the scale and rotation center points. --- clutter/clutter-actor.c | 429 +++++++++++++++++++++++++++++++--------- clutter/clutter-actor.h | 1 + 2 files changed, 333 insertions(+), 97 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 81e7757c2..999c7f686 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -183,10 +183,33 @@ #include "cogl/cogl.h" typedef struct _ShaderData ShaderData; +typedef struct _AnchorCoord AnchorCoord; #define CLUTTER_ACTOR_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_ACTOR, ClutterActorPrivate)) +/* Internal helper struct to represent a point that can be stored in + either direct pixel coordinates or as a fraction of the actor's + size. It is used for the anchor point, scale center and rotation + centers. */ +struct _AnchorCoord +{ + gboolean is_fractional; + + union + { + /* Used when is_fractional == TRUE */ + struct + { + gdouble x; + gdouble y; + } fraction; + + /* Use when is_fractional == FALSE */ + ClutterVertex units; + } v; +}; + struct _ClutterActorPrivate { /* fixed_x, fixed_y, and the allocation box are all in parent @@ -244,8 +267,7 @@ struct _ClutterActorPrivate ClutterUnit rzy; /* Anchor point coordinates */ - ClutterUnit anchor_x; - ClutterUnit anchor_y; + AnchorCoord anchor; /* depth */ ClutterUnit z; @@ -329,6 +351,7 @@ enum PROP_ANCHOR_X, PROP_ANCHOR_Y, + PROP_ANCHOR_GRAVITY, PROP_SHOW_ON_SET_PARENT }; @@ -395,6 +418,38 @@ static void clutter_actor_set_natural_height_set (ClutterActor *self, static void clutter_actor_set_request_mode (ClutterActor *self, ClutterRequestMode mode); +/* Helper routines for managing anchor coords */ +static void clutter_anchor_coord_get_units (ClutterActor *self, + const AnchorCoord *coord, + ClutterUnit *x, + ClutterUnit *y, + ClutterUnit *z); +static void clutter_anchor_coord_set_units (AnchorCoord *coord, + ClutterUnit x, + ClutterUnit y, + ClutterUnit z); +static ClutterGravity clutter_anchor_coord_get_gravity (AnchorCoord *coord); +static void clutter_anchor_coord_set_gravity (AnchorCoord *coord, + ClutterGravity gravity); +static gboolean clutter_anchor_coord_is_zero (const AnchorCoord *coord); + +/* Helper macro which translates by the anchor coord, applies the + given transformation and then translates back */ +#define TRANSFORM_ABOUT_ANCHOR_COORD(actor, coord, transform) \ + do \ + { \ + ClutterUnit __tx, __ty, __tz; \ + clutter_anchor_coord_get_units ((actor), (coord), \ + &__tx, &__ty, &__tz); \ + cogl_translate (CLUTTER_UNITS_TO_FLOAT (__tx), \ + CLUTTER_UNITS_TO_FLOAT (__ty), \ + CLUTTER_UNITS_TO_FLOAT (__tz)); \ + (transform); \ + cogl_translate (-CLUTTER_UNITS_TO_FLOAT (__tx), \ + -CLUTTER_UNITS_TO_FLOAT (__ty), \ + -CLUTTER_UNITS_TO_FLOAT (__tz)); \ + } while (0) + G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ClutterActor, clutter_actor, G_TYPE_INITIALLY_UNOWNED, @@ -1410,10 +1465,12 @@ _clutter_actor_apply_modelview_transform (ClutterActor *self) CLUTTER_UNITS_TO_FLOAT (-(priv->z + priv->rxz))); } - if (!is_stage && (priv->anchor_x || priv->anchor_y)) - cogl_translate (CLUTTER_UNITS_TO_FLOAT (-priv->anchor_x), - CLUTTER_UNITS_TO_FLOAT (-priv->anchor_y), - 0); + if (!is_stage && !clutter_anchor_coord_is_zero (&priv->anchor)) + { + ClutterUnit x, y, z; + clutter_anchor_coord_get_units (self, &priv->anchor, &x, &y, &z); + cogl_translate (-x, -y, -z); + } if (priv->z) cogl_translate (0, 0, priv->z); @@ -1776,19 +1833,31 @@ clutter_actor_set_property (GObject *object, case PROP_ANCHOR_X: { int anchor_x = g_value_get_int (value); + ClutterUnit anchor_y; + + clutter_anchor_coord_get_units (actor, &priv->anchor, + NULL, &anchor_y, NULL); clutter_actor_set_anchor_pointu (actor, CLUTTER_UNITS_FROM_DEVICE (anchor_x), - priv->anchor_y); + anchor_y); } break; case PROP_ANCHOR_Y: { - int anchor_y = g_value_get_int (value); + ClutterUnit anchor_x; + int anchor_y = g_value_get_int (value); + + clutter_anchor_coord_get_units (actor, &priv->anchor, + &anchor_x, NULL, NULL); clutter_actor_set_anchor_pointu (actor, - priv->anchor_x, + anchor_x, CLUTTER_UNITS_FROM_DEVICE (anchor_y)); } break; + case PROP_ANCHOR_GRAVITY: + clutter_actor_set_anchor_point_from_gravity (actor, + g_value_get_enum (value)); + break; case PROP_SHOW_ON_SET_PARENT: priv->show_on_set_parent = g_value_get_boolean (value); break; @@ -1940,10 +2009,23 @@ clutter_actor_get_property (GObject *object, } break; case PROP_ANCHOR_X: - g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE (priv->anchor_x)); + { + ClutterUnit anchor_x; + clutter_anchor_coord_get_units (actor, &priv->anchor, + &anchor_x, NULL, NULL); + g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE (anchor_x)); + } break; case PROP_ANCHOR_Y: - g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE (priv->anchor_y)); + { + ClutterUnit anchor_y; + clutter_anchor_coord_get_units (actor, &priv->anchor, + NULL, &anchor_y, NULL); + g_value_set_int (value, CLUTTER_UNITS_TO_DEVICE (anchor_y)); + } + break; + case PROP_ANCHOR_GRAVITY: + g_value_set_enum (value, clutter_actor_get_anchor_point_gravity (actor)); break; case PROP_SHOW_ON_SET_PARENT: g_value_set_boolean (value, priv->show_on_set_parent); @@ -2010,6 +2092,7 @@ static void clutter_actor_class_init (ClutterActorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; object_class->set_property = clutter_actor_set_property; object_class->get_property = clutter_actor_get_property; @@ -2601,6 +2684,22 @@ clutter_actor_class_init (ClutterActorClass *klass) 0, CLUTTER_PARAM_READWRITE)); + /** + * ClutterActor:anchor-gravity: + * + * The anchor point expressed as a #ClutterGravity. + * + * Since: 1.0 + */ + pspec = g_param_spec_enum ("anchor-gravity", + "Anchor-Gravity", + "The anchor point as a ClutterGravity", + CLUTTER_TYPE_GRAVITY, + CLUTTER_GRAVITY_NONE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (object_class, + PROP_ANCHOR_GRAVITY, pspec); + /** * ClutterActor:show-on-set-parent: * @@ -6241,24 +6340,11 @@ clutter_actor_move_anchor_point (ClutterActor *self, gint anchor_x, gint anchor_y) { - ClutterActorPrivate *priv; - ClutterUnit ax = CLUTTER_UNITS_FROM_DEVICE (anchor_x); - ClutterUnit ay = CLUTTER_UNITS_FROM_DEVICE (anchor_y); - ClutterUnit dx; - ClutterUnit dy; - g_return_if_fail (CLUTTER_IS_ACTOR (self)); - priv = self->priv; - - dx = ax - priv->anchor_x; - dy = ay - priv->anchor_y; - - priv->anchor_x = ax; - priv->anchor_y = ay; - - if (priv->position_set) - clutter_actor_move_byu (self, dx, dy); + clutter_actor_move_anchor_pointu (self, + CLUTTER_UNITS_FROM_DEVICE (anchor_x), + CLUTTER_UNITS_FROM_DEVICE (anchor_y)); } /** @@ -6277,16 +6363,19 @@ clutter_actor_get_anchor_point (ClutterActor *self, gint *anchor_y) { ClutterActorPrivate *priv; + ClutterUnit xu, yu; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; + clutter_anchor_coord_get_units (self, &priv->anchor, &xu, &yu, NULL); + if (anchor_x) - *anchor_x = CLUTTER_UNITS_TO_DEVICE (priv->anchor_x); + *anchor_x = CLUTTER_UNITS_TO_DEVICE (xu); if (anchor_y) - *anchor_y = CLUTTER_UNITS_TO_DEVICE (priv->anchor_y); + *anchor_y = CLUTTER_UNITS_TO_DEVICE (yu); } /** @@ -6309,6 +6398,7 @@ clutter_actor_set_anchor_pointu (ClutterActor *self, { ClutterActorPrivate *priv; gboolean changed = FALSE; + ClutterUnit old_anchor_x, old_anchor_y; g_return_if_fail (CLUTTER_IS_ACTOR (self)); @@ -6316,26 +6406,44 @@ clutter_actor_set_anchor_pointu (ClutterActor *self, g_object_freeze_notify (G_OBJECT (self)); - if (priv->anchor_x != anchor_x) + clutter_anchor_coord_get_units (self, &priv->anchor, + &old_anchor_x, &old_anchor_y, NULL); + + if (priv->anchor.is_fractional) + g_object_notify (G_OBJECT (self), "anchor-gravity"); + + if (old_anchor_x != anchor_x) { - priv->anchor_x = anchor_x; g_object_notify (G_OBJECT (self), "anchor-x"); changed = TRUE; } - if (priv->anchor_y != anchor_y) + if (old_anchor_y != anchor_y) { - priv->anchor_y = anchor_y; g_object_notify (G_OBJECT (self), "anchor-y"); changed = TRUE; } + clutter_anchor_coord_set_units (&priv->anchor, anchor_x, anchor_y, 0); + g_object_thaw_notify (G_OBJECT (self)); if (changed && CLUTTER_ACTOR_IS_VISIBLE (self)) clutter_actor_queue_redraw (self); } +ClutterGravity +clutter_actor_get_anchor_point_gravity (ClutterActor *self) +{ + ClutterActorPrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE); + + priv = self->priv; + + return clutter_anchor_coord_get_gravity (&priv->anchor); +} + /** * clutter_actor_move_anchor_pointu: * @self: a #ClutterActor @@ -6353,21 +6461,25 @@ clutter_actor_move_anchor_pointu (ClutterActor *self, ClutterUnit anchor_y) { ClutterActorPrivate *priv; - ClutterUnit dx; - ClutterUnit dy; + ClutterUnit old_anchor_x, old_anchor_y; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; - dx = anchor_x - priv->anchor_x; - dy = anchor_y - priv->anchor_y; + clutter_anchor_coord_get_units (self, &priv->anchor, + &old_anchor_x, &old_anchor_y, NULL); - priv->anchor_x = anchor_x; - priv->anchor_y = anchor_y; + g_object_freeze_notify (G_OBJECT (self)); + + clutter_actor_set_anchor_point (self, anchor_x, anchor_y); if (priv->position_set) - clutter_actor_move_byu (self, dx, dy); + clutter_actor_move_byu (self, + anchor_x - old_anchor_x, + anchor_y - old_anchor_y); + + g_object_thaw_notify (G_OBJECT (self)); } /** @@ -6391,11 +6503,8 @@ clutter_actor_get_anchor_pointu (ClutterActor *self, priv = self->priv; - if (anchor_x) - *anchor_x = priv->anchor_x; - - if (anchor_y) - *anchor_y = priv->anchor_y; + clutter_anchor_coord_get_units (self, &priv->anchor, + anchor_x, anchor_y, NULL); } /** @@ -6419,25 +6528,27 @@ void clutter_actor_move_anchor_point_from_gravity (ClutterActor *self, ClutterGravity gravity) { - ClutterUnit ax, ay, dx, dy; + ClutterUnit old_anchor_x, old_anchor_y, new_anchor_x, new_anchor_y; ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; - ax = priv->anchor_x; - ay = priv->anchor_y; + g_object_freeze_notify (G_OBJECT (self)); + clutter_anchor_coord_get_units (self, &priv->anchor, + &old_anchor_x, &old_anchor_y, NULL); clutter_actor_set_anchor_point_from_gravity (self, gravity); - - dx = priv->anchor_x - ax; - dy = priv->anchor_y - ay; + clutter_anchor_coord_get_units (self, &priv->anchor, + &new_anchor_x, &new_anchor_y, NULL); if (priv->position_set) - { - clutter_actor_move_byu (self, dx, dy); - } + clutter_actor_move_byu (self, + new_anchor_x - old_anchor_x, + new_anchor_y - old_anchor_y); + + g_object_thaw_notify (G_OBJECT (self)); } /** @@ -6460,53 +6571,18 @@ void clutter_actor_set_anchor_point_from_gravity (ClutterActor *self, ClutterGravity gravity) { - ClutterActorPrivate *priv; - ClutterUnit w, h, x, y; - g_return_if_fail (CLUTTER_IS_ACTOR (self)); - priv = self->priv; - - x = 0; - y = 0; - clutter_actor_get_sizeu (self, &w, &h); - - switch (gravity) + if (gravity == CLUTTER_GRAVITY_NONE) + clutter_actor_set_anchor_point (self, 0, 0); + else { - case CLUTTER_GRAVITY_NORTH: - x = w / 2; - break; - case CLUTTER_GRAVITY_SOUTH: - x = w / 2; - y = h; - break; - case CLUTTER_GRAVITY_EAST: - x = w; - y = h / 2; - break; - case CLUTTER_GRAVITY_NORTH_EAST: - x = w; - break; - case CLUTTER_GRAVITY_SOUTH_EAST: - x = w; - y = h; - break; - case CLUTTER_GRAVITY_SOUTH_WEST: - y = h; - break; - case CLUTTER_GRAVITY_WEST: - y = h / 2; - break; - case CLUTTER_GRAVITY_CENTER: - x = w / 2; - y = h / 2; - break; - case CLUTTER_GRAVITY_NONE: - case CLUTTER_GRAVITY_NORTH_WEST: - break; - } + clutter_anchor_coord_set_gravity (&self->priv->anchor, gravity); - clutter_actor_set_anchor_pointu (self, x, y); + g_object_notify (G_OBJECT (self), "anchor-gravity"); + g_object_notify (G_OBJECT (self), "anchor-x"); + g_object_notify (G_OBJECT (self), "anchor-y"); + } } typedef enum @@ -7777,3 +7853,162 @@ clutter_actor_create_pango_context (ClutterActor *self) return retval; } + +static void +clutter_anchor_coord_get_units (ClutterActor *self, + const AnchorCoord *coord, + ClutterUnit *x, + ClutterUnit *y, + ClutterUnit *z) +{ + if (G_UNLIKELY (coord->is_fractional)) + { + ClutterUnit actor_width, actor_height; + + clutter_actor_get_sizeu (self, &actor_width, &actor_height); + + if (x) + *x = actor_width * coord->v.fraction.x; + if (y) + *y = actor_height * coord->v.fraction.y; + if (z) + *z = 0; + } + else + { + if (x) + *x = coord->v.units.x; + if (y) + *y = coord->v.units.y; + if (z) + *z = coord->v.units.z; + } +} + +static void +clutter_anchor_coord_set_units (AnchorCoord *coord, + ClutterUnit x, + ClutterUnit y, + ClutterUnit z) +{ + coord->is_fractional = FALSE; + coord->v.units.x = x; + coord->v.units.y = y; + coord->v.units.z = z; +} + +static ClutterGravity +clutter_anchor_coord_get_gravity (AnchorCoord *coord) +{ + if (coord->is_fractional) + { + if (coord->v.fraction.x == 0.0) + { + if (coord->v.fraction.y == 0.0) + return CLUTTER_GRAVITY_NORTH_WEST; + else if (coord->v.fraction.y == 0.5) + return CLUTTER_GRAVITY_WEST; + else if (coord->v.fraction.y == 1.0) + return CLUTTER_GRAVITY_SOUTH_WEST; + else + return CLUTTER_GRAVITY_NONE; + } + else if (coord->v.fraction.x == 0.5) + { + if (coord->v.fraction.y == 0.0) + return CLUTTER_GRAVITY_NORTH; + else if (coord->v.fraction.y == 0.5) + return CLUTTER_GRAVITY_CENTER; + else if (coord->v.fraction.y == 1.0) + return CLUTTER_GRAVITY_SOUTH; + else + return CLUTTER_GRAVITY_NONE; + } + else if (coord->v.fraction.x == 1.0) + { + if (coord->v.fraction.y == 0.0) + return CLUTTER_GRAVITY_NORTH_EAST; + else if (coord->v.fraction.y == 0.5) + return CLUTTER_GRAVITY_EAST; + else if (coord->v.fraction.y == 1.0) + return CLUTTER_GRAVITY_SOUTH_EAST; + else + return CLUTTER_GRAVITY_NONE; + } + else + return CLUTTER_GRAVITY_NONE; + } + else + return CLUTTER_GRAVITY_NONE; +} + +static void +clutter_anchor_coord_set_gravity (AnchorCoord *coord, + ClutterGravity gravity) +{ + switch (gravity) + { + case CLUTTER_GRAVITY_NORTH: + coord->v.fraction.x = 0.5; + coord->v.fraction.y = 0.0; + break; + + case CLUTTER_GRAVITY_NORTH_EAST: + coord->v.fraction.x = 1.0; + coord->v.fraction.y = 0.0; + break; + + case CLUTTER_GRAVITY_EAST: + coord->v.fraction.x = 1.0; + coord->v.fraction.y = 0.5; + break; + + case CLUTTER_GRAVITY_SOUTH_EAST: + coord->v.fraction.x = 1.0; + coord->v.fraction.y = 1.0; + break; + + case CLUTTER_GRAVITY_SOUTH: + coord->v.fraction.x = 0.5; + coord->v.fraction.y = 1.0; + break; + + case CLUTTER_GRAVITY_SOUTH_WEST: + coord->v.fraction.x = 0.0; + coord->v.fraction.y = 1.0; + break; + + case CLUTTER_GRAVITY_WEST: + coord->v.fraction.x = 0.0; + coord->v.fraction.y = 0.5; + break; + + case CLUTTER_GRAVITY_NORTH_WEST: + coord->v.fraction.x = 0.0; + coord->v.fraction.y = 0.0; + break; + + case CLUTTER_GRAVITY_CENTER: + coord->v.fraction.x = 0.5; + coord->v.fraction.y = 0.5; + break; + + default: + coord->v.fraction.x = 0.0; + coord->v.fraction.y = 0.0; + break; + } + + coord->is_fractional = TRUE; +} + +static gboolean +clutter_anchor_coord_is_zero (const AnchorCoord *coord) +{ + if (coord->is_fractional) + return coord->v.fraction.x == 0.0 && coord->v.fraction.y == 0.0; + else + return (coord->v.units.x == 0.0 + && coord->v.units.y == 0.0 + && coord->v.units.z == 0.0); +} diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index f6d889982..d9748fcb8 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -526,6 +526,7 @@ void clutter_actor_move_anchor_point (ClutterActor *self, void clutter_actor_get_anchor_point (ClutterActor *self, gint *anchor_x, gint *anchor_y); +ClutterGravity clutter_actor_get_anchor_point_gravity (ClutterActor *self); void clutter_actor_set_anchor_pointu (ClutterActor *self, ClutterUnit anchor_x, ClutterUnit anchor_y); From 7818eb704d6fdf7714be010d2b7be7e34420bb63 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 22 Jan 2009 13:14:02 +0000 Subject: [PATCH 2/8] Add a conformance test for the anchor point This verifies that the anchor point can be set from a gravity and then it moves when the anchor point changes size. --- tests/conform/Makefile.am | 1 + tests/conform/test-anchors.c | 253 ++++++++++++++++++++++++++++++ tests/conform/test-conform-main.c | 2 + 3 files changed, 256 insertions(+) create mode 100644 tests/conform/test-anchors.c diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 46eea56fe..45165d6b7 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -25,6 +25,7 @@ test_conformance_SOURCES = \ test-binding-pool.c \ test-clutter-text.c \ test-text-cache.c \ + test-anchors.c \ $(NULL) # For convenience, this provides a way to easily run individual unit tests: diff --git a/tests/conform/test-anchors.c b/tests/conform/test-anchors.c new file mode 100644 index 000000000..6029c9b7b --- /dev/null +++ b/tests/conform/test-anchors.c @@ -0,0 +1,253 @@ +#include +#include + +#include "test-conform-common.h" + +#define NOTIFY_ANCHOR_X 1 +#define NOTIFY_ANCHOR_Y 2 +#define NOTIFY_ANCHOR_GRAVITY 4 + +#define RECT_WIDTH 100 +#define RECT_HEIGHT 80 + +/* Allow the transformed position by off by a certain number of + pixels */ +#define POSITION_TOLERANCE 2 + +typedef struct _TestState +{ + gulong notifications; + ClutterActor *rect; +} TestState; + +static const struct +{ + ClutterGravity gravity; + gint x_pos, y_pos; +} gravities[] = + { + { CLUTTER_GRAVITY_NORTH, RECT_WIDTH / 2, 0 }, + { CLUTTER_GRAVITY_NORTH_EAST, RECT_WIDTH, 0 }, + { CLUTTER_GRAVITY_EAST, RECT_WIDTH, RECT_HEIGHT / 2 }, + { CLUTTER_GRAVITY_SOUTH_EAST, RECT_WIDTH, RECT_HEIGHT }, + { CLUTTER_GRAVITY_SOUTH, RECT_WIDTH / 2, RECT_HEIGHT }, + { CLUTTER_GRAVITY_SOUTH_WEST, 0, RECT_HEIGHT }, + { CLUTTER_GRAVITY_WEST, 0, RECT_HEIGHT / 2 }, + { CLUTTER_GRAVITY_NORTH_WEST, 0, 0 }, + { CLUTTER_GRAVITY_CENTER, RECT_WIDTH / 2, RECT_HEIGHT / 2 } + }; + +#define make_notify_cb(func, flag) \ + static void \ + func (GObject *object, GParamSpec *pspec, TestState *state) \ + { \ + g_assert ((state->notifications & (flag)) == 0); \ + state->notifications |= (flag); \ + } + +make_notify_cb (anchor_x_cb, NOTIFY_ANCHOR_X); +make_notify_cb (anchor_y_cb, NOTIFY_ANCHOR_Y); +make_notify_cb (anchor_gravity_cb, NOTIFY_ANCHOR_GRAVITY); + +#define assert_notifications(flags) \ + do \ + { \ + g_assert (state->notifications == (flags)); \ + state->notifications = 0; \ + } while (0) + +/* Helper macro to assert the transformed position. This needs to be a + macro so that the assertion failure will report the right line + number */ +#define assert_position(state, x_, y_) \ + do \ + { \ + ClutterVertex verts[4]; \ + clutter_actor_get_abs_allocation_vertices ((state)->rect, verts); \ + check_position ((state), (x_), (y_), verts); \ + g_assert (abs ((x_) - CLUTTER_UNITS_TO_DEVICE (verts[0].x)) \ + <= POSITION_TOLERANCE); \ + g_assert (abs ((y_) - CLUTTER_UNITS_TO_DEVICE (verts[0].y)) \ + <= POSITION_TOLERANCE); \ + } while (0) + +static void +check_position (TestState *state, + gint pos_x, gint pos_y, + const ClutterVertex *verts) +{ + if (g_test_verbose ()) + g_print ("checking that (%i,%i) \xe2\x89\x88 (%i,%i): %s\n", + pos_x, pos_y, + CLUTTER_UNITS_TO_DEVICE (verts[0].x), + CLUTTER_UNITS_TO_DEVICE (verts[0].y), + abs (pos_x - verts[0].x) <= POSITION_TOLERANCE + && abs (pos_y - verts[0].y) <= POSITION_TOLERANCE + ? "yes" : "NO"); +} + +static gboolean +idle_cb (gpointer data) +{ + TestState *state = data; + ClutterActor *rect = state->rect; + gint anchor_x, anchor_y; + ClutterGravity anchor_gravity; + int i; + + /* Assert the default settings */ + g_assert (clutter_actor_get_x (rect) == 100); + g_assert (clutter_actor_get_y (rect) == 200); + g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); + g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); + g_object_get (rect, + "anchor-x", &anchor_x, "anchor-y", &anchor_y, + "anchor-gravity", &anchor_gravity, + NULL); + g_assert (anchor_x == 0); + g_assert (anchor_y == 0); + g_assert (anchor_gravity == CLUTTER_GRAVITY_NONE); + + /* Change the anchor point */ + clutter_actor_set_anchor_point (rect, 20, 30); + g_object_get (rect, + "anchor-x", &anchor_x, "anchor-y", &anchor_y, + "anchor-gravity", &anchor_gravity, + NULL); + g_assert (anchor_x == 20); + g_assert (anchor_y == 30); + g_assert (anchor_gravity == CLUTTER_GRAVITY_NONE); + assert_position (state, 80, 170); + assert_notifications (NOTIFY_ANCHOR_X | NOTIFY_ANCHOR_Y); + + /* Move the anchor point */ + clutter_actor_move_anchor_point (rect, 40, 50); + g_object_get (rect, + "anchor-x", &anchor_x, "anchor-y", &anchor_y, + "anchor-gravity", &anchor_gravity, + NULL); + g_assert (anchor_x == 40); + g_assert (anchor_y == 50); + g_assert (anchor_gravity == CLUTTER_GRAVITY_NONE); + assert_position (state, 80, 170); + assert_notifications (NOTIFY_ANCHOR_X | NOTIFY_ANCHOR_Y); + + /* Put the actor back to its default position */ + clutter_actor_set_position (rect, 100, 200); + + /* Change the anchor point with each of the gravities */ + for (i = 0; i < G_N_ELEMENTS (gravities); i++) + { + if (g_test_verbose ()) + { + GEnumClass *gravity_class = g_type_class_ref (CLUTTER_TYPE_GRAVITY); + GEnumValue *value = g_enum_get_value (gravity_class, + gravities[i].gravity); + g_print ("Setting gravity to %s\n", + value ? value->value_name : "?"); + g_type_class_unref (gravity_class); + } + + g_object_set (rect, "anchor-gravity", gravities[i].gravity, NULL); + + g_object_get (rect, + "anchor-x", &anchor_x, "anchor-y", &anchor_y, + "anchor-gravity", &anchor_gravity, + NULL); + g_assert (anchor_x == gravities[i].x_pos); + g_assert (anchor_y == gravities[i].y_pos); + g_assert (anchor_gravity == gravities[i].gravity); + assert_position (state, + 100 - gravities[i].x_pos, + 200 - gravities[i].y_pos); + + assert_notifications (NOTIFY_ANCHOR_X | NOTIFY_ANCHOR_Y + | NOTIFY_ANCHOR_GRAVITY); + } + + /* Verify that the anchor point moves if the actor changes size when + it is set from the gravity */ + clutter_actor_set_size (rect, RECT_WIDTH * 2, RECT_HEIGHT * 2); + g_object_get (rect, + "anchor-x", &anchor_x, "anchor-y", &anchor_y, + "anchor-gravity", &anchor_gravity, + NULL); + g_assert (anchor_x == RECT_WIDTH); + g_assert (anchor_y == RECT_HEIGHT); + g_assert (anchor_gravity == CLUTTER_GRAVITY_CENTER); + assert_position (state, 100 - RECT_WIDTH, 200 - RECT_HEIGHT); + assert_notifications (0); + clutter_actor_set_size (rect, RECT_WIDTH, RECT_HEIGHT); + + /* Change the anchor point using units again to assert that the + gravity property changes */ + clutter_actor_set_anchor_point (rect, 20, 30); + g_object_get (rect, + "anchor-x", &anchor_x, "anchor-y", &anchor_y, + "anchor-gravity", &anchor_gravity, + NULL); + g_assert (anchor_x == 20); + g_assert (anchor_y == 30); + g_assert (anchor_gravity == CLUTTER_GRAVITY_NONE); + assert_position (state, 80, 170); + assert_notifications (NOTIFY_ANCHOR_X | NOTIFY_ANCHOR_Y + | NOTIFY_ANCHOR_GRAVITY); + + /* Verify that the anchor point doesn't move if the actor changes + size when it is set from units */ + clutter_actor_set_size (rect, RECT_WIDTH * 2, RECT_HEIGHT * 2); + g_object_get (rect, + "anchor-x", &anchor_x, "anchor-y", &anchor_y, + "anchor-gravity", &anchor_gravity, + NULL); + g_assert (anchor_x == 20); + g_assert (anchor_y == 30); + g_assert (anchor_gravity == CLUTTER_GRAVITY_NONE); + assert_position (state, 80, 170); + assert_notifications (0); + clutter_actor_set_size (rect, RECT_WIDTH, RECT_HEIGHT); + + clutter_main_quit (); + + return FALSE; +} + +void +test_anchors (TestConformSimpleFixture *fixture, + gconstpointer data) +{ + TestState state; + ClutterActor *stage; + + stage = clutter_stage_get_default (); + + state.rect = clutter_rectangle_new (); + clutter_container_add (CLUTTER_CONTAINER (stage), state.rect, NULL); + clutter_actor_set_position (state.rect, 100, 200); + clutter_actor_set_size (state.rect, RECT_WIDTH, RECT_HEIGHT); + + /* Record notifications on the actor anchor properties */ + state.notifications = 0; + g_signal_connect (state.rect, "notify::anchor-x", + G_CALLBACK (anchor_x_cb), &state); + g_signal_connect (state.rect, "notify::anchor-y", + G_CALLBACK (anchor_y_cb), &state); + g_signal_connect (state.rect, "notify::anchor-gravity", + G_CALLBACK (anchor_gravity_cb), &state); + + /* Run the tests in a low priority idle function so that we can be + sure the stage is correctly setup */ + g_idle_add_full (G_PRIORITY_LOW, idle_cb, &state, NULL); + + clutter_actor_show (stage); + + clutter_main (); + + g_idle_remove_by_data (&state); + + clutter_actor_destroy (state.rect); + + if (g_test_verbose ()) + g_print ("OK\n"); +} + diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index f6e14d38f..58cbea789 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -108,5 +108,7 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/binding-pool", test_binding_pool); + TEST_CONFORM_SIMPLE ("/actor", test_anchors); + return g_test_run (); } From e10d255b83071014219a54e2f3d5c31444df340f Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 23 Jan 2009 11:11:24 +0000 Subject: [PATCH 3/8] Add a scale center property to ClutterActor This sets the center point from which the scaling will occur. This can be used insetad of the anchor point to avoid moving the actor. Like the anchor point, it can be specified as either a coordinate in units or a gravity enum. To set the center you can use two new variants of set_scale: clutter_actor_set_scale_full (ClutterActor *self, gdouble scale_x, gdouble scale_y, int center_x, int center_y); or clutter_actor_set_scale_with_gravity (ClutterActor *self, gdouble scale_x, gdouble scale_y, ClutterGravity gravity); The ClutterFixed variants of the set_scale functions have been removed and the scale value is now always stored as a double. --- clutter/clutter-actor.c | 279 +++++++++++++++++++++++------- clutter/clutter-actor.h | 27 ++- clutter/clutter-behaviour-scale.c | 4 +- 3 files changed, 245 insertions(+), 65 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 999c7f686..3bb885f4b 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -279,8 +279,10 @@ struct _ClutterActorPrivate gchar *name; guint32 id; /* Unique ID */ - ClutterFixed scale_x; - ClutterFixed scale_y; + gdouble scale_x; + gdouble scale_y; + + AnchorCoord scale_center; ShaderData *shader_data; @@ -341,6 +343,9 @@ enum PROP_SCALE_X, PROP_SCALE_Y, + PROP_SCALE_CENTER_X, + PROP_SCALE_CENTER_Y, + PROP_SCALE_GRAVITY, PROP_ROTATION_ANGLE_X, PROP_ROTATION_ANGLE_Y, @@ -1424,7 +1429,8 @@ _clutter_actor_apply_modelview_transform (ClutterActor *self) * entire object will move on the screen as a result of rotating it). */ if (priv->scale_x != 1.0 || priv->scale_y != 1.0) - cogl_scale (priv->scale_x, priv->scale_y); + TRANSFORM_ABOUT_ANCHOR_COORD (self, &priv->scale_center, + cogl_scale (priv->scale_x, priv->scale_y)); if (priv->rzang) { @@ -1726,16 +1732,48 @@ clutter_actor_set_property (GObject *object, clutter_actor_hide (actor); break; case PROP_SCALE_X: - clutter_actor_set_scalex - (actor, - CLUTTER_FLOAT_TO_FIXED (g_value_get_double (value)), - priv->scale_y); + clutter_actor_set_scale (actor, + g_value_get_double (value), + priv->scale_y); break; case PROP_SCALE_Y: - clutter_actor_set_scalex - (actor, - priv->scale_x, - CLUTTER_FLOAT_TO_FIXED (g_value_get_double (value))); + clutter_actor_set_scale (actor, + priv->scale_x, + g_value_get_double (value)); + break; + case PROP_SCALE_CENTER_X: + { + int center_x = g_value_get_int (value); + ClutterUnit center_y; + + clutter_anchor_coord_get_units (actor, &priv->scale_center, + NULL, ¢er_y, NULL); + clutter_actor_set_scale_fullu (actor, + priv->scale_x, + priv->scale_y, + CLUTTER_UNITS_FROM_DEVICE (center_x), + center_y); + } + break; + case PROP_SCALE_CENTER_Y: + { + ClutterUnit center_x; + gint center_y = g_value_get_int (value); + + clutter_anchor_coord_get_units (actor, &priv->scale_center, + ¢er_x, NULL, NULL); + clutter_actor_set_scale_fullu (actor, + priv->scale_x, + priv->scale_y, + center_x, + CLUTTER_UNITS_FROM_DEVICE (center_y)); + } + break; + case PROP_SCALE_GRAVITY: + clutter_actor_set_scale_with_gravity (actor, + priv->scale_x, + priv->scale_y, + g_value_get_enum (value)); break; case PROP_CLIP: { @@ -1961,10 +1999,27 @@ clutter_actor_get_property (GObject *object, } break; case PROP_SCALE_X: - g_value_set_double (value, CLUTTER_FIXED_TO_DOUBLE (priv->scale_x)); + g_value_set_double (value, priv->scale_x); break; case PROP_SCALE_Y: - g_value_set_double (value, CLUTTER_FIXED_TO_DOUBLE (priv->scale_y)); + g_value_set_double (value, priv->scale_y); + break; + case PROP_SCALE_CENTER_X: + { + gint center; + clutter_actor_get_scale_center (actor, ¢er, NULL); + g_value_set_int (value, center); + } + break; + case PROP_SCALE_CENTER_Y: + { + gint center; + clutter_actor_get_scale_center (actor, NULL, ¢er); + g_value_set_int (value, center); + } + break; + case PROP_SCALE_GRAVITY: + g_value_set_enum (value, clutter_actor_get_scale_gravity (actor)); break; case PROP_REACTIVE: g_value_set_boolean (value, clutter_actor_get_reactive (actor)); @@ -2553,6 +2608,51 @@ clutter_actor_class_init (ClutterActorClass *klass) G_MAXDOUBLE, 1.0, CLUTTER_PARAM_READWRITE)); + + /** + * ClutterActor:scale-center-x: + * + * The horizontal center point for scaling + * + * Since: 1.0 + */ + pspec = g_param_spec_int ("scale-center-x", + "Scale-Center-X", + "Horizontal scale center", + G_MININT, G_MAXINT, 0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_SCALE_CENTER_X, pspec); + + /** + * ClutterActor:scale-center-y: + * + * The vertical center point for scaling + * + * Since: 1.0 + */ + pspec = g_param_spec_int ("scale-center-y", + "Scale-Center-Y", + "Vertical scale center", + G_MININT, G_MAXINT, 0, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_SCALE_CENTER_Y, pspec); + + /** + * ClutterActor:scale-gravity: + * + * The center point for scaling expressed as a #ClutterGravity + * + * Since: 1.0 + */ + pspec = g_param_spec_enum ("scale-gravity", + "Scale-Gravity", + "The center of scaling", + CLUTTER_TYPE_GRAVITY, + CLUTTER_GRAVITY_NONE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (object_class, + PROP_SCALE_GRAVITY, pspec); + /** * ClutterActor:rotation-angle-x: * @@ -5018,20 +5118,20 @@ clutter_actor_get_yu (ClutterActor *self) } /** - * clutter_actor_set_scalex: + * clutter_actor_set_scale: * @self: A #ClutterActor - * @scale_x: #ClutterFixed factor to scale actor by horizontally. - * @scale_y: #ClutterFixed factor to scale actor by vertically. - * - * Fixed point version of clutter_actor_set_scale(). + * @scale_x: double factor to scale actor by horizontally. + * @scale_y: double factor to scale actor by vertically. * * Scales an actor with the given factors. The scaling is always * relative to the anchor point. + * + * Since: 0.2 */ void -clutter_actor_set_scalex (ClutterActor *self, - ClutterFixed scale_x, - ClutterFixed scale_y) +clutter_actor_set_scale (ClutterActor *self, + gdouble scale_x, + gdouble scale_y) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); @@ -5045,59 +5145,86 @@ clutter_actor_set_scalex (ClutterActor *self, g_object_notify (G_OBJECT (self), "scale-y"); g_object_thaw_notify (G_OBJECT (self)); - g_object_unref (self); if (CLUTTER_ACTOR_IS_VISIBLE (self)) clutter_actor_queue_redraw (self); + + g_object_unref (self); } -/** - * clutter_actor_set_scale: - * @self: A #ClutterActor - * @scale_x: double factor to scale actor by horizontally. - * @scale_y: double factor to scale actor by vertically. - * - * Scales an actor with the given factors. The scaling is always - * relative to the anchor point. - * - * Since: 0.2 - */ void -clutter_actor_set_scale (ClutterActor *self, - gdouble scale_x, - gdouble scale_y) +clutter_actor_set_scale_full (ClutterActor *self, + gdouble scale_x, + gdouble scale_y, + int center_x, + int center_y) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); - clutter_actor_set_scalex (self, - CLUTTER_FLOAT_TO_FIXED (scale_x), - CLUTTER_FLOAT_TO_FIXED (scale_y)); + clutter_actor_set_scale_fullu (self, scale_x, scale_y, + CLUTTER_UNITS_FROM_DEVICE (center_x), + CLUTTER_UNITS_FROM_DEVICE (center_y)); } -/** - * clutter_actor_get_scalex: - * @self: A #ClutterActor - * @scale_x: Location to store horizonal scale factor, or %NULL. - * @scale_y: Location to store vertical scale factor, or %NULL. - * - * Fixed point version of clutter_actor_get_scale(). - * - * Retrieves the scale factors of an actor. - * - * Since: 0.2 - */ void -clutter_actor_get_scalex (ClutterActor *self, - ClutterFixed *scale_x, - ClutterFixed *scale_y) +clutter_actor_set_scale_fullu (ClutterActor *self, + gdouble scale_x, + gdouble scale_y, + ClutterUnit center_x, + ClutterUnit center_y) { + ClutterActorPrivate *priv; + g_return_if_fail (CLUTTER_IS_ACTOR (self)); - if (scale_x) - *scale_x = self->priv->scale_x; + priv = self->priv; - if (scale_y) - *scale_y = self->priv->scale_y; + g_object_ref (self); + g_object_freeze_notify (G_OBJECT (self)); + + clutter_actor_set_scale (self, scale_x, scale_y); + + if (priv->scale_center.is_fractional) + g_object_notify (G_OBJECT (self), "scale-gravity"); + g_object_notify (G_OBJECT (self), "scale-center-x"); + g_object_notify (G_OBJECT (self), "scale-center-y"); + + clutter_anchor_coord_set_units (&priv->scale_center, center_x, center_y, 0); + + g_object_thaw_notify (G_OBJECT (self)); + g_object_unref (self); +} + +void +clutter_actor_set_scale_with_gravity (ClutterActor *self, + gdouble scale_x, + gdouble scale_y, + ClutterGravity gravity) +{ + ClutterActorPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + priv = self->priv; + + if (gravity == CLUTTER_GRAVITY_NONE) + clutter_actor_set_scale_full (self, scale_x, scale_y, 0, 0); + else + { + g_object_ref (self); + g_object_freeze_notify (G_OBJECT (self)); + + clutter_actor_set_scale (self, scale_x, scale_y); + + g_object_notify (G_OBJECT (self), "scale-gravity"); + g_object_notify (G_OBJECT (self), "scale-center-x"); + g_object_notify (G_OBJECT (self), "scale-center-y"); + + clutter_anchor_coord_set_gravity (&priv->scale_center, gravity); + + g_object_thaw_notify (G_OBJECT (self)); + g_object_unref (self); + } } /** @@ -5124,6 +5251,42 @@ clutter_actor_get_scale (ClutterActor *self, *scale_y = CLUTTER_FIXED_TO_FLOAT (self->priv->scale_y); } +void +clutter_actor_get_scale_center (ClutterActor *self, + gint *center_x, + gint *center_y) +{ + ClutterUnit xu, yu; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + clutter_actor_get_scale_centeru (self, &xu, &yu); + + if (center_x) + *center_x = CLUTTER_UNITS_TO_DEVICE (xu); + if (center_y) + *center_y = CLUTTER_UNITS_TO_DEVICE (yu); +} + +void +clutter_actor_get_scale_centeru (ClutterActor *self, + ClutterUnit *center_x, + ClutterUnit *center_y) +{ + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + clutter_anchor_coord_get_units (self, &self->priv->scale_center, + center_x, center_y, NULL); +} + +ClutterGravity +clutter_actor_get_scale_gravity (ClutterActor *self) +{ + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE); + + return clutter_anchor_coord_get_gravity (&self->priv->scale_center); +} + /** * clutter_actor_set_opacity: * @self: A #ClutterActor diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index d9748fcb8..8faa64cf4 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -474,18 +474,33 @@ void clutter_actor_set_depthu (ClutterActor ClutterUnit depth); ClutterUnit clutter_actor_get_depthu (ClutterActor *self); -void clutter_actor_set_scalex (ClutterActor *self, - ClutterFixed scale_x, - ClutterFixed scale_y); void clutter_actor_set_scale (ClutterActor *self, gdouble scale_x, gdouble scale_y); -void clutter_actor_get_scalex (ClutterActor *self, - ClutterFixed *scale_x, - ClutterFixed *scale_y); +void clutter_actor_set_scale_full (ClutterActor *self, + gdouble scale_x, + gdouble scale_y, + int center_x, + int center_y); +void clutter_actor_set_scale_fullu (ClutterActor *self, + gdouble scale_x, + gdouble scale_y, + ClutterUnit center_x, + ClutterUnit center_y); +void clutter_actor_set_scale_with_gravity (ClutterActor *self, + gdouble scale_x, + gdouble scale_y, + ClutterGravity gravity); void clutter_actor_get_scale (ClutterActor *self, gdouble *scale_x, gdouble *scale_y); +void clutter_actor_get_scale_center (ClutterActor *self, + gint *center_x, + gint *center_y); +void clutter_actor_get_scale_centeru (ClutterActor *self, + ClutterUnit *center_x, + ClutterUnit *center_y); +ClutterGravity clutter_actor_get_scale_gravity (ClutterActor *self); void clutter_actor_move_by (ClutterActor *self, gint dx, diff --git a/clutter/clutter-behaviour-scale.c b/clutter/clutter-behaviour-scale.c index 89b56958f..449f31bed 100644 --- a/clutter/clutter-behaviour-scale.c +++ b/clutter/clutter-behaviour-scale.c @@ -85,7 +85,9 @@ scale_frame_foreach (ClutterBehaviour *behaviour, { ScaleFrameClosure *closure = data; - clutter_actor_set_scalex (actor, closure->scale_x, closure->scale_y); + clutter_actor_set_scale (actor, + CLUTTER_FIXED_TO_DOUBLE (closure->scale_x), + CLUTTER_FIXED_TO_DOUBLE (closure->scale_y)); } static void From a8a986a1a2d4a32d8ce502f5cf8af6a98d8bd28e Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 23 Jan 2009 11:12:54 +0000 Subject: [PATCH 4/8] [test-anchors] Add tests for the scale center A separate set of tests for the scale center have been added that work in a similar way to the anchor point tests. --- tests/conform/test-anchors.c | 205 ++++++++++++++++++++++++++++++++--- 1 file changed, 188 insertions(+), 17 deletions(-) diff --git a/tests/conform/test-anchors.c b/tests/conform/test-anchors.c index 6029c9b7b..214ae88c3 100644 --- a/tests/conform/test-anchors.c +++ b/tests/conform/test-anchors.c @@ -6,6 +6,11 @@ #define NOTIFY_ANCHOR_X 1 #define NOTIFY_ANCHOR_Y 2 #define NOTIFY_ANCHOR_GRAVITY 4 +#define NOTIFY_SCALE_X 8 +#define NOTIFY_SCALE_Y 16 +#define NOTIFY_SCALE_CENTER_X 32 +#define NOTIFY_SCALE_CENTER_Y 64 +#define NOTIFY_SCALE_GRAVITY 128 #define RECT_WIDTH 100 #define RECT_HEIGHT 80 @@ -48,6 +53,11 @@ static const struct make_notify_cb (anchor_x_cb, NOTIFY_ANCHOR_X); make_notify_cb (anchor_y_cb, NOTIFY_ANCHOR_Y); make_notify_cb (anchor_gravity_cb, NOTIFY_ANCHOR_GRAVITY); +make_notify_cb (scale_x_cb, NOTIFY_SCALE_X); +make_notify_cb (scale_y_cb, NOTIFY_SCALE_Y); +make_notify_cb (scale_center_x_cb, NOTIFY_SCALE_CENTER_X); +make_notify_cb (scale_center_y_cb, NOTIFY_SCALE_CENTER_Y); +make_notify_cb (scale_gravity_cb, NOTIFY_SCALE_GRAVITY); #define assert_notifications(flags) \ do \ @@ -59,37 +69,47 @@ make_notify_cb (anchor_gravity_cb, NOTIFY_ANCHOR_GRAVITY); /* Helper macro to assert the transformed position. This needs to be a macro so that the assertion failure will report the right line number */ -#define assert_position(state, x_, y_) \ +#define assert_coords(state, x_1, y_1, x_2, y_2) \ do \ { \ ClutterVertex verts[4]; \ clutter_actor_get_abs_allocation_vertices ((state)->rect, verts); \ - check_position ((state), (x_), (y_), verts); \ - g_assert (abs ((x_) - CLUTTER_UNITS_TO_DEVICE (verts[0].x)) \ + check_coords ((state), (x_1), (y_1), (x_2), (y_2), verts); \ + g_assert (abs ((x_1) - CLUTTER_UNITS_TO_DEVICE (verts[0].x)) \ <= POSITION_TOLERANCE); \ - g_assert (abs ((y_) - CLUTTER_UNITS_TO_DEVICE (verts[0].y)) \ + g_assert (abs ((y_1) - CLUTTER_UNITS_TO_DEVICE (verts[0].y)) \ + <= POSITION_TOLERANCE); \ + g_assert (abs ((x_2) - CLUTTER_UNITS_TO_DEVICE (verts[3].x)) \ + <= POSITION_TOLERANCE); \ + g_assert (abs ((y_2) - CLUTTER_UNITS_TO_DEVICE (verts[3].y)) \ <= POSITION_TOLERANCE); \ } while (0) +#define assert_position(state, x, y) \ + assert_coords((state), (x), (y), (x) + RECT_WIDTH, (y) + RECT_HEIGHT) + static void -check_position (TestState *state, - gint pos_x, gint pos_y, - const ClutterVertex *verts) +check_coords (TestState *state, + gint x_1, gint y_1, gint x_2, gint y_2, + const ClutterVertex *verts) { if (g_test_verbose ()) - g_print ("checking that (%i,%i) \xe2\x89\x88 (%i,%i): %s\n", - pos_x, pos_y, + g_print ("checking that (%i,%i,%i,%i) \xe2\x89\x88 (%i,%i,%i,%i): %s\n", + x_1, y_1, x_2, y_2, CLUTTER_UNITS_TO_DEVICE (verts[0].x), CLUTTER_UNITS_TO_DEVICE (verts[0].y), - abs (pos_x - verts[0].x) <= POSITION_TOLERANCE - && abs (pos_y - verts[0].y) <= POSITION_TOLERANCE + CLUTTER_UNITS_TO_DEVICE (verts[3].x), + CLUTTER_UNITS_TO_DEVICE (verts[3].y), + abs (x_1 - verts[0].x) <= POSITION_TOLERANCE + && abs (y_1 - verts[0].y) <= POSITION_TOLERANCE + && abs (x_2 - verts[3].x) <= POSITION_TOLERANCE + && abs (y_2 - verts[3].y) <= POSITION_TOLERANCE ? "yes" : "NO"); } -static gboolean -idle_cb (gpointer data) +static void +test_anchor_point (TestState *state) { - TestState *state = data; ClutterActor *rect = state->rect; gint anchor_x, anchor_y; ClutterGravity anchor_gravity; @@ -175,7 +195,8 @@ idle_cb (gpointer data) g_assert (anchor_x == RECT_WIDTH); g_assert (anchor_y == RECT_HEIGHT); g_assert (anchor_gravity == CLUTTER_GRAVITY_CENTER); - assert_position (state, 100 - RECT_WIDTH, 200 - RECT_HEIGHT); + assert_coords (state, 100 - RECT_WIDTH, 200 - RECT_HEIGHT, + 100 + RECT_WIDTH, 200 + RECT_HEIGHT); assert_notifications (0); clutter_actor_set_size (rect, RECT_WIDTH, RECT_HEIGHT); @@ -203,10 +224,150 @@ idle_cb (gpointer data) g_assert (anchor_x == 20); g_assert (anchor_y == 30); g_assert (anchor_gravity == CLUTTER_GRAVITY_NONE); - assert_position (state, 80, 170); + assert_coords (state, 80, 170, 80 + RECT_WIDTH * 2, 170 + RECT_HEIGHT * 2); assert_notifications (0); clutter_actor_set_size (rect, RECT_WIDTH, RECT_HEIGHT); + /* Put the anchor back */ + clutter_actor_set_anchor_point_from_gravity (rect, CLUTTER_GRAVITY_NONE); + assert_notifications (NOTIFY_ANCHOR_X | NOTIFY_ANCHOR_Y); +} + +static void +test_scale_center (TestState *state) +{ + ClutterActor *rect = state->rect; + gdouble scale_x, scale_y; + gint center_x, center_y; + ClutterGravity gravity; + int i; + + /* Assert the default settings */ + g_assert (clutter_actor_get_x (rect) == 100); + g_assert (clutter_actor_get_y (rect) == 200); + g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); + g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); + g_object_get (rect, + "scale-center-x", ¢er_x, "scale-center-y", ¢er_y, + "scale-x", &scale_x, "scale-y", &scale_y, + "scale-gravity", &gravity, + NULL); + g_assert (center_x == 0); + g_assert (center_y == 0); + g_assert (scale_x == 1.0); + g_assert (scale_y == 1.0); + g_assert (gravity == CLUTTER_GRAVITY_NONE); + + /* Try changing the scale without affecting the center */ + g_object_set (rect, "scale-x", 2.0, "scale-y", 3.0, NULL); + g_assert (clutter_actor_get_x (rect) == 100); + g_assert (clutter_actor_get_y (rect) == 200); + g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); + g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); + g_object_get (rect, + "scale-center-x", ¢er_x, "scale-center-y", ¢er_y, + "scale-x", &scale_x, "scale-y", &scale_y, + "scale-gravity", &gravity, + NULL); + g_assert (center_x == 0); + g_assert (center_y == 0); + g_assert (scale_x == 2.0); + g_assert (scale_y == 3.0); + g_assert (gravity == CLUTTER_GRAVITY_NONE); + assert_notifications (NOTIFY_SCALE_X | NOTIFY_SCALE_Y); + assert_coords (state, 100, 200, 100 + RECT_WIDTH * 2, 200 + RECT_HEIGHT * 3); + + /* Change the scale and center */ + g_object_set (rect, "scale-x", 4.0, "scale-y", 2.0, + "scale-center-x", 10, "scale-center-y", 20, NULL); + g_assert (clutter_actor_get_x (rect) == 100); + g_assert (clutter_actor_get_y (rect) == 200); + g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); + g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); + g_object_get (rect, + "scale-center-x", ¢er_x, "scale-center-y", ¢er_y, + "scale-x", &scale_x, "scale-y", &scale_y, + "scale-gravity", &gravity, + NULL); + g_assert (center_x == 10); + g_assert (center_y == 20); + g_assert (scale_x == 4.0); + g_assert (scale_y == 2.0); + g_assert (gravity == CLUTTER_GRAVITY_NONE); + assert_notifications (NOTIFY_SCALE_X | NOTIFY_SCALE_Y + | NOTIFY_SCALE_CENTER_X | NOTIFY_SCALE_CENTER_Y); + assert_coords (state, 100 + 10 - 10 * 4, 200 + 20 - 20 * 2, + 100 + 10 + (RECT_WIDTH - 10) * 4, + 200 + 20 + (RECT_HEIGHT - 20) * 2); + + /* Change the anchor point with each of the gravities */ + for (i = 0; i < G_N_ELEMENTS (gravities); i++) + { + if (g_test_verbose ()) + { + GEnumClass *gravity_class = g_type_class_ref (CLUTTER_TYPE_GRAVITY); + GEnumValue *value = g_enum_get_value (gravity_class, + gravities[i].gravity); + g_print ("Setting scale center to %s\n", + value ? value->value_name : "?"); + g_type_class_unref (gravity_class); + } + + g_object_set (rect, "scale-gravity", gravities[i].gravity, NULL); + + g_assert (clutter_actor_get_x (rect) == 100); + g_assert (clutter_actor_get_y (rect) == 200); + g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); + g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); + g_object_get (rect, + "scale-center-x", ¢er_x, "scale-center-y", ¢er_y, + "scale-x", &scale_x, "scale-y", &scale_y, + "scale-gravity", &gravity, + NULL); + g_assert (center_x == gravities[i].x_pos); + g_assert (center_y == gravities[i].y_pos); + g_assert (scale_x == 4.0); + g_assert (scale_y == 2.0); + g_assert (gravity == gravities[i].gravity); + assert_notifications (NOTIFY_SCALE_X | NOTIFY_SCALE_Y + | NOTIFY_SCALE_CENTER_X | NOTIFY_SCALE_CENTER_Y + | NOTIFY_SCALE_GRAVITY); + assert_coords (state, + 100 - gravities[i].x_pos * 3, + 200 - gravities[i].y_pos, + 100 + (gravities[i].x_pos + + (RECT_WIDTH - gravities[i].x_pos) * 4), + 200 + (gravities[i].y_pos + + (RECT_HEIGHT - gravities[i].y_pos) * 2)); + } + + /* Change the scale center using units again to assert that the + gravity property changes */ + clutter_actor_set_scale_full (rect, 4, 2, 10, 20); + g_object_get (rect, + "scale-center-x", ¢er_x, "scale-center-y", ¢er_y, + "scale-x", &scale_x, "scale-y", &scale_y, + "scale-gravity", &gravity, + NULL); + g_assert (center_x == 10); + g_assert (center_y == 20); + g_assert (scale_x == 4.0); + g_assert (scale_y == 2.0); + g_assert (gravity == CLUTTER_GRAVITY_NONE); + assert_notifications (NOTIFY_SCALE_X | NOTIFY_SCALE_Y + | NOTIFY_SCALE_CENTER_X | NOTIFY_SCALE_CENTER_Y + | NOTIFY_SCALE_GRAVITY); + assert_coords (state, 100 + 10 - 10 * 4, 200 + 20 - 20 * 2, + 100 + 10 + (RECT_WIDTH - 10) * 4, + 200 + 20 + (RECT_HEIGHT - 20) * 2); +} + +static gboolean +idle_cb (gpointer data) +{ + test_anchor_point (data); + test_scale_center (data); + clutter_main_quit (); return FALSE; @@ -226,7 +387,7 @@ test_anchors (TestConformSimpleFixture *fixture, clutter_actor_set_position (state.rect, 100, 200); clutter_actor_set_size (state.rect, RECT_WIDTH, RECT_HEIGHT); - /* Record notifications on the actor anchor properties */ + /* Record notifications on the actor properties */ state.notifications = 0; g_signal_connect (state.rect, "notify::anchor-x", G_CALLBACK (anchor_x_cb), &state); @@ -234,6 +395,16 @@ test_anchors (TestConformSimpleFixture *fixture, G_CALLBACK (anchor_y_cb), &state); g_signal_connect (state.rect, "notify::anchor-gravity", G_CALLBACK (anchor_gravity_cb), &state); + g_signal_connect (state.rect, "notify::scale-x", + G_CALLBACK (scale_x_cb), &state); + g_signal_connect (state.rect, "notify::scale-y", + G_CALLBACK (scale_y_cb), &state); + g_signal_connect (state.rect, "notify::scale-center-x", + G_CALLBACK (scale_center_x_cb), &state); + g_signal_connect (state.rect, "notify::scale-center-y", + G_CALLBACK (scale_center_y_cb), &state); + g_signal_connect (state.rect, "notify::scale-gravity", + G_CALLBACK (scale_gravity_cb), &state); /* Run the tests in a low priority idle function so that we can be sure the stage is correctly setup */ From d5e5d35316046a192064d5a9cef20542a100cb7d Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 23 Jan 2009 15:55:41 +0000 Subject: [PATCH 5/8] Convert the rotation centers to be AnchorCoords Currently only the Z axis rotation center can be set using a gravity but the other rotations also store their center as an AnchorCoord for consistency. Specifying the center as a gravity makes less sense for the other axes because the actors have no size along the Z axis. The rotation angles are now stored as gdoubles and the fixed point *x entry points have been removed. The Z rotation can now be set with a gravity center using the following new function: void clutter_actor_set_z_rotation_from_gravity (ClutterActor *self, gdouble angle, ClutterGravity gravity); --- clutter/clutter-actor.c | 437 ++++++++++++----------------- clutter/clutter-actor.h | 15 +- clutter/clutter-behaviour-rotate.c | 10 +- 3 files changed, 193 insertions(+), 269 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 3bb885f4b..5a79e5791 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -250,21 +250,18 @@ struct _ClutterActorPrivate ClutterUnit clip[4]; /* Rotation angles */ - ClutterFixed rxang; - ClutterFixed ryang; - ClutterFixed rzang; + gdouble rxang; + gdouble ryang; + gdouble rzang; /* Rotation center: X axis */ - ClutterUnit rxy; - ClutterUnit rxz; + AnchorCoord rx_center; /* Rotation center: Y axis */ - ClutterUnit ryx; - ClutterUnit ryz; + AnchorCoord ry_center; /* Rotation center: Z axis */ - ClutterUnit rzx; - ClutterUnit rzy; + AnchorCoord rz_center; /* Anchor point coordinates */ AnchorCoord anchor; @@ -353,6 +350,10 @@ enum PROP_ROTATION_CENTER_X, PROP_ROTATION_CENTER_Y, PROP_ROTATION_CENTER_Z, + /* This property only makes sense for the z rotation because the + others would depend on the actor having a size along the + z-axis */ + PROP_ROTATION_CENTER_Z_GRAVITY, PROP_ANCHOR_X, PROP_ANCHOR_Y, @@ -1422,6 +1423,9 @@ _clutter_actor_apply_modelview_transform (ClutterActor *self) CLUTTER_UNITS_TO_FLOAT (priv->allocation.y1), 0); + if (priv->z) + cogl_translate (0, 0, priv->z); + /* * because the rotation involves translations, we must scale before * applying the rotations (if we apply the scale after the rotations, @@ -1432,44 +1436,20 @@ _clutter_actor_apply_modelview_transform (ClutterActor *self) TRANSFORM_ABOUT_ANCHOR_COORD (self, &priv->scale_center, cogl_scale (priv->scale_x, priv->scale_y)); - if (priv->rzang) - { - cogl_translate (CLUTTER_UNITS_TO_FLOAT (priv->rzx), - CLUTTER_UNITS_TO_FLOAT (priv->rzy), - 0); - - cogl_rotate (priv->rzang, 0, 0, 1.0); - - cogl_translate (CLUTTER_UNITS_TO_FLOAT (-priv->rzx), - CLUTTER_UNITS_TO_FLOAT (-priv->rzy), - 0); - } + if (priv->rzang) + TRANSFORM_ABOUT_ANCHOR_COORD (self, &priv->rz_center, + cogl_rotate (priv->rzang, + 0, 0, 1.0)); if (priv->ryang) - { - cogl_translate (CLUTTER_UNITS_TO_FLOAT (priv->ryx), - 0, - CLUTTER_UNITS_TO_FLOAT (priv->z + priv->ryz)); - - cogl_rotate (priv->ryang, 0, 1.0, 0); - - cogl_translate (CLUTTER_UNITS_TO_FLOAT (-priv->ryx), - 0, - CLUTTER_UNITS_TO_FLOAT (-(priv->z + priv->ryz))); - } + TRANSFORM_ABOUT_ANCHOR_COORD (self, &priv->ry_center, + cogl_rotate (priv->ryang, + 0, 1.0, 0)); if (priv->rxang) - { - cogl_translate (0, - CLUTTER_UNITS_TO_FLOAT (priv->rxy), - CLUTTER_UNITS_TO_FLOAT (priv->z + priv->rxz)); - - cogl_rotate (priv->rxang, 1.0, 0, 0); - - cogl_translate (0, - CLUTTER_UNITS_TO_FLOAT (-priv->rxy), - CLUTTER_UNITS_TO_FLOAT (-(priv->z + priv->rxz))); - } + TRANSFORM_ABOUT_ANCHOR_COORD (self, &priv->rx_center, + cogl_rotate (priv->rxang, + 1.0, 0, 0)); if (!is_stage && !clutter_anchor_coord_is_zero (&priv->anchor)) { @@ -1477,9 +1457,6 @@ _clutter_actor_apply_modelview_transform (ClutterActor *self) clutter_anchor_coord_get_units (self, &priv->anchor, &x, &y, &z); cogl_translate (-x, -y, -z); } - - if (priv->z) - cogl_translate (0, 0, priv->z); } /* Recursively applies the transforms associated with this actor and @@ -1602,17 +1579,13 @@ clutter_actor_paint (ClutterActor *self) CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_ACTOR_IN_PAINT); } -/* fixed point, unit based rotation setter, to be used by - * set_property() so that we don't lose precision in the - * center coordinates by converting them to and from units +/* internal helper function set the rotation angle without affecting + the center point */ -static inline void +static void clutter_actor_set_rotation_internal (ClutterActor *self, ClutterRotateAxis axis, - ClutterFixed angle, - ClutterUnit center_x, - ClutterUnit center_y, - ClutterUnit center_z) + gdouble angle) { ClutterActorPrivate *priv = self->priv; @@ -1623,26 +1596,17 @@ clutter_actor_set_rotation_internal (ClutterActor *self, { case CLUTTER_X_AXIS: priv->rxang = angle; - priv->rxy = center_y; - priv->rxz = center_z; g_object_notify (G_OBJECT (self), "rotation-angle-x"); - g_object_notify (G_OBJECT (self), "rotation-center-x"); break; case CLUTTER_Y_AXIS: priv->ryang = angle; - priv->ryx = center_x; - priv->ryz = center_z; g_object_notify (G_OBJECT (self), "rotation-angle-y"); - g_object_notify (G_OBJECT (self), "rotation-center-y"); break; case CLUTTER_Z_AXIS: priv->rzang = angle; - priv->rzx = center_x; - priv->rzy = center_y; g_object_notify (G_OBJECT (self), "rotation-angle-z"); - g_object_notify (G_OBJECT (self), "rotation-center-z"); break; } @@ -1788,86 +1752,63 @@ clutter_actor_set_property (GObject *object, clutter_actor_set_reactive (actor, g_value_get_boolean (value)); break; case PROP_ROTATION_ANGLE_X: - { - ClutterFixed angle; - - angle = CLUTTER_FLOAT_TO_FIXED (g_value_get_double (value)); - clutter_actor_set_rotation_internal (actor, - CLUTTER_X_AXIS, - angle, - 0, - priv->rxy, - priv->rxz); - } + clutter_actor_set_rotation_internal (actor, + CLUTTER_X_AXIS, + g_value_get_double (value)); break; case PROP_ROTATION_ANGLE_Y: - { - ClutterFixed angle; - - angle = CLUTTER_FLOAT_TO_FIXED (g_value_get_double (value)); - clutter_actor_set_rotation_internal (actor, - CLUTTER_Y_AXIS, - angle, - priv->ryx, - 0, - priv->ryz); - } + clutter_actor_set_rotation_internal (actor, + CLUTTER_Y_AXIS, + g_value_get_double (value)); break; case PROP_ROTATION_ANGLE_Z: - { - ClutterFixed angle; - - angle = CLUTTER_FLOAT_TO_FIXED (g_value_get_double (value)); - clutter_actor_set_rotation_internal (actor, - CLUTTER_Z_AXIS, - angle, - priv->rzx, - priv->rzy, - 0); - } + clutter_actor_set_rotation_internal (actor, + CLUTTER_Z_AXIS, + g_value_get_double (value)); break; case PROP_ROTATION_CENTER_X: { - ClutterVertex *center; + const ClutterVertex *center; - center = g_value_get_boxed (value); - if (center) - clutter_actor_set_rotation_internal (actor, - CLUTTER_X_AXIS, - priv->rxang, - 0, - center->y, - center->z); + if ((center = g_value_get_boxed (value))) + clutter_actor_set_rotation (actor, + CLUTTER_X_AXIS, + priv->rxang, + center->x, + center->y, + center->z); } break; case PROP_ROTATION_CENTER_Y: { - ClutterVertex *center; + const ClutterVertex *center; - center = g_value_get_boxed (value); - if (center) - clutter_actor_set_rotation_internal (actor, - CLUTTER_Y_AXIS, - priv->ryang, - center->x, - 0, - center->z); + if ((center = g_value_get_boxed (value))) + clutter_actor_set_rotation (actor, + CLUTTER_Y_AXIS, + priv->ryang, + center->x, + center->y, + center->z); } break; case PROP_ROTATION_CENTER_Z: { - ClutterVertex *center; + const ClutterVertex *center; - center = g_value_get_boxed (value); - if (center) - clutter_actor_set_rotation_internal (actor, - CLUTTER_Z_AXIS, - priv->rzang, - center->x, - center->y, - 0); + if ((center = g_value_get_boxed (value))) + clutter_actor_set_rotation (actor, + CLUTTER_Z_AXIS, + priv->rzang, + center->x, + center->y, + center->z); } break; + case PROP_ROTATION_CENTER_Z_GRAVITY: + clutter_actor_set_z_rotation_from_gravity + (actor, priv->rzang, g_value_get_enum (value)); + break; case PROP_ANCHOR_X: { int anchor_x = g_value_get_int (value); @@ -2025,44 +1966,47 @@ clutter_actor_get_property (GObject *object, g_value_set_boolean (value, clutter_actor_get_reactive (actor)); break; case PROP_ROTATION_ANGLE_X: - g_value_set_double (value, CLUTTER_FIXED_TO_DOUBLE (priv->rxang)); + g_value_set_double (value, priv->rxang); break; case PROP_ROTATION_ANGLE_Y: - g_value_set_double (value, CLUTTER_FIXED_TO_DOUBLE (priv->ryang)); + g_value_set_double (value, priv->ryang); break; case PROP_ROTATION_ANGLE_Z: - g_value_set_double (value, CLUTTER_FIXED_TO_DOUBLE (priv->rzang)); + g_value_set_double (value, priv->rzang); break; case PROP_ROTATION_CENTER_X: { - ClutterVertex center = { 0, }; + ClutterVertex center; - center.y = priv->rxy; - center.z = priv->rxz; + clutter_actor_get_rotationu (actor, CLUTTER_X_AXIS, + ¢er.x, ¢er.y, ¢er.z); g_value_set_boxed (value, ¢er); } break; case PROP_ROTATION_CENTER_Y: { - ClutterVertex center = { 0, }; + ClutterVertex center; - center.x = priv->ryx; - center.z = priv->ryz; + clutter_actor_get_rotationu (actor, CLUTTER_Y_AXIS, + ¢er.x, ¢er.y, ¢er.z); g_value_set_boxed (value, ¢er); } break; case PROP_ROTATION_CENTER_Z: { - ClutterVertex center = { 0, }; + ClutterVertex center; - center.x = priv->rzx; - center.y = priv->rzy; + clutter_actor_get_rotationu (actor, CLUTTER_Z_AXIS, + ¢er.x, ¢er.y, ¢er.z); g_value_set_boxed (value, ¢er); } break; + case PROP_ROTATION_CENTER_Z_GRAVITY: + g_value_set_enum (value, clutter_actor_get_z_rotation_gravity (actor)); + break; case PROP_ANCHOR_X: { ClutterUnit anchor_x; @@ -2749,6 +2693,24 @@ clutter_actor_class_init (ClutterActorClass *klass) "The rotation center on the Z axis", CLUTTER_TYPE_VERTEX, CLUTTER_PARAM_READWRITE)); + + /** + * ClutterActor:rotation-center-z-gravity: + * + * The rotation center on the Z axis expressed as a #ClutterGravity. + * + * Since: 1.0 + */ + pspec = g_param_spec_enum ("rotation-center-z-gravity", + "Rotation-Center-Z-Gravity", + "Center point for rotation around the Z axis", + CLUTTER_TYPE_GRAVITY, + CLUTTER_GRAVITY_NONE, + CLUTTER_PARAM_READWRITE); + g_object_class_install_property (object_class, + PROP_ROTATION_CENTER_Z_GRAVITY, + pspec); + /** * ClutterActor:anchor-x: * @@ -5538,42 +5500,35 @@ clutter_actor_set_rotationu (ClutterActor *self, ClutterUnit y, ClutterUnit z) { + ClutterActorPrivate *priv; + g_return_if_fail (CLUTTER_IS_ACTOR (self)); - clutter_actor_set_rotation_internal (self, axis, - CLUTTER_FLOAT_TO_FIXED (angle), - x, y, z); -} + priv = self->priv; -/** - * clutter_actor_set_rotationx: - * @self: a #ClutterActor - * @axis: the axis of rotation - * @angle: the angle of rotation - * @x: X coordinate of the rotation center - * @y: Y coordinate of the rotation center - * @z: Z coordinate of the rotation center - * - * Sets the rotation angle of @self around the given axis. - * - * This function is the fixed point variant of clutter_actor_set_rotation(). - * - * Since: 0.6 - */ -void -clutter_actor_set_rotationx (ClutterActor *self, - ClutterRotateAxis axis, - ClutterFixed angle, - gint x, - gint y, - gint z) -{ - g_return_if_fail (CLUTTER_IS_ACTOR (self)); + g_object_freeze_notify (G_OBJECT (self)); - clutter_actor_set_rotation_internal (self, axis, angle, - CLUTTER_UNITS_FROM_DEVICE (x), - CLUTTER_UNITS_FROM_DEVICE (y), - CLUTTER_UNITS_FROM_DEVICE (z)); + clutter_actor_set_rotation_internal (self, axis, angle); + + switch (axis) + { + case CLUTTER_X_AXIS: + clutter_anchor_coord_set_units (&priv->rx_center, x, y, z); + g_object_notify (G_OBJECT (self), "rotation-center-x"); + break; + case CLUTTER_Y_AXIS: + clutter_anchor_coord_set_units (&priv->ry_center, x, y, z); + g_object_notify (G_OBJECT (self), "rotation-center-y"); + break; + case CLUTTER_Z_AXIS: + if (priv->rz_center.is_fractional) + g_object_notify (G_OBJECT (self), "rotation-center-z-gravity"); + clutter_anchor_coord_set_units (&priv->rz_center, x, y, z); + g_object_notify (G_OBJECT (self), "rotation-center-z"); + break; + } + + g_object_thaw_notify (G_OBJECT (self)); } /** @@ -5610,9 +5565,37 @@ clutter_actor_set_rotation (ClutterActor *self, { g_return_if_fail (CLUTTER_IS_ACTOR (self)); - clutter_actor_set_rotationx (self, axis, - CLUTTER_FLOAT_TO_FIXED (angle), - x, y, z); + clutter_actor_set_rotationu (self, axis, angle, + CLUTTER_UNITS_FROM_DEVICE (x), + CLUTTER_UNITS_FROM_DEVICE (y), + CLUTTER_UNITS_FROM_DEVICE (z)); +} + +void +clutter_actor_set_z_rotation_from_gravity (ClutterActor *self, + gdouble angle, + ClutterGravity gravity) +{ + ClutterActorPrivate *priv; + + g_return_if_fail (CLUTTER_IS_ACTOR (self)); + + if (gravity == CLUTTER_GRAVITY_NONE) + clutter_actor_set_rotation (self, CLUTTER_Z_AXIS, angle, 0, 0, 0); + else + { + priv = self->priv; + + g_object_freeze_notify (G_OBJECT (self)); + + clutter_actor_set_rotation_internal (self, CLUTTER_Z_AXIS, angle); + + clutter_anchor_coord_set_gravity (&priv->rz_center, gravity); + g_object_notify (G_OBJECT (self), "rotation-center-z-gravity"); + g_object_notify (G_OBJECT (self), "rotation-center-z"); + + g_object_thaw_notify (G_OBJECT (self)); + } } /** @@ -5644,6 +5627,7 @@ clutter_actor_get_rotationu (ClutterActor *self, { ClutterActorPrivate *priv; gdouble retval = 0; + AnchorCoord *anchor_coord = NULL; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); @@ -5652,91 +5636,23 @@ clutter_actor_get_rotationu (ClutterActor *self, switch (axis) { case CLUTTER_X_AXIS: - retval = CLUTTER_FIXED_TO_DOUBLE (priv->rxang); - if (y) - *y = priv->rxy; - if (z) - *z = priv->rxz; - break; - - case CLUTTER_Y_AXIS: - retval = CLUTTER_FIXED_TO_DOUBLE (priv->ryang); - if (x) - *x = priv->ryx; - if (z) - *z = priv->ryz; - break; - - case CLUTTER_Z_AXIS: - retval = CLUTTER_FIXED_TO_DOUBLE (priv->rzang); - if (x) - *x = priv->rzx; - if (y) - *y = priv->rzy; - break; - } - - return retval; -} - -/** - * clutter_actor_get_rotationx: - * @self: a #ClutterActor - * @axis: the axis of rotation - * @x: return value for the X coordinate of the center of rotation - * @y: return value for the Y coordinate of the center of rotation - * @z: return value for the Z coordinate of the center of rotation - * - * Retrieves the angle and center of rotation on the given axis, - * set using clutter_actor_set_rotation(). - * - * This function is the fixed point variant of clutter_actor_get_rotation(). - * - * Return value: the angle of rotation as a fixed point value. - * - * Since: 0.6 - */ -ClutterFixed -clutter_actor_get_rotationx (ClutterActor *self, - ClutterRotateAxis axis, - gint *x, - gint *y, - gint *z) -{ - ClutterActorPrivate *priv; - ClutterFixed retval = 0; - - g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); - - priv = self->priv; - - switch (axis) - { - case CLUTTER_X_AXIS: + anchor_coord = &priv->rx_center; retval = priv->rxang; - if (y) - *y = CLUTTER_UNITS_TO_DEVICE (priv->rxy); - if (z) - *z = CLUTTER_UNITS_TO_DEVICE (priv->rxz); break; case CLUTTER_Y_AXIS: + anchor_coord = &priv->ry_center; retval = priv->ryang; - if (x) - *x = CLUTTER_UNITS_TO_DEVICE (priv->ryx); - if (z) - *z = CLUTTER_UNITS_TO_DEVICE (priv->ryz); break; case CLUTTER_Z_AXIS: + anchor_coord = &priv->rz_center; retval = priv->rzang; - if (x) - *x = CLUTTER_UNITS_TO_DEVICE (priv->rzx); - if (y) - *y = CLUTTER_UNITS_TO_DEVICE (priv->rzy); break; } + clutter_anchor_coord_get_units (self, anchor_coord, x, y, z); + return retval; } @@ -5765,11 +5681,26 @@ clutter_actor_get_rotation (ClutterActor *self, gint *y, gint *z) { + ClutterUnit xu, yu, zu; + gdouble angle; + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.0); - return CLUTTER_FIXED_TO_FLOAT (clutter_actor_get_rotationx (self, - axis, - x, y, z)); + angle = clutter_actor_get_rotationu (self, axis, &xu, &yu, &zu); + + *x = CLUTTER_UNITS_TO_DEVICE (xu); + *y = CLUTTER_UNITS_TO_DEVICE (yu); + *z = CLUTTER_UNITS_TO_DEVICE (zu); + + return angle; +} + +ClutterGravity +clutter_actor_get_z_rotation_gravity (ClutterActor *self) +{ + g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.0); + + return clutter_anchor_coord_get_gravity (&self->priv->rz_center); } /** @@ -6905,7 +6836,7 @@ out: typedef struct { ClutterRotateAxis axis; - ClutterFixed angle; + gdouble angle; ClutterUnit center_x; ClutterUnit center_y; @@ -6925,7 +6856,7 @@ parse_rotation_array (ClutterActor *actor, /* angle */ element = json_array_get_element (array, 0); if (JSON_NODE_TYPE (element) == JSON_NODE_VALUE) - info->angle = CLUTTER_FLOAT_TO_FIXED (json_node_get_double (element)); + info->angle = json_node_get_double (element); else return FALSE; @@ -7138,11 +7069,11 @@ clutter_actor_set_custom_property (ClutterScriptable *scriptable, info = g_value_get_pointer (value); - clutter_actor_set_rotation_internal (CLUTTER_ACTOR (scriptable), - info->axis, info->angle, - info->center_x, - info->center_y, - info->center_z); + clutter_actor_set_rotationu (CLUTTER_ACTOR (scriptable), + info->axis, info->angle, + info->center_x, + info->center_y, + info->center_z); g_slice_free (RotationInfo, info); } diff --git a/clutter/clutter-actor.h b/clutter/clutter-actor.h index 8faa64cf4..571bac1f5 100644 --- a/clutter/clutter-actor.h +++ b/clutter/clutter-actor.h @@ -390,33 +390,26 @@ void clutter_actor_set_rotation (ClutterActor gint x, gint y, gint z); -void clutter_actor_set_rotationx (ClutterActor *self, - ClutterRotateAxis axis, - ClutterFixed angle, - gint x, - gint y, - gint z); void clutter_actor_set_rotationu (ClutterActor *self, ClutterRotateAxis axis, gdouble angle, ClutterUnit x, ClutterUnit y, ClutterUnit z); +void clutter_actor_set_z_rotation_from_gravity (ClutterActor *self, + gdouble angle, + ClutterGravity gravity); gdouble clutter_actor_get_rotation (ClutterActor *self, ClutterRotateAxis axis, gint *x, gint *y, gint *z); -ClutterFixed clutter_actor_get_rotationx (ClutterActor *self, - ClutterRotateAxis axis, - gint *x, - gint *y, - gint *z); gdouble clutter_actor_get_rotationu (ClutterActor *self, ClutterRotateAxis axis, ClutterUnit *x, ClutterUnit *y, ClutterUnit *z); +ClutterGravity clutter_actor_get_z_rotation_gravity (ClutterActor *self); void clutter_actor_set_opacity (ClutterActor *self, guint8 opacity); diff --git a/clutter/clutter-behaviour-rotate.c b/clutter/clutter-behaviour-rotate.c index b7402868a..8fbd2c69c 100644 --- a/clutter/clutter-behaviour-rotate.c +++ b/clutter/clutter-behaviour-rotate.c @@ -95,11 +95,11 @@ alpha_notify_foreach (ClutterBehaviour *behaviour, rotate_behaviour = CLUTTER_BEHAVIOUR_ROTATE (behaviour); priv = rotate_behaviour->priv; - clutter_actor_set_rotationx (actor, priv->axis, - angle, - priv->center_x, - priv->center_y, - priv->center_z); + clutter_actor_set_rotation (actor, priv->axis, + CLUTTER_FIXED_TO_DOUBLE (angle), + priv->center_x, + priv->center_y, + priv->center_z); } static inline From 5acff562b40291402ef9a9c7977690824027804e Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 23 Jan 2009 15:57:20 +0000 Subject: [PATCH 6/8] [test-anchors] Add tests for the rotation centers The rotation centers are now tested in a similar way to the anchor point and scale centers. The notification handling code has been simplified a bit to handle the increased amount of properties. --- tests/conform/test-anchors.c | 376 ++++++++++++++++++++++++++++++----- 1 file changed, 325 insertions(+), 51 deletions(-) diff --git a/tests/conform/test-anchors.c b/tests/conform/test-anchors.c index 214ae88c3..f0211241f 100644 --- a/tests/conform/test-anchors.c +++ b/tests/conform/test-anchors.c @@ -1,16 +1,24 @@ #include #include +#include #include "test-conform-common.h" -#define NOTIFY_ANCHOR_X 1 -#define NOTIFY_ANCHOR_Y 2 -#define NOTIFY_ANCHOR_GRAVITY 4 -#define NOTIFY_SCALE_X 8 -#define NOTIFY_SCALE_Y 16 -#define NOTIFY_SCALE_CENTER_X 32 -#define NOTIFY_SCALE_CENTER_Y 64 -#define NOTIFY_SCALE_GRAVITY 128 +#define NOTIFY_ANCHOR_X (1 << 0) +#define NOTIFY_ANCHOR_Y (1 << 1) +#define NOTIFY_ANCHOR_GRAVITY (1 << 2) +#define NOTIFY_SCALE_X (1 << 3) +#define NOTIFY_SCALE_Y (1 << 4) +#define NOTIFY_SCALE_CENTER_X (1 << 5) +#define NOTIFY_SCALE_CENTER_Y (1 << 6) +#define NOTIFY_SCALE_GRAVITY (1 << 7) +#define NOTIFY_ROTATION_ANGLE_X (1 << 8) +#define NOTIFY_ROTATION_ANGLE_Y (1 << 9) +#define NOTIFY_ROTATION_ANGLE_Z (1 << 10) +#define NOTIFY_ROTATION_CENTER_X (1 << 11) +#define NOTIFY_ROTATION_CENTER_Y (1 << 12) +#define NOTIFY_ROTATION_CENTER_Z (1 << 13) +#define NOTIFY_ROTATION_CENTER_Z_GRAVITY (1 << 14) #define RECT_WIDTH 100 #define RECT_HEIGHT 80 @@ -42,22 +50,42 @@ static const struct { CLUTTER_GRAVITY_CENTER, RECT_WIDTH / 2, RECT_HEIGHT / 2 } }; -#define make_notify_cb(func, flag) \ - static void \ - func (GObject *object, GParamSpec *pspec, TestState *state) \ - { \ - g_assert ((state->notifications & (flag)) == 0); \ - state->notifications |= (flag); \ - } +static const char * const +properties[] = + { "anchor-x", + "anchor-y", + "anchor-gravity", + "scale-x", + "scale-y", + "scale-center-x", + "scale-center-y", + "scale-gravity", + "rotation-angle-x", + "rotation-angle-y", + "rotation-angle-z", + "rotation-center-x", + "rotation-center-y", + "rotation-center-z", + "rotation-center-z-gravity" }; -make_notify_cb (anchor_x_cb, NOTIFY_ANCHOR_X); -make_notify_cb (anchor_y_cb, NOTIFY_ANCHOR_Y); -make_notify_cb (anchor_gravity_cb, NOTIFY_ANCHOR_GRAVITY); -make_notify_cb (scale_x_cb, NOTIFY_SCALE_X); -make_notify_cb (scale_y_cb, NOTIFY_SCALE_Y); -make_notify_cb (scale_center_x_cb, NOTIFY_SCALE_CENTER_X); -make_notify_cb (scale_center_y_cb, NOTIFY_SCALE_CENTER_Y); -make_notify_cb (scale_gravity_cb, NOTIFY_SCALE_GRAVITY); +static void +notify_cb (GObject *object, GParamSpec *pspec, TestState *state) +{ + int i; + int new_flags = 0; + int flag = 1; + + for (i = 0; i < G_N_ELEMENTS (properties); i++) + { + if (!strcmp (properties[i], pspec->name)) + new_flags |= flag; + flag <<= 1; + } + + g_assert ((new_flags & state->notifications) == 0); + + state->notifications |= new_flags; +} #define assert_notifications(flags) \ do \ @@ -75,19 +103,34 @@ make_notify_cb (scale_gravity_cb, NOTIFY_SCALE_GRAVITY); ClutterVertex verts[4]; \ clutter_actor_get_abs_allocation_vertices ((state)->rect, verts); \ check_coords ((state), (x_1), (y_1), (x_2), (y_2), verts); \ - g_assert (abs ((x_1) - CLUTTER_UNITS_TO_DEVICE (verts[0].x)) \ - <= POSITION_TOLERANCE); \ - g_assert (abs ((y_1) - CLUTTER_UNITS_TO_DEVICE (verts[0].y)) \ - <= POSITION_TOLERANCE); \ - g_assert (abs ((x_2) - CLUTTER_UNITS_TO_DEVICE (verts[3].x)) \ - <= POSITION_TOLERANCE); \ - g_assert (abs ((y_2) - CLUTTER_UNITS_TO_DEVICE (verts[3].y)) \ - <= POSITION_TOLERANCE); \ + g_assert (approx_equal ((x_1), \ + CLUTTER_UNITS_TO_DEVICE (verts[0].x))); \ + g_assert (approx_equal ((y_1), \ + CLUTTER_UNITS_TO_DEVICE (verts[0].y))); \ + g_assert (approx_equal ((x_2), \ + CLUTTER_UNITS_TO_DEVICE (verts[3].x))); \ + g_assert (approx_equal ((y_2), \ + CLUTTER_UNITS_TO_DEVICE (verts[3].y))); \ } while (0) #define assert_position(state, x, y) \ assert_coords((state), (x), (y), (x) + RECT_WIDTH, (y) + RECT_HEIGHT) +#define assert_vertex_and_free(v, xc, yc, zc) \ + do \ + { \ + g_assert (approx_equal (CLUTTER_UNITS_TO_DEVICE (v->x), xc) \ + && approx_equal (CLUTTER_UNITS_TO_DEVICE (v->y), yc) \ + && approx_equal (CLUTTER_UNITS_TO_DEVICE (v->z), zc)); \ + g_boxed_free (CLUTTER_TYPE_VERTEX, v); \ + } while (0) + +static inline gboolean +approx_equal (int a, int b) +{ + return abs (a - b) <= POSITION_TOLERANCE; +} + static void check_coords (TestState *state, gint x_1, gint y_1, gint x_2, gint y_2, @@ -100,10 +143,10 @@ check_coords (TestState *state, CLUTTER_UNITS_TO_DEVICE (verts[0].y), CLUTTER_UNITS_TO_DEVICE (verts[3].x), CLUTTER_UNITS_TO_DEVICE (verts[3].y), - abs (x_1 - verts[0].x) <= POSITION_TOLERANCE - && abs (y_1 - verts[0].y) <= POSITION_TOLERANCE - && abs (x_2 - verts[3].x) <= POSITION_TOLERANCE - && abs (y_2 - verts[3].y) <= POSITION_TOLERANCE + approx_equal (x_1, CLUTTER_UNITS_TO_DEVICE (verts[0].x)) + && approx_equal (y_1, CLUTTER_UNITS_TO_DEVICE (verts[0].y)) + && approx_equal (x_2, CLUTTER_UNITS_TO_DEVICE (verts[3].x)) + && approx_equal (y_2, CLUTTER_UNITS_TO_DEVICE (verts[3].y)) ? "yes" : "NO"); } @@ -360,6 +403,250 @@ test_scale_center (TestState *state) assert_coords (state, 100 + 10 - 10 * 4, 200 + 20 - 20 * 2, 100 + 10 + (RECT_WIDTH - 10) * 4, 200 + 20 + (RECT_HEIGHT - 20) * 2); + + /* Put the scale back to normal */ + clutter_actor_set_scale_full (rect, 1, 1, 0, 0); + assert_notifications (NOTIFY_SCALE_X | NOTIFY_SCALE_Y + | NOTIFY_SCALE_CENTER_X | NOTIFY_SCALE_CENTER_Y); +} + +static void +test_rotate_center (TestState *state) +{ + ClutterActor *rect = state->rect; + gdouble angle_x, angle_y, angle_z; + ClutterVertex *center_x, *center_y, *center_z; + ClutterGravity z_center_gravity; + guint stage_width, stage_height; + gint rect_x, rect_y; + int i; + + /* Position the rectangle at the center of the stage so that + rotations by 90° along the X or Y axis will cause the actor to be + appear as a flat line. This makes verifying the transformations + easier */ + clutter_actor_get_size (clutter_actor_get_stage (rect), + &stage_width, &stage_height); + rect_x = stage_width / 2; + rect_y = stage_height / 2; + clutter_actor_set_position (rect, rect_x, rect_y); + + /* Assert the default settings */ + g_assert (clutter_actor_get_x (rect) == rect_x); + g_assert (clutter_actor_get_y (rect) == rect_y); + g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); + g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); + g_object_get (rect, + "rotation-angle-x", &angle_x, + "rotation-angle-y", &angle_y, + "rotation-angle-z", &angle_z, + "rotation-center-x", ¢er_x, + "rotation-center-y", ¢er_y, + "rotation-center-z", ¢er_z, + "rotation-center-z-gravity", &z_center_gravity, + NULL); + g_assert (angle_x == 0.0); + g_assert (angle_y == 0.0); + g_assert (angle_z == 0.0); + assert_vertex_and_free (center_x, 0, 0, 0); + assert_vertex_and_free (center_y, 0, 0, 0); + assert_vertex_and_free (center_z, 0, 0, 0); + g_assert (z_center_gravity == CLUTTER_GRAVITY_NONE); + + /* Change each of the rotation angles without affecting the center + point */ + for (i = CLUTTER_X_AXIS; i <= CLUTTER_Z_AXIS; i++) + { + char prop_name[] = "rotation-angle- "; + prop_name[sizeof (prop_name) - 2] = i - CLUTTER_X_AXIS + 'x'; + + if (g_test_verbose ()) + g_print ("Setting %s to 90 degrees\n", prop_name); + + g_object_set (rect, prop_name, 90.0, NULL); + assert_notifications (NOTIFY_ROTATION_ANGLE_X << (i - CLUTTER_X_AXIS)); + + g_assert (clutter_actor_get_x (rect) == rect_x); + g_assert (clutter_actor_get_y (rect) == rect_y); + g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); + g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); + g_object_get (rect, + "rotation-angle-x", &angle_x, + "rotation-angle-y", &angle_y, + "rotation-angle-z", &angle_z, + "rotation-center-x", ¢er_x, + "rotation-center-y", ¢er_y, + "rotation-center-z", ¢er_z, + "rotation-center-z-gravity", &z_center_gravity, + NULL); + if (i == CLUTTER_X_AXIS) + { + g_assert (angle_x == 90.0); + assert_coords (state, rect_x, rect_y, + CLUTTER_UNITS_TO_DEVICE (verts[3].x), rect_y); + } + else + g_assert (angle_x == 0.0); + if (i == CLUTTER_Y_AXIS) + { + g_assert (angle_y == 90.0); + assert_coords (state, rect_x, rect_y, + rect_x, CLUTTER_UNITS_TO_DEVICE (verts[3].y)); + } + else + g_assert (angle_y == 0.0); + if (i == CLUTTER_Z_AXIS) + { + g_assert (angle_z == 90.0); + assert_coords (state, rect_x, rect_y, + rect_x - RECT_HEIGHT, rect_y + RECT_WIDTH); + } + else + g_assert (angle_z == 0.0); + assert_vertex_and_free (center_x, 0, 0, 0); + assert_vertex_and_free (center_y, 0, 0, 0); + assert_vertex_and_free (center_z, 0, 0, 0); + g_assert (z_center_gravity == CLUTTER_GRAVITY_NONE); + + g_object_set (rect, prop_name, 0.0, NULL); + assert_notifications (NOTIFY_ROTATION_ANGLE_X << (i - CLUTTER_X_AXIS)); + } + + clutter_actor_set_position (rect, rect_x -= 10, rect_y -= 20); + + /* Same test but also change the center position */ + for (i = CLUTTER_X_AXIS; i <= CLUTTER_Z_AXIS; i++) + { + char prop_name[] = "rotation-angle- "; + prop_name[sizeof (prop_name) - 2] = i - CLUTTER_X_AXIS + 'x'; + + if (g_test_verbose ()) + g_print ("Setting %s to 90 degrees with center 10,20,0\n", prop_name); + + clutter_actor_set_rotation (rect, i, 90.0, 10, 20, 0); + assert_notifications ((NOTIFY_ROTATION_ANGLE_X << (i - CLUTTER_X_AXIS)) + | (NOTIFY_ROTATION_CENTER_X + << (i - CLUTTER_X_AXIS))); + + g_assert (clutter_actor_get_x (rect) == rect_x); + g_assert (clutter_actor_get_y (rect) == rect_y); + g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); + g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); + g_object_get (rect, + "rotation-angle-x", &angle_x, + "rotation-angle-y", &angle_y, + "rotation-angle-z", &angle_z, + "rotation-center-x", ¢er_x, + "rotation-center-y", ¢er_y, + "rotation-center-z", ¢er_z, + "rotation-center-z-gravity", &z_center_gravity, + NULL); + if (i == CLUTTER_X_AXIS) + { + g_assert (angle_x == 90.0); + assert_coords (state, + CLUTTER_UNITS_TO_DEVICE (verts[0].x), rect_y + 20, + CLUTTER_UNITS_TO_DEVICE (verts[3].x), rect_y + 20); + assert_vertex_and_free (center_x, 10, 20, 0); + } + else + { + g_assert (angle_x == 0.0); + assert_vertex_and_free (center_x, 0, 0, 0); + } + if (i == CLUTTER_Y_AXIS) + { + g_assert (angle_y == 90.0); + assert_coords (state, + rect_x + 10, CLUTTER_UNITS_TO_DEVICE (verts[0].y), + rect_x + 10, CLUTTER_UNITS_TO_DEVICE (verts[3].y)); + assert_vertex_and_free (center_y, 10, 20, 0); + } + else + { + g_assert (angle_y == 0.0); + assert_vertex_and_free (center_y, 0, 0, 0); + } + if (i == CLUTTER_Z_AXIS) + { + g_assert (angle_z == 90.0); + assert_coords (state, + rect_x + 10 + 20, + rect_y + 20 - 10, + rect_x + 10 + 20 - RECT_HEIGHT, + rect_y + 20 + RECT_WIDTH - 10); + assert_vertex_and_free (center_z, 10, 20, 0); + } + else + { + g_assert (angle_z == 0.0); + assert_vertex_and_free (center_z, 0, 0, 0); + } + g_assert (z_center_gravity == CLUTTER_GRAVITY_NONE); + + clutter_actor_set_rotation (rect, i, 0, 0, 0, 0); + assert_notifications ((NOTIFY_ROTATION_ANGLE_X << (i - CLUTTER_X_AXIS)) + | (NOTIFY_ROTATION_CENTER_X + << (i - CLUTTER_X_AXIS))); + + } + + /* Try rotating the z with all of the gravities */ + for (i = 0; i < G_N_ELEMENTS (gravities); i++) + { + if (g_test_verbose ()) + { + GEnumClass *gravity_class = g_type_class_ref (CLUTTER_TYPE_GRAVITY); + GEnumValue *value = g_enum_get_value (gravity_class, + gravities[i].gravity); + g_print ("Setting z rotation to 90 degrees with center at %s\n", + value ? value->value_name : "?"); + g_type_class_unref (gravity_class); + } + + clutter_actor_set_z_rotation_from_gravity (rect, 90, + gravities[i].gravity); + assert_notifications (NOTIFY_ROTATION_ANGLE_Z + | NOTIFY_ROTATION_CENTER_Z + | NOTIFY_ROTATION_CENTER_Z_GRAVITY); + + g_assert (clutter_actor_get_x (rect) == rect_x); + g_assert (clutter_actor_get_y (rect) == rect_y); + g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); + g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); + g_object_get (rect, + "rotation-angle-x", &angle_x, + "rotation-angle-y", &angle_y, + "rotation-angle-z", &angle_z, + "rotation-center-x", ¢er_x, + "rotation-center-y", ¢er_y, + "rotation-center-z", ¢er_z, + "rotation-center-z-gravity", &z_center_gravity, + NULL); + g_assert (angle_x == 0.0); + g_assert (angle_y == 0.0); + g_assert (angle_z == 90.0); + assert_vertex_and_free (center_x, 0, 0, 0); + assert_vertex_and_free (center_y, 0, 0, 0); + assert_vertex_and_free (center_z, + gravities[i].x_pos, gravities[i].y_pos, 0); + assert_coords (state, + rect_x + gravities[i].x_pos + gravities[i].y_pos, + rect_y + gravities[i].y_pos - gravities[i].x_pos, + rect_x + gravities[i].x_pos + gravities[i].y_pos + - RECT_HEIGHT, + rect_y + gravities[i].y_pos + RECT_WIDTH + - gravities[i].x_pos); + g_assert (z_center_gravity == gravities[i].gravity); + g_assert (clutter_actor_get_z_rotation_gravity (rect) + == gravities[i].gravity); + + /* Put the rotation back */ + clutter_actor_set_z_rotation_from_gravity (rect, 0, CLUTTER_GRAVITY_NONE); + assert_notifications (NOTIFY_ROTATION_ANGLE_Z + | NOTIFY_ROTATION_CENTER_Z + | NOTIFY_ROTATION_CENTER_Z_GRAVITY); + } } static gboolean @@ -367,6 +654,7 @@ idle_cb (gpointer data) { test_anchor_point (data); test_scale_center (data); + test_rotate_center (data); clutter_main_quit (); @@ -389,22 +677,8 @@ test_anchors (TestConformSimpleFixture *fixture, /* Record notifications on the actor properties */ state.notifications = 0; - g_signal_connect (state.rect, "notify::anchor-x", - G_CALLBACK (anchor_x_cb), &state); - g_signal_connect (state.rect, "notify::anchor-y", - G_CALLBACK (anchor_y_cb), &state); - g_signal_connect (state.rect, "notify::anchor-gravity", - G_CALLBACK (anchor_gravity_cb), &state); - g_signal_connect (state.rect, "notify::scale-x", - G_CALLBACK (scale_x_cb), &state); - g_signal_connect (state.rect, "notify::scale-y", - G_CALLBACK (scale_y_cb), &state); - g_signal_connect (state.rect, "notify::scale-center-x", - G_CALLBACK (scale_center_x_cb), &state); - g_signal_connect (state.rect, "notify::scale-center-y", - G_CALLBACK (scale_center_y_cb), &state); - g_signal_connect (state.rect, "notify::scale-gravity", - G_CALLBACK (scale_gravity_cb), &state); + g_signal_connect (state.rect, "notify", + G_CALLBACK (notify_cb), &state); /* Run the tests in a low priority idle function so that we can be sure the stage is correctly setup */ From 9ea6ab76fb09891dd1c1380c4e163aaf656c507c Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 23 Jan 2009 17:25:43 +0000 Subject: [PATCH 7/8] Fix gtk-doc reference since the anchor point changes The documentation has been updated to reflect the fact that the anchor point will move when the actor changes size if it was specified using a gravity value. The new functions for setting the scale center and z rotation gravity are also documented. --- clutter/clutter-actor.c | 154 +++++++++++++++++++-- doc/reference/clutter/clutter-sections.txt | 15 +- 2 files changed, 148 insertions(+), 21 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index 5a79e5791..cce705195 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -57,13 +57,13 @@ * the actor settings by the following order of operations: * * Translation by actor x, y coords, + * Translation by actor depth (z), * Scaling by scale_x, scale_y, - * Negative translation by anchor point x, - * y, * Rotation around z axis, * Rotation around y axis, * Rotation around x axis, - * Translation by actor depth (z), + * Negative translation by anchor point x, + * y, * Rectangular Clip is applied (this is not an operation on * the matrix as such, but it is done as part of the transform set * up). @@ -5085,8 +5085,9 @@ clutter_actor_get_yu (ClutterActor *self) * @scale_x: double factor to scale actor by horizontally. * @scale_y: double factor to scale actor by vertically. * - * Scales an actor with the given factors. The scaling is always - * relative to the anchor point. + * Scales an actor with the given factors. The scaling is relative to + * the scale center and the anchor point. The scale center is + * unchanged by this function and defaults to 0,0. * * Since: 0.2 */ @@ -5114,6 +5115,20 @@ clutter_actor_set_scale (ClutterActor *self, g_object_unref (self); } +/** + * clutter_actor_set_scale_full: + * @self: A #ClutterActor + * @scale_x: double factor to scale actor by horizontally. + * @scale_y: double factor to scale actor by vertically. + * @center_x: X coordinate of the center of the scale. + * @center_y: Y coordinate of the center of the scale + * + * Scales an actor with the given factors around the given center + * point. The center point is specified in pixels relative to the + * anchor point (usually the top left corner of the actor). + * + * Since: 1.0 + */ void clutter_actor_set_scale_full (ClutterActor *self, gdouble scale_x, @@ -5128,6 +5143,23 @@ clutter_actor_set_scale_full (ClutterActor *self, CLUTTER_UNITS_FROM_DEVICE (center_y)); } +/** + * clutter_actor_set_scale_fullu: + * @self: A #ClutterActor + * @scale_x: double factor to scale actor by horizontally. + * @scale_y: double factor to scale actor by vertically. + * @center_x: X coordinate of the center of the scale. + * @center_y: Y coordinate of the center of the scale + * + * %ClutterUnit version of clutter_actor_set_scale_full(). + * + * Scales an actor with the given factors around the given center + * point. The center point is specified in + * %ClutterUnits relative to the anchor point (usually + * the top left corner of the actor). + * + * Since: 1.0 + */ void clutter_actor_set_scale_fullu (ClutterActor *self, gdouble scale_x, @@ -5157,6 +5189,22 @@ clutter_actor_set_scale_fullu (ClutterActor *self, g_object_unref (self); } +/** + * clutter_actor_set_scale_with_gravity: + * @self: A #ClutterActor + * @scale_x: double factor to scale actor by horizontally. + * @scale_y: double factor to scale actor by vertically. + * @gravity: the location of the scale center expressed as a compass + * direction. + * + * Scales an actor with the given factors around the given + * center point. The center point is specified as one of the compass + * directions in #ClutterGravity. For example, setting it to north + * will cause the top of the actor to remain unchanged and the rest of + * the actor to expand left, right and downwards. + * + * Since: 1.0 + */ void clutter_actor_set_scale_with_gravity (ClutterActor *self, gdouble scale_x, @@ -5213,6 +5261,19 @@ clutter_actor_get_scale (ClutterActor *self, *scale_y = CLUTTER_FIXED_TO_FLOAT (self->priv->scale_y); } +/** + * clutter_actor_get_scale_center: + * @self: A #ClutterActor + * @center_x: Location to store the X position of the scale center, or %NULL. + * @center_y: Location to store the Y position of the scale center, or %NULL. + * + * Retrieves the scale center coordinate in pixels relative to the top + * left corner of the actor. If the scale center was specified using a + * #ClutterGravity this will calculate the pixel offset using the + * current size of the actor. + * + * Since: 1.0 + */ void clutter_actor_get_scale_center (ClutterActor *self, gint *center_x, @@ -5230,6 +5291,21 @@ clutter_actor_get_scale_center (ClutterActor *self, *center_y = CLUTTER_UNITS_TO_DEVICE (yu); } +/** + * clutter_actor_get_scale_centeru: + * @self: A #ClutterActor + * @center_x: Location to store the X position of the scale center, or %NULL. + * @center_y: Location to store the Y position of the scale center, or %NULL. + * + * ClutterUnits version of clutter_actor_get_scale_center(). + * + * Retrieves the scale center coordinate in units relative to the top + * left corner of the actor. If the scale center was specified using a + * #ClutterGravity this will calculate the unit offset using the + * current size of the actor. + * + * Since: 1.0 + */ void clutter_actor_get_scale_centeru (ClutterActor *self, ClutterUnit *center_x, @@ -5241,6 +5317,18 @@ clutter_actor_get_scale_centeru (ClutterActor *self, center_x, center_y, NULL); } +/** + * clutter_actor_get_scale_gravity: + * @self: A #ClutterActor + * + * Retrieves the scale center as a compass direction. If the scale + * center was specified in pixels or units this will return + * %CLUTTER_GRAVITY_NONE. + * + * Return value: the scale gravity + * + * Since: 1.0 + */ ClutterGravity clutter_actor_get_scale_gravity (ClutterActor *self) { @@ -5571,6 +5659,20 @@ clutter_actor_set_rotation (ClutterActor *self, CLUTTER_UNITS_FROM_DEVICE (z)); } +/** + * clutter_actor_set_z_rotation_from_gravity: + * @self: a #ClutterActor + * @angle: the angle of rotation + * @gravity: the center point of the rotation + * + * Sets the rotation angle of @self around the Z axis using the center + * point specified as a compass point. For example to rotate such that + * the center of the actor remains static you can use + * %CLUTTER_GRAVITY_CENTER. If the actor changes size the center point + * will move accordingly. + * + * Since: 1.0 + */ void clutter_actor_set_z_rotation_from_gravity (ClutterActor *self, gdouble angle, @@ -5695,6 +5797,18 @@ clutter_actor_get_rotation (ClutterActor *self, return angle; } +/** + * clutter_actor_get_z_rotation_gravity: + * @self: A #ClutterActor + * + * Retrieves the center for the rotation around the Z axis as a + * compass direction. If the center was specified in pixels or units + * this will return %CLUTTER_GRAVITY_NONE. + * + * Return value: the Z rotation center + * + * Since: 1.0 + */ ClutterGravity clutter_actor_get_z_rotation_gravity (ClutterActor *self) { @@ -6526,6 +6640,16 @@ clutter_actor_set_anchor_pointu (ClutterActor *self, clutter_actor_queue_redraw (self); } +/** + * clutter_actor_get_anchor_point_gravity: + * @self: a #ClutterActor + * + * Retrieves the anchor position expressed as a #ClutterGravity. If + * the anchor point was specified using pixels or units this will + * return %CLUTTER_GRAVITY_NONE. + * + * Since: 1.0 + */ ClutterGravity clutter_actor_get_anchor_point_gravity (ClutterActor *self) { @@ -6610,11 +6734,11 @@ clutter_actor_get_anchor_pointu (ClutterActor *self, * actor postion so that its relative position within its parent remains * unchanged. * - * Note that the anchor is still stored as a point and the gravity - * value is forgotten. For example, if you set the anchor point to - * %CLUTTER_GRAVITY_SOUTH_EAST and later double the size of the actor, - * the anchor point will not move to the bottom right and will now be - * in the center of the actor. + * Since version 1.0 the anchor point will be stored as a gravity so + * that if the actor changes size then the anchor point will move. For + * example, if you set the anchor point to %CLUTTER_GRAVITY_SOUTH_EAST + * and later double the size of the actor, the anchor point will move + * to the bottom right. * * Since: 0.6 */ @@ -6653,11 +6777,11 @@ clutter_actor_move_anchor_point_from_gravity (ClutterActor *self, * Sets an anchor point on the actor, based on the given gravity (this is a * convenience function wrapping clutter_actor_set_anchor_point()). * - * Note that the anchor is still stored as a point and the gravity - * value is forgotten. For example, if you set the anchor point to - * %CLUTTER_GRAVITY_SOUTH_EAST and later double the size of the actor, - * the anchor point will not move to the bottom right and will now be - * in the center of the actor. + * Since version 1.0 the anchor point will be stored as a gravity so + * that if the actor changes size then the anchor point will move. For + * example, if you set the anchor point to %CLUTTER_GRAVITY_SOUTH_EAST + * and later double the size of the actor, the anchor point will move + * to the bottom right. * * Since: 0.6 */ diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt index 8720e2023..e753b953c 100644 --- a/doc/reference/clutter/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -352,7 +352,9 @@ clutter_actor_set_y clutter_actor_get_y clutter_actor_move_by clutter_actor_set_rotation +clutter_actor_set_z_rotation_from_gravity clutter_actor_get_rotation +clutter_actor_get_z_rotation_gravity clutter_actor_is_rotated clutter_actor_set_opacity clutter_actor_get_opacity @@ -381,7 +383,11 @@ clutter_actor_get_stage clutter_actor_set_depth clutter_actor_get_depth clutter_actor_set_scale +clutter_actor_set_scale_full +clutter_actor_set_scale_with_gravity clutter_actor_get_scale +clutter_actor_get_scale_center +clutter_actor_get_scale_gravity clutter_actor_is_scaled clutter_actor_apply_transform_to_point clutter_actor_transform_stage_point @@ -400,6 +406,7 @@ clutter_actor_box_get_from_vertices clutter_actor_set_anchor_point clutter_actor_get_anchor_point clutter_actor_set_anchor_point_from_gravity +clutter_actor_get_anchor_point_gravity clutter_actor_move_anchor_point clutter_actor_move_anchor_point_from_gravity @@ -427,6 +434,8 @@ clutter_actor_set_positionu clutter_actor_get_positionu clutter_actor_set_sizeu clutter_actor_get_sizeu +clutter_actor_set_scale_fullu +clutter_actor_get_scale_centeru clutter_actor_set_anchor_pointu clutter_actor_get_anchor_pointu clutter_actor_move_anchor_pointu @@ -438,12 +447,6 @@ clutter_actor_move_byu clutter_actor_get_transformed_positionu clutter_actor_get_transformed_sizeu - -clutter_actor_set_scalex -clutter_actor_get_scalex -clutter_actor_set_rotationx -clutter_actor_get_rotationx - clutter_actor_grab_key_focus clutter_actor_get_pango_context From 1cd313477b2818b9117a1f0bbeeb113553ac447e Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 28 Jan 2009 15:25:38 +0000 Subject: [PATCH 8/8] [clutter-actor] Use G_STMT_START/END instead of do { } while (0) In the TRANSFORM_ABOUT_ANCHOR_COORD macro it now uses G_STMT_START and G_STMT_END instead of directly using do/while because it's more readable. --- clutter/clutter-actor.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index cce705195..b8abb2bf3 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -442,7 +442,7 @@ static gboolean clutter_anchor_coord_is_zero (const AnchorCoord *coord); /* Helper macro which translates by the anchor coord, applies the given transformation and then translates back */ #define TRANSFORM_ABOUT_ANCHOR_COORD(actor, coord, transform) \ - do \ + G_STMT_START \ { \ ClutterUnit __tx, __ty, __tz; \ clutter_anchor_coord_get_units ((actor), (coord), \ @@ -454,7 +454,8 @@ static gboolean clutter_anchor_coord_is_zero (const AnchorCoord *coord); cogl_translate (-CLUTTER_UNITS_TO_FLOAT (__tx), \ -CLUTTER_UNITS_TO_FLOAT (__ty), \ -CLUTTER_UNITS_TO_FLOAT (__tz)); \ - } while (0) + } \ + G_STMT_END G_DEFINE_ABSTRACT_TYPE_WITH_CODE (ClutterActor, clutter_actor,