2007-12-05 Tomas Frydrych <tf@openedhand.com>

* clutter/clutter-actor.c:
	* clutter/clutter-actor.h:
	* tests/Makefile.am:
	* tests/test-unproject:
	(clutter_actor_transform_stage_point):
	Added new function for translating stage coordinates into local
	actor coordinates.
This commit is contained in:
Tomas Frydrych 2007-12-05 14:54:15 +00:00
parent dabe850551
commit e9d9715dcf
5 changed files with 362 additions and 12 deletions

View File

@ -1,3 +1,13 @@
2007-12-05 Tomas Frydrych <tf@openedhand.com>
* clutter/clutter-actor.c:
* clutter/clutter-actor.h:
* tests/Makefile.am:
* tests/test-unproject:
(clutter_actor_transform_stage_point):
Added new function for translating stage coordinates into local
actor coordinates.
2007-12-04 Emmanuele Bassi <ebassi@openedhand.com>
* clutter/clutter-score.c: Better document ClutterScore
@ -39,7 +49,7 @@
(clutter_stage_glx_unrealize): unbind all shaders on stage unrealize.
* clutter/Makefile.am: added clutter-shader.[ch]
* clutter/clutter-actor.[ch]: adding shader capability to
* clutter/clutter-actor.[ch]: adding shader capability to
actors.
* clutter/clutter-feature.h: added CLUTTER_FEATURE_SHADERS_GLSL
* clutter/clutter-private.h: added stack of shaders to context.

View File

@ -928,8 +928,6 @@ clutter_actor_paint (ClutterActor *self)
cogl_pop_matrix();
}
#undef M
static void
clutter_actor_real_request_coords (ClutterActor *self,
ClutterActorBox *box)
@ -3870,7 +3868,7 @@ parse_units (ClutterActor *self,
else if (G_VALUE_HOLDS (&value, G_TYPE_DOUBLE))
{
gint val;
if (CLUTTER_PRIVATE_FLAGS (self) & CLUTTER_ACTOR_IS_TOPLEVEL)
{
g_warning ("Unable to set percentage of %s on a top-level "
@ -3948,6 +3946,191 @@ clutter_scriptable_iface_init (ClutterScriptableIface *iface)
iface->parse_custom_node = clutter_actor_parse_custom_node;
}
/**
* clutter_actor_transform_stage_point
* @self: A #ClutterActor
* @x: x screen coordiance of point to unproject, in #ClutterUnit
* @y: y screen coordiance of point to unproject, in #ClutterUnit
* @x: x_out location where to store the unprojected x coordinance, in
* #ClutterUnit.
* @y: y_out location where to store the unprojected y coordinance, in
* #ClutterUnit.
*
* Return value: TRUE if conversion was successful.
*
* The function translates point with screen coordinates x,y to coordinates
* relative to the actor, i.e., it can be used, to translate screen events
* from global screen coordinates into local coordinates.
*
* The conversion can fail, notably if the transform stack results in the
* actor being projected on the screen as a mere line.
*
* The conversion should not be expected to be pixel-perfect due to the nature
* of the operation. In general the error grows when the skewing of the actor
* rectangle on screen increases.
*
* WARNING: This function is fairly computationally intensive.
*
* Since: 0.6
*/
gboolean
clutter_actor_transform_stage_point (ClutterActor *self,
ClutterUnit x,
ClutterUnit y,
ClutterUnit *x_out,
ClutterUnit *y_out)
{
ClutterVertex v[4];
ClutterFixed ST[3][3];
ClutterFixed RQ[3][3];
int du, dv, xi, yi;
ClutterFixed xf, yf, wf, px, py, det;
ClutterActorPrivate *priv;
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
priv = self->priv;
/*
* This implementation is based on the quad -> quad projection algorithm
* described by Paul Heckbert in
*
* http://www.cs.cmu.edu/~ph/texfund/texfund.pdf
*
* and the sample implementaion at http://www.cs.cmu.edu/~ph/src/texfund/.
*
* Our texture is a rectangle with origin [0,0], so we are mapping from quad
* to rectangle only, which significantly simplifies things; the function
* calls have been unrolled, and most of the math is done in fixed point.
*/
clutter_actor_get_vertices (self, v);
/*
* Keeping these as ints simplifies the multiplication (no significant loss
* of precission here).
*/
du = CLUTTER_UNITS_TO_DEVICE (priv->coords.x2 - priv->coords.x1);
dv = CLUTTER_UNITS_TO_DEVICE (priv->coords.y2 - priv->coords.y1);
if (!du || !dv)
return FALSE;
#define FP2FX CLUTTER_FLOAT_TO_FIXED
#define FX2FP CLUTTER_FIXED_TO_DOUBLE
#define FP2INT CLUTTER_FLOAT_TO_INT
#define DET2X(a,b, c,d) (CFX_QMUL(a,d) - CFX_QMUL(b,c))
/*
* First, find mapping from unit uv square to xy quadrilateral; this
* equivalent to the pmap_square_quad() functions in the sample
* implementation, which we can simplify, since our target is always
* a rectangle.
*/
px = v[0].x - v[1].x + v[3].x - v[2].x;
py = v[0].y - v[1].y + v[3].y - v[2].y;
if (!px && !py)
{ /* affine transform */
RQ[0][0] = v[1].x - v[0].x;
RQ[1][0] = v[3].x - v[1].x;
RQ[2][0] = v[0].x;
RQ[0][1] = v[1].y - v[0].y;
RQ[1][1] = v[3].y - v[1].y;
RQ[2][1] = v[0].y;
RQ[0][2] = 0;
RQ[1][2] = 0;
RQ[2][2] = CFX_ONE;
}
else
{ /* projective transform */
ClutterFixed dx1, dx2, dy1, dy2, del;
dx1 = v[1].x - v[3].x;
dx2 = v[2].x - v[3].x;
dy1 = v[1].y - v[3].y;
dy2 = v[2].y - v[3].y;
del = DET2X (dx1,dx2, dy1,dy2);
if (!del)
return FALSE;
/*
* The division here needs to be done in floating point for
* precisions reasons.
*/
RQ[0][2] = FP2FX (FX2FP (DET2X (px,dx2, py,dy2) / FX2FP (del)));
RQ[1][2] = FP2FX (FX2FP (DET2X (dx1,px, dy1,py) / FX2FP (del)));
RQ[1][2] = CFX_DIV (DET2X(dx1,px, dy1,py), del);
RQ[2][2] = CFX_ONE;
RQ[0][0] = v[1].x - v[0].x + CFX_QMUL (RQ[0][2], v[1].x);
RQ[1][0] = v[2].x - v[0].x + CFX_QMUL (RQ[1][2], v[2].x);
RQ[2][0] = v[0].x;
RQ[0][1] = v[1].y - v[0].y + CFX_QMUL (RQ[0][2], v[1].y);
RQ[1][1] = v[2].y - v[0].y + CFX_QMUL (RQ[1][2], v[2].y);
RQ[2][1] = v[0].y;
}
/*
* Now combine with transform from our rectangle (u0,v0,u1,v1) to unit
* square. Since our rectangle is based at 0,0 we only need to scale.
*/
RQ[0][0] /= du;
RQ[1][0] /= dv;
RQ[0][1] /= du;
RQ[1][1] /= dv;
RQ[0][2] /= du;
RQ[1][2] /= dv;
/*
* Now RQ is transform from uv rectangle to xy quadrilateral; we need an
* inverse of that.
*/
ST[0][0] = DET2X(RQ[1][1], RQ[1][2], RQ[2][1], RQ[2][2]);
ST[1][0] = DET2X(RQ[1][2], RQ[1][0], RQ[2][2], RQ[2][0]);
ST[2][0] = DET2X(RQ[1][0], RQ[1][1], RQ[2][0], RQ[2][1]);
ST[0][1] = DET2X(RQ[2][1], RQ[2][2], RQ[0][1], RQ[0][2]);
ST[1][1] = DET2X(RQ[2][2], RQ[2][0], RQ[0][2], RQ[0][0]);
ST[2][1] = DET2X(RQ[2][0], RQ[2][1], RQ[0][0], RQ[0][1]);
ST[0][2] = DET2X(RQ[0][1], RQ[0][2], RQ[1][1], RQ[1][2]);
ST[1][2] = DET2X(RQ[0][2], RQ[0][0], RQ[1][2], RQ[1][0]);
ST[2][2] = DET2X(RQ[0][0], RQ[0][1], RQ[1][0], RQ[1][1]);
/*
* Check the resutling martix is OK.
*/
det = CFX_QMUL (RQ[0][0], ST[0][0]) + CFX_QMUL (RQ[0][1], ST[0][1]) +
CFX_QMUL (RQ[0][2], ST[0][2]);
if (!det)
return FALSE;
/*
* Now transform our point with the ST matrix; the notional w coordiance
* is 1, hence the last part is simply added.
*/
xi = CLUTTER_UNITS_TO_DEVICE (x);
yi = CLUTTER_UNITS_TO_DEVICE (y);
xf = xi*ST[0][0] + yi*ST[1][0] + ST[2][0];
yf = xi*ST[0][1] + yi*ST[1][1] + ST[2][1];
wf = xi*ST[0][2] + yi*ST[1][2] + ST[2][2];
/*
* The division needs to be done in floating point for precission reasons.
*/
*x_out = CLUTTER_UNITS_FROM_FLOAT (FX2FP (xf) / FX2FP (wf));
*y_out = CLUTTER_UNITS_FROM_FLOAT (FX2FP (yf) / FX2FP (wf));
#undef FP2FX
#undef FX2FP
#undef FP2INT
#undef DET2X
return TRUE;
}
/*
* ClutterGeometry
*/
@ -4097,7 +4280,7 @@ gboolean clutter_actor_apply_shader (ClutterActor *self,
ShaderData *shader_data;
g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE);
actor_priv = self->priv;
shader_data = actor_priv->shader_data;
@ -4143,7 +4326,7 @@ clutter_actor_shader_pre_paint (ClutterActor *actor,
ClutterMainContext *context;
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
actor_priv = actor->priv;
shader_data = actor_priv->shader_data;
@ -4175,7 +4358,7 @@ clutter_actor_shader_post_paint (ClutterActor *actor)
ClutterMainContext *context;
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
actor_priv = actor->priv;
shader_data = actor_priv->shader_data;
@ -4211,7 +4394,7 @@ clutter_actor_set_shader_param (ClutterActor *actor,
BoxedFloat *box;
g_return_if_fail (CLUTTER_IS_ACTOR (actor));
actor_priv = actor->priv;
shader_data = actor_priv->shader_data;
@ -4222,3 +4405,5 @@ clutter_actor_set_shader_param (ClutterActor *actor,
box->value = value;
g_hash_table_insert (shader_data->float1f_hash, g_strdup (param), box);
}
#undef M

View File

@ -415,6 +415,12 @@ void clutter_actor_get_anchor_pointu (ClutterActor *se
void clutter_actor_set_anchor_point_from_gravity (ClutterActor *self,
ClutterGravity gravity);
gboolean clutter_actor_transform_stage_point (ClutterActor *self,
ClutterUnit x,
ClutterUnit y,
ClutterUnit *x_out,
ClutterUnit *y_out);
G_END_DECLS
#endif /* _HAVE_CLUTTER_ACTOR_H */

View File

@ -2,12 +2,13 @@ noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \
test-actors test-behave test-text test-entry test-project \
test-perspective test-rotate test-depth \
test-threads test-timeline test-score test-script \
test-model test-grab test-effects test-fullscreen test-shader
test-model test-grab test-effects test-fullscreen \
test-shader test-unproject
INCLUDES = -I$(top_srcdir)/
LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la
AM_CFLAGS = $(CLUTTER_CFLAGS)
AM_LDFLAGS = $(CLUTTER_LIBS)
AM_LDFLAGS = $(CLUTTER_LIBS)
test_textures_SOURCES = test-textures.c
test_events_SOURCES = test-events.c
@ -15,10 +16,11 @@ test_offscreen_SOURCES = test-offscreen.c
test_scale_SOURCES = test-scale.c
test_actors_SOURCES = test-actors.c
test_grab_SOURCES = test-grab.c
test_behave_SOURCES = test-behave.c
test_text_SOURCES = test-text.c
test_behave_SOURCES = test-behave.c
test_text_SOURCES = test-text.c
test_entry_SOURCES = test-entry.c
test_project_SOURCES = test-project.c
test_unproject_SOURCES = test-unproject.c
test_perspective_SOURCES = test-perspective.c
test_rotate_SOURCES = test-rotate.c
test_depth_SOURCES = test-depth.c

147
tests/test-unproject.c Normal file
View File

@ -0,0 +1,147 @@
#include <clutter/clutter.h>
#include <stdio.h>
#include <stdlib.h>
ClutterActor *label;
#define RECT_L 200
#define RECT_T 150
#define RECT_W 320
#define RECT_H 240
void
on_event (ClutterStage *stage,
ClutterEvent *event,
gpointer user_data)
{
static ClutterActor * dragging = NULL;
switch (event->type)
{
case CLUTTER_BUTTON_PRESS:
{
gint x, y;
ClutterActor * actor;
ClutterUnit xu2, yu2;
clutter_event_get_coords (event, &x, &y);
actor = clutter_stage_get_actor_at_pos (stage, x, y);
if (clutter_actor_transform_stage_point (actor,
CLUTTER_UNITS_FROM_DEVICE (x),
CLUTTER_UNITS_FROM_DEVICE (y),
&xu2, &yu2))
{
gchar *txt;
if (actor != stage)
txt = g_strdup_printf ("Click on rectangle\n"
"Screen coords: [%d, %d]\n"
"Local coords : [%d, %d]",
x, y,
CLUTTER_UNITS_TO_DEVICE (xu2),
CLUTTER_UNITS_TO_DEVICE (yu2));
else
txt = g_strdup_printf ("Click on stage\n"
"Screen coords: [%d, %d]\n"
"Local coords : [%d, %d]",
x, y,
CLUTTER_UNITS_TO_DEVICE (xu2),
CLUTTER_UNITS_TO_DEVICE (yu2));
clutter_label_set_text (CLUTTER_LABEL (label), txt);
g_free (txt);
}
else
clutter_label_set_text (CLUTTER_LABEL (label),
"Unprojection failed.");
}
break;
default:
break;
}
}
int
main (int argc, char *argv[])
{
gchar *txt;
ClutterActor *rect, *stage, *label0;
int i, rotate_x = 0, rotate_y = 60, rotate_z = 0;
ClutterColor stage_clr = { 0x0, 0x0, 0x0, 0xff },
white = { 0xff, 0xff, 0xff, 0xff },
blue = { 0, 0xff, 0xff, 0xff };
for (i = 0; i < argc; ++i)
{
if (!strncmp (argv[i], "--rotate-x", 10))
{
rotate_x = atoi (argv[i] + 11);
}
else if (!strncmp (argv[i], "--rotate-y", 10))
{
rotate_y = atoi (argv[i] + 11);
}
else if (!strncmp (argv[i], "--rotate-z", 10))
{
rotate_z = atoi (argv[i] + 11);
}
else if (!strncmp (argv[i], "--help", 6))
{
printf ("%s [--rotage-x=degrees] [--rotage-y=degrees] "
"[--rotage-z=degrees]\n",
argv[0]);
exit (0);
}
}
clutter_init (&argc, &argv);
stage = clutter_stage_get_default ();
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_clr);
clutter_actor_set_size (stage, 640, 480);
rect = clutter_rectangle_new_with_color (&white);
clutter_actor_set_size (rect, RECT_W, RECT_H);
clutter_actor_set_position (rect, RECT_L, RECT_T);
clutter_actor_set_rotation (rect, CLUTTER_X_AXIS, rotate_x, 0, 0, 0);
clutter_actor_set_rotation (rect, CLUTTER_Y_AXIS, rotate_y, 0, 0, 0);
clutter_actor_set_rotation (rect, CLUTTER_Z_AXIS, rotate_z, 0, 0, 0);
clutter_group_add (CLUTTER_GROUP (stage), rect);
txt = g_strdup_printf ("Rectangle: L %d, R %d, T %d, B %d\n"
"Rotation : x %d, y %d, z %d",
RECT_L, RECT_L + RECT_W,
RECT_T, RECT_T + RECT_H,
rotate_x, rotate_y, rotate_z);
label0 = clutter_label_new_with_text ("Mono 8pt", txt);
clutter_label_set_color (CLUTTER_LABEL (label0), &white);
clutter_actor_set_position (label0, 10, 10);
clutter_group_add (CLUTTER_GROUP (stage), label0);
g_free (txt);
label =
clutter_label_new_with_text ("Mono 8pt", "Click around!");
clutter_label_set_color (CLUTTER_LABEL (label), &blue);
clutter_actor_set_position (label, 10, 50);
clutter_group_add (CLUTTER_GROUP (stage), label);
clutter_actor_show_all (stage);
g_signal_connect (stage, "event", G_CALLBACK (on_event), NULL);
clutter_main();
return EXIT_SUCCESS;
}