1 /*
2 * Clutter.
3 *
4 * An OpenGL based 'interactive canvas' library.
5 *
6 * Authored By:
7 * Matthew Allum <mallum@openedhand.com>
8 * Neil Roberts <neil@linux.intel.com>
9 *
10 * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd
11 * Copyright (C) 2009, 2010 Intel Corp
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
25 */
26
27 /**
28 * SECTION:clutter-behaviour-path
29 * @Title: ClutterBehaviourPath
30 * @short_description: A behaviour for moving actors along a #ClutterPath
31 * @Deprecated: 1.6: Use #ClutterPathConstraint and clutter_actor_animate()
32 * with the #ClutterPathConstraint:offset property instead.
33 *
34 * #ClutterBehaviourPath interpolates actors along a defined path.
35 *
36 * A path is described by a #ClutterPath object. The path can contain
37 * straight line parts and bezier curves. If the path contains
38 * %CLUTTER_PATH_MOVE_TO parts then the actors will jump to those
39 * coordinates. This can be used make disjoint paths.
40 *
41 * When creating a path behaviour in a #ClutterScript, you can specify
42 * the path property directly as a string. For example:
43 *
44 * |[
45 * {
46 * "id" : "spline-path",
47 * "type" : "ClutterBehaviourPath",
48 * "path" : "M 50 50 L 100 100",
49 * "alpha" : {
50 * "timeline" : "main-timeline",
51 * "function" : "ramp
52 * }
53 * }
54 * ]|
55 *
56 * If the alpha function is a periodic function, i.e. it returns to
57 * 0.0 after reaching 1.0, then the actors will walk the path back to the
58 * starting #ClutterKnot.
59 *
60 * #ClutterBehaviourPath is available since Clutter 0.2
61 *
62 * Deprecated: 1.6: Use #ClutterPath and #ClutterPathConstraint with
63 * clutter_actor_animate() instead.
64 */
65
66 #ifdef HAVE_CONFIG_H
67 #include "clutter-build-config.h"
68 #endif
69
70 #define CLUTTER_DISABLE_DEPRECATION_WARNINGS
71
72 #include "clutter-alpha.h"
73 #include "clutter-behaviour.h"
74 #include "clutter-behaviour-path.h"
75 #include "clutter-bezier.h"
76 #include "clutter-debug.h"
77 #include "clutter-enum-types.h"
78 #include "clutter-main.h"
79 #include "clutter-marshal.h"
80 #include "clutter-private.h"
81 #include "clutter-script-private.h"
82 #include "clutter-scriptable.h"
83
84 #include <math.h>
85
86 struct _ClutterBehaviourPathPrivate
87 {
88 ClutterPath *path;
89 guint last_knot_passed;
90 };
91
92 enum
93 {
94 KNOT_REACHED,
95
96 LAST_SIGNAL
97 };
98
99 static guint path_signals[LAST_SIGNAL] = { 0, };
100
101 enum
102 {
103 PROP_0,
104
105 PROP_PATH,
106
107 PROP_LAST
108 };
109
110 static GParamSpec *obj_props[PROP_LAST];
111
112 static void clutter_scriptable_iface_init (ClutterScriptableIface *iface);
113
G_DEFINE_TYPE_WITH_CODE(ClutterBehaviourPath,clutter_behaviour_path,CLUTTER_TYPE_BEHAVIOUR,G_ADD_PRIVATE (ClutterBehaviourPath)G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,clutter_scriptable_iface_init))114 G_DEFINE_TYPE_WITH_CODE (ClutterBehaviourPath,
115 clutter_behaviour_path,
116 CLUTTER_TYPE_BEHAVIOUR,
117 G_ADD_PRIVATE (ClutterBehaviourPath)
118 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE,
119 clutter_scriptable_iface_init))
120
121 static void
122 actor_apply_knot_foreach (ClutterBehaviour *behaviour,
123 ClutterActor *actor,
124 gpointer data)
125 {
126 ClutterKnot *knot = data;
127
128 CLUTTER_NOTE (ANIMATION, "Setting actor to %ix%i", knot->x, knot->y);
129
130 clutter_actor_set_position (actor, knot->x, knot->y);
131 }
132
133 static void
clutter_behaviour_path_alpha_notify(ClutterBehaviour * behave,gdouble alpha_value)134 clutter_behaviour_path_alpha_notify (ClutterBehaviour *behave,
135 gdouble alpha_value)
136 {
137 ClutterBehaviourPath *pathb = CLUTTER_BEHAVIOUR_PATH (behave);
138 ClutterBehaviourPathPrivate *priv = pathb->priv;
139 ClutterKnot position;
140 guint knot_num;
141
142 if (priv->path)
143 knot_num = clutter_path_get_position (priv->path, alpha_value, &position);
144 else
145 {
146 memset (&position, 0, sizeof (position));
147 knot_num = 0;
148 }
149
150 clutter_behaviour_actors_foreach (behave,
151 actor_apply_knot_foreach,
152 &position);
153
154 if (knot_num != priv->last_knot_passed)
155 {
156 g_signal_emit (behave, path_signals[KNOT_REACHED], 0, knot_num);
157 priv->last_knot_passed = knot_num;
158 }
159 }
160
161 static void
clutter_behaviour_path_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)162 clutter_behaviour_path_get_property (GObject *gobject,
163 guint prop_id,
164 GValue *value,
165 GParamSpec *pspec)
166 {
167 ClutterBehaviourPath *pathb = CLUTTER_BEHAVIOUR_PATH (gobject);
168
169 switch (prop_id)
170 {
171 case PROP_PATH:
172 g_value_set_object (value, clutter_behaviour_path_get_path (pathb));
173 break;
174 default:
175 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
176 break;
177 }
178 }
179
180 static void
clutter_behaviour_path_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * pspec)181 clutter_behaviour_path_set_property (GObject *gobject,
182 guint prop_id,
183 const GValue *value,
184 GParamSpec *pspec)
185 {
186 ClutterBehaviourPath *pathb = CLUTTER_BEHAVIOUR_PATH (gobject);
187
188 switch (prop_id)
189 {
190 case PROP_PATH:
191 clutter_behaviour_path_set_path (pathb, g_value_get_object (value));
192 break;
193 default:
194 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
195 break;
196 }
197 }
198
199 static void
clutter_behaviour_path_dispose(GObject * gobject)200 clutter_behaviour_path_dispose (GObject *gobject)
201 {
202 ClutterBehaviourPath *pathb = CLUTTER_BEHAVIOUR_PATH (gobject);
203
204 clutter_behaviour_path_set_path (pathb, NULL);
205
206 G_OBJECT_CLASS (clutter_behaviour_path_parent_class)->dispose (gobject);
207 }
208
209 static void
clutter_behaviour_path_class_init(ClutterBehaviourPathClass * klass)210 clutter_behaviour_path_class_init (ClutterBehaviourPathClass *klass)
211 {
212 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
213 ClutterBehaviourClass *behave_class = CLUTTER_BEHAVIOUR_CLASS (klass);
214 GParamSpec *pspec;
215
216 gobject_class->get_property = clutter_behaviour_path_get_property;
217 gobject_class->set_property = clutter_behaviour_path_set_property;
218 gobject_class->dispose = clutter_behaviour_path_dispose;
219
220 pspec = g_param_spec_object ("path",
221 P_("Path"),
222 P_("The ClutterPath object representing the path "
223 "to animate along"),
224 CLUTTER_TYPE_PATH,
225 CLUTTER_PARAM_READWRITE);
226 obj_props[PROP_PATH] = pspec;
227 g_object_class_install_property (gobject_class, PROP_PATH, pspec);
228
229 /**
230 * ClutterBehaviourPath::knot-reached:
231 * @pathb: the object which received the signal
232 * @knot_num: the index of the #ClutterKnot reached
233 *
234 * This signal is emitted each time a node defined inside the path
235 * is reached.
236 *
237 * Since: 0.2
238 *
239 * Deprecated: 1.6
240 */
241 path_signals[KNOT_REACHED] =
242 g_signal_new ("knot-reached",
243 G_TYPE_FROM_CLASS (gobject_class),
244 G_SIGNAL_RUN_LAST,
245 G_STRUCT_OFFSET (ClutterBehaviourPathClass, knot_reached),
246 NULL, NULL,
247 _clutter_marshal_VOID__UINT,
248 G_TYPE_NONE, 1,
249 G_TYPE_UINT);
250
251 behave_class->alpha_notify = clutter_behaviour_path_alpha_notify;
252 }
253
254 static ClutterScriptableIface *parent_scriptable_iface = NULL;
255
256 static gboolean
clutter_behaviour_path_parse_custom_node(ClutterScriptable * scriptable,ClutterScript * script,GValue * value,const gchar * name,JsonNode * node)257 clutter_behaviour_path_parse_custom_node (ClutterScriptable *scriptable,
258 ClutterScript *script,
259 GValue *value,
260 const gchar *name,
261 JsonNode *node)
262 {
263 if (strcmp ("path", name) == 0)
264 {
265 ClutterPath *path;
266 GValue node_value = { 0 };
267
268 path = g_object_ref_sink (clutter_path_new ());
269
270 json_node_get_value (node, &node_value);
271
272 if (!G_VALUE_HOLDS (&node_value, G_TYPE_STRING)
273 || !clutter_path_set_description (path,
274 g_value_get_string (&node_value)))
275 g_warning ("Invalid path description");
276
277 g_value_unset (&node_value);
278
279 g_value_init (value, G_TYPE_OBJECT);
280 g_value_take_object (value, path);
281
282 return TRUE;
283 }
284 /* chain up */
285 else if (parent_scriptable_iface->parse_custom_node)
286 return parent_scriptable_iface->parse_custom_node (scriptable, script,
287 value, name, node);
288 else
289 return FALSE;
290 }
291
292 static void
clutter_scriptable_iface_init(ClutterScriptableIface * iface)293 clutter_scriptable_iface_init (ClutterScriptableIface *iface)
294 {
295 parent_scriptable_iface = g_type_interface_peek_parent (iface);
296
297 if (!parent_scriptable_iface)
298 parent_scriptable_iface
299 = g_type_default_interface_peek (CLUTTER_TYPE_SCRIPTABLE);
300
301 iface->parse_custom_node = clutter_behaviour_path_parse_custom_node;
302 }
303
304 static void
clutter_behaviour_path_init(ClutterBehaviourPath * self)305 clutter_behaviour_path_init (ClutterBehaviourPath *self)
306 {
307 self->priv = clutter_behaviour_path_get_instance_private (self);
308 self->priv->last_knot_passed = G_MAXUINT;
309 }
310
311 /**
312 * clutter_behaviour_path_new:
313 * @alpha: (allow-none): a #ClutterAlpha instance, or %NULL
314 * @path: a #ClutterPath or %NULL for an empty path
315 *
316 * Creates a new path behaviour. You can use this behaviour to drive
317 * actors along the nodes of a path, described by @path.
318 *
319 * This will claim the floating reference on the #ClutterPath so you
320 * do not need to unref if it.
321 *
322 * If @alpha is not %NULL, the #ClutterBehaviour will take ownership
323 * of the #ClutterAlpha instance. In the case when @alpha is %NULL,
324 * it can be set later with clutter_behaviour_set_alpha().
325 *
326 * Return value: (transfer full): a #ClutterBehaviour
327 *
328 * Since: 0.2
329 *
330 * Deprecated: 1.6
331 */
332 ClutterBehaviour *
clutter_behaviour_path_new(ClutterAlpha * alpha,ClutterPath * path)333 clutter_behaviour_path_new (ClutterAlpha *alpha,
334 ClutterPath *path)
335 {
336 return g_object_new (CLUTTER_TYPE_BEHAVIOUR_PATH,
337 "alpha", alpha,
338 "path", path,
339 NULL);
340 }
341
342 /**
343 * clutter_behaviour_path_new_with_description:
344 * @alpha: (allow-none): a #ClutterAlpha instance, or %NULL
345 * @desc: a string description of the path
346 *
347 * Creates a new path behaviour using the path described by @desc. See
348 * clutter_path_add_string() for a description of the format.
349 *
350 * If @alpha is not %NULL, the #ClutterBehaviour will take ownership
351 * of the #ClutterAlpha instance. In the case when @alpha is %NULL,
352 * it can be set later with clutter_behaviour_set_alpha().
353 *
354 * Return value: (transfer full): a #ClutterBehaviour
355 *
356 * Since: 1.0
357 *
358 * Deprecated: 1.6
359 */
360 ClutterBehaviour *
clutter_behaviour_path_new_with_description(ClutterAlpha * alpha,const gchar * desc)361 clutter_behaviour_path_new_with_description (ClutterAlpha *alpha,
362 const gchar *desc)
363 {
364 return g_object_new (CLUTTER_TYPE_BEHAVIOUR_PATH,
365 "alpha", alpha,
366 "path", clutter_path_new_with_description (desc),
367 NULL);
368 }
369
370 /**
371 * clutter_behaviour_path_new_with_knots:
372 * @alpha: (allow-none): a #ClutterAlpha instance, or %NULL
373 * @knots: (array length=n_knots): an array of #ClutterKnot<!-- -->s
374 * @n_knots: number of entries in @knots
375 *
376 * Creates a new path behaviour that will make the actors visit all of
377 * the given knots in order with straight lines in between.
378 *
379 * A path will be created where the first knot is used in a
380 * %CLUTTER_PATH_MOVE_TO and the subsequent knots are used in
381 * %CLUTTER_PATH_LINE_TO<!-- -->s.
382 *
383 * If @alpha is not %NULL, the #ClutterBehaviour will take ownership
384 * of the #ClutterAlpha instance. In the case when @alpha is %NULL,
385 * it can be set later with clutter_behaviour_set_alpha().
386 *
387 * Return value: (transfer full): a #ClutterBehaviour
388 *
389 * Since: 1.0
390 *
391 * Deprecated: 1.6
392 */
393 ClutterBehaviour *
clutter_behaviour_path_new_with_knots(ClutterAlpha * alpha,const ClutterKnot * knots,guint n_knots)394 clutter_behaviour_path_new_with_knots (ClutterAlpha *alpha,
395 const ClutterKnot *knots,
396 guint n_knots)
397 {
398 ClutterPath *path = clutter_path_new ();
399 guint i;
400
401 if (n_knots > 0)
402 {
403 clutter_path_add_move_to (path, knots[0].x, knots[0].y);
404
405 for (i = 1; i < n_knots; i++)
406 clutter_path_add_line_to (path, knots[i].x, knots[i].y);
407 }
408
409 return g_object_new (CLUTTER_TYPE_BEHAVIOUR_PATH,
410 "alpha", alpha,
411 "path", path,
412 NULL);
413 }
414
415 /**
416 * clutter_behaviour_path_set_path:
417 * @pathb: the path behaviour
418 * @path: the new path to follow
419 *
420 * Change the path that the actors will follow. This will take the
421 * floating reference on the #ClutterPath so you do not need to unref
422 * it.
423 *
424 * Since: 1.0
425 *
426 * Deprecated: 1.6
427 */
428 void
clutter_behaviour_path_set_path(ClutterBehaviourPath * pathb,ClutterPath * path)429 clutter_behaviour_path_set_path (ClutterBehaviourPath *pathb,
430 ClutterPath *path)
431 {
432 ClutterBehaviourPathPrivate *priv;
433
434 g_return_if_fail (CLUTTER_IS_BEHAVIOUR_PATH (pathb));
435
436 priv = pathb->priv;
437
438 if (path)
439 g_object_ref_sink (path);
440
441 if (priv->path)
442 g_object_unref (priv->path);
443
444 priv->path = path;
445
446 g_object_notify_by_pspec (G_OBJECT (pathb), obj_props[PROP_PATH]);
447 }
448
449 /**
450 * clutter_behaviour_path_get_path:
451 * @pathb: a #ClutterBehaviourPath instance
452 *
453 * Get the current path of the behaviour
454 *
455 * Return value: (transfer none): the path
456 *
457 * Since: 1.0
458 *
459 * Deprecated: 1.6
460 */
461 ClutterPath *
clutter_behaviour_path_get_path(ClutterBehaviourPath * pathb)462 clutter_behaviour_path_get_path (ClutterBehaviourPath *pathb)
463 {
464 g_return_val_if_fail (CLUTTER_IS_BEHAVIOUR_PATH (pathb), NULL);
465
466 return pathb->priv->path;
467 }
468