4
* An OpenGL based 'interactive canvas' library.
6
* Authored By Matthew Allum <mallum@openedhand.com>
7
* Jorn Baayen <jorn@openedhand.com>
8
* Emmanuele Bassi <ebassi@openedhand.com>
9
* Tomas Frydrych <tf@openedhand.com>
11
* Copyright (C) 2006, 2007 OpenedHand
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.
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.
23
* You should have received a copy of the GNU Lesser General Public
24
* License along with this library; if not, write to the
25
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26
* Boston, MA 02111-1307, USA.
31
* @short_description: A class for calculating an alpha value as a function
34
* #EggAlpha is a class for calculating an integer value between
35
* 0 and %EGG_ALPHA_MAX_ALPHA as a function of time. You should
36
* provide a #EggTimeline and bind it to the #EggAlpha object;
37
* you should also provide a function returning the alpha value depending
38
* on the position inside the timeline; this function will be executed
39
* each time a new frame in the #EggTimeline is reached. Since the
40
* alpha function is controlled by the timeline instance, you can pause
41
* or stop the #EggAlpha from calling the alpha function by controlling
42
* the #EggTimeline object.
44
* #EggAlpha is used to "drive" a #EggBehaviour instance.
46
* <figure id="alpha-functions">
47
* <title>Graphic representation of some alpha functions</title>
48
* <graphic fileref="alpha-func.png" format="PNG"/>
60
#include "egg-alpha.h"
62
// #include "egg-marshal.h"
63
// #include "egg-private.h"
64
#include "egg-debug.h"
66
G_DEFINE_TYPE (EggAlpha, egg_alpha, G_TYPE_INITIALLY_UNOWNED);
69
struct _EggAlphaPrivate
71
EggTimeline *timeline;
72
guint timeline_new_frame_id;
88
timeline_new_frame_cb (EggTimeline *timeline,
89
guint current_frame_num,
92
EggAlphaPrivate *priv = alpha->priv;
94
/* Update alpha value and notify */
95
priv->alpha = egg_alpha_get_alpha (alpha);
96
g_object_notify (G_OBJECT (alpha), "alpha");
100
egg_alpha_set_property (GObject *object,
107
alpha = EGG_ALPHA (object);
112
egg_alpha_set_timeline (alpha, g_value_get_object (value));
115
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
121
egg_alpha_get_property (GObject *object,
127
EggAlphaPrivate *priv;
129
alpha = EGG_ALPHA (object);
135
g_value_set_object (value, priv->timeline);
138
g_value_set_uint (value, priv->alpha);
141
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147
egg_alpha_finalize (GObject *object)
149
EggAlphaPrivate *priv = EGG_ALPHA (object)->priv;
152
g_closure_unref (priv->closure);
154
G_OBJECT_CLASS (egg_alpha_parent_class)->finalize (object);
158
egg_alpha_dispose (GObject *object)
160
EggAlpha *self = EGG_ALPHA(object);
162
egg_alpha_set_timeline (self, NULL);
164
G_OBJECT_CLASS (egg_alpha_parent_class)->dispose (object);
169
egg_alpha_class_init (EggAlphaClass *klass)
171
GObjectClass *object_class = G_OBJECT_CLASS (klass);
173
object_class->set_property = egg_alpha_set_property;
174
object_class->get_property = egg_alpha_get_property;
175
object_class->finalize = egg_alpha_finalize;
176
object_class->dispose = egg_alpha_dispose;
178
g_type_class_add_private (klass, sizeof (EggAlphaPrivate));
183
* A #EggTimeline instance used to drive the alpha function.
187
g_object_class_install_property (object_class,
189
g_param_spec_object ("timeline",
193
EGG_PARAM_READWRITE));
197
* The alpha value as computed by the alpha function.
201
g_object_class_install_property (object_class,
203
g_param_spec_uint ("alpha",
209
EGG_PARAM_READABLE));
213
egg_alpha_init (EggAlpha *self)
215
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
221
* egg_alpha_get_alpha:
222
* @alpha: A #EggAlpha
224
* Query the current alpha value.
226
* Return Value: The current alpha value for the alpha
231
egg_alpha_get_alpha (EggAlpha *alpha)
233
EggAlphaPrivate *priv;
236
g_return_val_if_fail (EGG_IS_ALPHA (alpha), 0);
240
if (G_LIKELY (priv->closure))
242
GValue params = { 0, };
243
GValue result_value = { 0, };
245
g_object_ref (alpha);
247
g_value_init (&result_value, G_TYPE_UINT);
249
g_value_init (¶ms, EGG_TYPE_ALPHA);
250
g_value_set_object (¶ms, alpha);
252
g_closure_invoke (priv->closure,
258
retval = g_value_get_uint (&result_value);
260
g_value_unset (&result_value);
261
g_value_unset (¶ms);
263
g_object_unref (alpha);
270
* egg_alpha_set_closure:
271
* @alpha: A #EggAlpha
272
* @closure: A #GClosure
274
* Sets the #GClosure used to compute
275
* the alpha value at each frame of the #EggTimeline
281
egg_alpha_set_closure (EggAlpha *alpha,
284
EggAlphaPrivate *priv;
286
g_return_if_fail (EGG_IS_ALPHA (alpha));
287
g_return_if_fail (closure != NULL);
292
g_closure_unref (priv->closure);
294
priv->closure = g_closure_ref (closure);
295
g_closure_sink (closure);
297
if (G_CLOSURE_NEEDS_MARSHAL (closure))
299
GClosureMarshal marshal = egg_marshal_UINT__VOID;
301
g_closure_set_marshal (closure, marshal);
306
* egg_alpha_set_func:
307
* @alpha: A #EggAlpha
308
* @func: A #EggAlphaAlphaFunc
309
* @data: user data to be passed to the alpha function, or %NULL
310
* @destroy: notify function used when disposing the alpha function
312
* Sets the #EggAlphaFunc function used to compute
313
* the alpha value at each frame of the #EggTimeline
319
egg_alpha_set_func (EggAlpha *alpha,
322
GDestroyNotify destroy)
326
g_return_if_fail (EGG_IS_ALPHA (alpha));
327
g_return_if_fail (func != NULL);
329
closure = g_cclosure_new (G_CALLBACK (func), data, (GClosureNotify) destroy);
330
egg_alpha_set_closure (alpha, closure);
334
* egg_alpha_set_timeline:
335
* @alpha: A #EggAlpha
336
* @timeline: A #EggTimeline
338
* Binds @alpha to @timeline.
343
egg_alpha_set_timeline (EggAlpha *alpha,
344
EggTimeline *timeline)
346
EggAlphaPrivate *priv;
348
g_return_if_fail (EGG_IS_ALPHA (alpha));
349
g_return_if_fail (timeline == NULL || EGG_IS_TIMELINE (timeline));
355
g_signal_handlers_disconnect_by_func (priv->timeline,
356
timeline_new_frame_cb,
359
g_object_unref (priv->timeline);
360
priv->timeline = NULL;
365
priv->timeline = g_object_ref (timeline);
367
g_signal_connect (priv->timeline, "new-frame",
368
G_CALLBACK (timeline_new_frame_cb),
374
* egg_alpha_get_timeline:
375
* @alpha: A #EggAlpha
377
* Gets the #EggTimeline bound to @alpha.
379
* Return value: a #EggTimeline instance
384
egg_alpha_get_timeline (EggAlpha *alpha)
386
g_return_val_if_fail (EGG_IS_ALPHA (alpha), NULL);
388
return alpha->priv->timeline;
394
* Creates a new #EggAlpha instance. You must set a function
395
* to compute the alpha value using egg_alpha_set_func() and
396
* bind a #EggTimeline object to the #EggAlpha instance
397
* using egg_alpha_set_timeline().
399
* You should use the newly created #EggAlpha instance inside
400
* a #EggBehaviour object.
402
* Return value: the newly created empty #EggAlpha instance.
409
return g_object_new (EGG_TYPE_ALPHA, NULL);
413
* egg_alpha_new_full:
414
* @timeline: #EggTimeline timeline
415
* @func: #EggAlphaFunc alpha function
416
* @data: data to be passed to the alpha function
417
* @destroy: notify to be called when removing the alpha function
419
* Creates a new #EggAlpha instance and sets the timeline
420
* and alpha function.
422
* Return Value: the newly created #EggAlpha
427
egg_alpha_new_full (EggTimeline *timeline,
430
GDestroyNotify destroy)
434
g_return_val_if_fail (EGG_IS_TIMELINE (timeline), NULL);
435
g_return_val_if_fail (func != NULL, NULL);
437
retval = egg_alpha_new ();
439
egg_alpha_set_timeline (retval, timeline);
440
egg_alpha_set_func (retval, func, data, destroy);
446
* EGG_ALPHA_RAMP_INC:
448
* Convenience symbol for egg_ramp_inc_func().
455
* @alpha: a #EggAlpha
456
* @dummy: unused argument
458
* Convenience alpha function for a monotonic increasing ramp. You
459
* can use this function as the alpha function for egg_alpha_set_func().
461
* Return value: an alpha value.
466
egg_ramp_inc_func (EggAlpha *alpha,
469
EggTimeline *timeline;
470
gint current_frame_num, n_frames;
472
timeline = egg_alpha_get_timeline (alpha);
474
current_frame_num = egg_timeline_get_current_frame (timeline);
475
n_frames = egg_timeline_get_n_frames (timeline);
477
return (current_frame_num * EGG_ALPHA_MAX_ALPHA) / n_frames;
481
* EGG_ALPHA_RAMP_DEC:
483
* Convenience symbol for egg_ramp_dec_func().
490
* @alpha: a #EggAlpha
491
* @dummy: unused argument
493
* Convenience alpha function for a monotonic decreasing ramp. You
494
* can use this function as the alpha function for egg_alpha_set_func().
496
* Return value: an alpha value.
501
egg_ramp_dec_func (EggAlpha *alpha,
504
EggTimeline *timeline;
505
gint current_frame_num, n_frames;
507
timeline = egg_alpha_get_timeline (alpha);
509
current_frame_num = egg_timeline_get_current_frame (timeline);
510
n_frames = egg_timeline_get_n_frames (timeline);
512
return (n_frames - current_frame_num)
513
* EGG_ALPHA_MAX_ALPHA
520
* Convenience symbol for egg_ramp_func().
527
* @alpha: a #EggAlpha
528
* @dummy: unused argument
530
* Convenience alpha function for a full ramp function (increase for
531
* half the time, decrease for the remaining half). You can use this
532
* function as the alpha function for egg_alpha_set_func().
534
* Return value: an alpha value.
539
egg_ramp_func (EggAlpha *alpha,
542
EggTimeline *timeline;
543
gint current_frame_num, n_frames;
545
timeline = egg_alpha_get_timeline (alpha);
547
current_frame_num = egg_timeline_get_current_frame (timeline);
548
n_frames = egg_timeline_get_n_frames (timeline);
550
if (current_frame_num > (n_frames / 2))
552
return (n_frames - current_frame_num)
553
* EGG_ALPHA_MAX_ALPHA
558
return current_frame_num
559
* EGG_ALPHA_MAX_ALPHA
565
sincx1024_func (EggAlpha *alpha,
569
EggTimeline *timeline;
570
gint current_frame_num, n_frames;
574
timeline = egg_alpha_get_timeline (alpha);
576
current_frame_num = egg_timeline_get_current_frame (timeline);
577
n_frames = egg_timeline_get_n_frames (timeline);
579
x = angle * current_frame_num / n_frames;
581
x -= (512 * 512 / angle);
583
sine = ((egg_sini (x) + offset)/2) * EGG_ALPHA_MAX_ALPHA;
585
sine = sine >> CFX_Q;
591
* The following two functions are left in place for reference
595
sincx_func (EggAlpha *alpha,
599
EggTimeline *timeline;
600
gint current_frame_num, n_frames;
603
timeline = egg_alpha_get_timeline (alpha);
605
current_frame_num = egg_timeline_get_current_frame (timeline);
606
n_frames = egg_timeline_get_n_frames (timeline);
608
x = angle * current_frame_num / n_frames;
609
x = EGG_FIXED_MUL (x, CFX_PI) - EGG_FIXED_DIV (CFX_PI, angle);
611
sine = (egg_fixed_sin (x) + offset)/2;
613
EGG_NOTE (ALPHA, "sine: %2f\n", EGG_FIXED_TO_DOUBLE (sine));
615
return EGG_FIXED_TO_INT (sine * EGG_ALPHA_MAX_ALPHA);
618
/* NB: angle is not in radians but in muliples of PI, i.e., 2.0
619
* represents full circle.
622
sinc_func (EggAlpha *alpha,
626
EggTimeline *timeline;
627
gint current_frame_num, n_frames;
630
timeline = egg_alpha_get_timeline (alpha);
632
current_frame_num = egg_timeline_get_current_frame (timeline);
633
n_frames = egg_timeline_get_n_frames (timeline);
635
/* FIXME: fixed point, and fixed point sine() */
637
x = (gdouble) (current_frame_num * angle * G_PI) / n_frames ;
638
sine = (sin (x - (G_PI / angle)) + offset) * 0.5f;
640
EGG_NOTE (ALPHA, "sine: %2f\n",sine);
642
return EGG_FLOAT_TO_INT ((sine * (gdouble) EGG_ALPHA_MAX_ALPHA));
649
* Convenience symbol for egg_sine_func().
656
* @alpha: a #EggAlpha
657
* @dummy: unused argument
659
* Convenience alpha function for a sine wave. You can use this
660
* function as the alpha function for egg_alpha_set_func().
662
* Return value: an alpha value.
667
egg_sine_func (EggAlpha *alpha,
671
return sinc_func (alpha, 2.0, 1.0);
673
/* 2.0 above represents full circle */
674
return sincx1024_func (alpha, 1024, CFX_ONE);
679
* EGG_ALPHA_SINE_INC:
681
* Convenience symbol for egg_sine_inc_func().
688
* @alpha: a #EggAlpha
689
* @dummy: unused argument
691
* Convenience alpha function for a sine wave over interval [0, pi / 2].
692
* You can use this function as the alpha function for
693
* egg_alpha_set_func().
695
* Return value: an alpha value.
700
egg_sine_inc_func (EggAlpha *alpha,
703
EggTimeline * timeline;
709
timeline = egg_alpha_get_timeline (alpha);
710
frame = egg_timeline_get_current_frame (timeline);
711
n_frames = egg_timeline_get_n_frames (timeline);
713
x = 256 * frame / n_frames;
715
sine = egg_sini (x) * EGG_ALPHA_MAX_ALPHA;
717
return ((guint32)sine) >> CFX_Q;
721
* EGG_ALPHA_SINE_DEC:
723
* Convenience symbol for egg_sine_dec_func().
730
* @alpha: a #EggAlpha
731
* @dummy: unused argument
733
* Convenience alpha function for a sine wave over interval [pi / 2, pi].
734
* You can use this function as the alpha function for
735
* egg_alpha_set_func().
737
* Return value: an alpha value.
742
egg_sine_dec_func (EggAlpha *alpha,
745
EggTimeline * timeline;
751
timeline = egg_alpha_get_timeline (alpha);
752
frame = egg_timeline_get_current_frame (timeline);
753
n_frames = egg_timeline_get_n_frames (timeline);
755
x = 256 * frame / n_frames + 256;
757
sine = egg_sini (x) * EGG_ALPHA_MAX_ALPHA;
759
return ((guint32)sine) >> CFX_Q;
763
* EGG_ALPHA_SINE_HALF:
765
* Convenience symbol for egg_sine_half_func().
771
* egg_sine_half_func:
772
* @alpha: a #EggAlpha
773
* @dummy: unused argument
775
* Convenience alpha function for a sine wave over interval [0, pi].
776
* You can use this function as the alpha function for
777
* egg_alpha_set_func().
779
* Return value: an alpha value.
784
egg_sine_half_func (EggAlpha *alpha,
787
EggTimeline * timeline;
793
timeline = egg_alpha_get_timeline (alpha);
794
frame = egg_timeline_get_current_frame (timeline);
795
n_frames = egg_timeline_get_n_frames (timeline);
797
x = 512 * frame / n_frames;
799
sine = egg_sini (x) * EGG_ALPHA_MAX_ALPHA;
801
return ((guint32)sine) >> CFX_Q;
807
* Convenience symbol for egg_square_func().
814
* @alpha: a #EggAlpha
815
* @dummy: unused argument
817
* Convenience alpha function for a square wave. You can use this
818
* function as the alpha function for egg_alpha_set_func().
820
* Return value: an alpha value
825
egg_square_func (EggAlpha *alpha,
828
EggTimeline *timeline;
829
gint current_frame_num, n_frames;
831
timeline = egg_alpha_get_timeline (alpha);
833
current_frame_num = egg_timeline_get_current_frame (timeline);
834
n_frames = egg_timeline_get_n_frames (timeline);
836
return (current_frame_num > (n_frames / 2)) ? EGG_ALPHA_MAX_ALPHA
841
* EGG_ALPHA_SMOOTHSTEP_INC:
843
* Convenience symbol for egg_smoothstep_inc_func().
849
* egg_smoothstep_inc_func:
850
* @alpha: a #EggAlpha
853
* Convenience alpha function for a smoothstep curve. You can use this
854
* function as the alpha function for egg_alpha_set_func().
856
* Return value: an alpha value
861
egg_smoothstep_inc_func (EggAlpha *alpha,
864
EggTimeline *timeline;
871
* The smoothstep function uses f(x) = -2x^3 + 3x^2 where x is from <0,1>,
872
* and precission is critical -- we use 8.24 fixed format for this operation.
873
* The earlier operations involve division, which we cannot do in 8.24 for
874
* numbers in <0,1> we use EggFixed.
876
timeline = egg_alpha_get_timeline (alpha);
877
frame = egg_timeline_get_current_frame (timeline);
878
n_frames = egg_timeline_get_n_frames (timeline);
881
* Convert x to 8.24 for next step.
883
x = CFX_DIV (frame, n_frames) << 8;
886
* f(x) = -2x^3 + 3x^2
888
* Convert result to EggFixed to avoid overflow in next step.
890
r = ((x >> 12) * (x >> 12) * 3 - (x >> 15) * (x >> 16) * (x >> 16)) >> 8;
892
return CFX_INT (r * EGG_ALPHA_MAX_ALPHA);
896
* EGG_ALPHA_SMOOTHSTEP_DEC:
898
* Convenience symbol for egg_smoothstep_dec_func().
904
* egg_smoothstep_dec_func:
905
* @alpha: a #EggAlpha
908
* Convenience alpha function for a downward smoothstep curve. You can use
909
* this function as the alpha function for egg_alpha_set_func().
911
* Return value: an alpha value
916
egg_smoothstep_dec_func (EggAlpha *alpha,
919
return EGG_ALPHA_MAX_ALPHA - egg_smoothstep_inc_func (alpha, dummy);
925
* Convenience symbol for egg_exp_inc_func()
932
* @alpha: a #EggAlpha
933
* @dummy: unused argument
935
* Convenience alpha function for a 2^x curve. You can use this function as the
936
* alpha function for egg_alpha_set_func().
938
* Return value: an alpha value.
943
egg_exp_inc_func (EggAlpha *alpha,
946
EggTimeline * timeline;
950
EggFixed x_alpha_max = 0x100000;
954
* Choose x_alpha_max such that
956
* (2^x_alpha_max) - 1 == EGG_ALPHA_MAX_ALPHA
958
#if EGG_ALPHA_MAX_ALPHA != 0xffff
959
#error Adjust x_alpha_max to match EGG_ALPHA_MAX_ALPHA
962
timeline = egg_alpha_get_timeline (alpha);
963
frame = egg_timeline_get_current_frame (timeline);
964
n_frames = egg_timeline_get_n_frames (timeline);
966
x = x_alpha_max * frame / n_frames;
968
result = CLAMP (egg_pow2x (x) - 1, 0, EGG_ALPHA_MAX_ALPHA);
976
* Convenience symbold for egg_exp_dec_func().
983
* @alpha: a #EggAlpha
984
* @dummy: unused argument
986
* Convenience alpha function for a decreasing 2^x curve. You can use this
987
* function as the alpha function for egg_alpha_set_func().
989
* Return value: an alpha value.
994
egg_exp_dec_func (EggAlpha *alpha,
997
EggTimeline * timeline;
1001
EggFixed x_alpha_max = 0x100000;
1005
* Choose x_alpha_max such that
1007
* (2^x_alpha_max) - 1 == EGG_ALPHA_MAX_ALPHA
1009
#if EGG_ALPHA_MAX_ALPHA != 0xffff
1010
#error Adjust x_alpha_max to match EGG_ALPHA_MAX_ALPHA
1013
timeline = egg_alpha_get_timeline (alpha);
1014
frame = egg_timeline_get_current_frame (timeline);
1015
n_frames = egg_timeline_get_n_frames (timeline);
1017
x = (x_alpha_max * (n_frames - frame)) / n_frames;
1019
result = CLAMP (egg_pow2x (x) - 1, 0, EGG_ALPHA_MAX_ALPHA);