2008-06-02 08:34:10 -04:00
|
|
|
#include <clutter/clutter.h>
|
2008-11-07 14:32:28 -05:00
|
|
|
#include <gmodule.h>
|
2011-01-24 12:17:16 -05:00
|
|
|
#include <math.h>
|
2008-06-02 08:34:10 -04:00
|
|
|
|
|
|
|
typedef struct _CallbackData CallbackData;
|
2008-12-04 08:45:09 -05:00
|
|
|
typedef struct _Clip Clip;
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
CLIP_NONE,
|
|
|
|
CLIP_RECTANGLE,
|
2011-01-24 12:17:16 -05:00
|
|
|
CLIP_ROTATED_RECTANGLE,
|
2008-12-04 08:45:09 -05:00
|
|
|
CLIP_SHAPES
|
|
|
|
} ClipType;
|
|
|
|
|
|
|
|
struct _Clip
|
|
|
|
{
|
|
|
|
ClipType type;
|
2009-01-07 07:33:40 -05:00
|
|
|
gint x1, y1, x2, y2;
|
2008-12-04 08:45:09 -05:00
|
|
|
};
|
2008-06-02 08:34:10 -04:00
|
|
|
|
|
|
|
struct _CallbackData
|
|
|
|
{
|
2008-12-04 08:45:09 -05:00
|
|
|
ClutterActor *stage;
|
|
|
|
CoglHandle hand;
|
|
|
|
|
|
|
|
Clip current_clip;
|
2009-01-07 07:33:40 -05:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
GSList *clips;
|
2008-06-02 08:34:10 -04:00
|
|
|
};
|
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
static const char
|
|
|
|
instructions[] =
|
2013-12-02 14:13:55 -05:00
|
|
|
"Left button and drag to draw a rectangle, control+left to draw a rotated "
|
|
|
|
"rectangle or shift+left to draw a path. Press 'r' to reset or 'u' "
|
|
|
|
"to undo the last clip.";
|
2008-12-04 08:45:09 -05:00
|
|
|
|
|
|
|
static void
|
|
|
|
path_shapes (gint x, gint y, gint width, gint height)
|
|
|
|
{
|
2009-03-09 13:34:23 -04:00
|
|
|
cogl_path_move_to (x, y);
|
|
|
|
cogl_path_line_to (x, (y + height * 4 / 5));
|
|
|
|
cogl_path_line_to ((x + width * 4 / 15), (y + height * 4 / 5));
|
2008-12-04 08:45:09 -05:00
|
|
|
cogl_path_close ();
|
|
|
|
|
2009-03-13 08:20:26 -04:00
|
|
|
cogl_path_rectangle (x + width / 3,
|
2009-03-09 13:34:23 -04:00
|
|
|
y,
|
2009-03-13 08:20:26 -04:00
|
|
|
x + width * 9 / 15,
|
|
|
|
y + height * 4 / 5);
|
2008-12-04 08:45:09 -05:00
|
|
|
|
2009-03-09 13:34:23 -04:00
|
|
|
cogl_path_ellipse ((x + width * 4 / 5),
|
|
|
|
(y + height * 2 / 5),
|
|
|
|
(width * 2 / 15),
|
|
|
|
(height * 2 / 5));
|
2008-12-04 08:45:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
draw_shapes (gint x, gint y)
|
|
|
|
{
|
|
|
|
path_shapes (x, y, 300, 100);
|
|
|
|
cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
|
|
|
|
cogl_path_fill_preserve ();
|
|
|
|
cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
|
|
|
|
cogl_path_stroke ();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
make_clip_path (Clip *clip)
|
|
|
|
{
|
|
|
|
switch (clip->type)
|
|
|
|
{
|
|
|
|
case CLIP_NONE:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CLIP_RECTANGLE:
|
2009-03-09 13:34:23 -04:00
|
|
|
cogl_path_rectangle (clip->x1,
|
|
|
|
clip->y1,
|
2009-03-13 08:20:26 -04:00
|
|
|
clip->x2,
|
|
|
|
clip->y2);
|
2008-12-04 08:45:09 -05:00
|
|
|
break;
|
|
|
|
|
2011-01-24 12:17:16 -05:00
|
|
|
case CLIP_ROTATED_RECTANGLE:
|
|
|
|
{
|
|
|
|
int size = MIN (ABS (clip->x2 - clip->x1),
|
|
|
|
ABS (clip->y2 - clip->y1));
|
|
|
|
int cx = (clip->x1 + clip->x2) / 2;
|
|
|
|
int cy = (clip->y1 + clip->y2) / 2;
|
|
|
|
|
|
|
|
cogl_path_move_to (cx - size / 2, cy);
|
|
|
|
cogl_path_line_to (cx, cy - size / 2);
|
|
|
|
cogl_path_line_to (cx + size / 2, cy);
|
|
|
|
cogl_path_line_to (cx, cy + size / 2);
|
|
|
|
cogl_path_close ();
|
|
|
|
}
|
2008-12-04 08:45:09 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CLIP_SHAPES:
|
|
|
|
{
|
|
|
|
int x, y, width, height;
|
|
|
|
|
|
|
|
if (clip->x1 < clip->x2)
|
|
|
|
{
|
|
|
|
x = clip->x1;
|
|
|
|
width = clip->x2 - x;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
x = clip->x2;
|
|
|
|
width = clip->x1 - x;
|
|
|
|
}
|
|
|
|
if (clip->y1 < clip->y2)
|
|
|
|
{
|
|
|
|
y = clip->y1;
|
|
|
|
height = clip->y2 - y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
y = clip->y2;
|
|
|
|
height = clip->y1 - y;
|
|
|
|
}
|
|
|
|
|
|
|
|
path_shapes (x, y, width, height);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-02 08:34:10 -04:00
|
|
|
static void
|
2008-12-04 08:45:09 -05:00
|
|
|
on_paint (ClutterActor *actor, CallbackData *data)
|
2008-06-02 08:34:10 -04:00
|
|
|
{
|
|
|
|
int i;
|
2008-12-04 08:45:09 -05:00
|
|
|
ClutterGeometry stage_size;
|
2009-01-07 07:33:40 -05:00
|
|
|
gint hand_width, hand_height;
|
2008-12-04 08:45:09 -05:00
|
|
|
GSList *node;
|
|
|
|
|
|
|
|
clutter_actor_get_allocation_geometry (data->stage, &stage_size);
|
|
|
|
|
|
|
|
hand_width = cogl_texture_get_width (data->hand);
|
|
|
|
hand_height = cogl_texture_get_height (data->hand);
|
|
|
|
|
|
|
|
/* Setup the clipping */
|
|
|
|
for (node = data->clips; node; node = node->next)
|
|
|
|
{
|
|
|
|
Clip *clip = (Clip *) node->data;
|
|
|
|
|
|
|
|
if (clip->type == CLIP_RECTANGLE)
|
2009-11-05 11:50:24 -05:00
|
|
|
cogl_clip_push_rectangle (clip->x1,
|
|
|
|
clip->y1,
|
|
|
|
clip->x2,
|
|
|
|
clip->y2);
|
2011-01-24 12:17:16 -05:00
|
|
|
else if (clip->type == CLIP_ROTATED_RECTANGLE)
|
|
|
|
{
|
|
|
|
float size = MIN (ABS (clip->x2 - clip->x1),
|
|
|
|
ABS (clip->y2 - clip->y1));
|
|
|
|
int cx = (clip->x1 + clip->x2) / 2;
|
|
|
|
int cy = (clip->y1 + clip->y2) / 2;
|
|
|
|
|
|
|
|
size = sqrtf ((size / 2) * (size / 2) * 2);
|
|
|
|
|
|
|
|
cogl_push_matrix ();
|
|
|
|
|
|
|
|
/* Rotate 45° about the centre point */
|
|
|
|
cogl_translate (cx, cy, 0.0f);
|
|
|
|
cogl_rotate (45.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
cogl_clip_push_rectangle (-size / 2, -size / 2, size / 2, size / 2);
|
|
|
|
|
|
|
|
cogl_pop_matrix ();
|
|
|
|
}
|
2008-12-04 08:45:09 -05:00
|
|
|
else
|
|
|
|
{
|
|
|
|
make_clip_path (clip);
|
2009-02-12 06:08:00 -05:00
|
|
|
cogl_clip_push_from_path ();
|
2008-12-04 08:45:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Draw a rectangle filling the entire stage */
|
|
|
|
cogl_set_source_color4ub (0x80, 0x80, 0xff, 0xff);
|
|
|
|
cogl_rectangle (0, 0, stage_size.width, stage_size.height);
|
|
|
|
|
|
|
|
draw_shapes (10, 10);
|
|
|
|
|
|
|
|
/* Draw the hand at different rotations */
|
|
|
|
for (i = -2; i <= 2; i++)
|
2008-06-02 08:34:10 -04:00
|
|
|
{
|
2008-12-04 08:45:09 -05:00
|
|
|
cogl_push_matrix ();
|
|
|
|
|
|
|
|
cogl_translate (stage_size.width / 2 + stage_size.width / 6 * i,
|
|
|
|
stage_size.height / 2, 0);
|
|
|
|
|
|
|
|
cogl_rotate (i * 40, 0, 1, 0);
|
|
|
|
|
|
|
|
cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff);
|
|
|
|
|
Fully integrates CoglMaterial throughout the rest of Cogl
This glues CoglMaterial in as the fundamental way that Cogl describes how to
fill in geometry.
It adds cogl_set_source (), which is used to set the material which will be
used by all subsequent drawing functions
It adds cogl_set_source_texture as a convenience for setting up a default
material with a single texture layer, and cogl_set_source_color is now also
a convenience for setting up a material with a solid fill.
"drawing functions" include, cogl_rectangle, cogl_texture_rectangle,
cogl_texture_multiple_rectangles, cogl_texture_polygon (though the
cogl_texture_* funcs have been renamed; see below for details),
cogl_path_fill/stroke and cogl_vertex_buffer_draw*.
cogl_texture_rectangle, cogl_texture_multiple_rectangles and
cogl_texture_polygon no longer take a texture handle; instead the current
source material is referenced. The functions have also been renamed to:
cogl_rectangle_with_texture_coords, cogl_rectangles_with_texture_coords
and cogl_polygon respectivly.
Most code that previously did:
cogl_texture_rectangle (tex_handle, x, y,...);
needs to be changed to now do:
cogl_set_source_texture (tex_handle);
cogl_rectangle_with_texture_coords (x, y,....);
In the less likely case where you were blending your source texture with a color
like:
cogl_set_source_color4ub (r,g,b,a); /* where r,g,b,a isn't just white */
cogl_texture_rectangle (tex_handle, x, y,...);
you will need your own material to do that:
mat = cogl_material_new ();
cogl_material_set_color4ub (r,g,b,a);
cogl_material_set_layer (mat, 0, tex_handle));
cogl_set_source_material (mat);
Code that uses the texture coordinates, 0, 0, 1, 1 don't need to use
cog_rectangle_with_texure_coords since these are the coordinates that
cogl_rectangle will use.
For cogl_texture_polygon; as well as dropping the texture handle, the
n_vertices and vertices arguments were transposed for consistency. So
code previously written as:
cogl_texture_polygon (tex_handle, 3, verts, TRUE);
need to be written as:
cogl_set_source_texture (tex_handle);
cogl_polygon (verts, 3, TRUE);
All of the unit tests have been updated to now use the material API and
test-cogl-material has been renamed to test-cogl-multitexture since any
textured quad is now technically a test of CoglMaterial but this test
specifically creates a material with multiple texture layers.
Note: The GLES backend has not been updated yet; that will be done in a
following commit.
2009-01-23 11:15:40 -05:00
|
|
|
cogl_set_source_texture (data->hand);
|
2009-03-09 13:34:23 -04:00
|
|
|
cogl_rectangle_with_texture_coords ((-hand_width / 2),
|
|
|
|
(-hand_height / 2),
|
|
|
|
(hand_width / 2),
|
|
|
|
(hand_height / 2),
|
|
|
|
0, 0, 1, 1);
|
2008-12-04 08:45:09 -05:00
|
|
|
|
|
|
|
cogl_pop_matrix ();
|
|
|
|
}
|
|
|
|
|
|
|
|
draw_shapes (stage_size.width - 310, stage_size.height - 110);
|
|
|
|
|
|
|
|
/* Remove all of the clipping */
|
2009-02-12 06:08:00 -05:00
|
|
|
g_slist_foreach (data->clips, (GFunc) cogl_clip_pop, NULL);
|
2008-12-04 08:45:09 -05:00
|
|
|
|
|
|
|
/* Draw the bounding box for each of the clips */
|
|
|
|
for (node = data->clips; node; node = node->next)
|
|
|
|
{
|
|
|
|
Clip *clip = (Clip *) node->data;
|
|
|
|
|
|
|
|
make_clip_path (clip);
|
|
|
|
cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff);
|
|
|
|
cogl_path_stroke ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Draw the bounding box for the pending new clip */
|
|
|
|
if (data->current_clip.type != CLIP_NONE)
|
|
|
|
{
|
|
|
|
make_clip_path (&data->current_clip);
|
|
|
|
cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff);
|
|
|
|
cogl_path_stroke ();
|
2008-06-02 08:34:10 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
static gboolean
|
|
|
|
on_button_press (ClutterActor *stage, ClutterButtonEvent *event,
|
|
|
|
CallbackData *data)
|
2008-06-02 08:34:10 -04:00
|
|
|
{
|
2008-12-04 08:45:09 -05:00
|
|
|
data->current_clip.x1 = data->current_clip.x2 = event->x;
|
|
|
|
data->current_clip.y1 = data->current_clip.y2 = event->y;
|
2008-06-02 08:34:10 -04:00
|
|
|
|
2013-12-02 14:13:55 -05:00
|
|
|
switch (event->button)
|
|
|
|
{
|
|
|
|
case CLUTTER_BUTTON_PRIMARY:
|
|
|
|
if (clutter_event_has_shift_modifier ((ClutterEvent *) event))
|
|
|
|
data->current_clip.type = CLIP_SHAPES;
|
|
|
|
else if (clutter_event_has_control_modifier ((ClutterEvent *) event))
|
|
|
|
data->current_clip.type = CLIP_ROTATED_RECTANGLE;
|
|
|
|
else
|
|
|
|
data->current_clip.type = CLIP_RECTANGLE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CLUTTER_BUTTON_SECONDARY:
|
|
|
|
data->current_clip.type = CLIP_ROTATED_RECTANGLE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CLUTTER_BUTTON_MIDDLE:
|
|
|
|
data->current_clip.type = CLIP_SHAPES;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
data->current_clip.type = CLIP_NONE;
|
|
|
|
break;
|
|
|
|
}
|
2008-06-02 08:34:10 -04:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
clutter_actor_queue_redraw (stage);
|
2008-06-02 08:34:10 -04:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
return FALSE;
|
|
|
|
}
|
2008-06-02 08:34:10 -04:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
static gboolean
|
|
|
|
on_button_release (ClutterActor *stage, ClutterButtonEvent *event,
|
|
|
|
CallbackData *data)
|
|
|
|
{
|
|
|
|
if (data->current_clip.type != CLIP_NONE)
|
|
|
|
{
|
|
|
|
data->clips = g_slist_prepend (data->clips,
|
|
|
|
g_slice_copy (sizeof (Clip),
|
|
|
|
&data->current_clip));
|
2008-06-02 08:34:10 -04:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
data->current_clip.type = CLIP_NONE;
|
|
|
|
}
|
2008-06-02 08:34:10 -04:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
clutter_actor_queue_redraw (stage);
|
2008-06-02 08:34:10 -04:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
return FALSE;
|
|
|
|
}
|
2008-06-02 08:34:10 -04:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
static gboolean
|
|
|
|
on_motion (ClutterActor *stage, ClutterMotionEvent *event,
|
|
|
|
CallbackData *data)
|
|
|
|
{
|
|
|
|
if (data->current_clip.type != CLIP_NONE)
|
2008-06-02 08:34:10 -04:00
|
|
|
{
|
2008-12-04 08:45:09 -05:00
|
|
|
data->current_clip.x2 = event->x;
|
|
|
|
data->current_clip.y2 = event->y;
|
2009-01-07 07:33:40 -05:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
clutter_actor_queue_redraw (stage);
|
2008-06-02 08:34:10 -04:00
|
|
|
}
|
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_clips (CallbackData *data)
|
|
|
|
{
|
|
|
|
GSList *node;
|
2008-06-02 08:34:10 -04:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
for (node = data->clips; node; node = node->next)
|
|
|
|
g_slice_free (Clip, node->data);
|
2008-06-02 08:34:10 -04:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
g_slist_free (data->clips);
|
2008-06-02 08:34:10 -04:00
|
|
|
|
2008-12-04 08:45:09 -05:00
|
|
|
data->clips = NULL;
|
|
|
|
}
|
|
|
|
|
2010-05-27 04:23:26 -04:00
|
|
|
static gboolean
|
2010-09-10 08:41:49 -04:00
|
|
|
on_key_press (ClutterActor *stage,
|
|
|
|
ClutterEvent *event,
|
2008-12-04 08:45:09 -05:00
|
|
|
CallbackData *data)
|
|
|
|
{
|
2010-09-10 08:41:49 -04:00
|
|
|
switch (clutter_event_get_key_symbol (event))
|
2008-12-04 08:45:09 -05:00
|
|
|
{
|
2010-09-10 08:41:49 -04:00
|
|
|
case CLUTTER_KEY_r:
|
2008-12-04 08:45:09 -05:00
|
|
|
free_clips (data);
|
|
|
|
clutter_actor_queue_redraw (stage);
|
|
|
|
break;
|
|
|
|
|
2010-09-10 08:41:49 -04:00
|
|
|
case CLUTTER_KEY_u:
|
2008-12-04 08:45:09 -05:00
|
|
|
if (data->clips)
|
|
|
|
{
|
|
|
|
g_slice_free (Clip, data->clips->data);
|
|
|
|
data->clips = g_slist_delete_link (data->clips, data->clips);
|
|
|
|
clutter_actor_queue_redraw (stage);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
G_MODULE_EXPORT int
|
|
|
|
test_clip_main (int argc, char **argv)
|
|
|
|
{
|
|
|
|
CallbackData data;
|
|
|
|
ClutterActor *stub_actor, *label;
|
2009-11-05 12:30:33 -05:00
|
|
|
gchar *file;
|
2008-12-04 08:45:09 -05:00
|
|
|
|
2011-02-21 19:19:35 -05:00
|
|
|
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
|
|
|
|
return 1;
|
2008-12-04 08:45:09 -05:00
|
|
|
|
|
|
|
data.current_clip.type = CLIP_NONE;
|
|
|
|
data.clips = NULL;
|
|
|
|
|
2011-11-09 08:41:15 -05:00
|
|
|
data.stage = clutter_stage_new ();
|
|
|
|
clutter_stage_set_title (CLUTTER_STAGE (data.stage), "Clipping");
|
|
|
|
g_signal_connect (data.stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
|
2008-12-04 08:45:09 -05:00
|
|
|
|
|
|
|
stub_actor = clutter_rectangle_new ();
|
|
|
|
clutter_container_add (CLUTTER_CONTAINER (data.stage), stub_actor, NULL);
|
|
|
|
|
2009-11-05 12:30:33 -05:00
|
|
|
file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL);
|
|
|
|
data.hand = cogl_texture_new_from_file (file,
|
2009-01-18 10:00:18 -05:00
|
|
|
COGL_TEXTURE_NONE,
|
|
|
|
COGL_PIXEL_FORMAT_ANY,
|
|
|
|
NULL);
|
2009-11-05 12:30:33 -05:00
|
|
|
g_free (file);
|
2008-12-04 08:45:09 -05:00
|
|
|
|
2008-12-11 08:48:01 -05:00
|
|
|
label = clutter_text_new_with_text ("Sans 12px", instructions);
|
|
|
|
clutter_text_set_line_wrap (CLUTTER_TEXT (label), TRUE);
|
2008-12-04 08:45:09 -05:00
|
|
|
clutter_actor_set_width (label, clutter_actor_get_width (data.stage) - 310);
|
|
|
|
clutter_actor_set_y (label,
|
|
|
|
clutter_actor_get_height (data.stage)
|
|
|
|
- clutter_actor_get_height (label));
|
|
|
|
clutter_container_add (CLUTTER_CONTAINER (data.stage), label, NULL);
|
|
|
|
|
|
|
|
g_signal_connect (stub_actor, "paint", G_CALLBACK (on_paint), &data);
|
|
|
|
|
|
|
|
g_signal_connect (data.stage, "button-press-event",
|
|
|
|
G_CALLBACK (on_button_press), &data);
|
|
|
|
g_signal_connect (data.stage, "button-release-event",
|
|
|
|
G_CALLBACK (on_button_release), &data);
|
|
|
|
g_signal_connect (data.stage, "motion-event",
|
|
|
|
G_CALLBACK (on_motion), &data);
|
|
|
|
g_signal_connect (data.stage, "key-press-event",
|
|
|
|
G_CALLBACK (on_key_press), &data);
|
2008-06-02 08:34:10 -04:00
|
|
|
|
|
|
|
clutter_actor_show (data.stage);
|
|
|
|
|
|
|
|
clutter_main ();
|
|
|
|
|
2009-04-01 12:16:44 -04:00
|
|
|
cogl_handle_unref (data.hand);
|
2008-12-04 08:45:09 -05:00
|
|
|
|
|
|
|
free_clips (&data);
|
|
|
|
|
2008-06-02 08:34:10 -04:00
|
|
|
return 0;
|
|
|
|
}
|
2011-11-09 08:41:15 -05:00
|
|
|
|
|
|
|
G_MODULE_EXPORT const char *
|
|
|
|
test_clip_describe (void)
|
|
|
|
{
|
|
|
|
return "Actor clipping with various techniques";
|
|
|
|
}
|