diff --git a/.gitmodules b/.gitmodules index e69de29..018c244 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "animation/third_party/glm"] + path = animation/third_party/glm + url = git://github.com/g-truc/glm diff --git a/.travis.yml b/.travis.yml index df5b8a2..8bede8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,12 +3,13 @@ language: cpp services: - docker before_install: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker pull ubuntu:bionic; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker pull ubuntu:disco; fi script: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo FROM ubuntu:bionic > Dockerfile; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo FROM ubuntu:disco > Dockerfile; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo ADD . /root >> Dockerfile; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo RUN apt-get update >> Dockerfile; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo RUN apt-get install -y meson ninja-build build-essential git pkg-config libglib2.0-dev gir1.2-glib-2.0 gobject-introspection libgirepository1.0-dev >> Dockerfile; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo RUN apt-get install --allow-unauthenticated -y meson ninja-build build-essential git pkg-config libglib2.0-dev gir1.2-glib-2.0 gobject-introspection libgirepository1.0-dev libmutter-4-dev git-core autoconf automake gjs >> Dockerfile; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then echo "RUN git clone git://github.com/ptomato/jasmine-gjs && cd jasmine-gjs && ./autogen.sh --prefix=/usr && make && make install && rm -rf jasmine-gjs" >> Dockerfile; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker build -t withgit .; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker run withgit /bin/sh -c "cd /root && TRAVIS=true CC=$CC CXX=$CXX meson -Db_sanitize=address,undefined -Dwerror=true builddir && ninja -C builddir && G_SLICE=always-malloc ./builddir/tests/animation_test --gtest_color=yes"; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker run withgit /bin/sh -c "cd /root && TRAVIS=true CC=$CC CXX=$CXX meson -Dwerror=true builddir && meson test -C builddir -v --num-processes=1"; fi diff --git a/animation-clutter/animation-clutter-actor-box-query.c b/animation-clutter/animation-clutter-actor-box-query.c new file mode 100644 index 0000000..df58dd9 --- /dev/null +++ b/animation-clutter/animation-clutter-actor-box-query.c @@ -0,0 +1,179 @@ +/* + * animation-clutter/animation-clutter-actor-box-query.cpp + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * AnimationClutterActorBoxQuery subclass for ClutterActor + */ + +#include + +struct _AnimationClutterActorBoxQuery +{ + GObject parent_instance; +}; + +typedef struct _AnimationClutterActorBoxQueryPrivate +{ + ClutterActor *actor; +} AnimationClutterActorBoxQueryPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationClutterActorBoxQuery, + animation_clutter_actor_box_query, + ANIMATION_TYPE_BOX_QUERY) + + +enum { + PROP_0, + PROP_ACTOR, + NPROPS +}; + +static GParamSpec *animation_clutter_actor_box_query_props[NPROPS] = { NULL, }; + +static void +update_box_for_actor_geometry (AnimationClutterActorBoxQuery *box_query, + ClutterActor *actor) +{ + float x, y, width, height; + + clutter_actor_get_position (actor, &x, &y); + clutter_actor_get_size (actor, &width, &height); + + const AnimationBox box = { + .top_left = { x, y }, + .bottom_right = { x + width, y + height } + }; + + animation_box_query_update (ANIMATION_BOX_QUERY (box_query), &box); +} + +static void +actor_geometry_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + ClutterActor *actor = CLUTTER_ACTOR (object); + AnimationClutterActorBoxQuery *box_query = ANIMATION_CLUTTER_ACTOR_BOX_QUERY (user_data); + + update_box_for_actor_geometry (box_query, actor); +} + +static void +disconnect_signals_and_unref (gpointer data) +{ + ClutterActor *actor = data; + + g_signal_handlers_disconnect_by_data (actor, actor); + g_object_unref (actor); +} + +static ClutterActor * +connect_signals_and_update (ClutterActor *actor, + AnimationClutterActorBoxQuery *box_query) +{ + g_signal_connect_object (actor, "notify::x", G_CALLBACK (actor_geometry_changed), box_query, G_CONNECT_AFTER); + g_signal_connect_object (actor, "notify::y", G_CALLBACK (actor_geometry_changed), box_query, G_CONNECT_AFTER); + g_signal_connect_object (actor, "notify::width", G_CALLBACK (actor_geometry_changed), box_query, G_CONNECT_AFTER); + g_signal_connect_object (actor, "notify::height", G_CALLBACK (actor_geometry_changed), box_query, G_CONNECT_AFTER); + + update_box_for_actor_geometry (box_query, actor); + + return actor; +} + +static void +animation_clutter_actor_box_query_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationClutterActorBoxQuery *box_query = ANIMATION_CLUTTER_ACTOR_BOX_QUERY (object); + AnimationClutterActorBoxQueryPrivate *priv = animation_clutter_actor_box_query_get_instance_private (box_query); + + switch (prop_id) + { + case PROP_ACTOR: + g_clear_pointer (&priv->actor, (GDestroyNotify) disconnect_signals_and_unref); + priv->actor = connect_signals_and_update (CLUTTER_ACTOR (g_value_dup_object (value)), box_query); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_clutter_actor_box_query_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AnimationClutterActorBoxQuery *box_query = ANIMATION_CLUTTER_ACTOR_BOX_QUERY (object); + AnimationClutterActorBoxQueryPrivate *priv = animation_clutter_actor_box_query_get_instance_private (box_query); + + switch (prop_id) + { + case PROP_ACTOR: + g_value_set_object (value, priv->actor); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_clutter_actor_box_query_dispose (GObject *object) +{ + AnimationClutterActorBoxQuery *box_query = ANIMATION_CLUTTER_ACTOR_BOX_QUERY (object); + AnimationClutterActorBoxQueryPrivate *priv = animation_clutter_actor_box_query_get_instance_private (box_query); + + g_clear_object (&priv->actor); + + G_OBJECT_CLASS (animation_clutter_actor_box_query_parent_class)->dispose (object); +} + +static void +animation_clutter_actor_box_query_class_init (AnimationClutterActorBoxQueryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = animation_clutter_actor_box_query_get_property; + object_class->set_property = animation_clutter_actor_box_query_set_property; + object_class->dispose = animation_clutter_actor_box_query_dispose; + + animation_clutter_actor_box_query_props[PROP_ACTOR] = + g_param_spec_object ("actor", + "Clutter Actor", + "The ClutterActor to observe", + CLUTTER_TYPE_ACTOR, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (object_class, + NPROPS, + animation_clutter_actor_box_query_props); +} + +static void +animation_clutter_actor_box_query_init (AnimationClutterActorBoxQuery *box_query) +{ +} + +AnimationBoxQuery * +animation_clutter_actor_box_query_new_for_actor (ClutterActor *actor) +{ + return ANIMATION_BOX_QUERY (g_object_new (ANIMATION_CLUTTER_TYPE_ACTOR_BOX_QUERY, + "actor", actor, + NULL)); +} diff --git a/animation-clutter/animation-clutter-actor-box-query.h b/animation-clutter/animation-clutter-actor-box-query.h new file mode 100644 index 0000000..9da591e --- /dev/null +++ b/animation-clutter/animation-clutter-actor-box-query.h @@ -0,0 +1,36 @@ +/* + * animation-clutter/animation-clutter-actor-box-query.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Geometry query subclass for ClutterActor + */ +#pragma once + +#include + +#include + +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_CLUTTER_TYPE_ACTOR_BOX_QUERY animation_clutter_actor_box_query_get_type () +G_DECLARE_FINAL_TYPE (AnimationClutterActorBoxQuery, animation_clutter_actor_box_query, ANIMATION_CLUTTER, ACTOR_BOX_QUERY, GObject) + +AnimationBoxQuery * animation_clutter_actor_box_query_new_for_actor (ClutterActor *actor); + +G_END_DECLS diff --git a/animation-clutter/animation-clutter-affine-effect.c b/animation-clutter/animation-clutter-affine-effect.c new file mode 100644 index 0000000..764f00d --- /dev/null +++ b/animation-clutter/animation-clutter-affine-effect.c @@ -0,0 +1,312 @@ +/* + * animation-clutter/animation-clutter-affine-effect.c + * + * Copyright © 2013-2018 Endless Mobile, Inc. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Sam Spilsbury + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "animation-clutter-affine-effect.h" +#include "animation-clutter-common.h" + +struct _AnimationClutterAffineEffect { + ClutterEffect parent_instance; +}; + +typedef struct +{ + AnimationTransformAnimation *transform_animation; + gint64 last_usecs; + guint timeout_id; +} AnimationClutterAffineEffectPrivate; + +enum +{ + PROP_0, + PROP_TRANSFORM_ANIMATION, + PROP_LAST +}; + +static GParamSpec *object_properties[PROP_LAST]; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationClutterAffineEffect, + animation_clutter_affine_effect, + CLUTTER_TYPE_EFFECT) + +static gboolean +animation_clutter_affine_effect_modify_paint_volume (ClutterEffect *effect, + ClutterPaintVolume *volume) +{ + ClutterActorMeta *meta = CLUTTER_ACTOR_META (effect); + ClutterActor *actor = clutter_actor_meta_get_actor (meta); + AnimationClutterAffineEffect *affine_effect = ANIMATION_CLUTTER_AFFINE_EFFECT (effect); + AnimationClutterAffineEffectPrivate *priv = + animation_clutter_affine_effect_get_instance_private (affine_effect); + + /* We assume that the parent's get_paint_volume method always returns + * TRUE here. */ + CLUTTER_EFFECT_CLASS (animation_clutter_affine_effect_parent_class)->modify_paint_volume (effect, volume); + + if (priv->transform_animation && clutter_actor_meta_get_enabled (meta)) + { + AnimationVector corners[4]; + AnimationVector4D extremes[4]; + AnimationVector offset; + + animation_clutter_compute_corners_from_untransformed_paint_volume (actor, + volume, + corners, + &offset); + + animation_transform_animation_extremes (priv->transform_animation, + corners, + extremes); + + animation_clutter_expand_paint_volume_with_extremes (volume, extremes, &offset); + } + + return TRUE; +} + +static gboolean +animation_clutter_affine_effect_new_frame (gpointer user_data) +{ + AnimationClutterAffineEffect *affine_effect = ANIMATION_CLUTTER_AFFINE_EFFECT (user_data); + AnimationClutterAffineEffectPrivate *priv = + animation_clutter_affine_effect_get_instance_private (affine_effect); + ClutterActorMeta *meta = CLUTTER_ACTOR_META (affine_effect); + ClutterActor *actor = clutter_actor_meta_get_actor (meta); + gint64 usecs = g_get_monotonic_time (); + + static const unsigned int ms_to_us = 1000; + + g_assert (priv->transform_animation); + + /* Wraparound, priv->last_usecs -= G_MAXINT64. + * We make priv->last_usecs negative so that subtracting it + * from usecs results in the correct delta */ + if (G_UNLIKELY (priv->last_usecs > usecs)) + priv->last_usecs -= G_MAXINT64; + + gint64 msecs_delta = (usecs - priv->last_usecs) / ms_to_us; + priv->last_usecs = usecs; + + /* If there was no time movement, then we can't really step or remove + * models in a way that makes sense, so don't do it */ + if (msecs_delta == 0) + return G_SOURCE_CONTINUE; + + if (!animation_transform_animation_step (priv->transform_animation, msecs_delta)) + { + /* Reset the transform back to an identity matrix. This will + * also cause the transform-set property to be unset. We + * need to do this before the actor effect is disabled, since + * disabling it may cause the actor to be destroyed + * and the actor to be detached from the effect. */ + graphene_matrix_t matrix; + graphene_matrix_init_identity (&matrix); + clutter_actor_set_opacity (actor, 255); + clutter_actor_set_transform (actor, + (const graphene_matrix_t *) &matrix); + + /* Disable the effect */ + clutter_actor_meta_set_enabled (meta, FALSE); + + /* Finally, return false so that we don't keep animating */ + priv->timeout_id = 0; + return G_SOURCE_REMOVE; + } + else + { + /* We need to immediately set the opacity of the actor + * since if it is zero, then clutter_actor_paint will + * never get called, causing us to never be able to + * update the opacity of the actor */ + float scaled_opacity = animation_transform_animation_progress (priv->transform_animation) * 255.0; + guint8 opacity = (guint8) (scaled_opacity); + + clutter_actor_set_opacity (actor, opacity); + clutter_effect_queue_repaint (CLUTTER_EFFECT (affine_effect)); + } + + /* We always want to return true even if there was no time delta */ + return G_SOURCE_CONTINUE; +} + +static void +animation_clutter_affine_effect_paint (ClutterEffect *effect, + ClutterPaintContext *context, + ClutterEffectPaintFlags flags) +{ + ClutterActorMeta *meta = CLUTTER_ACTOR_META (effect); + ClutterActor *actor = clutter_actor_meta_get_actor (meta); + AnimationClutterAffineEffect *affine_effect = ANIMATION_CLUTTER_AFFINE_EFFECT (effect); + AnimationClutterAffineEffectPrivate *priv = + animation_clutter_affine_effect_get_instance_private (affine_effect); + + /* Apply the transform to the actor */ + graphene_matrix_t matrix; + graphene_matrix_init_from_float (&matrix, + animation_transform_animation_matrix (priv->transform_animation)); + + clutter_actor_set_pivot_point (actor, 0, 0); + clutter_actor_set_transform (actor, &matrix); + clutter_actor_continue_paint (actor, context); +} + +static void +animation_clutter_affine_effect_ensure_timeline (AnimationClutterAffineEffect *affine_effect) +{ + AnimationClutterAffineEffectPrivate *priv = + animation_clutter_affine_effect_get_instance_private (affine_effect); + + if (priv->timeout_id == 0) + { + static const unsigned int frame_length_ms = 16; /* 1000 / 60; */ + + priv->last_usecs = g_get_monotonic_time (); + priv->timeout_id = g_timeout_add (frame_length_ms, animation_clutter_affine_effect_new_frame, affine_effect); + + /* We need to show the actor and set the initial transform now to prevent flicker */ + graphene_matrix_t matrix; + graphene_matrix_init_from_float (&matrix, + animation_transform_animation_matrix (priv->transform_animation)); + + float scaled_opacity = animation_transform_animation_progress (priv->transform_animation) * 255.0; + guint8 opacity = (guint8) (scaled_opacity); + + ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (affine_effect)); + + clutter_actor_set_opacity (actor, opacity); + clutter_actor_set_pivot_point (actor, 0, 0); + clutter_actor_set_transform (actor, &matrix); + clutter_actor_show (actor); + } +} + +static void +animation_clutter_affine_effect_notify (GObject *object, + GParamSpec *pspec) +{ + ClutterActorMeta *actor_meta = CLUTTER_ACTOR_META (object); + AnimationClutterAffineEffect *affine_effect = ANIMATION_CLUTTER_AFFINE_EFFECT (object); + AnimationClutterAffineEffectPrivate *priv = + animation_clutter_affine_effect_get_instance_private (affine_effect); + + if (g_strcmp0 (pspec->name, "enabled") == 0) + { + if (clutter_actor_meta_get_enabled (actor_meta)) + animation_clutter_affine_effect_ensure_timeline (affine_effect); + else + g_clear_handle_id (&priv->timeout_id, g_source_remove); + } + + G_OBJECT_CLASS (animation_clutter_affine_effect_parent_class)->notify (object, pspec); +} + +static void +animation_clutter_affine_effect_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationClutterAffineEffect *affine_effect = ANIMATION_CLUTTER_AFFINE_EFFECT (object); + AnimationClutterAffineEffectPrivate *priv = + animation_clutter_affine_effect_get_instance_private (affine_effect); + + switch (prop_id) + { + case PROP_TRANSFORM_ANIMATION: + g_set_object (&priv->transform_animation, g_value_dup_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +animation_clutter_affine_effect_dispose (GObject *object) +{ + AnimationClutterAffineEffect *affine_effect = ANIMATION_CLUTTER_AFFINE_EFFECT (object); + AnimationClutterAffineEffectPrivate *priv = + animation_clutter_affine_effect_get_instance_private (affine_effect); + + g_clear_object (&priv->transform_animation); + + G_OBJECT_CLASS (animation_clutter_affine_effect_parent_class)->dispose (object); +} + +static void +animation_clutter_affine_effect_finalize (GObject *object) +{ + AnimationClutterAffineEffect *affine_effect = ANIMATION_CLUTTER_AFFINE_EFFECT (object); + AnimationClutterAffineEffectPrivate *priv = + animation_clutter_affine_effect_get_instance_private (affine_effect); + + g_clear_handle_id (&priv->timeout_id, g_source_remove); + + G_OBJECT_CLASS (animation_clutter_affine_effect_parent_class)->finalize (object); +} + +static void +animation_clutter_affine_effect_init (AnimationClutterAffineEffect *effect) +{ + AnimationClutterAffineEffectPrivate *priv = + animation_clutter_affine_effect_get_instance_private (effect); + + priv->timeout_id = 0; +} + +static void +animation_clutter_affine_effect_class_init (AnimationClutterAffineEffectClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); + + object_class->notify = animation_clutter_affine_effect_notify; + object_class->set_property = animation_clutter_affine_effect_set_property; + object_class->dispose = animation_clutter_affine_effect_dispose; + object_class->finalize = animation_clutter_affine_effect_finalize; + effect_class->modify_paint_volume = animation_clutter_affine_effect_modify_paint_volume; + effect_class->paint = animation_clutter_affine_effect_paint; + + object_properties[PROP_TRANSFORM_ANIMATION] = + g_param_spec_object ("transform-animation", + "Transform Animation", + "The underlying transform animation", + ANIMATION_TYPE_TRANSFORM_ANIMATION, + G_PARAM_WRITABLE); + + g_object_class_install_properties (object_class, PROP_LAST, object_properties); +} + +ClutterEffect * +animation_clutter_affine_effect_new (AnimationTransformAnimation *transform_animation) +{ + return g_object_new (ANIMATION_CLUTTER_TYPE_AFFINE_EFFECT, + "transform-animation", transform_animation, + NULL); +} diff --git a/animation-clutter/animation-clutter-affine-effect.h b/animation-clutter/animation-clutter-affine-effect.h new file mode 100644 index 0000000..e41da71 --- /dev/null +++ b/animation-clutter/animation-clutter-affine-effect.h @@ -0,0 +1,44 @@ +/* + * animation-clutter/clutter-animation-affine-effect.h + * + * Copyright © 2013-2016 Endless Mobile, Inc. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Sam Spilsbury + */ + +#pragma once + +#include +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_CLUTTER_TYPE_AFFINE_EFFECT (animation_clutter_affine_effect_get_type ()) +G_DECLARE_FINAL_TYPE (AnimationClutterAffineEffect, animation_clutter_affine_effect, ANIMATION_CLUTTER, AFFINE_EFFECT, ClutterEffect) + +/** + * animation_clutter_affine_effect_new: + * @transform_animation: An #AnimationTransformAnimation to wrap. + * + * Creates a new #ClutterEffect which uses the underlying + * AnimationZoomAnimation to apply a linear transformation to the actor. + * + * Returns: (transfer full): A new #ClutterEffect + */ +ClutterEffect * animation_clutter_affine_effect_new (AnimationTransformAnimation *transform_animation); + +G_END_DECLS diff --git a/animation-clutter/animation-clutter-common-private.h b/animation-clutter/animation-clutter-common-private.h new file mode 100644 index 0000000..72fe04f --- /dev/null +++ b/animation-clutter/animation-clutter-common-private.h @@ -0,0 +1,31 @@ +/* + * animation-clutter/animation-clutter-common-private.h + * + * Copyright © 2013-2016 Endless Mobile, Inc. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Sam Spilsbury + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +void animation_clutter_actor_box_enlarge_for_effects (ClutterActorBox *box); + +G_END_DECLS + diff --git a/animation-clutter/animation-clutter-common.c b/animation-clutter/animation-clutter-common.c new file mode 100644 index 0000000..a687b6c --- /dev/null +++ b/animation-clutter/animation-clutter-common.c @@ -0,0 +1,253 @@ +/* + * animation-clutter/animation-clutter-common.c + * + * Copyright © 2013-2018 Endless Mobile, Inc. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Sam Spilsbury + */ + +#include + +#include + +#include "animation-clutter-common.h" + +/* This code was borrowed from clutter-actor-box.c + * + * Copyright (c) 2006, 2007, 2008, OpenedHand Ltd. + * Copyright (c) 2009, 2010, 2011, 2012, Intel Corporation. + * + * The reason why we need this code is that for the wobbly effect, + * both the model and the renderer must agree on the geometry of the + * surface to be animated. In the case of clutter, ClutterDeformEffect + * is a subclass of ClutterOffscreenEffect, such that the actor is first + * painted into an FBO, then the FBO is rendered with the specified mesh. + * + * However, "behind our back", clutter will make the FBO a little bit + * bigger than the actual actor geometry, meaning that if we were to + * deform vertices as though the surface size was the same as the actor + * size, we would actually be "pinching" the FBO every so slightly, leading + * to an irritating visual glitch where the window gets slightly smaller + * as soon as it starts moving. + * + * Therefore, we tell clutter that the actor paint box is the normal actor + * paint box extents without any effects applied as usual, but we tell + * libanimation that the model is the same size that clutter would have + * computed the FBO to be, such that the FBO size and the model size + * agree and there is no "pinching" effect. + */ +static inline int +nearby_int (float x) +{ + return (int) (x < 0.0f ? x - 0.5f : x + 0.5f); +} + +void +animation_clutter_actor_box_enlarge_for_effects (ClutterActorBox *box) +{ + float width, height; + + /* The aim here is that for a given rectangle defined with floating point + * coordinates we want to determine a stable quantized size in pixels + * that doesn't vary due to the original box's sub-pixel position. + * + * The reason this is important is because effects will use this + * API to determine the size of offscreen framebuffers and so for + * a fixed-size object that may be animated accross the screen we + * want to make sure that the stage paint-box has an equally stable + * size so that effects aren't made to continuously re-allocate + * a corresponding fbo. + * + * The other thing we consider is that the calculation of this box is + * subject to floating point precision issues that might be slightly + * different to the precision issues involved with actually painting the + * actor, which might result in painting slightly leaking outside the + * user's calculated paint-volume. For this we simply aim to pad out the + * paint-volume by at least half a pixel all the way around. + */ + width = box->x2 - box->x1; + height = box->y2 - box->y1; + width = nearby_int (width); + height = nearby_int (height); + /* XXX: NB the width/height may now be up to 0.5px too small so we + * must also pad by 0.25px all around to account for this. In total we + * must padd by at least 0.75px around all sides. */ + + /* XXX: The furthest that we can overshoot the bottom right corner by + * here is 1.75px in total if you consider that the 0.75 padding could + * just cross an integer boundary and so ceil will effectively add 1. + */ + box->x2 = ceilf (box->x2 + 0.75); + box->y2 = ceilf (box->y2 + 0.75); + + /* Now we redefine the top-left relative to the bottom right based on the + * rounded width/height determined above + a constant so that the overall + * size of the box will be stable and not dependant on the box's + * position. + * + * Adding 3px to the width/height will ensure we cover the maximum of + * 1.75px padding on the bottom/right and still ensure we have > 0.75px + * padding on the top/left. + */ + box->x1 = box->x2 - width - 3; + box->y1 = box->y2 - height - 3; +} + +/* This constant is used to deal with rounding error in computing + * paint boxes. See also https://gitlab.gnome.org/GNOME/mutter/blob/master/clutter/clutter/clutter-paint-volume.c#L1212 */ +#define PAINT_BOX_OFFSET 1 + +/** + * animation_clutter_get_untransformed_paint_box_from_existing_volume: + * @actor: A #ClutterActor + * @volume: A #ClutterPaintVolume + * @box: (out caller-allocates): A #ClutterActorBox to write extents to + * + * Assuming that the paint volume is orthogonal, get the stage-relative 2D + * extents of the paint volume and write them to the @box + * out parameter. + */ +void +animation_clutter_get_untransformed_paint_box_from_existing_volume (ClutterActor *actor, + const ClutterPaintVolume *volume, + ClutterActorBox *box) +{ + graphene_point3d_t origin; + + /* We don't have access to the stage projection matrix + * so the best we can do is hope here that the volume is + * two dimensional and orthogonal. */ + clutter_paint_volume_get_origin (volume, &origin); + + box->x1 = floor (origin.x + clutter_actor_get_x (actor)) - PAINT_BOX_OFFSET; + box->y1 = floor (origin.y + clutter_actor_get_y (actor)) - PAINT_BOX_OFFSET; + box->x2 = box->x1 + ceil (clutter_paint_volume_get_width (volume)) + PAINT_BOX_OFFSET * 2; + box->y2 = box->y1 + ceil (clutter_paint_volume_get_height (volume)) + PAINT_BOX_OFFSET * 2; +} + +/** + * animation_clutter_get_best_known_paint_extents_box: + * @actor: A #ClutterActor + * @box: (out caller-allocates): A #ClutterActorBox + * + * Get the paint box if possible, otherwise fall back to + * using the actor geometry. + */ +void +animation_clutter_get_best_known_paint_extents_box (ClutterActor *actor, + ClutterActorBox *box) +{ + float x, y, width, height; + + g_return_if_fail (box != NULL); + + if (clutter_actor_get_paint_box (actor, box)) + return; + + clutter_actor_get_position (actor, &x, &y); + clutter_actor_get_size (actor, &width, &height); + + box->x1 = x; + box->y1 = y; + box->x2 = x + width; + box->y2 = y + height; +} + +/** + * animation_clutter_compute_corners_from_paint_volume: + * @actor: A #ClutterActor that we're computing corners for. + * @volume: An existing #ClutterPaintVolume. + * @out_corners: (out caller-allocates) (array fixed-size=4): An array + * of corners representing a 3D bounding plane for this + * paint area. + * @offset: (out caller-allocates): A 2D vector representing the offset + * from the actor paint box corners to the real actor's real + * position. + */ +void +animation_clutter_compute_corners_from_untransformed_paint_volume (ClutterActor *actor, + ClutterPaintVolume *volume, + AnimationVector *out_corners, + AnimationVector *out_offset) +{ + ClutterActorBox box; + float actor_x, actor_y; + float actor_paint_box_x, actor_paint_box_y; + float actor_paint_box_width, actor_paint_box_height; + + g_return_if_fail (out_corners != NULL); + g_return_if_fail (out_offset != NULL); + + animation_clutter_get_untransformed_paint_box_from_existing_volume (actor, volume, &box); + clutter_actor_get_position (actor, &actor_x, &actor_y); + + actor_paint_box_x = box.x1; + actor_paint_box_y = box.y1; + actor_paint_box_width = box.x2 - box.x1; + actor_paint_box_height = box.y2 - box.y1; + + out_corners[0].x = actor_paint_box_x; + out_corners[0].y = actor_paint_box_y; + out_corners[1].x = actor_paint_box_x + actor_paint_box_width; + out_corners[1].y = actor_paint_box_y; + out_corners[2].x = actor_paint_box_x; + out_corners[2].y = actor_paint_box_y + actor_paint_box_height; + out_corners[3].x = actor_paint_box_x + actor_paint_box_width; + out_corners[3].y = actor_paint_box_y + actor_paint_box_height; + + out_offset->x = actor_paint_box_x - actor_x; + out_offset->y = actor_paint_box_y - actor_y; +} + +/** + * animation_clutter_expand_paint_volume_with_extremes: + * @volume: A #ClutterPaintVolume + * @extremes: (array fixed-size=4): An array + * of corners representing a 3D bounding plane for this + * paint area. + * @offset: A 2D vector representing the offset from the actor paint + * box corners to the real actor's real position. + */ +void +animation_clutter_expand_paint_volume_with_extremes (ClutterPaintVolume *volume, + AnimationVector4D *extremes, + AnimationVector *offset) +{ + float x1, y1, x2, y2, z1, z2; + + g_return_if_fail (extremes != NULL); + g_return_if_fail (offset != NULL); + + x1 = MIN (extremes[0].x, extremes[2].x) - offset->x; + y1 = MIN (extremes[0].y, extremes[1].y) - offset->y; + x2 = MAX (extremes[1].x, extremes[3].x) + offset->x; + y2 = MAX (extremes[2].y, extremes[3].y) + offset->y; + z1 = MIN (MIN (extremes[0].z, extremes[1].z), + MIN (extremes[2].z, extremes[3].z)); + z2 = MAX (MAX (extremes[0].z, extremes[1].z), + MAX (extremes[2].z, extremes[3].z)); + + g_autoptr(ClutterPaintVolume) extremes_volume = + clutter_paint_volume_copy (volume); + + graphene_point3d_t const origin = { x1, y1, z1 }; + clutter_paint_volume_set_origin (extremes_volume, &origin); + clutter_paint_volume_set_width (extremes_volume, x2 - x1); + clutter_paint_volume_set_height (extremes_volume, y2 - y1); + clutter_paint_volume_set_depth (extremes_volume, z2 - z1); + + clutter_paint_volume_union (volume, extremes_volume); +} diff --git a/animation-clutter/animation-clutter-common.h b/animation-clutter/animation-clutter-common.h new file mode 100644 index 0000000..2310f4b --- /dev/null +++ b/animation-clutter/animation-clutter-common.h @@ -0,0 +1,47 @@ +/* + * animation-clutter/animation-clutter-common.h + * + * Copyright © 2013-2016 Endless Mobile, Inc. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Sam Spilsbury + */ + +#pragma once + +#include +#include +#include +#include + +G_BEGIN_DECLS + +void animation_clutter_get_untransformed_paint_box_from_existing_volume (ClutterActor *actor, + const ClutterPaintVolume *volume, + ClutterActorBox *box); + +void animation_clutter_get_best_known_paint_extents_box (ClutterActor *actor, + ClutterActorBox *box); + +void animation_clutter_compute_corners_from_untransformed_paint_volume (ClutterActor *actor, + ClutterPaintVolume *volume, + AnimationVector *out_corners, + AnimationVector *out_offset); + +void animation_clutter_expand_paint_volume_with_extremes (ClutterPaintVolume *volume, + AnimationVector4D *extremes, + AnimationVector *offset); + +G_END_DECLS diff --git a/animation-clutter/animation-clutter-grid-effect.c b/animation-clutter/animation-clutter-grid-effect.c new file mode 100644 index 0000000..7914fb8 --- /dev/null +++ b/animation-clutter/animation-clutter-grid-effect.c @@ -0,0 +1,333 @@ +/* + * animation-clutter/animation-clutter-grid-effect.c + * + * Copyright © 2013-2018 Endless Mobile, Inc. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Sam Spilsbury + */ + +#include + +#include +#include +#include +#include + +#include +#include + +#include "animation-clutter-grid-effect.h" +#include "animation-clutter-common.h" + +struct _AnimationClutterGridEffect { + ClutterDeformEffect parent_instance; +}; + +typedef struct +{ + AnimationGridAnimation *grid_animation; + gint64 last_usecs; + guint timeout_id; +} AnimationClutterGridEffectPrivate; + +enum +{ + PROP_0, + PROP_GRID_ANIMATION, + PROP_LAST +}; + +static GParamSpec *object_properties[PROP_LAST]; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationClutterGridEffect, + animation_clutter_grid_effect, + CLUTTER_TYPE_DEFORM_EFFECT) + +static gboolean +animation_clutter_grid_effect_modify_paint_volume (ClutterEffect *effect, + ClutterPaintVolume *volume) +{ + ClutterActorMeta *meta = CLUTTER_ACTOR_META (effect); + ClutterActor *actor = clutter_actor_meta_get_actor (meta); + AnimationClutterGridEffect *grid_effect = ANIMATION_CLUTTER_GRID_EFFECT (effect); + AnimationClutterGridEffectPrivate *priv = + animation_clutter_grid_effect_get_instance_private (grid_effect); + + /* We assume that the parent's modify_paint_volume method always returns + * TRUE here. */ + CLUTTER_EFFECT_CLASS (animation_clutter_grid_effect_parent_class)->modify_paint_volume (effect, volume); + + if (priv->grid_animation && clutter_actor_meta_get_enabled (meta)) + { + ClutterActorBox box; + float actor_x, actor_y; + + animation_clutter_get_untransformed_paint_box_from_existing_volume (actor, volume, &box); + clutter_actor_get_position (actor, &actor_x, &actor_y); + + AnimationVector corners[4] = { + { box.x1, box.y1 }, + { box.x2, box.y1 }, + { box.x1, box.y2 }, + { box.x2, box.y2 } + }; + AnimationVector4D extremes[4]; + + animation_grid_animation_extremes (priv->grid_animation, + corners, + extremes); + + float x1 = MIN (extremes[0].x, extremes[2].x); + float y1 = MIN (extremes[0].y, extremes[1].y); + float z1 = MIN (MIN (extremes[0].z, extremes[1].z), + MIN (extremes[2].z, extremes[3].z)); + float x2 = MAX (extremes[1].x, extremes[3].x); + float y2 = MAX (extremes[2].y, extremes[3].y); + float z2 = MAX (MAX (extremes[0].z, extremes[1].z), + MAX (extremes[2].z, extremes[3].z)); + + g_autoptr(ClutterPaintVolume) extremes_volume = + clutter_paint_volume_copy (volume); + + graphene_point3d_t const origin = { x1 - actor_x, y1 - actor_y, z1 }; + clutter_paint_volume_set_origin (extremes_volume, &origin); + clutter_paint_volume_set_width (extremes_volume, MAX (x2 - x1, 1.0)); + clutter_paint_volume_set_height (extremes_volume, MAX (y2 - y1, 1.0)); + clutter_paint_volume_set_depth (extremes_volume, z2 - z1); + + clutter_paint_volume_union (volume, extremes_volume); + } + + return TRUE; +} + +static void +animation_clutter_grid_effect_deform_vertex (ClutterDeformEffect *effect, + gfloat width G_GNUC_UNUSED, + gfloat height G_GNUC_UNUSED, + CoglTextureVertex *vertex) +{ + ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); + AnimationClutterGridEffect *grid_effect = ANIMATION_CLUTTER_GRID_EFFECT (effect); + AnimationClutterGridEffectPrivate *priv = + animation_clutter_grid_effect_get_instance_private (grid_effect); + AnimationVector uv = { vertex->tx, vertex->ty }; + AnimationVector deformed; + float x, y; + + animation_grid_animation_deform_uv_to_model_space (priv->grid_animation, + &uv, + &deformed); + + clutter_actor_get_position (actor, &x, &y); + + vertex->x = deformed.x - x; + vertex->y = deformed.y - y; +} + + +static gboolean +animation_clutter_grid_effect_new_frame (gpointer user_data) +{ + AnimationClutterGridEffect *grid_effect = ANIMATION_CLUTTER_GRID_EFFECT (user_data); + AnimationClutterGridEffectPrivate *priv = + animation_clutter_grid_effect_get_instance_private (grid_effect); + ClutterActorMeta *meta = CLUTTER_ACTOR_META (grid_effect); + gint64 msecs = g_get_monotonic_time (); + + static const unsigned int ms_to_us = 1000; + + g_assert (priv->grid_animation); + + /* Wraparound, priv->last_usecs -= G_MAXINT64. + * We make priv->last_usecs negative so that subtracting it + * from msecs results in the correct delta */ + if (G_UNLIKELY (priv->last_usecs > msecs)) + priv->last_usecs -= G_MAXINT64; + + gint64 msecs_delta = (msecs - priv->last_usecs) / ms_to_us; + priv->last_usecs = msecs; + + /* If there was no time movement, then we can't really step or remove + * models in a way that makes sense, so don't do it */ + if (msecs_delta == 0) + return G_SOURCE_CONTINUE; + + if (!animation_grid_animation_step (priv->grid_animation, msecs_delta)) + { + /* Disable the effect */ + clutter_actor_meta_set_enabled (meta, FALSE); + + /* Finally, return false so that we don't keep animating */ + priv->timeout_id = 0; + return G_SOURCE_REMOVE; + } + else + { + clutter_actor_meta_set_enabled (meta, TRUE); + clutter_deform_effect_invalidate (CLUTTER_DEFORM_EFFECT (grid_effect)); + } + + /* We always want to return true even if there was no time delta */ + return G_SOURCE_CONTINUE; +} + +static void +animation_clutter_grid_effect_ensure_timeline (AnimationClutterGridEffect *grid_effect) +{ + AnimationClutterGridEffectPrivate *priv = + animation_clutter_grid_effect_get_instance_private (grid_effect); + + if (priv->timeout_id == 0) + { + static const unsigned int frame_length_ms = 16; /* 1000 / 60 */; + + priv->last_usecs = g_get_monotonic_time (); + priv->timeout_id = g_timeout_add (frame_length_ms, animation_clutter_grid_effect_new_frame, grid_effect); + + ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (grid_effect)); + + /* Make sure to show the actor so that we can see the animation + * if the animation was on open or unminimize */ + clutter_actor_show (actor); + } +} + +static void +animation_clutter_grid_effect_notify (GObject *object, + GParamSpec *pspec) +{ + ClutterActorMeta *actor_meta = CLUTTER_ACTOR_META (object); + AnimationClutterGridEffect *grid_effect = ANIMATION_CLUTTER_GRID_EFFECT (object); + AnimationClutterGridEffectPrivate *priv = + animation_clutter_grid_effect_get_instance_private (grid_effect); + + if (g_strcmp0 (pspec->name, "enabled") == 0) + { + if (clutter_actor_meta_get_enabled (actor_meta)) + animation_clutter_grid_effect_ensure_timeline (grid_effect); + else + g_clear_handle_id (&priv->timeout_id, g_source_remove); + } + + G_OBJECT_CLASS (animation_clutter_grid_effect_parent_class)->notify (object, pspec); +} + +static void +animation_clutter_grid_effect_set_actor (ClutterActorMeta *actor_meta, + ClutterActor *actor) +{ + CLUTTER_ACTOR_META_CLASS (animation_clutter_grid_effect_parent_class)->set_actor (actor_meta, actor); + + AnimationClutterGridEffect *grid_effect = ANIMATION_CLUTTER_GRID_EFFECT (actor_meta); + AnimationClutterGridEffectPrivate *priv = + animation_clutter_grid_effect_get_instance_private (grid_effect); + + /* Disable the effect before we do anything else - this ensures that + * for instance, we get the right paint box because we don't have applied + * effects that haven't had their paint boxes computed. */ + clutter_actor_meta_set_enabled (actor_meta, FALSE); + + g_clear_handle_id (&priv->timeout_id, g_source_remove); +} + +static void +animation_clutter_grid_effect_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationClutterGridEffect *grid_effect = ANIMATION_CLUTTER_GRID_EFFECT (object); + AnimationClutterGridEffectPrivate *priv = + animation_clutter_grid_effect_get_instance_private (grid_effect); + + switch (prop_id) + { + case PROP_GRID_ANIMATION: + g_set_object (&priv->grid_animation, g_value_dup_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +animation_clutter_grid_effect_dispose (GObject *object) +{ + AnimationClutterGridEffect *grid_effect = ANIMATION_CLUTTER_GRID_EFFECT (object); + AnimationClutterGridEffectPrivate *priv = + animation_clutter_grid_effect_get_instance_private (grid_effect); + + g_clear_object (&priv->grid_animation); + + G_OBJECT_CLASS (animation_clutter_grid_effect_parent_class)->dispose (object); +} + +static void +animation_clutter_grid_effect_finalize (GObject *object) +{ + AnimationClutterGridEffect *grid_effect = ANIMATION_CLUTTER_GRID_EFFECT (object); + AnimationClutterGridEffectPrivate *priv = + animation_clutter_grid_effect_get_instance_private (grid_effect); + + g_clear_handle_id (&priv->timeout_id, g_source_remove); + + G_OBJECT_CLASS (animation_clutter_grid_effect_parent_class)->finalize (object); +} + +static void +animation_clutter_grid_effect_init (AnimationClutterGridEffect *effect) +{ + AnimationClutterGridEffectPrivate *priv = + animation_clutter_grid_effect_get_instance_private (effect); + + priv->timeout_id = 0; +} + +static void +animation_clutter_grid_effect_class_init (AnimationClutterGridEffectClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); + ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); + ClutterDeformEffectClass *deform_class = CLUTTER_DEFORM_EFFECT_CLASS (klass); + + object_class->notify = animation_clutter_grid_effect_notify; + object_class->set_property = animation_clutter_grid_effect_set_property; + object_class->dispose = animation_clutter_grid_effect_dispose; + object_class->finalize = animation_clutter_grid_effect_finalize; + meta_class->set_actor = animation_clutter_grid_effect_set_actor; + effect_class->modify_paint_volume = animation_clutter_grid_effect_modify_paint_volume; + deform_class->deform_vertex = animation_clutter_grid_effect_deform_vertex; + + object_properties[PROP_GRID_ANIMATION] = + g_param_spec_object ("grid-animation", + "Grid Animation", + "The underlying grid animation", + ANIMATION_TYPE_GRID_ANIMATION, + G_PARAM_WRITABLE); + + g_object_class_install_properties (object_class, PROP_LAST, object_properties); +} + +ClutterEffect * +animation_clutter_grid_effect_new (AnimationGridAnimation *grid_animation) +{ + return g_object_new (ANIMATION_CLUTTER_TYPE_GRID_EFFECT, + "grid-animation", grid_animation, + NULL); +} diff --git a/animation-clutter/animation-clutter-grid-effect.h b/animation-clutter/animation-clutter-grid-effect.h new file mode 100644 index 0000000..9af2b5c --- /dev/null +++ b/animation-clutter/animation-clutter-grid-effect.h @@ -0,0 +1,45 @@ +/* + * animation-clutter/animation-clutter-grid-effect.h + * + * Copyright © 2013-2016 Endless Mobile, Inc. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Sam Spilsbury + */ + +#pragma once + +#include +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_CLUTTER_TYPE_GRID_EFFECT (animation_clutter_grid_effect_get_type ()) +G_DECLARE_FINAL_TYPE (AnimationClutterGridEffect, animation_clutter_grid_effect, ANIMATION_CLUTTER, GRID_EFFECT, ClutterDeformEffect) + + +/** + * animation_clutter_grid_effect_new: + * @grid_animation: An #AnimationGridAnimation to wrap. + * + * Creates a new #ClutterEffect which uses the underlying + * AnimationGridAnimation to apply grid based effects to the actor. + * + * Returns: (transfer full): A new #ClutterEffect + */ +ClutterEffect * animation_clutter_grid_effect_new (AnimationGridAnimation *grid_animation); + +G_END_DECLS diff --git a/animation-clutter/animation-clutter-wobbly-effect.c b/animation-clutter/animation-clutter-wobbly-effect.c new file mode 100644 index 0000000..46d465a --- /dev/null +++ b/animation-clutter/animation-clutter-wobbly-effect.c @@ -0,0 +1,523 @@ +/* + * animation-clutter/animation-clutter-wobbly-effect.c + * + * Copyright © 2013-2018 Endless Mobile, Inc. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Sam Spilsbury + */ + +#include + +#include +#include +#include + +#include +#include +#include + +#include "animation-clutter-common.h" +#include "animation-clutter-common-private.h" +#include "animation-clutter-wobbly-effect.h" + +struct _AnimationClutterWobblyEffect { + ClutterDeformEffect parent_instance; +}; + +typedef struct _AnimationClutterWobblyEffectPrivate +{ + float slowdown_factor; + double spring_constant; + double friction; + double movement_range; + AnimationWobblyModel *model; + AnimationWobblyAnchor *anchor; + gint64 last_msecs; + guint timeout_id; + guint width_changed_signal; + guint height_changed_signal; + gboolean ungrab_pending; +} AnimationClutterWobblyEffectPrivate; + +enum +{ + PROP_0, + + PROP_SPRING_K, + PROP_FRICTION, + PROP_SLOWDOWN_FACTOR, + PROP_OBJECT_MOVEMENT_RANGE, + + PROP_LAST +}; + +static GParamSpec *object_properties[PROP_LAST]; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationClutterWobblyEffect, + animation_clutter_wobbly_effect, + CLUTTER_TYPE_DEFORM_EFFECT) + +static gboolean +animation_clutter_wobbly_effect_modify_paint_volume (ClutterEffect *effect, + ClutterPaintVolume *volume) +{ + ClutterActorMeta *meta = CLUTTER_ACTOR_META (effect); + ClutterActor *actor = clutter_actor_meta_get_actor (meta); + AnimationClutterWobblyEffect *wobbly_effect = ANIMATION_CLUTTER_WOBBLY_EFFECT (effect); + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (wobbly_effect); + + /* We assume that the parent's modify_paint_volume method always returns + * TRUE here. */ + CLUTTER_EFFECT_CLASS (animation_clutter_wobbly_effect_parent_class)->modify_paint_volume (effect, volume); + + if (priv->model && clutter_actor_meta_get_enabled (meta)) + { + AnimationVector extremes[4]; + + animation_wobbly_model_query_extremes (priv->model, + &extremes[0], + &extremes[1], + &extremes[2], + &extremes[3]); + + float x1 = MIN (extremes[0].x, extremes[2].x); + float y1 = MIN (extremes[0].y, extremes[1].y); + float x2 = MAX (extremes[1].x, extremes[3].x); + float y2 = MAX (extremes[2].y, extremes[3].y); + + ClutterActorBox const extremesBox = + { + floor (x1), + floor (y1), + ceil (x2), + ceil (y2) + }; + + clutter_paint_volume_union_box (volume, &extremesBox); + } + + return TRUE; +} + +static void +animation_clutter_wobbly_effect_deform_vertex (ClutterDeformEffect *effect, + gfloat x G_GNUC_UNUSED, + gfloat y G_GNUC_UNUSED, + CoglTextureVertex *vertex) +{ + AnimationClutterWobblyEffect *wobbly_effect = ANIMATION_CLUTTER_WOBBLY_EFFECT (effect); + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (wobbly_effect); + + /* The reversal of ty and tx here is intentional */ + AnimationVector uv = { vertex->ty, vertex->tx }; + AnimationVector deformed; + animation_wobbly_model_deform_texcoords (priv->model, + uv, + &deformed); + + vertex->x = deformed.x; + vertex->y = deformed.y; +} + +static void +remove_anchor_if_pending (AnimationClutterWobblyEffectPrivate *priv) +{ + if (priv->ungrab_pending) + { + g_clear_object (&priv->anchor); + priv->ungrab_pending = FALSE; + } +} + +/* It turns out that clutter doesn't contain any mechanism whatsoever + * to do timeline-less animations. We're just using a timeout here + * to keep performing animations on the actor */ +static gboolean +animation_clutter_wobbly_effect_new_frame (gpointer user_data) +{ + AnimationClutterWobblyEffect *wobbly_effect = ANIMATION_CLUTTER_WOBBLY_EFFECT (user_data); + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (wobbly_effect); + gint64 msecs = g_get_monotonic_time (); + + static const unsigned int ms_to_us = 1000; + + g_assert (priv->model); + + /* Wraparound, priv->last_msecs -= G_MAXINT64. + * We make priv->last_msecs negative so that subtracting it + * from msecs results in the correct delta */ + if (G_UNLIKELY (priv->last_msecs > msecs)) + priv->last_msecs -= G_MAXINT64; + + gint64 msecs_delta = (msecs - priv->last_msecs ) / ms_to_us; + priv->last_msecs = msecs; + + /* If there was no time movement, then we can't really step or remove + * models in a way that makes sense, so don't do it */ + if (msecs_delta) + { + if (animation_wobbly_model_step (priv->model, msecs_delta / priv->slowdown_factor)) + { + clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (wobbly_effect), TRUE); + clutter_deform_effect_invalidate (CLUTTER_DEFORM_EFFECT (wobbly_effect)); + } + else + { + remove_anchor_if_pending (priv); + + /* Also disable the effect */ + clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (wobbly_effect), FALSE); + + /* Finally, return false so that we don't keep animating */ + priv->timeout_id = -1; + return FALSE; + } + } + + /* We always want to return true even if there was no time delta */ + return TRUE; +} + +static void +animation_clutter_wobbly_effect_ensure_timeline (AnimationClutterWobblyEffect *wobbly_effect) +{ + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (wobbly_effect); + + if (priv->timeout_id == -1) + { + static const unsigned int frame_length_ms = 16; // 60 / 1000; + + priv->last_msecs = g_get_monotonic_time (); + priv->timeout_id = g_timeout_add (frame_length_ms, animation_clutter_wobbly_effect_new_frame, wobbly_effect); + } +} + +void +animation_clutter_wobbly_effect_grab (AnimationClutterWobblyEffect *effect, + double x, + double y) +{ + ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (effect); + + g_assert (!priv->anchor || priv->ungrab_pending); + + /* Either ungrab here or at the end of the animation */ + remove_anchor_if_pending (priv); + + if (priv->model) + { + /* Make sure to update the model geometry and move + * to the right position, it may have changed + * in the meantime */ + ClutterActorBox box; + float actor_paint_box_width, actor_paint_box_height; + animation_clutter_get_best_known_paint_extents_box (actor, &box); + + /* See animation-clutter-common.c for why we need to do this */ + animation_clutter_actor_box_enlarge_for_effects (&box); + + actor_paint_box_width = box.x2 - box.x1; + actor_paint_box_height = box.y2 - box.y1; + + AnimationVector position = { 0, 0 }; + AnimationVector size = { actor_paint_box_width, actor_paint_box_height }; + + animation_wobbly_model_resize (priv->model, size); + animation_wobbly_model_move_to (priv->model, position); + + animation_clutter_wobbly_effect_ensure_timeline (effect); + + float actor_x, actor_y; + clutter_actor_get_position (actor, &actor_x, &actor_y); + + AnimationVector anchor_position = { x - actor_x, y - actor_y }; + + priv->anchor = animation_wobbly_model_grab_anchor (priv->model, anchor_position); + } +} + +void +animation_clutter_wobbly_effect_ungrab (AnimationClutterWobblyEffect *effect) +{ + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (effect); + + g_assert (priv->anchor && !priv->ungrab_pending); + + /* Don't immediately ungrab. We can be a little bit more + * clever here and make the ungrab pending on the completion + * of the animation */ + if (priv->timeout_id != -1) + priv->ungrab_pending = TRUE; + else + g_clear_object (&priv->anchor); +} + +void +animation_clutter_wobbly_effect_move_by (AnimationClutterWobblyEffect *effect, + double dx, + double dy) +{ + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (effect); + + if (priv->anchor) + { + AnimationVector delta = { dx, dy }; + + animation_clutter_wobbly_effect_ensure_timeline (effect); + animation_wobbly_anchor_move_by (priv->anchor, delta); + + AnimationVector reverse_delta = delta; + reverse_delta.x *= -1; + reverse_delta.y *= -1; + + /* Now move the entire model back - this ensures that + * we stay in sync with the actor's relative position */ + animation_wobbly_model_move_by (priv->model, reverse_delta); + } +} + +static void +animation_clutter_wobbly_effect_size_changed (GObject *object, + GParamSpec *spec G_GNUC_UNUSED, + gpointer user_data) +{ + ClutterActor *actor = CLUTTER_ACTOR (object); + AnimationClutterWobblyEffect *effect = ANIMATION_CLUTTER_WOBBLY_EFFECT (user_data); + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (effect); + + /* We don't ensure a timeline here because we only want to redistribute + * non-anchor points if we're already grabbed, which the wobbly effect will + * do internally anyways */ + if (priv->model) + { + ClutterActorBox box; + float actor_paint_box_width, actor_paint_box_height; + animation_clutter_get_best_known_paint_extents_box (actor, &box); + + /* See animation-clutter-common.c for why we need to do this */ + animation_clutter_actor_box_enlarge_for_effects (&box); + + actor_paint_box_width = box.x2 - box.x1; + actor_paint_box_height = box.y2 - box.y1; + + /* If we have any pending anchors, we should release them now - + * the model move and resize code explicitly does not move + * anchors around (because that'd put them out of sync with + * the cursor) */ + remove_anchor_if_pending (priv); + + AnimationVector actor_size = { actor_paint_box_width, actor_paint_box_height }; + AnimationVector actor_position = { 0.0, 0.0 }; + + g_message ("Actor paint box size %f %f %f %f\n", box.x1, box.y1, actor_paint_box_width, actor_paint_box_height); + animation_wobbly_model_resize (priv->model, actor_size); + animation_wobbly_model_move_to (priv->model, actor_position); + } +} + +static void +animation_clutter_wobbly_effect_set_actor (ClutterActorMeta *actor_meta, + ClutterActor *actor) +{ + ClutterActor *prev_actor = clutter_actor_meta_get_actor (actor_meta); + + CLUTTER_ACTOR_META_CLASS (animation_clutter_wobbly_effect_parent_class)->set_actor (actor_meta, actor); + + AnimationClutterWobblyEffect *wobbly_effect = ANIMATION_CLUTTER_WOBBLY_EFFECT (actor_meta); + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (wobbly_effect); + + g_clear_object (&priv->anchor); + g_clear_object (&priv->model); + + priv->ungrab_pending = FALSE; + + if (priv->timeout_id != -1) + { + g_source_remove (priv->timeout_id); + priv->timeout_id = -1; + } + + if (prev_actor) + { + g_signal_handler_disconnect (prev_actor, priv->width_changed_signal); + priv->width_changed_signal = 0; + + g_signal_handler_disconnect (prev_actor, priv->height_changed_signal); + priv->height_changed_signal = 0; + } + + if (actor) + { + ClutterActorBox box; + float actor_paint_box_width, actor_paint_box_height; + animation_clutter_get_best_known_paint_extents_box (actor, &box); + + actor_paint_box_width = box.x2 - box.x1; + actor_paint_box_height = box.y2 - box.y1; + + AnimationVector actor_position = { 0, 0 }; + AnimationVector actor_size = { actor_paint_box_width, actor_paint_box_height }; + + priv->model = animation_wobbly_model_new (actor_position, + actor_size, + priv->spring_constant, + priv->friction, + priv->movement_range); + + priv->width_changed_signal = + g_signal_connect_object (actor, + "notify::width", + G_CALLBACK (animation_clutter_wobbly_effect_size_changed), + wobbly_effect, + G_CONNECT_AFTER); + priv->height_changed_signal = + g_signal_connect_object (actor, + "notify::height", + G_CALLBACK (animation_clutter_wobbly_effect_size_changed), + wobbly_effect, + G_CONNECT_AFTER); + } + + /* Whatever the actor, ensure that the effect is disabled at this point */ + clutter_actor_meta_set_enabled (actor_meta, FALSE); +} + +static void +animation_clutter_wobbly_effect_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationClutterWobblyEffect *wobbly_effect = ANIMATION_CLUTTER_WOBBLY_EFFECT (object); + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (wobbly_effect); + + switch (prop_id) + { + case PROP_SPRING_K: + priv->spring_constant = g_value_get_double (value); + + if (priv->model != NULL) + animation_wobbly_model_set_spring_k (priv->model, priv->spring_constant); + break; + case PROP_FRICTION: + priv->friction = g_value_get_double (value); + + if (priv->model != NULL) + animation_wobbly_model_set_friction (priv->model, priv->friction); + break; + case PROP_SLOWDOWN_FACTOR: + priv->slowdown_factor = g_value_get_double (value); + break; + case PROP_OBJECT_MOVEMENT_RANGE: + priv->movement_range = g_value_get_double (value); + + if (priv->model != NULL) + animation_wobbly_model_set_maximum_range (priv->model, priv->movement_range); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +animation_clutter_wobbly_effect_finalize (GObject *object) +{ + AnimationClutterWobblyEffect *wobbly_effect = ANIMATION_CLUTTER_WOBBLY_EFFECT (object); + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (wobbly_effect); + + g_clear_object (&priv->model); + + if (priv->timeout_id != -1) + { + g_source_remove (priv->timeout_id); + priv->timeout_id = -1; + } + + G_OBJECT_CLASS (animation_clutter_wobbly_effect_parent_class)->finalize (object); +} + +static void +animation_clutter_wobbly_effect_init (AnimationClutterWobblyEffect *effect) +{ + AnimationClutterWobblyEffectPrivate *priv = + animation_clutter_wobbly_effect_get_instance_private (effect); + + priv->timeout_id = -1; +} + +static void +animation_clutter_wobbly_effect_class_init (AnimationClutterWobblyEffectClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); + ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); + ClutterDeformEffectClass *deform_class = CLUTTER_DEFORM_EFFECT_CLASS (klass); + + object_class->set_property = animation_clutter_wobbly_effect_set_property; + object_class->finalize = animation_clutter_wobbly_effect_finalize; + meta_class->set_actor = animation_clutter_wobbly_effect_set_actor; + effect_class->modify_paint_volume = animation_clutter_wobbly_effect_modify_paint_volume; + deform_class->deform_vertex = animation_clutter_wobbly_effect_deform_vertex; + + object_properties[PROP_SPRING_K] = + g_param_spec_double ("spring-k", + "Spring Constant", + "How springy the model is", + 2.0, 10.0, 8.0, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT); + + object_properties[PROP_FRICTION] = + g_param_spec_double ("friction", + "Friction Constant", + "How much friction force should be applied to moving objects", + 2.0, 10.0, 3.0, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT); + + object_properties[PROP_SLOWDOWN_FACTOR] = + g_param_spec_double ("slowdown-factor", + "Slowdown Factor", + "How much to slow the model's timesteps down", + 1.0, 5.0, 1.0, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT); + + object_properties[PROP_OBJECT_MOVEMENT_RANGE] = + g_param_spec_double ("object-movement-range", + "Object Movement Range", + "How much objects are allowed to move around", + 10.0, 500.0, 100.0, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT); + + g_object_class_install_properties (object_class, PROP_LAST, object_properties); +} + +ClutterEffect * +animation_clutter_wobbly_effect_new (void) +{ + return g_object_new (ANIMATION_CLUTTER_TYPE_WOBBLY, NULL); +} diff --git a/animation-clutter/animation-clutter-wobbly-effect.h b/animation-clutter/animation-clutter-wobbly-effect.h new file mode 100644 index 0000000..e68eb8a --- /dev/null +++ b/animation-clutter/animation-clutter-wobbly-effect.h @@ -0,0 +1,90 @@ +/* + * animation-clutter/animation-clutter-wobbly-effect.h + * + * Copyright © 2013-2016 Endless Mobile, Inc. + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Sam Spilsbury + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_CLUTTER_TYPE_WOBBLY animation_clutter_wobbly_effect_get_type () +G_DECLARE_FINAL_TYPE (AnimationClutterWobblyEffect, animation_clutter_wobbly_effect, ANIMATION_CLUTTER, WOBBLY_EFFECT, ClutterDeformEffect) + +/** + * animation_clutter_wobbly_effect_grab: + * @effect: An #AnimationClutterWobblyEffect + * @x: The x-coordinate on the mesh to grab, specified relative to the + * upper-left corner of the mesh + * @y: The y-coordinate on the mesh to grab, specified relative to the + * upper-left corner of the mesh. + * + * Grabs the anchor specified by @x and @y on the mesh. While + * the mesh is in this state, this point will move immediately, + * causing spring forces to be applied to other points on the mesh + * + * It is a precondition violation to call this function when the mesh is + * already grabbed. + * + */ +void animation_clutter_wobbly_effect_grab (AnimationClutterWobblyEffect *effect, + double x, + double y); + +/** + * animation_clutter_wobbly_effect_ungrab: + * @effect: An #AnimationClutterWobblyEffect + * Removes the current grab. When the actor is moved, the mesh will + * move uniformly. + * + * It is a precondition violation to call this function when the mesh is + * not grabbed. + */ +void animation_clutter_wobbly_effect_ungrab (AnimationClutterWobblyEffect *effect); + +/** + * animation_clutter_wobbly_effect_move_by: + * @effect: An #AnimationClutterWobblyEffect + * @dx: A delta-x coordinate to move the mesh by + * @dy: A delta-y coordinate to move the mesh by + * + * Moves the mesh by @dx and @dy + * + * If the mesh is grabbed, then spring forces will be applied causing + * some points on the mesh to move more slowly than others. The nature + * of the moment will depend on the window's maximization state. + * + */ +void animation_clutter_wobbly_effect_move_by (AnimationClutterWobblyEffect *effect, + double dx, + double dy); + +/** + * animation_clutter_wobbly_effect_new: + * + * Creates a new #ClutterEffect which makes the window "wobble" + * on a spring mesh for the actor + * + * Returns: (transfer full): A new #ClutterEffect + */ +ClutterEffect * animation_clutter_wobbly_effect_new (void); + +G_END_DECLS diff --git a/animation-clutter/meson.build b/animation-clutter/meson.build new file mode 100644 index 0000000..074cb52 --- /dev/null +++ b/animation-clutter/meson.build @@ -0,0 +1,108 @@ +# animation-clutter/meson.build +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Meson build definitions for clutter backend to libanimation. + +api_version = '0' + +animation_clutter_introspectable_sources = files([]) +animation_clutter_private_headers = files([]) +animation_clutter_private_sources = files([]) +animation_clutter_headers = files([]) +animation_clutter_headers_subdir = 'animation-clutter' +animation_clutter_sources = files([]) + +animation_clutter_toplevel_private_headers = files([ + 'animation-clutter-common-private.h' +]) +animation_clutter_toplevel_headers = files([ + 'animation-clutter-affine-effect.h', + 'animation-clutter-actor-box-query.h', + 'animation-clutter-common.h', + 'animation-clutter-grid-effect.h', + 'animation-clutter-wobbly-effect.h' +]) + +animation_clutter_toplevel_private_sources = files([]) +animation_clutter_toplevel_introspectable_sources = files([ + 'animation-clutter-affine-effect.c', + 'animation-clutter-actor-box-query.c', + 'animation-clutter-common.c', + 'animation-clutter-grid-effect.c', + 'animation-clutter-wobbly-effect.c' +]) + +animation_clutter_introspectable_sources += animation_clutter_toplevel_introspectable_sources +animation_clutter_private_sources += animation_clutter_toplevel_private_sources +animation_clutter_headers += animation_clutter_toplevel_headers +animation_clutter_private_headers += animation_clutter_toplevel_private_headers + +install_headers(animation_clutter_toplevel_headers, subdir: animation_clutter_headers_subdir) + +animation_clutter_sources = animation_clutter_introspectable_sources + animation_clutter_private_sources + +glib = dependency('glib-2.0') +gobject = dependency('gobject-2.0') +clutter = dependency('mutter-clutter-8') +mutter = dependency('libmutter-8') + +mutter_typelibdir = mutter.get_pkgconfig_variable('typelibdir') +install_rpath = mutter_typelibdir + +animation_clutter_lib = shared_library( + 'animation-clutter', + animation_clutter_sources, + soversion: api_version, + install: true, + include_directories: [ animation_inc ], + dependencies: [ clutter, glib, gobject, mutter, animation_dep, animation_glib_dep ], + build_rpath: mutter_typelibdir, + install_rpath: mutter_typelibdir +) + +animation_clutter_dep = declare_dependency( + link_with: animation_clutter_lib, + include_directories: [ animation_inc ], +) + +introspection_sources = [ animation_clutter_introspectable_sources, animation_clutter_headers ] + +gnome = import('gnome') +animation_clutter_gir = gnome.generate_gir( + animation_clutter_lib, + extra_args: ['--warn-all', '--warn-error'], + identifier_prefix: 'AnimationClutter', + include_directories: animation_inc, + includes: [animation_glib_gir, 'Clutter-8', 'GLib-2.0', 'GObject-2.0'], + install: true, + namespace: 'AnimationClutter', + nsversion: api_version, + sources: introspection_sources, + symbol_prefix: 'animation_clutter' +)[0] + +pkg = import('pkgconfig') +pkg.generate( + description: 'Library to provide 2D surface animations (Clutter Implementation)', + name: 'libanimation-clutter', + filebase: 'libanimation-clutter-' + api_version, + version: meson.project_version(), + libraries: animation_clutter_lib, + install_dir: join_paths(get_option('libdir'), 'pkgconfig') +) + diff --git a/animation-glib/bounce/bounce.cpp b/animation-glib/bounce/bounce.cpp new file mode 100644 index 0000000..f262e7e --- /dev/null +++ b/animation-glib/bounce/bounce.cpp @@ -0,0 +1,336 @@ +/* + * animation-glib/bounce/bounce.cpp + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject implementation for a "bounce" animation. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace agd = animation::geometry::dimension; +namespace agl = animation::glib; +namespace ab = animation::bounce; +namespace ag = animation::glib; +namespace as = animation::stepper; +namespace asg = animation::stepper::glib; +namespace at = animation::transform; + +struct _AnimationBounceAnimation +{ + AnimationTransformAnimation parent_instance; +}; + +typedef struct _AnimationBounceAnimationPrivate +{ +} AnimationBounceAnimationPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationBounceAnimation, + animation_bounce_animation, + ANIMATION_TYPE_TRANSFORM_ANIMATION) + +enum { + PROP_0, + PROP_INITIAL_SCALE, + PROP_MAXIMUM_SCALE, + PROP_N_BOUNCE, + PROP_TARGET, + PROP_STEPPER, + NPROPS +}; + +static GParamSpec *animation_bounce_animation_props [NPROPS] = { NULL, }; + +double +animation_bounce_animation_get_initial_scale (AnimationBounceAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->InitialScale (); +} + +void +animation_bounce_animation_set_initial_scale (AnimationBounceAnimation *animation, + double initial_scale) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->InitialScale (initial_scale); +} + +double +animation_bounce_animation_get_maximum_scale (AnimationBounceAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->MaximumScale (); +} + +void +animation_bounce_animation_set_maximum_scale (AnimationBounceAnimation *animation, + double maximum_scale) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->MaximumScale (maximum_scale); +} + +unsigned int +animation_bounce_animation_get_n_bounce (AnimationBounceAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->NBounce (); +} + +void +animation_bounce_animation_set_n_bounce (AnimationBounceAnimation *animation, + unsigned int n_bounce) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->NBounce (n_bounce); +} + +void +animation_bounce_animation_set_target (AnimationBounceAnimation *animation, + AnimationBoxQuery *target) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Target (std::make_shared (target)); +} + +/** + * animation_bounce_animation_get_target: + * @animation: An #AnimationBoxQuery + * + * Returns: (transfer none): Get the #AnimationBoxQuery target for this #AnimationBounceAnimation + */ +AnimationBoxQuery * +animation_bounce_animation_get_target (AnimationBounceAnimation *animation) +{ + return std::static_pointer_cast (LookupTypedInterfaceProp (G_OBJECT (animation))->Target ())->BoxQuery (); +} + +void +animation_bounce_animation_set_stepper (AnimationBounceAnimation *animation, + AnimationStepper *stepper) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper (std::make_shared (stepper)); +} + +/** + * animation_bounce_animation_get_stepper: + * @animation: An #AnimationBounceAnimation + * + * Returns: (transfer none): Get the #AnimationStepper for this #AnimationBounceAnimation + */ +AnimationStepper * +animation_bounce_animation_get_stepper (AnimationBounceAnimation *animation) +{ + auto const &stepper (LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper ()); + + return std::static_pointer_cast (stepper)->BaseStepper (); +} + +static void +animation_bounce_animation_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) + { + case PROP_INITIAL_SCALE: + animation_bounce_animation_set_initial_scale (ANIMATION_BOUNCE_ANIMATION (object), + g_value_get_double (value)); + break; + case PROP_MAXIMUM_SCALE: + animation_bounce_animation_set_maximum_scale (ANIMATION_BOUNCE_ANIMATION (object), + g_value_get_double (value)); + break; + case PROP_N_BOUNCE: + animation_bounce_animation_set_n_bounce (ANIMATION_BOUNCE_ANIMATION (object), + g_value_get_uint (value)); + break; + case PROP_TARGET: + animation_bounce_animation_set_target (ANIMATION_BOUNCE_ANIMATION (object), + ANIMATION_BOX_QUERY (g_value_get_object (value))); + break; + case PROP_STEPPER: + animation_bounce_animation_set_stepper (ANIMATION_BOUNCE_ANIMATION (object), + ANIMATION_STEPPER (g_value_get_object (value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_bounce_animation_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AnimationBounceAnimation *bounce_animation = ANIMATION_BOUNCE_ANIMATION (object); + + switch (prop_id) + { + case PROP_INITIAL_SCALE: + g_value_set_double (value, animation_bounce_animation_get_initial_scale (bounce_animation)); + break; + case PROP_MAXIMUM_SCALE: + g_value_set_double (value, animation_bounce_animation_get_maximum_scale (bounce_animation)); + break; + case PROP_N_BOUNCE: + g_value_set_uint (value, animation_bounce_animation_get_n_bounce (bounce_animation)); + break; + case PROP_TARGET: + g_value_set_object (value, animation_bounce_animation_get_target (bounce_animation)); + break; + case PROP_STEPPER: + g_value_set_object (value, animation_bounce_animation_get_stepper (bounce_animation)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static GObject * +animation_bounce_animation_constructor (GType type, + unsigned int n_construct_params, + GObjectConstructParam *construct_params) +{ + auto *interface = InterfaceConstructor ::construct (); + auto *transform_interface = static_cast (interface); + + /* We need to also set defaults for certain properties in order + * to ensure that they are bindable later on, in case they are + * not set by the caller. */ + replace_named_pointer_props_in_construct_params_if_null ( + construct_params, + n_construct_params, + { + ReplacePropSpec ("stepper", g_value_get_object, g_value_set_object, []() -> gpointer { + return animation_linear_stepper_new (1); + }), + ReplacePropSpec ("target", g_value_get_object, g_value_set_object, []() -> gpointer { + return animation_box_query_new (); + }) + } + ); + + replace_interface_prop_in_construct_params (construct_params, + n_construct_params, + transform_interface); + + return G_OBJECT_CLASS (animation_bounce_animation_parent_class)->constructor (type, + n_construct_params, + construct_params); +} + +static void +animation_bounce_animation_constructed (GObject *object) +{ + /* Take a size-zero step, which ensures that we update the + * internal state of the animation with all the properties we + * just set */ + animation_transform_animation_step (ANIMATION_TRANSFORM_ANIMATION (object), 0); +} + +static void +animation_bounce_animation_init (AnimationBounceAnimation *model) +{ +} + + +static void +animation_bounce_animation_class_init (AnimationBounceAnimationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = animation_bounce_animation_constructor; + object_class->constructed = animation_bounce_animation_constructed; + object_class->get_property = animation_bounce_animation_get_property; + object_class->set_property = animation_bounce_animation_set_property; + + animation_bounce_animation_props[PROP_INITIAL_SCALE] = + g_param_spec_double ("initial-scale", + "Initial Scale", + "The initial scale of the animation", + 0.1, + 1.0, + 0.7, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_bounce_animation_props[PROP_MAXIMUM_SCALE] = + g_param_spec_double ("maximum-scale", + "Maximum Scale", + "The maximum scale of the animation", + 1.0, + 3.0, + 1.2, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_bounce_animation_props[PROP_N_BOUNCE] = + g_param_spec_uint ("n-bounce", + "Number of Bounces", + "The number of bounces in the animation", + 1, + 10, + 1, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_bounce_animation_props[PROP_TARGET] = + g_param_spec_object ("target", + "Target Box", + "Box that we are animating to", + ANIMATION_TYPE_BOX_QUERY, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_bounce_animation_props[PROP_STEPPER] = + g_param_spec_object ("stepper", + "Stepper", + "Stepper to use to progress the animation", + ANIMATION_TYPE_STEPPER, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_properties (object_class, + NPROPS, + animation_bounce_animation_props); +} + +/** + * animation_bounce_new: + * @initial_scale: Scale factor that the surface will initially have. + * @maximum_scale: Scale factor that the surface will have at maximum. + * @n_bounce: Number of bounces. + * @target: The #AnimationBoxQuery that we are animating to. + * @stepper: The #AnimationStepper of the animation. + * + * Returns: (transfer full): A new #AnimationBounceAnimation. + */ +AnimationBounceAnimation * +animation_bounce_new (double initial_scale, + double maximum_scale, + unsigned int n_bounce, + const AnimationBoxQuery *target, + AnimationStepper *stepper) +{ + return ANIMATION_BOUNCE_ANIMATION (g_object_new (ANIMATION_TYPE_BOUNCE_ANIMATION, + "initial-scale", initial_scale, + "maximum-scale", maximum_scale, + "n-bounce", n_bounce, + "target", target, + "stepper", stepper, + NULL)); +} diff --git a/animation-glib/bounce/bounce.h b/animation-glib/bounce/bounce.h new file mode 100644 index 0000000..4315fb3 --- /dev/null +++ b/animation-glib/bounce/bounce.h @@ -0,0 +1,63 @@ +/* + * animation-glib/bounce/bounce.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject Interface for "bounce" animation. + */ +#pragma once + +#include + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_BOUNCE_ANIMATION animation_bounce_animation_get_type () +G_DECLARE_FINAL_TYPE (AnimationBounceAnimation, animation_bounce_animation, ANIMATION, BOUNCE_ANIMATION, AnimationTransformAnimation) + +double animation_bounce_animation_get_initial_scale (AnimationBounceAnimation *animation); +void animation_bounce_animation_set_initial_scale (AnimationBounceAnimation *animation, + double initial_scale); + +double animation_bounce_animation_get_maximum_scale (AnimationBounceAnimation *animation); +void animation_bounce_animation_set_maximum_scale (AnimationBounceAnimation *animation, + double maximum_scale); + +unsigned int animation_bounce_animation_get_n_bounce (AnimationBounceAnimation *animation); +void animation_bounce_animation_set_n_bounce (AnimationBounceAnimation *animation, + unsigned int n_bounce); + +void animation_bounce_animation_set_stepper (AnimationBounceAnimation *animation, + AnimationStepper *stepper); +AnimationStepper * animation_bounce_animation_get_stepper (AnimationBounceAnimation *animation); + +void animation_bounce_animation_set_target (AnimationBounceAnimation *animation, + AnimationBoxQuery *target); +AnimationBoxQuery * animation_bounce_animation_get_target (AnimationBounceAnimation *animation); + +AnimationBounceAnimation * animation_bounce_new (double initial_scale, + double maximum_scale, + unsigned int n_bounce, + const AnimationBoxQuery *target, + AnimationStepper *stepper); + +G_END_DECLS + +#pragma once diff --git a/animation-glib/bounce/meson.build b/animation-glib/bounce/meson.build new file mode 100644 index 0000000..44c2279 --- /dev/null +++ b/animation-glib/bounce/meson.build @@ -0,0 +1,32 @@ +# animation-glib/bounce/meson.build +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (bounce animation component), GObject bindings. + +bounce_introspectable_sources = files([ + 'bounce.cpp' +]) + +bounce_headers = files([ + 'bounce.h' +]) + +animation_glib_introspectable_sources += bounce_introspectable_sources +animation_glib_headers += bounce_headers + +install_headers(bounce_headers, subdir: join_paths(animation_headers_subdir, 'bounce')) diff --git a/animation-glib/box.cpp b/animation-glib/box.cpp new file mode 100644 index 0000000..00af7b3 --- /dev/null +++ b/animation-glib/box.cpp @@ -0,0 +1,40 @@ +/* + * animation-glib/box.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject Interface for "wobbly" textures, box + * type implementation. + */ + +#include + +static gpointer +animation_box_copy (gpointer ptr) +{ + AnimationBox *src = reinterpret_cast (ptr); + AnimationBox *dst = g_new0 (AnimationBox, 1); + + *dst = *src; + + return reinterpret_cast (dst); +} + +G_DEFINE_BOXED_TYPE (AnimationBox, + animation_box, + animation_box_copy, + g_free); diff --git a/animation-glib/box.h b/animation-glib/box.h new file mode 100644 index 0000000..b583475 --- /dev/null +++ b/animation-glib/box.h @@ -0,0 +1,39 @@ +/* + * animation-glib/box.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject Interface for "wobbly" textures, 2D box type. + */ +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +typedef struct { + AnimationVector top_left; + AnimationVector bottom_right; +} AnimationBox; + +#define ANIMATION_TYPE_BOX animation_box_get_type () + +GType animation_box_get_type (); + +G_END_DECLS diff --git a/animation-glib/constructor-helpers.cpp b/animation-glib/constructor-helpers.cpp new file mode 100644 index 0000000..522a7a2 --- /dev/null +++ b/animation-glib/constructor-helpers.cpp @@ -0,0 +1,139 @@ +/* + * animation-glib/constructor-helpers.cpp + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Helpers for constructing C++ objects directly from GObject properties + * in a GObject constructor. + */ + +#include + +#include "constructor-helpers.h" + +/** + * replace_construct_param: + * @construct_params: (array length=n_construct_params) An array of #GObjectConstructParam + * @n_construct_params: Number of elements in @construct_params. + * @prop_name: The name of the construct prop to replace the value of. + * @initialize_func: A function which sets the GValue to something sensible. + * + * Replace a construction parameter @prop_name in the + * passed @construct_params by using the passed @initialize_func. + * + * This function must always replace one construct parameter, it is + * an error to pass a @prop_name that is not in the @construct_params. + */ +void +replace_construct_param (GObjectConstructParam *construct_params, + unsigned int n_construct_params, + const char *prop_name, + AnimationConstructorHelpersInitializeValueFunc initialize_func, + gpointer initialize_func_data) +{ + /* The prop should always be found in the array so that we can replace + * it, this function doesn't support appending the prop. That means + * that the relevant prop must always G_PARAM_CONSTRUCT or + * G_PARAM_CONSTRUCT_ONLY. */ + for (unsigned int i = 0; i < n_construct_params; ++i) + { + if (g_strcmp0 (construct_params[i].pspec->name, prop_name) == 0) + { + g_value_unset (construct_params[i].value); + initialize_func (construct_params[i].value, initialize_func_data); + return; + } + } + + g_assert_not_reached (); +} + +template +static typename std::result_of ::type +invoke_function_thunk (Args... args, gpointer lambda) +{ + FunctionType *f = reinterpret_cast (lambda); + + return (*f)(args...); +} + + +void +replace_named_pointer_prop_in_construct_params (GObjectConstructParam *construct_params, + unsigned int n_construct_params, + const char *prop_name, + gpointer ptr) +{ + auto set_value = [ptr](GValue *value) { + g_value_init (value, G_TYPE_POINTER); + g_value_set_pointer (value, ptr); + }; + replace_construct_param (construct_params, + n_construct_params, + prop_name, + (AnimationConstructorHelpersInitializeValueFunc) invoke_function_thunk , + &set_value); +} + +void +replace_interface_prop_in_construct_params (GObjectConstructParam *construct_params, + unsigned int n_construct_params, + gpointer interface) +{ + replace_named_pointer_prop_in_construct_params (construct_params, + n_construct_params, + "interface", + interface); +} + +void +replace_named_pointer_prop_in_construct_params_if_null (GObjectConstructParam *construct_params, + unsigned int n_construct_params, + const char *prop_name, + AnimationConstructorHelpersGValueGetPointerFunc get_func, + AnimationConstructorHelpersGValueSetPointerFunc set_func, + AnimationConstructorHelpersConstructDefaultValueFunc construct_func) +{ + /* The prop should always be found in the array so that we can replace + * it, this function doesn't support appending the prop. That means + * that the relevant prop must always G_PARAM_CONSTRUCT or + * G_PARAM_CONSTRUCT_ONLY. */ + for (unsigned int i = 0; i < n_construct_params; ++i) + { + if (g_strcmp0 (construct_params[i].pspec->name, prop_name) == 0) + { + if (get_func (construct_params[i].value) == nullptr) + set_func (construct_params[i].value, construct_func ()); + + return; + } + } + + g_assert_not_reached (); +} + +void replace_named_pointer_props_in_construct_params_if_null (GObjectConstructParam *construct_params, + unsigned int n_construct_params, + std::initializer_list replace_specs) +{ + for (auto const &spec : replace_specs) { + replace_named_pointer_prop_in_construct_params_if_null (construct_params, + n_construct_params, + spec.prop_name, + spec.get_func, + spec.set_func, + spec.construct_func); + } +} diff --git a/animation-glib/constructor-helpers.h b/animation-glib/constructor-helpers.h new file mode 100644 index 0000000..c6f9218 --- /dev/null +++ b/animation-glib/constructor-helpers.h @@ -0,0 +1,130 @@ +/* + * animation-glib/constructor-helpers.cpp + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Helpers for constructing C++ objects directly from GObject properties + * in a GObject constructor. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +inline GValue * +lookup_gvalue (GHashTable *ht, const char *key) +{ + return reinterpret_cast (g_hash_table_lookup (ht, key)); +} + +typedef void (*AnimationConstructorHelpersInitializeValueFunc) (GValue *value, + gpointer user_data); + +void replace_construct_param (GObjectConstructParam *construct_params, + unsigned int n_construct_params, + const char *prop_name, + AnimationConstructorHelpersInitializeValueFunc initialize_func, + gpointer initialize_func_data); + +void replace_named_pointer_prop_in_construct_params (GObjectConstructParam *construct_params, + unsigned int n_construct_params, + const char *prop_name, + gpointer ptr); + +void replace_interface_prop_in_construct_params (GObjectConstructParam *construct_params, + unsigned int n_construct_params, + gpointer interface); + +typedef gpointer (*AnimationConstructorHelpersGValueGetPointerFunc) (const GValue *value); +typedef void (*AnimationConstructorHelpersGValueSetPointerFunc) (GValue *value, gpointer); +typedef gpointer (*AnimationConstructorHelpersConstructDefaultValueFunc) (void); + +void replace_named_pointer_prop_in_construct_params_if_null (GObjectConstructParam *construct_params, + unsigned int n_construct_params, + const char *prop_name, + AnimationConstructorHelpersGValueGetPointerFunc get_func, + AnimationConstructorHelpersGValueSetPointerFunc set_func, + AnimationConstructorHelpersConstructDefaultValueFunc construct_func); + +G_END_DECLS + +#ifdef __cplusplus +struct ReplacePropSpec +{ + ReplacePropSpec (const char *prop_name, + AnimationConstructorHelpersGValueGetPointerFunc get_func, + AnimationConstructorHelpersGValueSetPointerFunc set_func, + AnimationConstructorHelpersConstructDefaultValueFunc construct_func) : + prop_name (prop_name), + get_func (get_func), + set_func (set_func), + construct_func (construct_func) + { + } + + const char *prop_name; + AnimationConstructorHelpersGValueGetPointerFunc get_func; + AnimationConstructorHelpersGValueSetPointerFunc set_func; + AnimationConstructorHelpersConstructDefaultValueFunc construct_func; +}; + +void replace_named_pointer_props_in_construct_params_if_null (GObjectConstructParam *construct_params, + unsigned int n_construct_params, + std::initializer_list replace_specs); + +template +typename std::result_of ::type ForwardFromValueHT (GHashTable *ht, + Marshaller &&m, + const char *name) +{ + return m (lookup_gvalue (ht, name)); +} + +template +struct InterfaceConstructor +{ + template + static Interface * construct (ArgTypes&&... args) + { + return new Interface (args...); + } +}; + +template +DerivedType * LookupTypedInterfaceProp (GObject *object) +{ + InterfaceType *iface = nullptr; + g_object_get (object, "interface", (gpointer) &iface, NULL); + + return static_cast (iface); +} +#endif diff --git a/animation-glib/glide/glide.cpp b/animation-glib/glide/glide.cpp new file mode 100644 index 0000000..d176286 --- /dev/null +++ b/animation-glib/glide/glide.cpp @@ -0,0 +1,440 @@ +/* + * animation-glib/glide/glide.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject implementation for a "glide" animation. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace agd = animation::geometry::dimension; +namespace ag = animation::glide; +namespace agl = animation::glib; +namespace at = animation::transform; +namespace asg = animation::stepper::glib; + +struct _AnimationGlideAnimation +{ + AnimationTransformAnimation parent_instance; +}; + +typedef struct _AnimationGlideAnimationPrivate +{ +} AnimationGlideAnimationPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationGlideAnimation, + animation_glide_animation, + ANIMATION_TYPE_TRANSFORM_ANIMATION) + +enum { + PROP_0, + PROP_INITIAL_DISTANCE, + PROP_X_ROTATION_ANGLE_DEGREES, + PROP_Y_ROTATION_ANGLE_DEGREES, + PROP_X_AXIS_LOCATION_UNIT, + PROP_Y_AXIS_LOCATION_UNIT, + PROP_VIEWPORT, + PROP_TARGET, + PROP_STEPPER, + NPROPS +}; + +static GParamSpec *animation_glide_animation_props [NPROPS] = { NULL, }; + +void +animation_glide_animation_set_initial_distance (AnimationGlideAnimation *animation, + double initial_distance) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->InitialDistance (initial_distance); +} + +double +animation_glide_animation_get_initial_distance (AnimationGlideAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->InitialDistance (); +} + +void +animation_glide_animation_set_x_rotation_angle_degrees (AnimationGlideAnimation *animation, + double x_rotation_angle_degrees) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->XRotationAngleDegrees (x_rotation_angle_degrees); +} + +double +animation_glide_animation_get_x_rotation_angle_degrees (AnimationGlideAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->XRotationAngleDegrees (); +} + +void +animation_glide_animation_set_y_rotation_angle_degrees (AnimationGlideAnimation *animation, + double y_rotation_angle_degrees) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->YRotationAngleDegrees (y_rotation_angle_degrees); +} + +double +animation_glide_animation_get_y_rotation_angle_degrees (AnimationGlideAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->YRotationAngleDegrees (); +} + +void +animation_glide_animation_set_x_axis_location_unit (AnimationGlideAnimation *animation, + double x_axis_location_unit) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->XAxisLocationUnit (x_axis_location_unit); +} + +double +animation_glide_animation_get_x_axis_location_unit (AnimationGlideAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->XAxisLocationUnit (); +} + +void +animation_glide_animation_set_y_axis_location_unit (AnimationGlideAnimation *animation, + double y_axis_location_unit) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->YAxisLocationUnit (y_axis_location_unit); +} + +double +animation_glide_animation_get_y_axis_location_unit (AnimationGlideAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->YAxisLocationUnit (); +} + +void +animation_glide_animation_set_target (AnimationGlideAnimation *animation, + AnimationBoxQuery *target) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Target (std::make_shared (target)); +} + +/** + * animation_glide_animation_get_target: + * @animation: An #AnimationBoxQuery + * + * Returns: (transfer none): Get the #AnimationBoxQuery target for this #AnimationGlideAnimation + */ +AnimationBoxQuery * +animation_glide_animation_get_target (AnimationGlideAnimation *animation) +{ + return std::static_pointer_cast (LookupTypedInterfaceProp (G_OBJECT (animation))->Target ())->BoxQuery (); +} + +void +animation_glide_animation_set_viewport (AnimationGlideAnimation *animation, + AnimationBoxQuery *viewport) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Viewport (std::make_shared (viewport)); +} + +/** + * animation_glide_animation_get_viewport: + * @animation: An #AnimationBoxQuery + * + * Returns: (transfer none): Get the #AnimationBoxQuery for the viewport for this #AnimationGlideAnimation + */ +AnimationBoxQuery * +animation_glide_animation_get_viewport (AnimationGlideAnimation *animation) +{ + return std::static_pointer_cast (LookupTypedInterfaceProp (G_OBJECT (animation))->Viewport ())->BoxQuery (); +} + +void +animation_glide_animation_set_stepper (AnimationGlideAnimation *animation, + AnimationStepper *stepper) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper (std::make_shared (stepper)); +} + +/** + * animation_glide_animation_get_stepper: + * @animation: An #AnimationGlideAnimation + * + * Returns: (transfer full): Get the stepper for this #AnimationGlideAnimation + */ +AnimationStepper * +animation_glide_animation_get_stepper (AnimationGlideAnimation *animation) +{ + auto const &stepper (LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper ()); + + return std::static_pointer_cast (stepper)->BaseStepper (); +} + +static void +animation_glide_animation_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationGlideAnimation *glide_animation = ANIMATION_GLIDE_ANIMATION (object); + + switch (prop_id) + { + case PROP_INITIAL_DISTANCE: + animation_glide_animation_set_initial_distance (glide_animation, g_value_get_double (value)); + break; + case PROP_X_ROTATION_ANGLE_DEGREES: + animation_glide_animation_set_x_rotation_angle_degrees (glide_animation, g_value_get_double (value)); + break; + case PROP_Y_ROTATION_ANGLE_DEGREES: + animation_glide_animation_set_y_rotation_angle_degrees (glide_animation, g_value_get_double (value)); + break; + case PROP_X_AXIS_LOCATION_UNIT: + animation_glide_animation_set_x_axis_location_unit (glide_animation, g_value_get_double (value)); + break; + case PROP_Y_AXIS_LOCATION_UNIT: + animation_glide_animation_set_y_axis_location_unit (glide_animation, g_value_get_double (value)); + break; + case PROP_VIEWPORT: + animation_glide_animation_set_viewport (glide_animation, + ANIMATION_BOX_QUERY (g_value_get_object (value))); + break; + case PROP_TARGET: + animation_glide_animation_set_target (glide_animation, + ANIMATION_BOX_QUERY (g_value_get_object (value))); + break; + case PROP_STEPPER: + animation_glide_animation_set_stepper (glide_animation, + ANIMATION_STEPPER (g_value_get_object (value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_glide_animation_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AnimationGlideAnimation *glide_animation = ANIMATION_GLIDE_ANIMATION (object); + + switch (prop_id) + { + case PROP_INITIAL_DISTANCE: + g_value_set_double (value, animation_glide_animation_get_initial_distance (glide_animation)); + break; + case PROP_X_ROTATION_ANGLE_DEGREES: + g_value_set_double (value, animation_glide_animation_get_x_rotation_angle_degrees (glide_animation)); + break; + case PROP_Y_ROTATION_ANGLE_DEGREES: + g_value_set_double (value, animation_glide_animation_get_y_rotation_angle_degrees (glide_animation)); + break; + case PROP_X_AXIS_LOCATION_UNIT: + g_value_set_double (value, animation_glide_animation_get_x_axis_location_unit (glide_animation)); + break; + case PROP_Y_AXIS_LOCATION_UNIT: + g_value_set_double (value, animation_glide_animation_get_y_axis_location_unit (glide_animation)); + break; + case PROP_VIEWPORT: + g_value_set_object (value, animation_glide_animation_get_viewport (glide_animation)); + break; + case PROP_TARGET: + g_value_set_object (value, animation_glide_animation_get_target (glide_animation)); + break; + case PROP_STEPPER: + g_value_set_object (value, animation_glide_animation_get_stepper (glide_animation)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static GObject * +animation_glide_animation_constructor (GType type, + unsigned int n_construct_params, + GObjectConstructParam *construct_params) +{ + auto *interface = InterfaceConstructor ::construct (); + auto *transform_interface = static_cast (interface); + + /* We need to also set defaults for certain properties in order + * to ensure that they are bindable later on, in case they are + * not set by the caller. */ + replace_named_pointer_props_in_construct_params_if_null ( + construct_params, + n_construct_params, + { + ReplacePropSpec ("stepper", g_value_get_object, g_value_set_object, []() -> gpointer { + return animation_linear_stepper_new (1); + }), + ReplacePropSpec ("target", g_value_get_object, g_value_set_object, []() -> gpointer { + return animation_box_query_new (); + }), + ReplacePropSpec ("viewport", g_value_get_object, g_value_set_object, []() -> gpointer { + return animation_box_query_new (); + }) + } + ); + + replace_interface_prop_in_construct_params (construct_params, + n_construct_params, + transform_interface); + + return G_OBJECT_CLASS (animation_glide_animation_parent_class)->constructor (type, + n_construct_params, + construct_params); +} + +static void +animation_glide_animation_constructed (GObject *object) +{ + /* Take a size-zero step, which ensures that we update the + * internal state of the animation with all the properties we + * just set */ + animation_transform_animation_step (ANIMATION_TRANSFORM_ANIMATION (object), 0); +} + +static void +animation_glide_animation_init (AnimationGlideAnimation *model) +{ +} + + +static void +animation_glide_animation_class_init (AnimationGlideAnimationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = animation_glide_animation_constructor; + object_class->constructed = animation_glide_animation_constructed; + object_class->get_property = animation_glide_animation_get_property; + object_class->set_property = animation_glide_animation_set_property; + + animation_glide_animation_props[PROP_INITIAL_DISTANCE] = + g_param_spec_double ("initial-distance", + "Initial Distance", + "The initial distance away from the camera", + -1.0, + 1.0, + -0.3, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_X_ROTATION_ANGLE_DEGREES] = + g_param_spec_double ("x-rotation-angle-degrees", + "X Rotation Angle Degrees", + "Number of degrees on the X axis to rotate", + -360.0, + 360.0, + 0.0, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_Y_ROTATION_ANGLE_DEGREES] = + g_param_spec_double ("y-rotation-angle-degrees", + "Y Rotation Angle Degrees", + "Number of degrees on the Y axis to rotate", + -360.0, + 360.0, + 0.0, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_X_AXIS_LOCATION_UNIT] = + g_param_spec_double ("x-axis-location-unit", + "X Axis Location Unit", + "Unit-coordinates of where the X axis is on the surface", + 0.0, + 1.0, + 0.2, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_Y_AXIS_LOCATION_UNIT] = + g_param_spec_double ("y-axis-location-unit", + "Y Axis Location Unit", + "Unit-coordinates of where the Y axis is on the surface", + 0.0, + 1.0, + 0.5, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_VIEWPORT] = + g_param_spec_object ("viewport", + "Viewport BoxQuery", + "BoxQuery for the viewport dimensions that the surface is in", + ANIMATION_TYPE_BOX_QUERY, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_TARGET] = + g_param_spec_object ("target", + "Target BoxQuery", + "BoxQuery for box that we are animating to", + ANIMATION_TYPE_BOX_QUERY, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_STEPPER] = + g_param_spec_object ("stepper", + "Stepper", + "Stepper to use to progress the animation", + ANIMATION_TYPE_STEPPER, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_properties (object_class, + NPROPS, + animation_glide_animation_props); +} + +/** + * animation_glide_new: + * @initial_distance: Initial distance frm the camera. + * @x_rotation_angle_degrees: Degrees of rotation towards the X axis. + * @y_rotation_angle_degrees: Degrees of rotation towards the Y axis. + * @y_axis_location_unit: Unit-coordinates of where the X axis is on the surface. + * @x_axis_location_unit: Unit-coordinates of where the Y axis is on the surface. + * @screen_width: Width of the screen, in pixels. + * @target_box: The #AnimationBoxQuery that we are animating to. + * @length: The length of the animation. + * + * Returns: (transfer full): A new #AnimationGlideAnimation. + */ +AnimationGlideAnimation * +animation_glide_new (double initial_distance, + double x_rotation_angle_degrees, + double y_rotation_angle_degrees, + double x_axis_location_unit, + double y_axis_location_unit, + unsigned int screen_width, + const AnimationBoxQuery *target_box, + unsigned int length) +{ + return ANIMATION_GLIDE_ANIMATION (g_object_new (ANIMATION_TYPE_GLIDE_ANIMATION, + "initial-distance", initial_distance, + "x-rotation-angle-degrees", x_rotation_angle_degrees, + "y-rotation-angle-degrees", y_rotation_angle_degrees, + "x-axis-location-unit", x_axis_location_unit, + "y-axis-location-unit", y_axis_location_unit, + "screen-width", screen_width, + "target", target_box, + "length", length, + NULL)); +} diff --git a/animation-glib/glide/glide.h b/animation-glib/glide/glide.h new file mode 100644 index 0000000..1bec121 --- /dev/null +++ b/animation-glib/glide/glide.h @@ -0,0 +1,78 @@ +/* + * animation-glib/glide/glide.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject Interface for "glide" animation. + */ +#pragma once + +#include + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_GLIDE_ANIMATION animation_glide_animation_get_type () +G_DECLARE_FINAL_TYPE (AnimationGlideAnimation, animation_glide_animation, ANIMATION, GLIDE_ANIMATION, AnimationTransformAnimation) + +void animation_glide_animation_set_initial_distance (AnimationGlideAnimation *animation, + double initial_distance); +double animation_glide_animation_get_initial_distance (AnimationGlideAnimation *animation); + +void animation_glide_animation_set_x_rotation_angle_degrees (AnimationGlideAnimation *animation, + double x_rotation_angle_degrees); +double animation_glide_animation_get_x_rotation_angle_degrees (AnimationGlideAnimation *animation); + +void animation_glide_animation_set_y_rotation_angle_degrees (AnimationGlideAnimation *animation, + double y_rotation_angle_degrees); +double animation_glide_animation_get_y_rotation_angle_degrees (AnimationGlideAnimation *animation); + +void animation_glide_animation_set_x_axis_location_unit (AnimationGlideAnimation *animation, + double x_axis_location_unit); + +double animation_glide_animation_get_x_axis_location_unit (AnimationGlideAnimation *animation); +void animation_glide_animation_set_y_axis_location_unit (AnimationGlideAnimation *animation, + double y_axis_location_unit); + +double animation_glide_animation_get_y_axis_location_unit (AnimationGlideAnimation *animation); + +void animation_glide_animation_set_target (AnimationGlideAnimation *animation, + AnimationBoxQuery *target); +AnimationBoxQuery * animation_glide_animation_get_target (AnimationGlideAnimation *animation); + +void animation_glide_animation_set_viewport (AnimationGlideAnimation *animation, + AnimationBoxQuery *target); +AnimationBoxQuery * animation_glide_animation_get_viewport (AnimationGlideAnimation *animation); + +void animation_glide_animation_set_stepper (AnimationGlideAnimation *animation, + AnimationStepper *stepper); +AnimationStepper * animation_glide_animation_get_stepper (AnimationGlideAnimation *animation); + +AnimationGlideAnimation * animation_glide_new (double initial_distance, + double x_rotation_angle_degrees, + double y_rotation_angle_degrees, + double x_axis_location_unit, + double y_axis_location_unit, + unsigned int screen_width, + const AnimationBoxQuery *target_box, + unsigned int length); + +G_END_DECLS diff --git a/animation-glib/glide/meson.build b/animation-glib/glide/meson.build new file mode 100644 index 0000000..a7e1112 --- /dev/null +++ b/animation-glib/glide/meson.build @@ -0,0 +1,32 @@ +# animation/glide/meson.build +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (glide animation component), GObject bindings. + +glide_introspectable_sources = files([ + 'glide.cpp' +]) + +glide_headers = files([ + 'glide.h' +]) + +animation_glib_introspectable_sources += glide_introspectable_sources +animation_glib_headers += glide_headers + +install_headers(glide_headers, subdir: join_paths(animation_glib_headers_subdir, 'glide')) diff --git a/animation-glib/grid/grid.cpp b/animation-glib/grid/grid.cpp new file mode 100644 index 0000000..df064ac --- /dev/null +++ b/animation-glib/grid/grid.cpp @@ -0,0 +1,259 @@ +/* + * animation-glib/grid/grid.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject base class for grid animations. + */ + +#include + +#include +#include +#include +#include + +namespace agd = animation::geometry::dimension; +namespace agr = animation::grid; + +typedef struct _AnimationGridAnimationPrivate +{ + agr::GridAnimation *interface; +} AnimationGridAnimationPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationGridAnimation, + animation_grid_animation, + G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_INTERFACE, + NPROPS +}; + +static GParamSpec *animation_grid_animation_props [NPROPS] = { NULL, }; + +gboolean +animation_grid_animation_step (AnimationGridAnimation *grid_animation, + unsigned int ms) +{ + AnimationGridAnimationPrivate *priv = + reinterpret_cast (animation_grid_animation_get_instance_private (grid_animation)); + + return priv->interface->Step (ms); +} + +/** + * animation_grid_animation_deform_uv_to_model_space: + * @grid_animation: An #AnimationGridAnimation + * @uv: An #AnimationVector representing a point in unit coordinate + * space to be deformed. + * @model_space_point: (out caller-allocates): An #AnimationVector to write + * the deformed co-ordinate to. + * + * Determine where a unit coordinate lies in model space. + */ +void +animation_grid_animation_deform_uv_to_model_space (AnimationGridAnimation *grid_animation, + AnimationVector *uv, + AnimationVector *model_space_point) +{ + AnimationGridAnimationPrivate *priv = + reinterpret_cast (animation_grid_animation_get_instance_private (grid_animation)); + + g_return_if_fail (model_space_point != NULL); + + animation::Point point (uv->x, uv->y); + animation::Point deformed (priv->interface->DeformUVToModelSpace (point)); + + model_space_point->x = agd::get <0> (deformed); + model_space_point->y = agd::get <1> (deformed); +} + +/** + * animation_grid_animation_resolution: + * @grid_animation: An #AnimationGridAnimation + * @out_resolution: (out caller-allocates): An #AnimationVector specifying the + * ideal resolution of the animated surface grid. + * + * Return the expected grid resolution that would be required to + * make this animation look smooth. The renderer should subdivide + * the animated surface into this many equal sized chunks. + */ +void +animation_grid_animation_resolution (AnimationGridAnimation *grid_animation, + AnimationVector *out_resolution) +{ + AnimationGridAnimationPrivate *priv = + reinterpret_cast (animation_grid_animation_get_instance_private (grid_animation)); + + g_return_if_fail (out_resolution != NULL); + + auto resolution_cpp (priv->interface->Resolution ()); + + out_resolution->x = agd::get <0> (resolution_cpp); + out_resolution->y = agd::get <1> (resolution_cpp); +} + +/** + * animation_grid_animation_extremes: (skip) + * @grid_animation: A #AnimationGridAnimation + * @corners: (array fixed-size=4): The four #AnimationVector4D values + * describing the location of the surface corners. + * @out_extremes: (array fixed-size=4) (out): The grided four #AnimationVector + * values describing the location of the grided surface + * surface corners. + * + * Get the four co-ordinates of a 3D plane which bound the animated surface. + */ +void +animation_grid_animation_extremes (AnimationGridAnimation *grid_animation, + AnimationVector const *corners, + AnimationVector4D *out_extremes) +{ + g_return_if_fail (corners != NULL); + g_return_if_fail (out_extremes != NULL); + + AnimationGridAnimationPrivate *priv = + reinterpret_cast (animation_grid_animation_get_instance_private (grid_animation)); + + std::array points = { + animation::Point (corners[0].x, corners[0].y), + animation::Point (corners[1].x, corners[1].y), + animation::Point (corners[2].x, corners[2].y), + animation::Point (corners[3].x, corners[3].y) + }; + + std::array extremes = priv->interface->Extremes (points); + + agd::assign (out_extremes[0], extremes[0]); + agd::assign (out_extremes[1], extremes[1]); + agd::assign (out_extremes[2], extremes[2]); + agd::assign (out_extremes[3], extremes[3]); +} + +/** + * animation_grid_animation_alloc_extremes: (rename-to animation_grid_animation_extremes) + * @grid_animation: A #AnimationGridAnimation + * @corners: (array fixed-size=4): The four #AnimationVector4D values + * describing the location of the surface corners. + * + * Get the four co-ordinates of a 3D plane which bound the animated surface. This + * function exists as a workaround for the fact that some language bindings do not support + * caller allocation for out-array types. + * + * Returns: (array fixed-size=4) (transfer full): The transformed four #AnimationVector + * values describing the location of the transformed surface + * surface corners. + */ +AnimationVector4D * +animation_grid_animation_alloc_extremes (AnimationGridAnimation *grid_animation, + AnimationVector const *corners) +{ + AnimationVector4D *out_array = g_new0 (AnimationVector4D, 4); + + animation_grid_animation_extremes (grid_animation, corners, out_array); + return out_array; +} + +float +animation_grid_animation_progress (AnimationGridAnimation *grid_animation) +{ + AnimationGridAnimationPrivate *priv = + reinterpret_cast (animation_grid_animation_get_instance_private (grid_animation)); + + return priv->interface->Progress (); +} + +static void +animation_grid_animation_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationGridAnimation *grid_animation = ANIMATION_GRID_ANIMATION (object); + AnimationGridAnimationPrivate *priv = + reinterpret_cast (animation_grid_animation_get_instance_private (grid_animation)); + + switch (prop_id) + { + case PROP_INTERFACE: + priv->interface = reinterpret_cast (g_value_get_pointer (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_grid_animation_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AnimationGridAnimation *grid_animation = ANIMATION_GRID_ANIMATION (object); + AnimationGridAnimationPrivate *priv = + reinterpret_cast (animation_grid_animation_get_instance_private (grid_animation)); + + switch (prop_id) + { + case PROP_INTERFACE: + g_value_set_pointer (value, reinterpret_cast (priv->interface)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_grid_animation_finalize (GObject *object) +{ + AnimationGridAnimation *grid_animation = ANIMATION_GRID_ANIMATION (object); + AnimationGridAnimationPrivate *priv = + reinterpret_cast (animation_grid_animation_get_instance_private (grid_animation)); + + delete priv->interface; + priv->interface = nullptr; + + G_OBJECT_CLASS (animation_grid_animation_parent_class)->finalize (object); +} + +static void +animation_grid_animation_init (AnimationGridAnimation *model) +{ +} + + +static void +animation_grid_animation_class_init (AnimationGridAnimationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = animation_grid_animation_get_property; + object_class->set_property = animation_grid_animation_set_property; + object_class->finalize = animation_grid_animation_finalize; + + animation_grid_animation_props[PROP_INTERFACE] = + g_param_spec_pointer ("interface", + "Internal Interface", + "Internal C++ interface that this class wraps", + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_properties (object_class, + NPROPS, + animation_grid_animation_props); +} diff --git a/animation-glib/grid/grid.h b/animation-glib/grid/grid.h new file mode 100644 index 0000000..1666ac4 --- /dev/null +++ b/animation-glib/grid/grid.h @@ -0,0 +1,57 @@ +/* + * animation-glib/grid/grid.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject base class for grid animations. + */ +#pragma once + +#include + +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_GRID_ANIMATION animation_grid_animation_get_type () +G_DECLARE_DERIVABLE_TYPE (AnimationGridAnimation, animation_grid_animation, ANIMATION, GRID_ANIMATION, GObject) + +struct _AnimationGridAnimationClass { + GObjectClass parent_class; +}; + +gboolean animation_grid_animation_step (AnimationGridAnimation *grid_animation, + unsigned int ms); + +float animation_grid_animation_progress (AnimationGridAnimation *grid_animation); + +void animation_grid_animation_deform_uv_to_model_space (AnimationGridAnimation *grid_animation, + AnimationVector *uv, + AnimationVector *model_space_point); + +void animation_grid_animation_resolution (AnimationGridAnimation *grid_animation, + AnimationVector *out_resolution); + +void animation_grid_animation_extremes (AnimationGridAnimation *grid_animation, + AnimationVector const *corners, + AnimationVector4D *out_extremes); + +AnimationVector4D * animation_grid_animation_alloc_extremes (AnimationGridAnimation *grid_animation, + AnimationVector const *corners); + +G_END_DECLS diff --git a/animation-glib/grid/meson.build b/animation-glib/grid/meson.build new file mode 100644 index 0000000..eeaf92a --- /dev/null +++ b/animation-glib/grid/meson.build @@ -0,0 +1,32 @@ +# animation/grid/meson.build +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (grid animation base component GObject interface) + +grid_introspectable_sources = files([ + 'grid.cpp' +]) + +grid_headers = files([ + 'grid.h' +]) + +animation_glib_introspectable_sources += grid_introspectable_sources +animation_glib_headers += grid_headers + +install_headers(grid_headers, subdir: join_paths(animation_glib_headers_subdir, 'grid')) diff --git a/animation-glib/magiclamp/magiclamp.cpp b/animation-glib/magiclamp/magiclamp.cpp new file mode 100644 index 0000000..689a3af --- /dev/null +++ b/animation-glib/magiclamp/magiclamp.cpp @@ -0,0 +1,455 @@ +/* + * animation-glib/magiclamp/magiclamp.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject implementation for a "magiclamp" animation. + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ag = animation::grid; +namespace agd = animation::geometry::dimension; +namespace age = animation::geometry; +namespace agl = animation::glib; +namespace aml = animation::magiclamp; +namespace asg = animation::stepper::glib; + +struct _AnimationMagicLampAnimation +{ + AnimationGridAnimation parent_instance; +}; + +typedef struct _AnimationMagicLampAnimationPrivate +{ +} AnimationMagicLampAnimationPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationMagicLampAnimation, + animation_magiclamp_animation, + ANIMATION_TYPE_GRID_ANIMATION) + +enum { + PROP_0, + PROP_SOURCE, + PROP_TARGET, + PROP_RESOLUTION, + PROP_BEND_FACTOR, + PROP_OFFSET_FACTOR, + PROP_STRETCH_FACTOR, + PROP_DEFORM_SPEED_FACTOR, + PROP_STEPPER, + NPROPS +}; + +static GParamSpec *animation_magiclamp_animation_props [NPROPS] = { NULL, }; + +void +animation_magiclamp_animation_set_bend_factor (AnimationMagicLampAnimation *animation, + double bend_factor) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->BendFactor (bend_factor); +} + +double +animation_magiclamp_animation_get_bend_factor (AnimationMagicLampAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->BendFactor (); +} + +void +animation_magiclamp_animation_set_offset_factor (AnimationMagicLampAnimation *animation, + double offset_factor) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->OffsetFactor (offset_factor); +} + +double +animation_magiclamp_animation_get_offset_factor (AnimationMagicLampAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->OffsetFactor (); +} + +void +animation_magiclamp_animation_set_stretch_factor (AnimationMagicLampAnimation *animation, + double stretch_factor) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->StretchFactor (stretch_factor); +} + +double +animation_magiclamp_animation_get_stretch_factor (AnimationMagicLampAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->StretchFactor (); +} + +void +animation_magiclamp_animation_set_deform_speed_factor (AnimationMagicLampAnimation *animation, + double deform_speed_factor) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->DeformSpeedFactor (deform_speed_factor); +} + +double +animation_magiclamp_animation_get_deform_speed_factor (AnimationMagicLampAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->DeformSpeedFactor (); +} + +void +animation_magiclamp_animation_set_resolution (AnimationMagicLampAnimation *animation, + AnimationVector *vector) +{ + auto resolution = age::PointModel (vector->x, vector->y); + LookupTypedInterfaceProp (G_OBJECT (animation))->GridResolution (resolution); +} + +/** + * animation_magiclamp_animation_get_resolution: + * @animation: An #AnimationMagicLampAnimation + * @vector: (out): The #AnimationVector to write the resolution into. + */ +void +animation_magiclamp_animation_get_resolution (AnimationMagicLampAnimation *animation, + AnimationVector *vector) +{ + auto resolution = LookupTypedInterfaceProp (G_OBJECT (animation))->GridResolution (); + + vector->x = static_cast (resolution.x); + vector->y = static_cast (resolution.y); +} + +void +animation_magiclamp_animation_set_source (AnimationMagicLampAnimation *animation, + AnimationBoxQuery *source) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Source (std::make_shared (source)); +} + +/** + * animation_magiclamp_animation_get_source: + * @animation: A #AnimationMagicLampAnimation + * + * Returns: (transfer none): Get the #AnimationBoxQuery source for this #AnimationMagicLampAnimation + */ +AnimationBoxQuery * +animation_magiclamp_animation_get_source (AnimationMagicLampAnimation *animation) +{ + return std::static_pointer_cast (LookupTypedInterfaceProp (G_OBJECT (animation))->Source ())->BoxQuery (); +} + +void +animation_magiclamp_animation_set_target (AnimationMagicLampAnimation *animation, + AnimationBoxQuery *target) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Target (std::make_shared (target)); +} + +/** + * animation_magiclamp_animation_get_target: + * @animation: A #AnimationMagicLampAnimation + * + * Returns: (transfer none): Get the #AnimationBoxQuery target for this #AnimationMagicLampAnimation + */ +AnimationBoxQuery * +animation_magiclamp_animation_get_target (AnimationMagicLampAnimation *animation) +{ + return std::static_pointer_cast (LookupTypedInterfaceProp (G_OBJECT (animation))->Target ())->BoxQuery (); +} + +void +animation_magiclamp_animation_set_stepper (AnimationMagicLampAnimation *animation, + AnimationStepper *stepper) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper (std::make_shared (stepper)); +} + +/** + * animation_magiclamp_animation_get_stepper: + * @animation: An #AnimationMagicLampAnimation + * + * Returns: (transfer full): Get the stepper for this #AnimationMagicLampAnimation + */ +AnimationStepper * +animation_magiclamp_animation_get_stepper (AnimationMagicLampAnimation *animation) +{ + auto const &stepper (LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper ()); + + return std::static_pointer_cast (stepper)->BaseStepper (); +} + +static void +animation_magiclamp_animation_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationMagicLampAnimation *magiclamp_animation = ANIMATION_MAGIC_LAMP_ANIMATION (object); + + switch (prop_id) + { + case PROP_SOURCE: + animation_magiclamp_animation_set_source (magiclamp_animation, + ANIMATION_BOX_QUERY (g_value_get_object (value))); + break; + case PROP_TARGET: + animation_magiclamp_animation_set_target (magiclamp_animation, + ANIMATION_BOX_QUERY (g_value_get_object (value))); + break; + case PROP_RESOLUTION: + animation_magiclamp_animation_set_resolution (magiclamp_animation, + reinterpret_cast (g_value_get_boxed (value))); + break; + case PROP_BEND_FACTOR: + animation_magiclamp_animation_set_bend_factor (magiclamp_animation, g_value_get_double (value)); + break; + case PROP_OFFSET_FACTOR: + animation_magiclamp_animation_set_offset_factor (magiclamp_animation, g_value_get_double (value)); + break; + case PROP_STRETCH_FACTOR: + animation_magiclamp_animation_set_stretch_factor (magiclamp_animation, g_value_get_double (value)); + break; + case PROP_DEFORM_SPEED_FACTOR: + animation_magiclamp_animation_set_deform_speed_factor (magiclamp_animation, g_value_get_double (value)); + break; + case PROP_STEPPER: + animation_magiclamp_animation_set_stepper (magiclamp_animation, + ANIMATION_STEPPER (g_value_get_object (value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_magiclamp_animation_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AnimationMagicLampAnimation *magiclamp_animation = ANIMATION_MAGIC_LAMP_ANIMATION (object); + + switch (prop_id) + { + case PROP_SOURCE: + g_value_set_object (value, animation_magiclamp_animation_get_source (magiclamp_animation)); + break; + case PROP_TARGET: + g_value_set_object (value, animation_magiclamp_animation_get_target (magiclamp_animation)); + break; + case PROP_RESOLUTION: + { + AnimationVector vector; + animation_magiclamp_animation_get_resolution (magiclamp_animation, &vector); + g_value_set_boxed (value, reinterpret_cast (&vector)); + } + break; + case PROP_BEND_FACTOR: + g_value_set_double (value, animation_magiclamp_animation_get_bend_factor (magiclamp_animation)); + break; + case PROP_STRETCH_FACTOR: + g_value_set_double (value, animation_magiclamp_animation_get_stretch_factor (magiclamp_animation)); + break; + case PROP_OFFSET_FACTOR: + g_value_set_double (value, animation_magiclamp_animation_get_offset_factor (magiclamp_animation)); + break; + case PROP_DEFORM_SPEED_FACTOR: + g_value_set_double (value, animation_magiclamp_animation_get_deform_speed_factor (magiclamp_animation)); + break; + case PROP_STEPPER: + g_value_set_object (value, animation_magiclamp_animation_get_stepper (magiclamp_animation)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static GObject * +animation_magiclamp_animation_constructor (GType type, + unsigned int n_construct_params, + GObjectConstructParam *construct_params) +{ + auto *interface = InterfaceConstructor ::construct (); + auto *grid_interface = static_cast (interface); + + /* We need to also set defaults for certain properties in order + * to ensure that they are bindable later on, in case they are + * not set by the caller. */ + replace_named_pointer_props_in_construct_params_if_null ( + construct_params, + n_construct_params, + { + ReplacePropSpec ("stepper", g_value_get_object, g_value_take_object, []() -> gpointer { + return animation_linear_stepper_new (1); + }), + ReplacePropSpec ("source", g_value_get_object, g_value_take_object, []() -> gpointer { + return animation_box_query_new (); + }), + ReplacePropSpec ("target", g_value_get_object, g_value_take_object, []() -> gpointer { + return animation_box_query_new (); + }) + } + ); + + replace_interface_prop_in_construct_params (construct_params, + n_construct_params, + grid_interface); + + return G_OBJECT_CLASS (animation_magiclamp_animation_parent_class)->constructor (type, + n_construct_params, + construct_params); +} + +static void +animation_magiclamp_animation_constructed (GObject *object) +{ + /* Take a size-zero step, which ensures that we update the + * internal state of the animation with all the properties we + * just set */ + animation_grid_animation_step (ANIMATION_GRID_ANIMATION (object), 0); +} + +static void +animation_magiclamp_animation_init (AnimationMagicLampAnimation *model) +{ +} + + +static void +animation_magiclamp_animation_class_init (AnimationMagicLampAnimationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = animation_magiclamp_animation_constructor; + object_class->constructed = animation_magiclamp_animation_constructed; + object_class->get_property = animation_magiclamp_animation_get_property; + object_class->set_property = animation_magiclamp_animation_set_property; + + animation_magiclamp_animation_props[PROP_SOURCE] = + g_param_spec_object ("source", + "Source Box", + "BoxQuery for box that we are animating from", + ANIMATION_TYPE_BOX_QUERY, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_magiclamp_animation_props[PROP_TARGET] = + g_param_spec_object ("target", + "Target Box", + "BoxQuery for box that we are animating to", + ANIMATION_TYPE_BOX_QUERY, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_magiclamp_animation_props[PROP_RESOLUTION] = + g_param_spec_boxed ("resolution", + "Resolution", + "Grid Resolution", + ANIMATION_TYPE_VECTOR, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_magiclamp_animation_props[PROP_BEND_FACTOR] = + g_param_spec_double ("bend-factor", + "Bend Factor", + "How much the window should bend", + 1.0, + 20.0, + 10.0, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_magiclamp_animation_props[PROP_OFFSET_FACTOR] = + g_param_spec_double ("offset-factor", + "Offset Factor", + "How big the curves of the animation should be", + 0.1, + 1.0, + 0.5, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_magiclamp_animation_props[PROP_STRETCH_FACTOR] = + g_param_spec_double ("stretch-factor", + "Stretch Factor", + "How much the window should stretch when animating", + 0.2, + 1.0, + 0.45, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_magiclamp_animation_props[PROP_DEFORM_SPEED_FACTOR] = + g_param_spec_double ("deform-speed-factor", + "Deform Speed Factor", + "How quickly the deformation phase should happen", + 1.0, + 4.0, + 2.3, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_magiclamp_animation_props[PROP_STEPPER] = + g_param_spec_object ("stepper", + "Stepper", + "Stepper to use to progress the animation", + ANIMATION_TYPE_STEPPER, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_properties (object_class, + NPROPS, + animation_magiclamp_animation_props); +} + +/** + * animation_magiclamp_new: + * @source_box: The #AnimationBoxQuery that we are animating from. + * @target_box: The #AnimationBoxQuery that we are animating to. + * @resolution: The #AnimationVector representing the grid resolution + * @bend_factor: How much the window should bend + * @offset_factor: How big the curves of the animation should be + * @deform_speed_factor: How quickly the deformation should complete. + * @stepper: An #AnimationStepper used for progressing the animation. + * + * Returns: (transfer full): A new #AnimationMagicLampAnimation. + */ +AnimationMagicLampAnimation * +animation_magiclamp_new (const AnimationBoxQuery *source_box, + const AnimationBoxQuery *target_box, + const AnimationVector *resolution, + double bend_factor, + double offset_factor, + double stretch_factor, + double deform_speed_factor, + AnimationStepper *stepper) +{ + return ANIMATION_MAGIC_LAMP_ANIMATION (g_object_new (ANIMATION_TYPE_MAGIC_LAMP_ANIMATION, + "source", source_box, + "target", target_box, + "resolution", resolution, + "bend-factor", bend_factor, + "offset-factor", offset_factor, + "stretch-factor", stretch_factor, + "deform-speed-factor", deform_speed_factor, + "stepper", stepper, + NULL)); +} diff --git a/animation-glib/magiclamp/magiclamp.h b/animation-glib/magiclamp/magiclamp.h new file mode 100644 index 0000000..674289a --- /dev/null +++ b/animation-glib/magiclamp/magiclamp.h @@ -0,0 +1,73 @@ +/* + * animation-glib/magiclamp/magiclamp.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject Interface for "magiclamp" animation. + */ +#pragma once + +#include + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_MAGIC_LAMP_ANIMATION animation_magiclamp_animation_get_type () +G_DECLARE_FINAL_TYPE (AnimationMagicLampAnimation, animation_magiclamp_animation, ANIMATION, MAGIC_LAMP_ANIMATION, AnimationGridAnimation) + +void animation_magiclamp_animation_set_bend_factor (AnimationMagicLampAnimation *animation, + double bend_factor); +double animation_magiclamp_animation_get_bend_factor (AnimationMagicLampAnimation *animation); + +void animation_magiclamp_animation_set_offset_factor (AnimationMagicLampAnimation *animation, + double offset_factor); +double animation_magiclamp_animation_get_offset_factor (AnimationMagicLampAnimation *animation); + +void animation_magiclamp_animation_set_stretch_factor (AnimationMagicLampAnimation *animation, + double stretch_factor); +double animation_magiclamp_animation_get_stretch_factor (AnimationMagicLampAnimation *animation); + +void animation_magiclamp_animation_set_deform_speed_factor (AnimationMagicLampAnimation *animation, + double deform_speed_factor); +double animation_magiclamp_animation_get_deform_speed_factor (AnimationMagicLampAnimation *animation); + +void animation_magiclamp_animation_set_source (AnimationMagicLampAnimation *animation, + AnimationBoxQuery *source); +AnimationBoxQuery * animation_magiclamp_animation_get_source (AnimationMagicLampAnimation *animation); + +void animation_magiclamp_animation_set_target (AnimationMagicLampAnimation *animation, + AnimationBoxQuery *target); +AnimationBoxQuery * animation_magiclamp_animation_get_target (AnimationMagicLampAnimation *animation); + +void animation_magiclamp_animation_set_stepper (AnimationMagicLampAnimation *animation, + AnimationStepper *stepper); +AnimationStepper * animation_magiclamp_animation_get_stepper (AnimationMagicLampAnimation *animation); + +AnimationMagicLampAnimation * animation_magiclamp_new (const AnimationBox *source_box, + const AnimationBox *target_box, + const AnimationVector *resolution, + double bend_factor, + double offset_factor, + double stretch_factor, + double deform_speed_factor, + AnimationStepper *stepper); + +G_END_DECLS diff --git a/animation-glib/magiclamp/meson.build b/animation-glib/magiclamp/meson.build new file mode 100644 index 0000000..0e9e139 --- /dev/null +++ b/animation-glib/magiclamp/meson.build @@ -0,0 +1,32 @@ +# /animation-glib/magiclamp/meson.build +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (magiclamp animation component), GObject binding. + +magiclamp_introspectable_sources = files([ + 'magiclamp.cpp' +]) + +magiclamp_headers = files([ + 'magiclamp.h' +]) + +animation_glib_introspectable_sources += magiclamp_introspectable_sources +animation_glib_headers += magiclamp_headers + +install_headers(magiclamp_headers, subdir: join_paths(animation_glib_headers_subdir, 'magiclamp')) diff --git a/animation-glib/meson.build b/animation-glib/meson.build index fb997bb..2238e3c 100644 --- a/animation-glib/meson.build +++ b/animation-glib/meson.build @@ -20,18 +20,44 @@ api_version = '0' -animation_glib_toplevel_headers = ['vector.h'] -animation_glib_toplevel_introspectable_sources = ['vector.cpp'] +animation_glib_toplevel_headers = files([ + 'box.h', + 'vector.h', + 'vector4d.h' +]) +animation_glib_toplevel_introspectable_sources = files([ + 'box.cpp', + 'vector.cpp', + 'vector4d.cpp' +]) +animation_glib_toplevel_private_headers = files([ + 'constructor-helpers.h', + 'vector4d-internal.h' +]) +animation_glib_toplevel_private_sources = files([ + 'constructor-helpers.cpp' +]) -animation_glib_introspectable_sources = [] -animation_glib_private_sources = [] -animation_glib_headers = [] +animation_glib_introspectable_sources = files([]) +animation_glib_private_headers = files([]) +animation_glib_private_sources = files([]) +animation_glib_headers = files([]) animation_glib_headers_subdir = 'animation-glib' +subdir('bounce') +subdir('glide') +subdir('grid') +subdir('magiclamp') +subdir('query') +subdir('stepper') +subdir('transform') subdir('wobbly') +subdir('zoom') -animation_glib_introspectable_sources += files(animation_glib_toplevel_introspectable_sources) -animation_glib_headers += files(animation_glib_toplevel_headers) +animation_glib_introspectable_sources += animation_glib_toplevel_introspectable_sources +animation_glib_private_sources += animation_glib_toplevel_private_sources +animation_glib_headers += animation_glib_toplevel_headers +animation_glib_private_headers += animation_glib_toplevel_private_headers install_headers(animation_glib_toplevel_headers, subdir: animation_glib_headers_subdir) @@ -57,7 +83,7 @@ animation_glib_dep = declare_dependency( introspection_sources = [ animation_glib_introspectable_sources, animation_glib_headers ] gnome = import('gnome') -gnome.generate_gir( +animation_glib_gir = gnome.generate_gir( animation_glib_lib, extra_args: ['--warn-all', '--warn-error'], identifier_prefix: 'Animation', @@ -68,7 +94,7 @@ gnome.generate_gir( nsversion: api_version, sources: introspection_sources, symbol_prefix: 'animation' -) +)[0] pkg = import('pkgconfig') pkg.generate( diff --git a/animation-glib/query/geometry-query-internal.cpp b/animation-glib/query/geometry-query-internal.cpp new file mode 100644 index 0000000..fad0557 --- /dev/null +++ b/animation-glib/query/geometry-query-internal.cpp @@ -0,0 +1,74 @@ +/* + * animation-glib/query/geometry-query-internal.cpp + * + * Copyright 2019 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Wrapper for the AnimationBoxQuery object into a C++ object satisfying + * the animation::BoxQuery interface, implementation. + */ + +#include +#include +#include + +namespace ag = animation::glib; + +namespace animation +{ + namespace glib + { + struct BoxQueryWrapper::Private + { + Private (AnimationBoxQuery *); + ~Private (); + + AnimationBoxQuery *query; + }; + } +} + +ag::BoxQueryWrapper::Private::Private (AnimationBoxQuery *query) : + query (ANIMATION_BOX_QUERY (g_object_ref (query))) +{ +} + +ag::BoxQueryWrapper::Private::~Private () +{ + g_clear_object (&query); +} + +animation::Box const & +ag::BoxQueryWrapper::Geometry () const +{ + return animation_box_query_get_storage (priv->query).Geometry (); +} + +AnimationBoxQuery * +ag::BoxQueryWrapper::BoxQuery () const +{ + return priv->query; +} + +ag::BoxQueryWrapper::BoxQueryWrapper (AnimationBoxQuery *query) : + priv (std::make_unique (query)) +{ +} + +ag::BoxQueryWrapper::~BoxQueryWrapper () +{ +} + diff --git a/animation-glib/query/geometry-query-private.h b/animation-glib/query/geometry-query-private.h new file mode 100644 index 0000000..bad90e0 --- /dev/null +++ b/animation-glib/query/geometry-query-private.h @@ -0,0 +1,62 @@ +/* + * animation-glib/query/geometry-query-private.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Wrapper for the AnimationBoxQuery object into a C++ object satisfying + * the animation::BoxQuery interface. + */ +#pragma once + +#include + +#include + +#include +#include +#include + +#ifdef __cplusplus +namespace animation +{ + namespace glib + { + class BoxQueryWrapper : + public BoxQuery + { + public: + + BoxQueryWrapper (AnimationBoxQuery *); + ~BoxQueryWrapper (); + + geometry::Box const & Geometry () const override; + + AnimationBoxQuery * BoxQuery () const; + + private: + + class Private; + std::unique_ptr priv; + }; + } +} +#endif + +G_BEGIN_DECLS + +animation::BoxQueryStorage const & +animation_box_query_get_storage (AnimationBoxQuery *query); + +G_END_DECLS diff --git a/animation-glib/query/geometry-query.cpp b/animation-glib/query/geometry-query.cpp new file mode 100644 index 0000000..d90ae3a --- /dev/null +++ b/animation-glib/query/geometry-query.cpp @@ -0,0 +1,153 @@ +/* + * animation-glib/query/geometry-query.cpp + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject interface for the BoxQuery class. + */ + +#include + +#include +#include +#include +#include +#include + +namespace agd = animation::geometry::dimension; + +typedef struct _AnimationBoxQueryPrivate +{ + animation::BoxQueryStorage query; +} AnimationBoxQueryPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationBoxQuery, + animation_box_query, + G_TYPE_OBJECT) + +/** + * animation_box_query_update: + * @query: An #AnimationBoxQuery + * @box: An #AnimationBox + * + * Update the internal geometry in this geometry query. + */ +void +animation_box_query_update (AnimationBoxQuery *query, + const AnimationBox *box) +{ + AnimationBoxQueryPrivate *priv = + reinterpret_cast (animation_box_query_get_instance_private (query)); + + priv->query.Update (animation::Box (animation::Point (box->top_left.x, + box->top_left.y), + animation::Point (box->bottom_right.x, + box->bottom_right.y))); +} + +/** + * animation_box_query_get_storage: + * @query: An #AnimationBoxQuery + * + * Get the underlying storage for the #AnimationBoxQuery. This is + * an internal method that is used by animation::glib::BoxQueryWrapper + * to directly get the geometry referred to in the storage. + * + * Return: An animation::BoxQueryStorage used by this #AnimationBoxQuery. + */ +animation::BoxQueryStorage const & +animation_box_query_get_storage (AnimationBoxQuery *query) +{ + AnimationBoxQueryPrivate *priv = + reinterpret_cast (animation_box_query_get_instance_private (query)); + + return priv->query; +} + +/** + * animation_box_query_get_geometry: + * @query: An #AnimationBoxQuery + * @out_box: (out caller-allocates): An #AnimationBox to write the return value to. + * + * Get the currently stored geometry as an AnimationBox. + */ +void +animation_box_query_geometry (AnimationBoxQuery *query, + AnimationBox *out_box) +{ + AnimationBoxQueryPrivate *priv = + reinterpret_cast (animation_box_query_get_instance_private (query)); + + g_return_if_fail (out_box != NULL); + + auto box = priv->query.Geometry (); + + out_box->top_left.x = agd::get <0> (box.topLeft ()); + out_box->top_left.y = agd::get <1> (box.topLeft ()); + out_box->bottom_right.x = agd::get <0> (box.bottomRight ()); + out_box->bottom_right.y = agd::get <1> (box.bottomRight ()); +} + +static void +animation_box_query_finalize (GObject *object) +{ + AnimationBoxQuery *query = ANIMATION_BOX_QUERY (object); + AnimationBoxQueryPrivate *priv = + reinterpret_cast (animation_box_query_get_instance_private (query)); + + /* Explicitly call destructor, which will release the pointer + * and return the memory to an uninitialized state */ + priv->query.~BoxQueryStorage(); + + G_OBJECT_CLASS (animation_box_query_parent_class)->finalize (object); +} + +static void +animation_box_query_init (AnimationBoxQuery *query) +{ + AnimationBoxQueryPrivate *priv = + reinterpret_cast (animation_box_query_get_instance_private (query)); + + new (&priv->query) animation::BoxQueryStorage ( + animation::Box ( + animation::Point (0, 0), + animation::Point (1, 1) + ) + ); +} + +static void +animation_box_query_class_init (AnimationBoxQueryClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = animation_box_query_finalize; +} + +/** + * animation_box_query_new: + * + * Create a new AnimationBoxQuery * with default-initialized geometry. + * + * You probably don't want to directly use this, and instead use a derived class + * class constructor. + * + * Returns: A new #AnimationBoxQuery + */ +AnimationBoxQuery * +animation_box_query_new (void) +{ + return ANIMATION_BOX_QUERY (g_object_new (ANIMATION_TYPE_BOX_QUERY, NULL)); +} diff --git a/animation-glib/query/geometry-query.h b/animation-glib/query/geometry-query.h new file mode 100644 index 0000000..8219a75 --- /dev/null +++ b/animation-glib/query/geometry-query.h @@ -0,0 +1,43 @@ +/* + * animation-glib/query/geometry-query.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject Interface for the BoxQuery class. + */ +#pragma once + +#include + +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_BOX_QUERY animation_box_query_get_type () +G_DECLARE_DERIVABLE_TYPE (AnimationBoxQuery, animation_box_query, ANIMATION, BOX_QUERY, GObject) + +struct _AnimationBoxQueryClass { + GObjectClass parent_class; +}; + +void animation_box_query_update (AnimationBoxQuery *query, + const AnimationBox *box); + +AnimationBox * animation_box_query_geometry (AnimationBoxQuery *query); + +AnimationBoxQuery * animation_box_query_new (void); + +G_END_DECLS diff --git a/animation-glib/query/meson.build b/animation-glib/query/meson.build new file mode 100644 index 0000000..7826ee9 --- /dev/null +++ b/animation-glib/query/meson.build @@ -0,0 +1,28 @@ +# /animation-glib/query/meson.build +# +# Build the libanimation library (box query GLib binding component). +# +# See /LICENCE.md for Copyright information. + +geometry_query_introspectable_sources = files([ + 'geometry-query.cpp' +]) + +geometry_query_headers = files([ + 'geometry-query.h' +]) + +geometry_query_private_headers = files([ + 'geometry-query-private.h' +]) + +geometry_query_private_sources = files([ + 'geometry-query-internal.cpp' +]) + +animation_glib_introspectable_sources += geometry_query_introspectable_sources +animation_glib_headers += geometry_query_headers +animation_glib_private_headers += geometry_query_private_headers +animation_glib_private_sources += geometry_query_private_sources + +install_headers(magiclamp_headers, subdir: join_paths(animation_glib_headers_subdir, 'query')) diff --git a/animation-glib/stepper/linear.cpp b/animation-glib/stepper/linear.cpp new file mode 100644 index 0000000..3852fcd --- /dev/null +++ b/animation-glib/stepper/linear.cpp @@ -0,0 +1,136 @@ +/* + * animation-glib/stepper/linear.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject wrapper for linear stepper. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +namespace as = animation::stepper; + +struct _AnimationLinearStepper +{ + GObject parent_instance; +}; + +G_DEFINE_TYPE (AnimationLinearStepper, + animation_linear_stepper, + ANIMATION_TYPE_STEPPER_HOLDER) + +enum { + PROP_0, + PROP_LENGTH, + NPROPS +}; + +static GParamSpec *animation_linear_stepper_properties[NPROPS]; + +static void +animation_linear_stepper_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationStepperHolder *holder = ANIMATION_STEPPER_HOLDER (object); + + switch (prop_id) + { + case PROP_LENGTH: + std::static_pointer_cast (animation_stepper_holder_get_ptr (holder))->Length (g_value_get_uint (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_linear_stepper_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AnimationStepperHolder *holder = ANIMATION_STEPPER_HOLDER (object); + + switch (prop_id) + { + case PROP_LENGTH: + g_value_set_uint (value, + std::static_pointer_cast (animation_stepper_holder_get_ptr (holder))->Length ()); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_linear_stepper_init (AnimationLinearStepper *stepper) +{ + static const unsigned int DefaultAnimationLength = 300; + AnimationStepperHolder *holder = ANIMATION_STEPPER_HOLDER (stepper); + + animation_stepper_holder_set_ptr (holder, std::make_shared (DefaultAnimationLength)); +} + +static void +animation_linear_stepper_class_init (AnimationLinearStepperClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = animation_linear_stepper_get_property; + object_class->set_property = animation_linear_stepper_set_property; + + animation_linear_stepper_properties[PROP_LENGTH] = + g_param_spec_uint ("length", + "Length", + "How long the animation lasts", + 1, + 5000, + 300, + static_cast (G_PARAM_READWRITE)); + + g_object_class_install_properties (object_class, + NPROPS, + animation_linear_stepper_properties); +} + +/** + * animation_linear_stepper_new: + * @length: Length of the transition in milliseconds. + * + * Return a new #AnimationStepper which linearly increments progress + * every time the step() method is called on it. + * + * Returns: (transfer full): An #AnimationLinearStepper + * implementation of #AnimationStepper + */ +AnimationStepper * +animation_linear_stepper_new (unsigned int length) +{ + return ANIMATION_STEPPER (g_object_new (ANIMATION_TYPE_LINEAR_STEPPER, + "length", length, + NULL)); +} diff --git a/animation-glib/stepper/linear.h b/animation-glib/stepper/linear.h new file mode 100644 index 0000000..1c83c5f --- /dev/null +++ b/animation-glib/stepper/linear.h @@ -0,0 +1,36 @@ +/* + * animation-glib/stepper/linear.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject class for linear stepper. + */ +#pragma once + +#include + +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_LINEAR_STEPPER animation_linear_stepper_get_type () +G_DECLARE_FINAL_TYPE (AnimationLinearStepper, animation_linear_stepper, ANIMATION, LINEAR_STEPPER, AnimationStepperHolder) + +AnimationStepper * animation_linear_stepper_new (unsigned int length); + +G_END_DECLS diff --git a/animation-glib/stepper/meson.build b/animation-glib/stepper/meson.build new file mode 100644 index 0000000..abb1907 --- /dev/null +++ b/animation-glib/stepper/meson.build @@ -0,0 +1,45 @@ +# /animation/stepper/meson.build +# +# Meson build file for libanimation stepper GObject classes. +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (stepper base component) + +stepper_introspectable_sources = files([ + 'linear.cpp', + 'reverse.cpp', + 'stepper.cpp', + 'stepper-holder.cpp' +]) +stepper_private_sources = files([ + 'stepper-internal.cpp' +]) + +stepper_headers = files([ + 'linear.h', + 'reverse.h', + 'stepper.h', + 'stepper-holder.h', + 'stepper-internal.h' +]) + +animation_glib_introspectable_sources += stepper_introspectable_sources +animation_glib_private_sources += stepper_private_sources +animation_glib_headers += stepper_headers + +install_headers(stepper_headers, subdir: join_paths(animation_glib_headers_subdir, 'stepper')) diff --git a/animation-glib/stepper/reverse.cpp b/animation-glib/stepper/reverse.cpp new file mode 100644 index 0000000..d47ee05 --- /dev/null +++ b/animation-glib/stepper/reverse.cpp @@ -0,0 +1,148 @@ +/* + * animation-glib/stepper/reverse.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject wrapper for reverse stepper. + */ + +#include +#include + +#include +#include +#include +#include + +namespace as = animation::stepper; +namespace asg = animation::stepper::glib; + +struct _AnimationReverseStepper +{ + GObject parent_instance; +}; + +G_DEFINE_TYPE (AnimationReverseStepper, + animation_reverse_stepper, + ANIMATION_TYPE_STEPPER_HOLDER) + +enum { + PROP_0, + PROP_BASE_STEPPER, + NPROPS +}; + +static GParamSpec *animation_reverse_stepper_properties[NPROPS]; + +static std::shared_ptr +construct_reverse_stepper_wrapper (AnimationStepper *base_stepper) +{ + unsigned int const DefaultAnimationLength = 300; + + if (base_stepper != nullptr) + return std::make_shared (std::make_shared (base_stepper)); + + return std::make_shared (std::make_shared (DefaultAnimationLength)); +} + +static void +animation_reverse_stepper_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationStepperHolder *holder = ANIMATION_STEPPER_HOLDER (object); + + switch (prop_id) + { + case PROP_BASE_STEPPER: + animation_stepper_holder_set_ptr (holder, + construct_reverse_stepper_wrapper (ANIMATION_STEPPER (g_value_get_object (value)))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_reverse_stepper_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AnimationStepperHolder *holder = ANIMATION_STEPPER_HOLDER (object); + + switch (prop_id) + { + case PROP_BASE_STEPPER: + g_value_set_object (value, + G_OBJECT (std::static_pointer_cast (animation_stepper_holder_get_ptr (holder))->BaseStepper ())); + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_reverse_stepper_dispose (GObject *object) +{ + AnimationStepperHolder *holder = ANIMATION_STEPPER_HOLDER (object); + + animation_stepper_holder_set_ptr (holder, as::Stepper::Shared ()); +} + +static void +animation_reverse_stepper_init (AnimationReverseStepper *reverse_stepper) +{ +} + +static void +animation_reverse_stepper_class_init (AnimationReverseStepperClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = animation_reverse_stepper_get_property; + object_class->set_property = animation_reverse_stepper_set_property; + object_class->dispose = animation_reverse_stepper_dispose; + + animation_reverse_stepper_properties[PROP_BASE_STEPPER] = + g_param_spec_object ("base-stepper", + "Base Stepper", + "Stepper to reverse", + ANIMATION_TYPE_STEPPER, + static_cast (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_properties (object_class, + NPROPS, + animation_reverse_stepper_properties); +} + +/** + * animation_reverse_stepper_new: + * @base_stepper: The stepper to reverse. + * + * Return a new #AnimationStepper which runs @base_stepper in reverse. + * + * Returns: (transfer full): An #AnimationReverseStepper + * implementation of #AnimationStepper + */ +AnimationStepper * +animation_reverse_stepper_new (AnimationStepper *base_stepper) +{ + return ANIMATION_STEPPER (g_object_new (ANIMATION_TYPE_REVERSE_STEPPER, + "base-stepper", base_stepper, + NULL)); +} diff --git a/animation-glib/stepper/reverse.h b/animation-glib/stepper/reverse.h new file mode 100644 index 0000000..0a4f625 --- /dev/null +++ b/animation-glib/stepper/reverse.h @@ -0,0 +1,36 @@ +/* + * animation-glib/stepper/reverse.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject class for reverse stepper. + */ +#pragma once + +#include + +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_REVERSE_STEPPER animation_reverse_stepper_get_type () +G_DECLARE_FINAL_TYPE (AnimationReverseStepper, animation_reverse_stepper, ANIMATION, REVERSE_STEPPER, AnimationStepperHolder) + +AnimationStepper * animation_reverse_stepper_new (AnimationStepper *base_stepper); + +G_END_DECLS diff --git a/animation-glib/stepper/stepper-holder-internal.h b/animation-glib/stepper/stepper-holder-internal.h new file mode 100644 index 0000000..f5063d0 --- /dev/null +++ b/animation-glib/stepper/stepper-holder-internal.h @@ -0,0 +1,33 @@ +/* + * animation-glib/stepper/stepper-holder-internal.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Internally public methods for GObject stepper holder implementation. + */ +#pragma once + +#include + +#include +#include + + +animation::stepper::Stepper::Shared animation_stepper_holder_get_ptr (AnimationStepperHolder *holder); + +void animation_stepper_holder_set_ptr (AnimationStepperHolder *holder, + animation::stepper::Stepper::Shared const &ptr); diff --git a/animation-glib/stepper/stepper-holder.cpp b/animation-glib/stepper/stepper-holder.cpp new file mode 100644 index 0000000..968bea2 --- /dev/null +++ b/animation-glib/stepper/stepper-holder.cpp @@ -0,0 +1,113 @@ +/* + * animation-glib/stepper/stepper-holder.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject base class for steppers. Copies the stepper + * passed to it in its construct parameters. + */ + +#include +#include + +#include +#include + +namespace as = animation::stepper; + +struct _AnimationStepperHolder +{ + GObject parent_instance; +}; + +typedef struct _AnimationStepperHolderPrivate +{ + as::Stepper::Shared stepper; +} AnimationStepperHolderPrivate; + +static void animation_stepper_iface_init (AnimationStepperInterface *stepper_iface); + +G_DEFINE_TYPE_WITH_CODE (AnimationStepperHolder, + animation_stepper_holder, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (ANIMATION_TYPE_STEPPER, animation_stepper_iface_init) + G_ADD_PRIVATE (AnimationStepperHolder)) + +static float +animation_stepper_holder_step (AnimationStepper *stepper, + unsigned int ms) +{ + AnimationStepperHolderPrivate *priv = + reinterpret_cast (animation_stepper_holder_get_instance_private (ANIMATION_STEPPER_HOLDER (stepper))); + + return (*priv->stepper) (ms); +} + +as::Stepper::Shared +animation_stepper_holder_get_ptr (AnimationStepperHolder *holder) +{ + AnimationStepperHolderPrivate *priv = + reinterpret_cast (animation_stepper_holder_get_instance_private (holder)); + + return priv->stepper; +} + +void +animation_stepper_holder_set_ptr (AnimationStepperHolder *holder, + as::Stepper::Shared const &ptr) +{ + AnimationStepperHolderPrivate *priv = + reinterpret_cast (animation_stepper_holder_get_instance_private (holder)); + + priv->stepper = ptr; +} + +static void +animation_stepper_holder_finalize (GObject *object) +{ + AnimationStepperHolder *holder = ANIMATION_STEPPER_HOLDER (object); + AnimationStepperHolderPrivate *priv = + reinterpret_cast (animation_stepper_holder_get_instance_private (holder)); + + using Ptr = as::Stepper::Shared; + priv->stepper.~Ptr (); + + G_OBJECT_CLASS (animation_stepper_holder_parent_class)->finalize (object); +} + +static void +animation_stepper_holder_init (AnimationStepperHolder *holder) +{ + AnimationStepperHolderPrivate *priv = + reinterpret_cast (animation_stepper_holder_get_instance_private (holder)); + + new (&priv->stepper) std::shared_ptr (); +} + +static void +animation_stepper_iface_init (AnimationStepperInterface *interface) +{ + interface->step = animation_stepper_holder_step; +} + +static void +animation_stepper_holder_class_init (AnimationStepperHolderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = animation_stepper_holder_finalize; +} diff --git a/animation-glib/stepper/stepper-holder.h b/animation-glib/stepper/stepper-holder.h new file mode 100644 index 0000000..89e69fb --- /dev/null +++ b/animation-glib/stepper/stepper-holder.h @@ -0,0 +1,34 @@ +/* + * animation-glib/stepper/stepper-holder.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject base class for steppers, which holds a copy of a stepper. + */ +#pragma once + +#include + +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_STEPPER_HOLDER animation_stepper_holder_get_type () +G_DECLARE_FINAL_TYPE (AnimationStepperHolder, animation_stepper_holder, ANIMATION, STEPPER_HOLDER, GObject) + +G_END_DECLS diff --git a/animation-glib/stepper/stepper-internal.cpp b/animation-glib/stepper/stepper-internal.cpp new file mode 100644 index 0000000..5e74321 --- /dev/null +++ b/animation-glib/stepper/stepper-internal.cpp @@ -0,0 +1,71 @@ +/* + * animation-glib/stepper/stepper-internal.cp + * + * Copyright 2019 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Internal helper class for stepper, which wraps a GObject stepper + * in the C++ interface, so it can be consumed directly by other C++ + * steppers, implementation. + */ + +#include + +#include +#include +#include + + +namespace as = animation::stepper; +namespace asg = animation::stepper::glib; + +namespace animation { + namespace stepper { + namespace glib { + struct StepperWrapper::Private + { + AnimationStepper *baseStepper; + + Private (AnimationStepper *baseStepper) : + baseStepper (ANIMATION_STEPPER (g_object_ref (baseStepper))) + { + } + + ~Private () + { + g_clear_object (&baseStepper); + } + }; + } + } +} + +ANIMATION_DEFINE_PROPERTY (asg::StepperWrapper, BaseStepper, AnimationStepper *, priv->baseStepper) + +asg::StepperWrapper::StepperWrapper (AnimationStepper *baseStepper) : + priv (std::make_unique (baseStepper)) +{ +} + +asg::StepperWrapper::~StepperWrapper () +{ +} + +float +asg::StepperWrapper::Step (unsigned int ms) +{ + return animation_stepper_step (priv->baseStepper, ms); +} diff --git a/animation-glib/stepper/stepper-internal.h b/animation-glib/stepper/stepper-internal.h new file mode 100644 index 0000000..cf90676 --- /dev/null +++ b/animation-glib/stepper/stepper-internal.h @@ -0,0 +1,61 @@ +/* + * animation-glib/stepper/stepper-internal.h + * + * Copyright 2019 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Internal helper class for stepper, which wraps a GObject stepper + * in the C++ interface, so it can be consumed directly by other C++ + * steppers. + */ +#pragma once + +#ifdef __cplusplus + +#include + +#include +#include +#include + + +namespace animation { + namespace stepper { + namespace glib { + class StepperWrapper : + public Stepper + { + public: + + StepperWrapper (AnimationStepper *); + ~StepperWrapper (); + + ANIMATION_DECLARE_PROPERTY (StepperWrapper, BaseStepper, AnimationStepper *); + + protected: + + float Step (unsigned int ms) override; + + private: + + struct Private; + std::unique_ptr priv; + }; + } + } +} + +#endif diff --git a/animation-glib/stepper/stepper-wrapper.cpp b/animation-glib/stepper/stepper-wrapper.cpp new file mode 100644 index 0000000..5d93762 --- /dev/null +++ b/animation-glib/stepper/stepper-wrapper.cpp @@ -0,0 +1,160 @@ +/* + * animation-glib/stepper/stepper-wrapper.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject base class for steppers. Only observes the stepper passed + * to it in its construct parameters - not expected to outlive the stepper. + */ + +#include +#include + +#include +#include + +namespace as = animation::stepper; + +struct _AnimationStepperWrapper +{ + GObject parent_instance; +}; + +typedef struct _AnimationStepperWrapperPrivate +{ + as::Stepper *stepper; +} AnimationStepperWrapperPrivate; + +static void animation_stepper_iface_init (AnimationStepperInterface *stepper_iface); + +G_DEFINE_TYPE_WITH_CODE (AnimationStepperWrapper, + animation_stepper_wrapper, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (ANIMATION_TYPE_STEPPER, animation_stepper_iface_init) + G_ADD_PRIVATE (AnimationStepperWrapper)) + +enum { + PROP_0, + PROP_STEPPER, + NPROPS +}; + +static float +animation_stepper_wrapper_step (AnimationStepper *stepper, + unsigned int ms) +{ + AnimationStepperWrapperPrivate *priv = + reinterpret_cast (animation_stepper_wrapper_get_instance_private (ANIMATION_STEPPER_WRAPPER (stepper))); + + return (*priv->stepper) (ms); +} + +static void +animation_stepper_wrapper_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationStepperWrapper *holder = ANIMATION_STEPPER_WRAPPER (object); + AnimationStepperWrapperPrivate *priv = + reinterpret_cast (animation_stepper_wrapper_get_instance_private (holder)); + + switch (prop_id) + { + case PROP_STEPPER: + priv->stepper = reinterpret_cast > (g_value_get_pointer (value))->get (); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_stepper_wrapper_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AnimationStepperWrapper *holder = ANIMATION_STEPPER_WRAPPER (object); + AnimationStepperWrapperPrivate *priv = + reinterpret_cast (animation_stepper_wrapper_get_instance_private (holder)); + + switch (prop_id) + { + case PROP_STEPPER: + g_value_set_pointer (value, reinterpret_cast (priv->stepper)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_stepper_wrapper_finalize (GObject *object) +{ + AnimationStepperWrapper *holder = ANIMATION_STEPPER_WRAPPER (object); + AnimationStepperWrapperPrivate *priv = + reinterpret_cast (animation_stepper_wrapper_get_instance_private (holder)); + + /* The pointer was only observing, so we need not free it */ + priv->stepper = nullptr; + + G_OBJECT_CLASS (animation_stepper_wrapper_parent_class)->finalize (object); +} + +static void +animation_stepper_wrapper_init (AnimationStepperWrapper *stepper_wrapper) +{ +} + +static void +animation_stepper_iface_init (AnimationStepperInterface *interface) +{ + interface->step = animation_stepper_wrapper_step; +} + +static void +animation_stepper_wrapper_class_init (AnimationStepperWrapperClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = animation_stepper_wrapper_get_property; + object_class->set_property = animation_stepper_wrapper_set_property; + object_class->finalize = animation_stepper_wrapper_finalize; + + g_object_class_override_property (object_class, + PROP_STEPPER, + "stepper"); +} + +/** + * animation_stepper_wrapper_new: (skip): + * @interface: A pointer to an interlying stepper implementation. + * + * Create a new #AnimationStepperWrapper, an implementation of + * #AnimationStepper which only observes the underlying stepper when it + * is constructed (keeping its internal state). However, the wrapper + * must not outlive the underlying stepper. + * + * Returns: (transfer full): A new #AnimationStepper with the underlying + * stepper merely observed. + */ +AnimationStepper * +animation_stepper_wrapper_new (gpointer interface) +{ + return ANIMATION_STEPPER (g_object_new (ANIMATION_TYPE_STEPPER_WRAPPER, "stepper", interface, NULL)); +} diff --git a/animation-glib/stepper/stepper-wrapper.h b/animation-glib/stepper/stepper-wrapper.h new file mode 100644 index 0000000..bd59e53 --- /dev/null +++ b/animation-glib/stepper/stepper-wrapper.h @@ -0,0 +1,36 @@ +/* + * animation-glib/stepper/stepper-wrapper.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject base class for steppers, which holds a copy of a stepper. + */ +#pragma once + +#include + +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_STEPPER_WRAPPER animation_stepper_wrapper_get_type () +G_DECLARE_FINAL_TYPE (AnimationStepperWrapper, animation_stepper_wrapper, ANIMATION, STEPPER_WRAPPER, GObject) + +AnimationStepper * animation_stepper_wrapper_new (gpointer interface); + +G_END_DECLS diff --git a/animation-glib/stepper/stepper.cpp b/animation-glib/stepper/stepper.cpp new file mode 100644 index 0000000..2bdf5b1 --- /dev/null +++ b/animation-glib/stepper/stepper.cpp @@ -0,0 +1,44 @@ +/* + * animation-glib/stepper/stepper.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject base interface for steppers. + */ + +#include +#include + +namespace as = animation::stepper; + + +G_DEFINE_INTERFACE (AnimationStepper, + animation_stepper, + G_TYPE_OBJECT) +float +animation_stepper_step (AnimationStepper *stepper, + unsigned int ms) +{ + g_return_val_if_fail (ANIMATION_IS_STEPPER (stepper), 0.0f); + + return ANIMATION_STEPPER_GET_IFACE (stepper)->step (stepper, ms); +} + +static void +animation_stepper_default_init (AnimationStepperInterface *iface) +{ +} diff --git a/animation-glib/stepper/stepper.h b/animation-glib/stepper/stepper.h new file mode 100644 index 0000000..40e98f3 --- /dev/null +++ b/animation-glib/stepper/stepper.h @@ -0,0 +1,44 @@ +/* + * animation-glib/stepper/stepper.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject base interface for steppers. + */ +#pragma once + +#include + +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_STEPPER animation_stepper_get_type () +G_DECLARE_INTERFACE (AnimationStepper, animation_stepper, ANIMATION, STEPPER, GObject) + +struct _AnimationStepperInterface +{ + GTypeInterface parent_iface; + + float (*step) (AnimationStepper *self, + unsigned int ms); +}; + +float animation_stepper_step (AnimationStepper *stepper, + unsigned int ms); + +G_END_DECLS diff --git a/animation-glib/transform/meson.build b/animation-glib/transform/meson.build new file mode 100644 index 0000000..fa8c476 --- /dev/null +++ b/animation-glib/transform/meson.build @@ -0,0 +1,34 @@ +# animation-glib/transform/meson.build +# +# Toplevel meson build file for libanimation. +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (transform animation base component + +transform_introspectable_sources = files([ + 'transform.cpp' +]) + +transform_headers = files([ + 'transform.h' +]) + +animation_glib_introspectable_sources += transform_introspectable_sources +animation_glib_headers += transform_headers + +install_headers(transform_headers, subdir: join_paths(animation_glib_headers_subdir, 'transform')) diff --git a/animation-glib/transform/transform.cpp b/animation-glib/transform/transform.cpp new file mode 100644 index 0000000..bc3f854 --- /dev/null +++ b/animation-glib/transform/transform.cpp @@ -0,0 +1,226 @@ +/* + * animation-glib/transform/transform.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject base class for affine transform based animations. + */ + +#include + +#include +#include +#include +#include + +namespace agd = animation::geometry::dimension; +namespace at = animation::transform; + +typedef struct _AnimationTransformAnimationPrivate +{ + at::TransformAnimation *interface; +} AnimationTransformAnimationPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationTransformAnimation, + animation_transform_animation, + G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_INTERFACE, + NPROPS +}; + +static GParamSpec *animation_transform_animation_props [NPROPS] = { NULL, }; + +gboolean +animation_transform_animation_step (AnimationTransformAnimation *transform_animation, + unsigned int ms) +{ + AnimationTransformAnimationPrivate *priv = + reinterpret_cast (animation_transform_animation_get_instance_private (transform_animation)); + + return priv->interface->Step (ms); +} + +/** + * animation_transform_animation_matrix: + * @transform_animation: A #AnimationTransformAnimation + * + * Get the 4x4 column-major transformation matrix for this + * representing the state of this animation. + * + * Returns: (array fixed-size=16): The 4x4 column-major ordered + * transformation matrix. + */ +float const * +animation_transform_animation_matrix (AnimationTransformAnimation *transform_animation) +{ + AnimationTransformAnimationPrivate *priv = + reinterpret_cast (animation_transform_animation_get_instance_private (transform_animation)); + + return priv->interface->Matrix (); +} + +/** + * animation_transform_animation_extremes: (skip) + * @transform_animation: A #AnimationTransformAnimation + * @corners: (array fixed-size=4): The four #AnimationVector4D values + * describing the location of the surface corners. + * @out_extremes: (array fixed-size=4) (out): The transformed four #AnimationVector + * values describing the location of the transformed surface + * surface corners. + * + * Get the four co-ordinates of a 3D plane which bound the animated surface. + */ +void +animation_transform_animation_extremes (AnimationTransformAnimation *transform_animation, + AnimationVector const *corners, + AnimationVector4D *out_extremes) +{ + g_return_if_fail (corners != NULL); + g_return_if_fail (out_extremes != NULL); + + AnimationTransformAnimationPrivate *priv = + reinterpret_cast (animation_transform_animation_get_instance_private (transform_animation)); + + std::array points = { + animation::Point (corners[0].x, corners[0].y), + animation::Point (corners[1].x, corners[1].y), + animation::Point (corners[2].x, corners[2].y), + animation::Point (corners[3].x, corners[3].y) + }; + + std::array extremes = priv->interface->Extremes (points); + + agd::assign (out_extremes[0], extremes[0]); + agd::assign (out_extremes[1], extremes[1]); + agd::assign (out_extremes[2], extremes[2]); + agd::assign (out_extremes[3], extremes[3]); +} + +/** + * animation_transform_animation_alloc_extremes: (rename-to animation_transform_animation_extremes) + * @transform_animation: A #AnimationTransformAnimation + * @corners: (array fixed-size=4): The four #AnimationVector4D values + * describing the location of the surface corners. + * + * Get the four co-ordinates of a 3D plane which bound the animated surface. This + * function exists as a workaround for the fact that some language bindings do not support + * caller allocation for out-array types. + * + * Returns: (array fixed-size=4) (transfer full): The transformed four #AnimationVector + * values describing the location of the transformed surface + * surface corners. + */ +AnimationVector4D * +animation_transform_animation_alloc_extremes (AnimationTransformAnimation *transform_animation, + AnimationVector const *corners) +{ + AnimationVector4D *out_array = g_new0 (AnimationVector4D, 4); + + animation_transform_animation_extremes (transform_animation, corners, out_array); + return out_array; +} + +float +animation_transform_animation_progress (AnimationTransformAnimation *transform_animation) +{ + AnimationTransformAnimationPrivate *priv = + reinterpret_cast (animation_transform_animation_get_instance_private (transform_animation)); + + return priv->interface->Progress (); +} + +static void +animation_transform_animation_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationTransformAnimation *transform_animation = ANIMATION_TRANSFORM_ANIMATION (object); + AnimationTransformAnimationPrivate *priv = + reinterpret_cast (animation_transform_animation_get_instance_private (transform_animation)); + + switch (prop_id) + { + case PROP_INTERFACE: + priv->interface = reinterpret_cast (g_value_get_pointer (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_transform_animation_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AnimationTransformAnimation *transform_animation = ANIMATION_TRANSFORM_ANIMATION (object); + AnimationTransformAnimationPrivate *priv = + reinterpret_cast (animation_transform_animation_get_instance_private (transform_animation)); + + switch (prop_id) + { + case PROP_INTERFACE: + g_value_set_pointer (value, reinterpret_cast (priv->interface)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_transform_animation_finalize (GObject *object) +{ + AnimationTransformAnimation *transform_animation = ANIMATION_TRANSFORM_ANIMATION (object); + AnimationTransformAnimationPrivate *priv = + reinterpret_cast (animation_transform_animation_get_instance_private (transform_animation)); + + delete priv->interface; + priv->interface = nullptr; + + G_OBJECT_CLASS (animation_transform_animation_parent_class)->finalize (object); +} + +static void +animation_transform_animation_init (AnimationTransformAnimation *model) +{ +} + + +static void +animation_transform_animation_class_init (AnimationTransformAnimationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = animation_transform_animation_get_property; + object_class->set_property = animation_transform_animation_set_property; + object_class->finalize = animation_transform_animation_finalize; + + animation_transform_animation_props[PROP_INTERFACE] = + g_param_spec_pointer ("interface", + "Internal Interface", + "Internal C++ interface that this class wraps", + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_properties (object_class, + NPROPS, + animation_transform_animation_props); +} diff --git a/animation-glib/transform/transform.h b/animation-glib/transform/transform.h new file mode 100644 index 0000000..480b95a --- /dev/null +++ b/animation-glib/transform/transform.h @@ -0,0 +1,52 @@ +/* + * animation-glib/transform/transform.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject Interface for affine transform animations. + */ +#pragma once + +#include + +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_TRANSFORM_ANIMATION animation_transform_animation_get_type () +G_DECLARE_DERIVABLE_TYPE (AnimationTransformAnimation, animation_transform_animation, ANIMATION, TRANSFORM_ANIMATION, GObject) + +struct _AnimationTransformAnimationClass { + GObjectClass parent_class; +}; + +gboolean animation_transform_animation_step (AnimationTransformAnimation *transform_animation, + unsigned int ms); + +float animation_transform_animation_progress (AnimationTransformAnimation *transform_animation); + +float const * animation_transform_animation_matrix (AnimationTransformAnimation *transform_animation); + +void animation_transform_animation_extremes (AnimationTransformAnimation *transform_animation, + AnimationVector const *corners, + AnimationVector4D *out_extremes); + +AnimationVector4D * animation_transform_animation_alloc_extremes (AnimationTransformAnimation *transform_animation, + AnimationVector const *corners); + +G_END_DECLS diff --git a/animation-glib/vector.cpp b/animation-glib/vector.cpp index b2f0ec6..e81036d 100644 --- a/animation-glib/vector.cpp +++ b/animation-glib/vector.cpp @@ -17,8 +17,7 @@ * License along with eos-companion-app-service. If not, see * . * - * GObject Interface for "wobbly" textures, vector - * type implementation. + * GObject boxed type vector implementation. */ #include diff --git a/animation-glib/vector4d-internal.h b/animation-glib/vector4d-internal.h new file mode 100644 index 0000000..ca5e305 --- /dev/null +++ b/animation-glib/vector4d-internal.h @@ -0,0 +1,99 @@ +/* + * animation-glib/vector4d-internal.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject boxed type 4D vector implementation. + */ + +#include + +namespace animation +{ + namespace geometry + { + namespace dimension + { + template <> + struct Dimension + { + typedef double data_type; + static const size_t dimensions = 4; + }; + + template <> + struct DimensionAccess + { + static inline double get (AnimationVector4D const &p) + { + return p.x; + } + + static inline void + set (AnimationVector4D &p, double const &value) + { + p.x = value; + } + }; + + template <> + struct DimensionAccess + { + static inline double get (AnimationVector4D const &p) + { + return p.y; + } + + static inline void + set (AnimationVector4D &p, double const &value) + { + p.y = value; + } + }; + + template <> + struct DimensionAccess + { + static inline double get (AnimationVector4D const &p) + { + return p.z; + } + + static inline void + set (AnimationVector4D &p, double const &value) + { + p.z = value; + } + }; + + template <> + struct DimensionAccess + { + static inline double get (AnimationVector4D const &p) + { + return p.w; + } + + static inline void + set (AnimationVector4D &p, double const &value) + { + p.w = value; + } + }; + } + } +} diff --git a/animation-glib/vector4d.cpp b/animation-glib/vector4d.cpp new file mode 100644 index 0000000..a705a29 --- /dev/null +++ b/animation-glib/vector4d.cpp @@ -0,0 +1,39 @@ +/* + * animation-glib/vector4d.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject boxed type 4D vector implementation. + */ + +#include + +static gpointer +animation_vector4d_copy (gpointer ptr) +{ + AnimationVector4D *src = reinterpret_cast (ptr); + AnimationVector4D *dst = g_new0 (AnimationVector4D, 1); + + *dst = *src; + + return reinterpret_cast (dst); +} + +G_DEFINE_BOXED_TYPE (AnimationVector4D, + animation_vector4d, + animation_vector4d_copy, + g_free); diff --git a/animation-glib/vector4d.h b/animation-glib/vector4d.h new file mode 100644 index 0000000..ff2b2b5 --- /dev/null +++ b/animation-glib/vector4d.h @@ -0,0 +1,39 @@ +/* + * animation-glib/vector4d.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject boxed type 4D vector implementation. + */ +#pragma once + +#include + +G_BEGIN_DECLS + +typedef struct { + double x; + double y; + double z; + double w; +} AnimationVector4D; + +#define ANIMATION_TYPE_VECTOR4D animation_vector4d_get_type () + +GType animation_vector4d_get_type (void); + +G_END_DECLS diff --git a/animation-glib/zoom/meson.build b/animation-glib/zoom/meson.build new file mode 100644 index 0000000..d58b2f8 --- /dev/null +++ b/animation-glib/zoom/meson.build @@ -0,0 +1,32 @@ +# animation-glib/zoom/meson.build +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (zoom animation component), GObject bindings. + +zoom_introspectable_sources = files([ + 'zoom.cpp' +]) + +zoom_headers = files([ + 'zoom.h' +]) + +animation_glib_introspectable_sources += zoom_introspectable_sources +animation_glib_headers += zoom_headers + +install_headers(zoom_headers, subdir: join_paths(animation_glib_headers_subdir, 'zoom')) diff --git a/animation-glib/zoom/zoom.cpp b/animation-glib/zoom/zoom.cpp new file mode 100644 index 0000000..db9bd77 --- /dev/null +++ b/animation-glib/zoom/zoom.cpp @@ -0,0 +1,274 @@ +/* + * animation-glib/zoom/zoom.cpp + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject interface for a zoom animation. + */ + +#include + +#include +#include +#include +#include +#include +#include + +namespace asg = animation::stepper::glib; +namespace agl = animation::glib; +namespace at = animation::transform; +namespace az = animation::zoom; + +struct _AnimationZoomAnimation +{ + AnimationTransformAnimation parent_instance; +}; + +typedef struct _AnimationZoomAnimationPrivate +{ +} AnimationZoomAnimationPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationZoomAnimation, + animation_zoom_animation, + ANIMATION_TYPE_TRANSFORM_ANIMATION) + +enum { + PROP_0, + PROP_SOURCE, + PROP_TARGET, + PROP_STEPPER, + NPROPS +}; + +static GParamSpec *animation_zoom_animation_props [NPROPS] = { NULL, }; + +void +animation_zoom_animation_set_source (AnimationZoomAnimation *animation, + AnimationBoxQuery *source) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Source (std::make_shared (source)); +} + +/** + * animation_zoom_animation_get_source: + * @animation: An #AnimationBoxQuery + * + * Returns: (transfer none): Get the #AnimationBoxQuery source for this #AnimationZoomAnimation + */ +AnimationBoxQuery * +animation_zoom_animation_get_source (AnimationZoomAnimation *animation) +{ + return std::static_pointer_cast (LookupTypedInterfaceProp (G_OBJECT (animation))->Source ())->BoxQuery (); +} + +void +animation_zoom_animation_set_target (AnimationZoomAnimation *animation, + AnimationBoxQuery *target) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Target (std::make_shared (target)); +} + +/** + * animation_zoom_animation_get_target: + * @animation: An #AnimationBoxQuery + * + * Returns: (transfer none): Get the #AnimationBoxQuery target for this #AnimationZoomAnimation + */ +AnimationBoxQuery * +animation_zoom_animation_get_target (AnimationZoomAnimation *animation) +{ + return std::static_pointer_cast (LookupTypedInterfaceProp (G_OBJECT (animation))->Target ())->BoxQuery (); +} + + +void +animation_zoom_animation_set_stepper (AnimationZoomAnimation *animation, + AnimationStepper *stepper) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper (std::make_shared (stepper)); +} + +/** + * animation_zoom_animation_get_stepper: + * @animation: An #AnimationZoomAnimation + * + * Returns: (transfer full): Get the stepper for this #AnimationZoomAnimation + */ +AnimationStepper * +animation_zoom_animation_get_stepper (AnimationZoomAnimation *animation) +{ + auto const &stepper (LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper ()); + + return std::static_pointer_cast (stepper)->BaseStepper (); +} + +static void +animation_zoom_animation_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationZoomAnimation *zoom_animation = ANIMATION_ZOOM_ANIMATION (object); + + switch (prop_id) + { + case PROP_SOURCE: + animation_zoom_animation_set_source (zoom_animation, + ANIMATION_BOX_QUERY (g_value_get_object (value))); + break; + case PROP_TARGET: + animation_zoom_animation_set_target (zoom_animation, + ANIMATION_BOX_QUERY (g_value_get_object (value))); + break; + case PROP_STEPPER: + animation_zoom_animation_set_stepper (zoom_animation, + ANIMATION_STEPPER (g_value_get_object (value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_zoom_animation_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AnimationZoomAnimation *zoom_animation = ANIMATION_ZOOM_ANIMATION (object); + + switch (prop_id) + { + case PROP_SOURCE: + g_value_set_object (value, + animation_zoom_animation_get_source (zoom_animation)); + break; + case PROP_TARGET: + g_value_set_object (value, + animation_zoom_animation_get_target (zoom_animation)); + break; + case PROP_STEPPER: + g_value_set_object (value, animation_zoom_animation_get_stepper (zoom_animation)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static GObject * +animation_zoom_animation_constructor (GType type, + unsigned int n_construct_params, + GObjectConstructParam *construct_params) +{ + auto *interface = InterfaceConstructor ::construct (); + auto *transform_interface = static_cast (interface); + + /* We need to also set defaults for certain properties in order + * to ensure that they are bindable later on, in case they are + * not set by the caller. */ + replace_named_pointer_props_in_construct_params_if_null ( + construct_params, + n_construct_params, + { + ReplacePropSpec ("stepper", g_value_get_object, g_value_set_object, []() -> gpointer { + return animation_linear_stepper_new (1); + }), + ReplacePropSpec ("source", g_value_get_object, g_value_set_object, []() -> gpointer { + return animation_box_query_new (); + }), + ReplacePropSpec ("target", g_value_get_object, g_value_set_object, []() -> gpointer { + return animation_box_query_new (); + }) + } + ); + + replace_interface_prop_in_construct_params (construct_params, + n_construct_params, + transform_interface); + + return G_OBJECT_CLASS (animation_zoom_animation_parent_class)->constructor (type, + n_construct_params, + construct_params); +} + +static void +animation_zoom_animation_constructed (GObject *object) +{ + /* Take a size-zero step, which ensures that we update the + * internal state of the animation with all the properties we + * just set */ + animation_transform_animation_step (ANIMATION_TRANSFORM_ANIMATION (object), 0); +} + +static void +animation_zoom_animation_init (AnimationZoomAnimation *model) +{ +} + +static void +animation_zoom_animation_class_init (AnimationZoomAnimationClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = animation_zoom_animation_constructor; + object_class->constructed = animation_zoom_animation_constructed; + object_class->get_property = animation_zoom_animation_get_property; + object_class->set_property = animation_zoom_animation_set_property; + + animation_zoom_animation_props[PROP_SOURCE] = + g_param_spec_object ("source", + "Source Box Query", + "BoxQuery that we are animating from", + ANIMATION_TYPE_BOX_QUERY, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_zoom_animation_props[PROP_TARGET] = + g_param_spec_object ("target", + "Target Box Query", + "BoxQuery that we are animating to", + ANIMATION_TYPE_BOX_QUERY, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + animation_zoom_animation_props[PROP_STEPPER] = + g_param_spec_object ("stepper", + "Stepper", + "Stepper to use to progress the animation", + ANIMATION_TYPE_STEPPER, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_properties (object_class, + NPROPS, + animation_zoom_animation_props); +} + +/** + * animation_zoom_new: + * @source: The #AnimationBoxQuery that we are animating from. + * @target: The #AnimationBoxQuery that we are animating to (current location). + * @stepper: The stepper to use. + * + * Returns: (transfer full): A new #AnimationZoomAnimation. + */ +AnimationZoomAnimation * +animation_zoom_new (const AnimationBoxQuery *source, + const AnimationBoxQuery *target, + AnimationStepper *stepper) +{ + return ANIMATION_ZOOM_ANIMATION (g_object_new (ANIMATION_TYPE_ZOOM_ANIMATION, + "source", source, + "target", target, + "stepper", stepper, + NULL)); +} diff --git a/animation-glib/zoom/zoom.h b/animation-glib/zoom/zoom.h new file mode 100644 index 0000000..ab79135 --- /dev/null +++ b/animation-glib/zoom/zoom.h @@ -0,0 +1,50 @@ +/* + * animation-glib/zoom/zoom.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * GObject Interface for "zoom" animation. + */ +#pragma once + +#include + +#include +#include +#include + +G_BEGIN_DECLS + +#define ANIMATION_TYPE_ZOOM_ANIMATION animation_zoom_animation_get_type () +G_DECLARE_FINAL_TYPE (AnimationZoomAnimation, animation_zoom_animation, ANIMATION, ZOOM_ANIMATION, AnimationTransformAnimation) + +void animation_zoom_animation_set_source (AnimationZoomAnimation *animation, + AnimationBoxQuery *source); +AnimationBoxQuery * animation_zoom_animation_get_source (AnimationZoomAnimation *animation); + +void animation_zoom_animation_set_target (AnimationZoomAnimation *animation, + AnimationBoxQuery *target); +AnimationBoxQuery * animation_zoom_animation_get_target (AnimationZoomAnimation *animation); + + +void animation_zoom_animation_set_stepper (AnimationZoomAnimation *animation, + AnimationStepper *stepper); +AnimationStepper * animation_zoom_animation_get_stepper (AnimationZoomAnimation *animation); + +AnimationZoomAnimation * animation_zoom_new (const AnimationBoxQuery *source, + const AnimationBoxQuery *target, + AnimationStepper *stepper); + +G_END_DECLS diff --git a/animation/bounce/bounce.cpp b/animation/bounce/bounce.cpp new file mode 100644 index 0000000..306e977 --- /dev/null +++ b/animation/bounce/bounce.cpp @@ -0,0 +1,226 @@ +/* + * animation/bounce/bounce.cpp + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * An animation that causes a surface to bounce onto screen, gently + * following an attenuating sine wave. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace agd = animation::geometry::dimension; +namespace ab = animation::bounce; +namespace abc = animation::box_calculation; +namespace am = animation::math; +namespace atc = animation::transform_calculation; + +namespace +{ + float SampleSineWave (float progress, + unsigned int nBounces) + { + return ::sin (progress * M_PI * nBounces); + } + + /* This animation simulates a gentle bounce on a sine wave from + * the center of the window outwards. + * + * The best way to think of this animation is to think of an attenuating + * sine wave squeezed inwards by two bounds that converge on a single point. + * + * In thise case those lines are running from the "initialScale" to 1.0 + * and the "maximumScale" down to 1.0. We run the sine wave for 2pi * nBounce + * iterations (scaling it it to fit within the time range), but the effect + * of the naimation is scaled according to where we are on the bunds. + * + * Now, rotate the attenuating sine wave so that it is facing towards you + * on the z-axis (OpenGL co-ordinates). This is essentially what the animation + * is. */ + float ComputeScaleFactorFromProgressParameters (float progress, + float initialScale, + float maximumScale, + unsigned int nBounces) + { + /* Squeeze the sine wave into place by applying a linear + * interpolation to it */ + float const targetScale = 1.0f; + float sampledSine = SampleSineWave (progress, nBounces); + float range = (maximumScale - initialScale) * (1.0f - progress); + float scaleFloor = initialScale + (targetScale - initialScale) * progress; + + return scaleFloor + range * sampledSine; + } + + void ComputeBounceTransform (glm::mat4 &transform, + float progress, + float initialScale, + float maximumScale, + unsigned int nBounces, + animation::Box const &targetBox) + { + animation::Point boxCenterOffset (abc::ComputeBoxCenterOffset (targetBox)); + + float const scaleFactor = ComputeScaleFactorFromProgressParameters (progress, + initialScale, + maximumScale, + nBounces); + auto centerMat = glm::translate (glm::mat4 (1.0), + glm::vec3 (-1 * agd::get <0> (boxCenterOffset), + -1 *agd::get <1> (boxCenterOffset), + 0.0)); + auto scaleMat = glm::scale (glm::mat4 (1.0), + glm::vec3 (scaleFactor, + scaleFactor, + 1.0)); + auto invCenterMat = glm::translate (glm::mat4 (1.0), + glm::vec3 (agd::get <0> (boxCenterOffset), + agd::get <1> (boxCenterOffset), + 0.0)); + + transform = invCenterMat * scaleMat * centerMat; + } +} + +namespace animation +{ + namespace bounce + { + struct BounceAnimation::Private + { + Private (float initialScale, + float maximumScale, + unsigned int nBounce, + animation::BoxQuery ::Shared const &targetQuery, + animation::stepper::Stepper::Shared const &stepper); + + float initialScale; + float maximumScale; + unsigned int nBounce; + animation::BoxQuery ::Shared targetQuery; + + glm::mat4 transform; + float progress; + + animation::stepper::Stepper::Shared stepper; + }; + + BounceAnimation::Private::Private (float initialScale, + float maximumScale, + unsigned int nBounce, + animation::BoxQuery ::Shared const &targetQuery, + animation::stepper::Stepper::Shared const &stepper) : + initialScale (initialScale), + maximumScale (maximumScale), + nBounce (nBounce), + targetQuery (targetQuery), + transform (glm::mat4 (1.0)), + progress ((*stepper) (0)), + stepper (stepper) + { + } + } +} + +std::array +ab::BounceAnimation::Extremes (std::array const &corners) const +{ + return std::array { + atc::TransformFlattened2DPointBy3DMatrix (corners[0], priv->transform), + atc::TransformFlattened2DPointBy3DMatrix (corners[1], priv->transform), + atc::TransformFlattened2DPointBy3DMatrix (corners[2], priv->transform), + atc::TransformFlattened2DPointBy3DMatrix (corners[3], priv->transform) + }; +} + +float +ab::BounceAnimation::Progress () const +{ + return priv->progress; +} + +bool +ab::BounceAnimation::Step (unsigned int ms) +{ + priv->progress = am::clamp ((*priv->stepper) (ms), 0.0f, 1.0f); + + auto target = priv->targetQuery->Geometry (); + + ComputeBounceTransform (priv->transform, + priv->progress, + priv->initialScale, + priv->maximumScale, + priv->nBounce, + target); + + return priv->progress != 0.0f && priv->progress != 1.0f; +} + +float * const +ab::BounceAnimation::Matrix () const +{ + return glm::value_ptr (priv->transform); +} + +ANIMATION_DEFINE_PROPERTY (ab::BounceAnimation, InitialScale, float, priv->initialScale) +ANIMATION_DEFINE_PROPERTY (ab::BounceAnimation, MaximumScale, float, priv->maximumScale) +ANIMATION_DEFINE_PROPERTY (ab::BounceAnimation, NBounce, unsigned int, priv->nBounce) +ANIMATION_DEFINE_PROPERTY (ab::BounceAnimation, Stepper, animation::stepper::Stepper::Shared, priv->stepper) +ANIMATION_DEFINE_PROPERTY (ab::BounceAnimation, Target, animation::BoxQuery ::Shared, priv->targetQuery) + +ab::BounceAnimation::BounceAnimation (float initialScale, + float maximumScale, + unsigned int nBounce, + animation::BoxQuery ::Shared const &targetQuery, + animation::stepper::Stepper::Shared const &stepper) : + priv (std::make_unique (initialScale, + maximumScale, + nBounce, + targetQuery, + stepper)) +{ + Step (0); +} + +ab::BounceAnimation::BounceAnimation () : + BounceAnimation (1.0, + 1.0, + 1, + animation::MakeStaticBoxQuery (animation::Box (animation::Point (0, 0), + animation::Point (1, 1))), + animation::stepper::Linear (1)) +{ +} + +ab::BounceAnimation::~BounceAnimation () +{ +} diff --git a/animation/bounce/bounce.h b/animation/bounce/bounce.h new file mode 100644 index 0000000..05f6d81 --- /dev/null +++ b/animation/bounce/bounce.h @@ -0,0 +1,67 @@ +/* + * animation/bounce/bounce.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * An animation that causes a surface to bounce onto screen, gently + * following an attenuating sine wave. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#pragma once + +namespace animation +{ + namespace bounce + { + class BounceAnimation : + public animation::transform::TransformAnimation + { + public: + + BounceAnimation (); + BounceAnimation (float initialScale, + float maximumScale, + unsigned int nBounce, + animation::BoxQuery ::Shared const &target, + animation::stepper::Stepper::Shared const &stepper); + ~BounceAnimation (); + + float * const Matrix () const; + float Progress () const; + bool Step (unsigned int ms); + std::array Extremes (std::array const &corners) const; + + ANIMATION_DECLARE_PROPERTY (BounceAnimation, InitialScale, float) + ANIMATION_DECLARE_PROPERTY (BounceAnimation, MaximumScale, float) + ANIMATION_DECLARE_PROPERTY (BounceAnimation, NBounce, unsigned int) + ANIMATION_DECLARE_PROPERTY (BounceAnimation, Stepper, animation::stepper::Stepper::Shared) + ANIMATION_DECLARE_PROPERTY (BounceAnimation, Target, animation::BoxQuery ::Shared) + + private: + + struct Private; + std::unique_ptr priv; + }; + } +} diff --git a/animation/bounce/meson.build b/animation/bounce/meson.build new file mode 100644 index 0000000..67a2837 --- /dev/null +++ b/animation/bounce/meson.build @@ -0,0 +1,32 @@ +# animation/bounce/meson.build +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (bounce animation component). + +bounce_sources = files([ + 'bounce.cpp' +]) + +bounce_headers = files([ + 'bounce.h' +]) + +animation_sources += bounce_sources +animation_headers += bounce_headers + +install_headers(bounce_headers, subdir: join_paths(animation_headers_subdir, 'bounce')) diff --git a/animation/box_calculation.h b/animation/box_calculation.h new file mode 100644 index 0000000..8821dec --- /dev/null +++ b/animation/box_calculation.h @@ -0,0 +1,70 @@ +/* + * animation/box_calculation.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Utility functions to calculate points and box offsets. + */ +#pragma once + +#include + +namespace animation +{ + namespace box_calculation + { + inline animation::Point ComputeBoxCenterOffset (animation::Box const &box) + { + namespace agd = animation::geometry::dimension; + + return animation::Point ((agd::get <0> (box.bottomRight ()) - + agd::get <0> (box.topLeft ())) / 2.0, + (agd::get <1> (box.bottomRight ()) - + agd::get <1> (box.topLeft ())) / 2.0); + } + + inline animation::Point ComputeBoxCenter (animation::Box const &box) + { + namespace agd = animation::geometry::dimension; + + animation::Point topLeft (box.topLeft ()); + agd::pointwise_add (topLeft, ComputeBoxCenterOffset (box)); + return topLeft; + } + + inline animation::Point ComputeRotationAxisOffset (animation::Box const &box, + float u, + float v) + { + namespace agd = animation::geometry::dimension; + + return animation::Point ((agd::get <0> (box.bottomRight ()) - + agd::get <0> (box.topLeft ())) * u, + (agd::get <1> (box.bottomRight ()) - + agd::get <1> (box.topLeft ())) * v); + } + + inline animation::Point ComputeRotationAxis (animation::Box const &box, + float u, + float v) + { + namespace agd = animation::geometry::dimension; + + animation::Point topLeft (box.topLeft ()); + agd::pointwise_add (topLeft, ComputeRotationAxisOffset (box, u, v)); + return topLeft; + } + } +} diff --git a/animation/geometry.h b/animation/geometry.h index 51413d8..413c312 100644 --- a/animation/geometry.h +++ b/animation/geometry.h @@ -22,6 +22,10 @@ */ #pragma once +#include +#include +#include + #include namespace animation @@ -367,12 +371,143 @@ namespace animation dimension::get <1> (p) >= y1 && dimension::get <1> (p) <= y2); } + + /** + * Vector4DModel: + * + * A detached 2D point or vector in space for a given data + * type T. This is a structure of two values. + * + * PointModel implements the Dimension trait, meaning that it + * can be used with functions in the animation::geometry::dimension + * namespace. + */ + template + struct Vector4DModel + { + Vector4DModel (T x, T y, T z, T w) noexcept : + x (x), + y (y), + z (z), + w (w) + { + } + + Vector4DModel () noexcept : + x (0), + y (0), + z (0), + w (0) + { + } + + Vector4DModel (Vector4DModel const &v) noexcept : + x (v.x), + y (v.y), + z (v.z), + w (v.w) + { + } + + void swap (Vector4DModel &a, Vector4DModel &b) noexcept + { + std::swap (a.x, b.x); + std::swap (a.y, b.y); + std::swap (a.z, b.w); + std::swap (a.w, b.z); + } + + Vector4DModel & operator= (Vector4DModel other) noexcept + { + swap (*this, other); + + return *this; + } + + T x; + T y; + T z; + T w; + }; + + typedef Vector4DModel Vector4D; + + namespace dimension + { + template + struct Dimension > + { + typedef T data_type; + static const size_t dimensions = 4; + }; + + template + struct DimensionAccess , 0> + { + static inline T get (Vector4DModel const &p) + { + return p.x; + } + + static inline void + set (Vector4DModel &p, T const &value) + { + p.x = value; + } + }; + + template + struct DimensionAccess , 1> + { + static inline T get (Vector4DModel const &p) + { + return p.y; + } + + static inline void + set (Vector4DModel &p, T const &value) + { + p.y = value; + } + }; + + template + struct DimensionAccess , 2> + { + static inline T get (Vector4DModel const &p) + { + return p.z; + } + + static inline void + set (Vector4DModel &p, T const &value) + { + p.z = value; + } + }; + + template + struct DimensionAccess , 3> + { + static inline T get (Vector4DModel const &p) + { + return p.w; + } + + static inline void + set (Vector4DModel &p, T const &value) + { + p.w = value; + } + }; + } } /* Import animation::geometry::Point types into * animation namespace for compatibility. */ typedef animation::geometry::Point Point; typedef animation::geometry::Vector Vector; + typedef animation::geometry::Vector4D Vector4D; template using PointView = animation::geometry::PointView ; diff --git a/animation/geometry_traits.h b/animation/geometry_traits.h index 5791657..3b4e27b 100644 --- a/animation/geometry_traits.h +++ b/animation/geometry_traits.h @@ -377,5 +377,10 @@ namespace animation } } + template + inline bool operator== (T const &l, U const &r) + { + return dimension::equals (l, r); + } } } diff --git a/animation/glide/glide.cpp b/animation/glide/glide.cpp new file mode 100644 index 0000000..71a7d31 --- /dev/null +++ b/animation/glide/glide.cpp @@ -0,0 +1,256 @@ +/* + * animation/glide/glide.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * An animation that causes a surface to glide onto screen, gently + * following an attenuating sine wave. + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace agd = animation::geometry::dimension; +namespace abc = animation::box_calculation; +namespace ag = animation::glide; +namespace am = animation::math; +namespace atc = animation::transform_calculation; + +namespace +{ + inline float DegreesToRadians (float degrees) + { + return (M_PI / 180.0f) * degrees; + } + + /* This undoes perspective distortion */ + glm::mat4 CreatePerspectiveDistortionMatrix (unsigned int screenWidth) + { + float v = -1.0 / screenWidth; + + return glm::mat4 (glm::vec4 (1.0, 0.0, 0.0, 0.0), + glm::vec4 (0.0, 1.0, 0.0, 0.0), + glm::vec4 (0.0, 0.0, 0.0, 0.0), + glm::vec4 (0.0, 0.0, v, 1.0)); + } + + /* This animation rotates the surface from some rotated position + * towards a resting position where its rotation angle is + * 0, 0, 0 on all three axes. The axis that the surface can rotate + * on is configurable in unit-cordinates. Rotation on 0.5, 0.5 rotates + * at the center of the surface, whereas rotating from 0, 0 would rotate + * from the top left corner. */ + void ComputeGlideTransform (glm::mat4 &transform, + float progress, + float initialDistance, + float xRotationAngleDegrees, + float yRotationAngleDegrees, + float xAxisLocationUnit, + float yAxisLocationUnit, + unsigned int screenWidth, + animation::Box const &targetBox) + { + animation::Point rotationAxis (abc::ComputeRotationAxisOffset (targetBox, + xAxisLocationUnit, + yAxisLocationUnit)); + + auto centerMat = glm::translate (glm::mat4 (1.0f), + glm::vec3 (-1.0f * agd::get <0> (rotationAxis), + -1.0f * agd::get <1> (rotationAxis), + 0.0f)); + auto xRotationMat = glm::rotate (glm::mat4 (1.0f), + DegreesToRadians (xRotationAngleDegrees) * (1.0f - progress), + glm::vec3 (1.0f, 0.0f, 0.0f)); + auto yRotationMat = glm::rotate (glm::mat4 (1.0f), + DegreesToRadians (yRotationAngleDegrees) * (1.0f - progress), + glm::vec3 (0.0f, 1.0f, 0.0f)); + auto translationMat = glm::translate (glm::mat4 (1.0f), + glm::vec3 (0.0f, + 0.0f, + -1.0f * initialDistance * (1.0f - progress))); + auto invCenterMat = glm::translate (glm::mat4 (1.0f), + glm::vec3 (agd::get <0> (rotationAxis), + agd::get <1> (rotationAxis), + 0.0f)); + + transform = invCenterMat * + translationMat * + xRotationMat * + yRotationMat * + CreatePerspectiveDistortionMatrix (screenWidth) * + centerMat; + } +} + +namespace animation +{ + namespace glide + { + struct GlideAnimation::Private + { + Private (float initialDistance, + float xRotationAngleDegrees, + float yRotationAngleDegrees, + float xAxisLocationUnit, + float yAxisLocationUnit, + animation::BoxQuery ::Shared const &viewportQuery, + animation::BoxQuery ::Shared const &targetQuery, + animation::stepper::Stepper::Shared const &stepper); + + float initialDistance; + float xRotationAngleDegrees; + float yRotationAngleDegrees; + float xAxisLocationUnit; + float yAxisLocationUnit; + animation::BoxQuery ::Shared viewportQuery; + animation::BoxQuery ::Shared targetQuery; + + glm::mat4 transform; + float progress; + + animation::stepper::Stepper::Shared stepper; + }; + + GlideAnimation::Private::Private (float initialDistance, + float xRotationAngleDegrees, + float yRotationAngleDegrees, + float xAxisLocationUnit, + float yAxisLocationUnit, + animation::BoxQuery ::Shared const &viewportQuery, + animation::BoxQuery ::Shared const &targetQuery, + animation::stepper::Stepper::Shared const &stepper) : + initialDistance (initialDistance), + xRotationAngleDegrees (xRotationAngleDegrees), + yRotationAngleDegrees (yRotationAngleDegrees), + xAxisLocationUnit (xAxisLocationUnit), + yAxisLocationUnit (yAxisLocationUnit), + viewportQuery (viewportQuery), + targetQuery (targetQuery), + transform (glm::mat4 (1.0)), + progress ((*stepper) (0)), + stepper (stepper) + { + } + } +} + +std::array +ag::GlideAnimation::Extremes (std::array const &corners) const +{ + return std::array { + atc::TransformFlattened2DPointBy3DMatrix (corners[0], priv->transform), + atc::TransformFlattened2DPointBy3DMatrix (corners[1], priv->transform), + atc::TransformFlattened2DPointBy3DMatrix (corners[2], priv->transform), + atc::TransformFlattened2DPointBy3DMatrix (corners[3], priv->transform) + }; +} + +float +ag::GlideAnimation::Progress () const +{ + return priv->progress; +} + +bool +ag::GlideAnimation::Step (unsigned int ms) +{ + priv->progress = am::clamp ((*priv->stepper) (ms), 0.0f, 1.0f); + + auto target = priv->targetQuery->Geometry (); + auto viewport = priv->viewportQuery->Geometry (); + + ComputeGlideTransform (priv->transform, + priv->progress, + priv->initialDistance, + priv->xRotationAngleDegrees, + priv->yRotationAngleDegrees, + priv->xAxisLocationUnit, + priv->yAxisLocationUnit, + viewport.bottomRight().x - viewport.topLeft().x, + target); + + return priv->progress != 0.0f && priv->progress != 1.0f; +} + +float * const +ag::GlideAnimation::Matrix () const +{ + return glm::value_ptr (priv->transform); +} + +ANIMATION_DEFINE_PROPERTY (ag::GlideAnimation, InitialDistance, float, priv->initialDistance) +ANIMATION_DEFINE_PROPERTY (ag::GlideAnimation, XRotationAngleDegrees, float, priv->xRotationAngleDegrees) +ANIMATION_DEFINE_PROPERTY (ag::GlideAnimation, YRotationAngleDegrees, float, priv->yRotationAngleDegrees) +ANIMATION_DEFINE_PROPERTY (ag::GlideAnimation, XAxisLocationUnit, float, priv->xAxisLocationUnit) +ANIMATION_DEFINE_PROPERTY (ag::GlideAnimation, YAxisLocationUnit, float, priv->yAxisLocationUnit) +ANIMATION_DEFINE_PROPERTY (ag::GlideAnimation, Stepper, animation::stepper::Stepper::Shared, priv->stepper) +ANIMATION_DEFINE_PROPERTY (ag::GlideAnimation, Viewport, animation::BoxQuery ::Shared, priv->viewportQuery) +ANIMATION_DEFINE_PROPERTY (ag::GlideAnimation, Target, animation::BoxQuery ::Shared, priv->targetQuery) + +ag::GlideAnimation::GlideAnimation (float initialDistance, + float xRotationAngleDegrees, + float yRotationAngleDegrees, + float xAxisLocationUnit, + float yAxisLocationUnit, + animation::BoxQuery ::Shared const &viewportQuery, + animation::BoxQuery ::Shared const &target, + std::shared_ptr const &stepper) : + priv (new ag::GlideAnimation::Private (initialDistance, + xRotationAngleDegrees, + yRotationAngleDegrees, + xAxisLocationUnit, + yAxisLocationUnit, + viewportQuery, + target, + stepper)) +{ + Step (0); +} + +ag::GlideAnimation::GlideAnimation () : + GlideAnimation (0.0, + 0.0, + 0.0, + 0.5, + 0.5, + animation::MakeStaticBoxQuery (animation::Box (animation::Point (0, 0), + animation::Point (1, 1))), + animation::MakeStaticBoxQuery (animation::Box (animation::Point (0, 0), + animation::Point (1, 1))), + animation::stepper::Linear (1)) +{ +} + +ag::GlideAnimation::~GlideAnimation () +{ +} diff --git a/animation/glide/glide.h b/animation/glide/glide.h new file mode 100644 index 0000000..994e933 --- /dev/null +++ b/animation/glide/glide.h @@ -0,0 +1,76 @@ +/* + * animation/glide/glide.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * An animation that causes a surface to glide onto screen, gently + * following an attenuating sine wave. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#pragma once + +namespace animation +{ + namespace glide + { + class GlideAnimation : + public animation::transform::TransformAnimation + { + public: + + GlideAnimation (); + GlideAnimation (float initialDistance, + float xRotationAngleDegrees, + float yRotationAngleDegrees, + float xAxisLocationUnit, + float yAxisLocationUnit, + animation::BoxQuery ::Shared const &viewportQuery, + animation::BoxQuery ::Shared const &target, + animation::stepper::Stepper::Shared const &stepper); + ~GlideAnimation (); + + float * const Matrix () const; + float Progress () const; + bool Step (unsigned int ms); + std::array Extremes (std::array const &corners) const; + + ANIMATION_DECLARE_PROPERTY (GlideAnimation, InitialDistance, float) + ANIMATION_DECLARE_PROPERTY (GlideAnimation, XRotationAngleDegrees, float) + ANIMATION_DECLARE_PROPERTY (GlideAnimation, YRotationAngleDegrees, float) + ANIMATION_DECLARE_PROPERTY (GlideAnimation, XAxisLocationUnit, float) + ANIMATION_DECLARE_PROPERTY (GlideAnimation, YAxisLocationUnit, float) + ANIMATION_DECLARE_PROPERTY (GlideAnimation, MaximumScale, float) + ANIMATION_DECLARE_PROPERTY (GlideAnimation, Target, animation::BoxQuery ::Shared) + ANIMATION_DECLARE_PROPERTY (GlideAnimation, Viewport, animation::BoxQuery ::Shared) + ANIMATION_DECLARE_PROPERTY (GlideAnimation, Stepper, animation::stepper::Stepper::Shared) + + private: + + struct Private; + std::unique_ptr priv; + }; + } +} diff --git a/animation/glide/meson.build b/animation/glide/meson.build new file mode 100644 index 0000000..1ec3922 --- /dev/null +++ b/animation/glide/meson.build @@ -0,0 +1,31 @@ +# animation/glide/meson.build +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (glide animation component). +glide_sources = files([ + 'glide.cpp' +]) + +glide_headers = files([ + 'glide.h' +]) + +animation_sources += glide_sources +animation_headers += glide_headers + +install_headers(glide_headers, subdir: join_paths(animation_headers_subdir, 'glide')) diff --git a/animation/grid/grid.h b/animation/grid/grid.h new file mode 100644 index 0000000..fd082aa --- /dev/null +++ b/animation/grid/grid.h @@ -0,0 +1,46 @@ +/* + * animation/grid/grid.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Interface definition for animations that perform + * a 2D grid based deformation of surface unit co-ordinates. + */ + +#include + +#include + +#pragma once + +namespace animation +{ + namespace grid + { + class GridAnimation + { + public: + + virtual ~GridAnimation() {}; + virtual animation::Point DeformUVToModelSpace (animation::Point const &) const = 0; + virtual animation::geometry::PointModel Resolution () const = 0; + virtual float Progress () const = 0; + virtual bool Step (unsigned int ms) = 0; + virtual std::array Extremes (std::array const &corners) const = 0; + }; + } +} diff --git a/animation/grid/meson.build b/animation/grid/meson.build new file mode 100644 index 0000000..51ffe0a --- /dev/null +++ b/animation/grid/meson.build @@ -0,0 +1,27 @@ +# animation/grid/meson.build +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (grid animation base class component). + +grid_headers = files([ + 'grid.h' +]) + +animation_headers += grid_headers + +install_headers(grid_headers, subdir: join_paths(animation_headers_subdir, 'grid')) diff --git a/animation/magiclamp/magiclamp.cpp b/animation/magiclamp/magiclamp.cpp new file mode 100644 index 0000000..2fff3e0 --- /dev/null +++ b/animation/magiclamp/magiclamp.cpp @@ -0,0 +1,353 @@ +/* + * animation/magiclamp/magiclamp.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * An animation that causes a surface to magiclamp onto screen, gently + * following an attenuating sine wave. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace abc = animation::box_calculation; +namespace ag = animation::geometry; +namespace agd = animation::geometry::dimension; +namespace agr = animation::grid; +namespace am = animation::math; +namespace aml = animation::magiclamp; +namespace atc = animation::transform_calculation; + +namespace +{ + std::vector InitializeGridObjects (ag::PointModel const &resolution) + { + size_t const gridWidth = agd::get <0> (resolution); + size_t const gridHeight = agd::get <1> (resolution); + return std::vector (gridWidth * gridHeight); + } + + float Sigmoid (float x, float bend, float offset) + { + /* A slightly stronger sigmoid curve */ + return (1.0 / (1.0 + exp (-bend * ((x) - offset)))); + } + + /* Moves from source to target, with the assumption that target + * represents the normal geometry of the surface + * + * This animation simulates the "genie" effect on another popular + * operating system, appearing to "suck" the window into a destination + * rectangle. + * + * The way it works is a little complicated. Basically, we subdivide + * the surface into a very high resolution grid and from each point + * in the source geometry to each point in the target geometry, we + * we have a sigmoid function (S-curve, eg e^x / (1 + ke^px) that each + * grid point travels down towards its destination. */ + void MoveGridVerticallyTowardsTargetWithXSigmoidDeformation (animation::Box const &source, + animation::Box const &target, + std::vector &grid, + ag::PointModel const &gridResolution, + float bendFactor, + float offsetFactor, + float stretchFactor, + float deformSpeedFactor, + float progress) + { + size_t const gridWidth = agd::get <0> (gridResolution); + size_t const gridHeight = agd::get <1> (gridResolution); + float const sourceWidth = agd::get <0> (source.bottomRight ()) - agd::get <0> (source.topLeft ()); + float const sourceHeight = agd::get <1> (source.bottomRight ()) - agd::get <1> (source.topLeft ()); + float const targetWidth = agd::get <0> (target.bottomRight ()) - agd::get <0> (target.topLeft ()); + float const targetHeight = agd::get <1> (target.bottomRight ()) - agd::get <1> (target.topLeft ()); + + animation::Point topLeftTargetPoint (target.topLeft ()); + animation::Point topLeftSourcePoint (source.topLeft ()); + float const topLeftTargetY = agd::get <1> (topLeftTargetPoint); + float const topLeftSourceY = agd::get <1> (topLeftSourcePoint); + float const yDistance = topLeftSourceY - topLeftTargetY; + float const invProgress = 1.0f - progress; + + float const sigmoid0 = Sigmoid (0.0f, bendFactor, offsetFactor); + float const sigmoid1 = Sigmoid (1.0f, bendFactor, offsetFactor); + float const sigmoid1SubSigmoid0 = sigmoid1 - sigmoid0; + + for (size_t j = 0; j < gridHeight; ++j) + { + for (size_t i = 0; i < gridWidth; ++i) + { + animation::Point sourcePoint (agd::get <0> (source.topLeft ()) + + i * sourceWidth / (gridWidth - 1), + agd::get <1> (source.topLeft ()) + + j * sourceHeight / (gridHeight - 1)); + animation::Point targetPoint (agd::get <0> (target.topLeft ()) + + i * targetWidth / (gridWidth - 1), + agd::get <1> (target.topLeft ()) + + j * targetHeight / (gridHeight - 1)); + + /* On the y-axis the grid object moves linearly towards + * its y-destination on the target */ + float yStretchFactor = 1.0f / (1.0f - (j / static_cast (gridHeight - 1)) * stretchFactor); + float yStretchProgress = std::min (invProgress * yStretchFactor, 1.0f); + float yDelta = agd::get <1> (sourcePoint) - agd::get <1> (targetPoint); + float stretchedYCoord = agd::get <1> (targetPoint) + yDelta * yStretchProgress; + float xDeformationProgress = std::min (1.0f - ((topLeftSourceY - stretchedYCoord) / yDistance), 1.0f) * + std::min (invProgress * deformSpeedFactor, 1.0f); + float xDeformationFactor = (Sigmoid (xDeformationProgress, bendFactor, offsetFactor) - sigmoid0) / sigmoid1SubSigmoid0; + float stretchedXCoord = agd::get <0> (targetPoint) + + (agd::get <0> (sourcePoint) - agd::get <0> (targetPoint)) * + xDeformationFactor; + + agd::set <0> (grid[j * gridWidth + i], stretchedXCoord); + agd::set <1> (grid[j * gridWidth + i], stretchedYCoord); + } + } + } + + /* When deforming a UV into the pre-computed grid we must do a linear + * interpolation along both axes, finding the nearest subdivision source + * point on a UV mesh, then interpolating into that mesh component + * on the scene-relative internal mesh. */ + animation::Point InterpolateIntoGrid (animation::Point const &uv, + ag::PointModel const &gridResolution, + std::vector const &grid) + { + animation::Point clampedUV (am::clamp (agd::get <0> (uv), 0.0, 1.0), + am::clamp (agd::get <1> (uv), 0.0, 1.0)); + size_t const gridWidth = agd::get <0> (gridResolution); + size_t const gridHeight = agd::get <1> (gridResolution); + size_t nearestLeftX = static_cast (agd::get <0> (uv) * (gridWidth - 1)); + size_t nearestTopY = static_cast (agd::get <1> (uv) * (gridHeight - 1)); + + size_t nearestRightX = std::min (gridWidth - 1, nearestLeftX + 1); + size_t nearestBottomY = std::min (gridHeight - 1, nearestTopY + 1); + + float unitGridCellWidth = 1.0f / (gridWidth - 1); + float unitGridCellHeight = 1.0f / (gridHeight - 1); + + float xUnit = (agd::get <0> (uv) - unitGridCellWidth * nearestLeftX) / unitGridCellWidth; + float yUnit = (agd::get <1> (uv) - unitGridCellHeight * nearestTopY) / unitGridCellHeight; + + float xGridInterpLeft = agd::get <0> (grid[nearestLeftX + nearestTopY * gridWidth]) * (1.0f - yUnit) + + agd::get <0> (grid[nearestLeftX + nearestBottomY * gridWidth]) * yUnit; + float xGridInterpRight = agd::get <0> (grid[nearestRightX + nearestTopY * gridWidth]) * (1.0f - yUnit) + + agd::get <0> (grid[nearestRightX + nearestBottomY * gridWidth]) * yUnit; + float xGridInterp = xGridInterpLeft + (xGridInterpRight - xGridInterpLeft) * xUnit; + + float yGridInterpTop = agd::get <1> (grid[nearestLeftX + nearestTopY * gridWidth]) * (1.0f - xUnit) + + agd::get <1> (grid[nearestRightX + nearestTopY * gridWidth]) * xUnit; + float yGridInterpBottom = agd::get <1> (grid[nearestLeftX + nearestBottomY * gridWidth]) * (1.0f - xUnit) + + agd::get <1> (grid[nearestRightX + nearestBottomY * gridWidth]) * xUnit; + float yGridInterp = yGridInterpTop + (yGridInterpBottom - yGridInterpTop) * yUnit; + + return animation::Point (xGridInterp, yGridInterp); + } +} + +namespace animation +{ + namespace magiclamp + { + struct MagicLampAnimation::Private + { + Private (animation::BoxQuery ::Shared const &source, + animation::BoxQuery ::Shared const &target, + ag::PointModel const &resolution, + float bendFactor, + float offsetFactor, + float stretchFactor, + float deformSpeedFactor, + animation::stepper::Stepper::Shared const &stepper); + + animation::BoxQuery ::Shared sourceQuery; + animation::BoxQuery ::Shared targetQuery; + + std::vector grid; + ag::PointModel gridResolution; + + float bendFactor; + float offsetFactor; + float stretchFactor; + float deformSpeedFactor; + + float progress; + + animation::stepper::Stepper::Shared stepper; + }; + + MagicLampAnimation::Private::Private (animation::BoxQuery ::Shared const &sourceQuery, + animation::BoxQuery ::Shared const &targetQuery, + ag::PointModel const &resolution, + float bendFactor, + float offsetFactor, + float stretchFactor, + float deformSpeedFactor, + animation::stepper::Stepper::Shared const &stepper) : + sourceQuery (sourceQuery), + targetQuery (targetQuery), + grid (InitializeGridObjects (resolution)), + gridResolution (resolution), + bendFactor (bendFactor), + offsetFactor (offsetFactor), + stretchFactor (stretchFactor), + deformSpeedFactor (deformSpeedFactor), + progress ((*stepper) (0)), + stepper (stepper) + { + } + } +} + +std::array +aml::MagicLampAnimation::Extremes (std::array const &corners) const +{ + size_t const gridWidth = agd::get <0> (priv->gridResolution); + size_t const gridHeight = agd::get <1> (priv->gridResolution); + + size_t topLeftIndex = 0; + size_t topRightIndex = gridWidth - 1; + size_t bottomLeftIndex = (gridHeight - 1) * gridWidth; + size_t bottomRightIndex = bottomLeftIndex + gridWidth - 1; + + return std::array { + animation::Vector4D (agd::get <0> (priv->grid[topLeftIndex]), + agd::get <1> (priv->grid[topLeftIndex]), 0, 1), + animation::Vector4D (agd::get <0> (priv->grid[topRightIndex]), + agd::get <1> (priv->grid[topRightIndex]), 0, 1), + animation::Vector4D (agd::get <0> (priv->grid[bottomLeftIndex]), + agd::get <1> (priv->grid[bottomLeftIndex]), 0, 1), + animation::Vector4D (agd::get <0> (priv->grid[bottomRightIndex]), + agd::get <1> (priv->grid[bottomRightIndex]), 0, 1) + }; +} + +float +aml::MagicLampAnimation::Progress () const +{ + return priv->progress; +} + +bool +aml::MagicLampAnimation::Step (unsigned int ms) +{ + priv->progress = am::clamp ((*priv->stepper) (ms), 0.0f, 1.0f); + + auto source = priv->sourceQuery->Geometry (); + auto target = priv->targetQuery->Geometry (); + + MoveGridVerticallyTowardsTargetWithXSigmoidDeformation (source, + target, + priv->grid, + priv->gridResolution, + priv->bendFactor, + priv->offsetFactor, + priv->stretchFactor, + priv->deformSpeedFactor, + priv->progress); + + return priv->progress != 0.0f && priv->progress != 1.0f; +} + +animation::Point +aml::MagicLampAnimation::DeformUVToModelSpace (animation::Point const &uv) const +{ + return InterpolateIntoGrid (uv, priv->gridResolution, priv->grid); +} + +animation::geometry::PointModel +aml::MagicLampAnimation::Resolution () const +{ + return priv->gridResolution; +} + +ANIMATION_DEFINE_PROPERTY (aml::MagicLampAnimation, Source, animation::BoxQuery ::Shared, priv->sourceQuery) +ANIMATION_DEFINE_PROPERTY (aml::MagicLampAnimation, Target, animation::BoxQuery ::Shared, priv->targetQuery) +ANIMATION_DEFINE_PROPERTY_WITH_HOOKS (aml::MagicLampAnimation, + GridResolution, + animation::geometry::PointModel , + [this]() -> animation::geometry::PointModel const & { + return this->priv->gridResolution; + }, + [this](animation::geometry::PointModel const &v) { + /* Once we update the grid resolution, we also need to re-initialize + * the stored grid and adjust its objects for the animation state, because + * priv->gridResolution and priv->grid must agree on the grid layout */ + this->priv->gridResolution = v; + this->priv->grid = InitializeGridObjects (this->priv->gridResolution); + this->Step (0); + }) +ANIMATION_DEFINE_PROPERTY (aml::MagicLampAnimation, BendFactor, float, priv->bendFactor) +ANIMATION_DEFINE_PROPERTY (aml::MagicLampAnimation, OffsetFactor, float, priv->offsetFactor) +ANIMATION_DEFINE_PROPERTY (aml::MagicLampAnimation, StretchFactor, float, priv->stretchFactor) +ANIMATION_DEFINE_PROPERTY (aml::MagicLampAnimation, DeformSpeedFactor, float, priv->deformSpeedFactor) +ANIMATION_DEFINE_PROPERTY (aml::MagicLampAnimation, Stepper, animation::stepper::Stepper::Shared, priv->stepper) + +aml::MagicLampAnimation::MagicLampAnimation (animation::BoxQuery ::Shared const &source, + animation::BoxQuery ::Shared const &target, + ag::PointModel const &resolution, + float bendFactor, + float offsetFactor, + float stretchFactor, + float deformSpeedFactor, + animation::stepper::Stepper::Shared const &stepper) : + priv (new aml::MagicLampAnimation::Private (source, + target, + resolution, + bendFactor, + offsetFactor, + stretchFactor, + deformSpeedFactor, + stepper)) +{ + Step (0); +} + +aml::MagicLampAnimation::MagicLampAnimation () : + MagicLampAnimation (animation::MakeStaticBoxQuery (animation::Box (animation::Point (0, 0), + animation::Point (1, 1))), + animation::MakeStaticBoxQuery (animation::Box (animation::Point (0, 0), + animation::Point (1, 1))), + ag::PointModel (1, 1), + 1.0, + 1.0, + 1.0, + 1.0, + animation::stepper::Linear (1)) +{ +} + +aml::MagicLampAnimation::~MagicLampAnimation () +{ +} diff --git a/animation/magiclamp/magiclamp.h b/animation/magiclamp/magiclamp.h new file mode 100644 index 0000000..e6d5d8d --- /dev/null +++ b/animation/magiclamp/magiclamp.h @@ -0,0 +1,74 @@ +/* + * animation/magiclamp/magiclamp.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * An animation that causes a surface to bounce onto screen, gently + * following an attenuating sine wave. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#pragma once + +namespace animation +{ + namespace magiclamp + { + class MagicLampAnimation : + public animation::grid::GridAnimation + { + public: + + MagicLampAnimation (); + MagicLampAnimation (animation::BoxQuery ::Shared const &source, + animation::BoxQuery ::Shared const &target, + animation::geometry::PointModel const &resolution, + float bendFactor, + float offsetFactor, + float stretchFactor, + float deformSpeedFactor, + animation::stepper::Stepper::Shared const &stepper); + ~MagicLampAnimation (); + + animation::Point DeformUVToModelSpace (animation::Point const &) const; + animation::geometry::PointModel Resolution () const; + float Progress () const; + bool Step (unsigned int ms); + std::array Extremes (std::array const &corners) const; + + ANIMATION_DECLARE_PROPERTY (MagicLampAnimation, Source, animation::BoxQuery ::Shared) + ANIMATION_DECLARE_PROPERTY (MagicLampAnimation, Target, animation::BoxQuery ::Shared) + ANIMATION_DECLARE_PROPERTY (MagicLampAnimation, GridResolution, animation::geometry::PointModel ) + ANIMATION_DECLARE_PROPERTY (MagicLampAnimation, BendFactor, float) + ANIMATION_DECLARE_PROPERTY (MagicLampAnimation, OffsetFactor, float) + ANIMATION_DECLARE_PROPERTY (MagicLampAnimation, StretchFactor, float) + ANIMATION_DECLARE_PROPERTY (MagicLampAnimation, DeformSpeedFactor, float) + ANIMATION_DECLARE_PROPERTY (MagicLampAnimation, Stepper, animation::stepper::Stepper::Shared) + + private: + + struct Private; + std::unique_ptr priv; + }; + } +} diff --git a/animation/magiclamp/meson.build b/animation/magiclamp/meson.build new file mode 100644 index 0000000..97a87d6 --- /dev/null +++ b/animation/magiclamp/meson.build @@ -0,0 +1,32 @@ +# /animation/magiclamp/meson.build +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (magiclamp animation component). + +magiclamp_sources = files([ + 'magiclamp.cpp' +]) + +magiclamp_headers = files([ + 'magiclamp.h' +]) + +animation_sources += magiclamp_sources +animation_headers += magiclamp_headers + +install_headers(magiclamp_headers, subdir: join_paths(animation_headers_subdir, 'magiclamp')) diff --git a/animation/math.h b/animation/math.h new file mode 100644 index 0000000..d47ec9d --- /dev/null +++ b/animation/math.h @@ -0,0 +1,33 @@ +/* + * animation/math.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Math utility functions not availble elsewhere. + */ +#pragma once + +#include + +namespace animation +{ + namespace math + { + template T clamp (T const &value, T const &lower, T const &higher) + { + return std::fmin (higher, std::fmax (lower, value)); + } + } +} diff --git a/animation/meson.build b/animation/meson.build index 706d5c1..b103b8f 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -20,18 +20,39 @@ api_version = '0' -animation_sources = [] -animation_headers = [] +animation_sources = files([]) +animation_headers = files([]) animation_headers_subdir = 'animation' +subdir('bounce') +subdir('glide') +subdir('grid') +subdir('magiclamp') +subdir('query') +subdir('stepper') +subdir('transform') subdir('wobbly') +subdir('zoom') + +animation_toplevel_headers = files([ + 'box_calculation.h', + 'geometry.h', + 'geometry_traits.h', + 'math.h', + 'property.h', + 'transform_calculation.h' +]) + +animation_headers += animation_toplevel_headers + +install_headers(animation_toplevel_headers, subdir: animation_headers_subdir) animation_lib = shared_library( 'animation', animation_sources, soversion: api_version, install: true, - include_directories: [ animation_inc ] + include_directories: [ animation_inc, glm_inc ] ) animation_dep = declare_dependency( @@ -39,12 +60,6 @@ animation_dep = declare_dependency( include_directories: [ animation_inc ], ) -install_headers( - 'geometry.h', - 'geometry_traits.h', - subdir: animation_headers_subdir -) - pkg = import('pkgconfig') pkg.generate( description: 'Library to provide 2D surface animations', diff --git a/animation/property.h b/animation/property.h new file mode 100644 index 0000000..32b64dd --- /dev/null +++ b/animation/property.h @@ -0,0 +1,53 @@ +/* + * animation/property.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Properties that can be get and set, without a bunch of boilerplate. + */ + +#pragma once + +#define ANIMATION_DECLARE_READONLY_PROPERTY(klass, name, type) \ + type const & name () const ; + +#define ANIMATION_DEFINE_READONLY_PROPERTY(qualifier, name, type, location) \ + type const & qualifier::name() const \ + { \ + return location; \ + } + +#define ANIMATION_DECLARE_PROPERTY(klass, name, type) \ + ANIMATION_DECLARE_READONLY_PROPERTY(klass, name, type) \ + klass & name (type const &v); + +#define ANIMATION_DEFINE_PROPERTY(qualifier, name, type, location) \ + ANIMATION_DEFINE_READONLY_PROPERTY(qualifier, name, type, location) \ + qualifier & qualifier::name(type const &v) \ + { \ + location = v; \ + return *this; \ + } + +#define ANIMATION_DEFINE_PROPERTY_WITH_HOOKS(qualifier, name, type, read_hook, write_hook) \ + type const & qualifier::name() const \ + { \ + return read_hook(); \ + } \ + qualifier & qualifier::name(type const &v) \ + { \ + write_hook(v); \ + return *this; \ + } diff --git a/animation/query/geometry_query.cpp b/animation/query/geometry_query.cpp new file mode 100644 index 0000000..1e22012 --- /dev/null +++ b/animation/query/geometry_query.cpp @@ -0,0 +1,27 @@ + /* + * animation/query/geometry_query.cpp + * + * Copyright 2019 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Implementation details for geometry cache. + */ + +#include "animation/query/geometry_query.h" + +animation::BoxQuery::~BoxQuery () +{ +} diff --git a/animation/query/geometry_query.h b/animation/query/geometry_query.h new file mode 100644 index 0000000..d04f267 --- /dev/null +++ b/animation/query/geometry_query.h @@ -0,0 +1,118 @@ +/* + * animation/query/geometry_query.h + * + * Copyright 2019 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Definition for geometry cache. A function is kept to query the + * geometry of the box in case it is invalidated. The idea behind + * this class to allow the animations to have an up to date view of + * of the relevant geometry at all times, but avoid expensive calls + * to re-query the geometry on every frame (for instance, the true + * geometry at any point might only be known by some javascript calling + * code - we allow it to signal on geometry changes that the geometry + * has been updated while the javascript code is being executed, as opposed + * to the C++ code calling back into the javascript code to determine + * the updated geometry on every frame). + */ + +#pragma once + +#include + +#include + +namespace animation +{ + template + class BoxQuery + { + public: + + typedef std::shared_ptr > Shared; + + virtual ~BoxQuery () {}; + virtual geometry::Box const & Geometry () const = 0; + }; + + template + class BoxQueryStorage : + public BoxQuery + { + public: + + typedef std::shared_ptr > Shared; + + BoxQueryStorage (geometry::Box const &box); + BoxQueryStorage (geometry::Box &&box); + + void Update (geometry::Box const &box); + void Update (geometry::Box &&box); + geometry::Box const & Geometry () const override; + + private: + + /* Not copyable or movable */ + BoxQueryStorage (BoxQueryStorage const &query) = delete; + BoxQueryStorage (BoxQueryStorage &&query) = delete; + BoxQueryStorage & operator= (BoxQueryStorage const &query) = delete; + BoxQueryStorage & operator= (BoxQueryStorage &&query) = delete; + + geometry::Box box; + }; + + template + BoxQueryStorage ::BoxQueryStorage (geometry::Box const &box) : + box(box) + { + } + + template + BoxQueryStorage ::BoxQueryStorage (geometry::Box &&box) : + box (std::move (box)) + { + } + + template + void BoxQueryStorage ::Update (geometry::Box const &box) + { + this->box = box; + } + + template + void BoxQueryStorage ::Update (geometry::Box &&box) + { + this->box = std::move(box); + } + + template + geometry::Box const & BoxQueryStorage ::Geometry () const + { + return box; + } + + template + typename animation::BoxQuery ::Shared MakeStaticBoxQuery (animation::Box &&box) + { + return std::make_shared > (std::move (box)); + } + + template + typename animation::BoxQuery ::Shared MakeStaticBoxQuery (animation::Box const &box) + { + return std::make_shared > (box); + } +} diff --git a/animation/query/meson.build b/animation/query/meson.build new file mode 100644 index 0000000..85ec262 --- /dev/null +++ b/animation/query/meson.build @@ -0,0 +1,29 @@ +# /animation/query/meson.build +# +# Meson build file for libanimation (geometry query component). +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (geometry query component). + +geometry_query_headers = files([ + 'geometry_query.h' +]) + +animation_headers += geometry_query_headers + +install_headers(glide_headers, subdir: join_paths(animation_headers_subdir, 'query')) diff --git a/animation/stepper/linear.cpp b/animation/stepper/linear.cpp new file mode 100644 index 0000000..1273781 --- /dev/null +++ b/animation/stepper/linear.cpp @@ -0,0 +1,59 @@ +/* + * animation/stepper/linear.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Definition for a linear stepping function - just adds to progress + * according to how long the animation is. + */ + +#include + +namespace as = animation::stepper; + +namespace animation +{ + namespace stepper + { + struct LinearStepper::Private + { + Private(float length) : + length (length) + { + } + + float length; + float progress; + }; + } +} + +ANIMATION_DEFINE_PROPERTY (as::LinearStepper, Length, float, priv->length) + +as::LinearStepper::LinearStepper (unsigned int length): + priv (std::make_unique (length)) +{ +} + +float +as::LinearStepper::Step (unsigned int ms) +{ + priv->progress += ms / priv->length; + return priv->progress; +} + +as::LinearStepper::~LinearStepper () = default; diff --git a/animation/stepper/linear.h b/animation/stepper/linear.h new file mode 100644 index 0000000..dfb3c60 --- /dev/null +++ b/animation/stepper/linear.h @@ -0,0 +1,63 @@ +/* + * animation/stepper/linear.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Definition for a linear stepping function - just adds to progress + * according to how long the animation is. + */ + +#include + +#include +#include + +#pragma once + +namespace animation +{ + namespace stepper + { + class LinearStepper: + public Stepper + { + public: + + LinearStepper(); + LinearStepper(unsigned int length); + ~LinearStepper(); + + static constexpr unsigned int DefaultAnimationLength = 300; + + ANIMATION_DECLARE_PROPERTY (LinearStepper, Length, float); + + protected: + + float Step (unsigned int ms) override; + + private: + + struct Private; + std::unique_ptr priv; + }; + + inline std::shared_ptr Linear (unsigned int length) + { + return std::make_shared (length); + } + } +} diff --git a/animation/stepper/meson.build b/animation/stepper/meson.build new file mode 100644 index 0000000..9677bc2 --- /dev/null +++ b/animation/stepper/meson.build @@ -0,0 +1,38 @@ +# /animation/stepper/meson.build +# +# Meson build file for libanimation stepper functions. +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (stepper animation component). + +stepper_headers = files([ + 'linear.h', + 'reverse.h', + 'stepper.h' +]) + +stepper_sources = files([ + 'linear.cpp', + 'reverse.cpp', + 'stepper.cpp' +]) + +animation_headers += stepper_headers +animation_sources += stepper_sources + +install_headers(stepper_headers, subdir: join_paths(animation_headers_subdir, 'stepper')) diff --git a/animation/stepper/reverse.cpp b/animation/stepper/reverse.cpp new file mode 100644 index 0000000..9ead31b --- /dev/null +++ b/animation/stepper/reverse.cpp @@ -0,0 +1,57 @@ +/* + * animation/stepper/reerse.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Definition for a reverse stepping function. Takes another stepping + * function and inverts its output. + */ + +#include + +namespace as = animation::stepper; + +namespace animation +{ + namespace stepper + { + struct ReverseStepper::Private + { + Private(as::Stepper::Shared const &stepper) : + stepper (stepper) + { + } + + as::Stepper::Shared stepper; + }; + } +} + +ANIMATION_DEFINE_READONLY_PROPERTY (as::ReverseStepper, Stepper, as::Stepper::Shared, priv->stepper) + +as::ReverseStepper::ReverseStepper (Stepper::Shared const &stepper): + priv (std::make_unique (stepper)) +{ +} + +float +as::ReverseStepper::Step (unsigned int ms) +{ + return 1.0 - (*priv->stepper) (ms); +} + +as::ReverseStepper::~ReverseStepper () = default; diff --git a/animation/stepper/reverse.h b/animation/stepper/reverse.h new file mode 100644 index 0000000..28a58cc --- /dev/null +++ b/animation/stepper/reverse.h @@ -0,0 +1,60 @@ +/* + * animation/stepper/reverse.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Definition for a reverse stepping function. Takes another stepping + * function and inverts its output. + */ + +#include + +#include +#include + +#pragma once + +namespace animation +{ + namespace stepper + { + class ReverseStepper : + public Stepper + { + public: + + ReverseStepper (Stepper::Shared const &stepper); + ~ReverseStepper(); + + ANIMATION_DECLARE_READONLY_PROPERTY (ReverseStepper, Stepper, Stepper::Shared); + + protected: + + float Step (unsigned int ms) override; + + private: + + struct Private; + std::unique_ptr priv; + }; + + inline std::shared_ptr Reverse (Stepper::Shared const &stepper) + { + return std::make_shared (stepper); + } + } +} diff --git a/animation/stepper/stepper.cpp b/animation/stepper/stepper.cpp new file mode 100644 index 0000000..7b5080d --- /dev/null +++ b/animation/stepper/stepper.cpp @@ -0,0 +1,32 @@ +/* + * animation/stepper/stepper.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Definition for a stepping function, taking an unsigned int of + * milliseconds passed and returning a floating point number + * between 0.0 and 1.0. + */ + +#include + +namespace as = animation::stepper; + +as::Stepper::~Stepper () +{ +} + diff --git a/animation/stepper/stepper.h b/animation/stepper/stepper.h new file mode 100644 index 0000000..1c7580f --- /dev/null +++ b/animation/stepper/stepper.h @@ -0,0 +1,53 @@ +/* + * animation/stepper/stepper.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Definition for a stepping function, taking an unsigned int of + * milliseconds passed and returning a floating point number + * between 0.0 and 1.0. + */ + +#include + +#pragma once + +namespace animation +{ + namespace stepper + { + class Stepper + { + public: + + typedef std::shared_ptr Shared; + + inline float operator() (unsigned int ms) + { + return Step (ms); + } + + protected: + + Stepper () {}; + + virtual float Step (unsigned int) = 0; + virtual ~Stepper (); + + }; + } +} diff --git a/animation/third_party/glm b/animation/third_party/glm new file mode 160000 index 0000000..fe7c7b5 --- /dev/null +++ b/animation/third_party/glm @@ -0,0 +1 @@ +Subproject commit fe7c7b5ac15ba8d5c9c45984831dc2e830726b33 diff --git a/animation/transform/meson.build b/animation/transform/meson.build new file mode 100644 index 0000000..322fe07 --- /dev/null +++ b/animation/transform/meson.build @@ -0,0 +1,13 @@ +# animation/transform/meson.build +# +# Build the libanimation library (transform animation base class component). +# +# See /LICENCE.md for Copyright information. + +transform_headers = files([ + 'transform.h' +]) + +animation_headers += transform_headers + +install_headers(transform_headers, subdir: join_paths(animation_headers_subdir, 'transform')) diff --git a/animation/transform/transform.h b/animation/transform/transform.h new file mode 100644 index 0000000..f35acf5 --- /dev/null +++ b/animation/transform/transform.h @@ -0,0 +1,45 @@ +/* + * animation/transform/transform.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Interface definition for animations that output + * an affine transformation (4x4) for a 2D surface. + */ + +#include + +#include + +#pragma once + +namespace animation +{ + namespace transform + { + class TransformAnimation + { + public: + + virtual ~TransformAnimation() {}; + virtual float * const Matrix () const = 0; + virtual float Progress () const = 0; + virtual bool Step (unsigned int ms) = 0; + virtual std::array Extremes (std::array const &corners) const = 0; + }; + } +} diff --git a/animation/transform_calculation.h b/animation/transform_calculation.h new file mode 100644 index 0000000..41f7b6d --- /dev/null +++ b/animation/transform_calculation.h @@ -0,0 +1,39 @@ +/* + * animation/transform_calculation.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Utility functions with some helpful matrix transforms. + */ +#pragma once + +#include + +#include + +namespace animation +{ + namespace transform_calculation + { + inline animation::Vector4D TransformFlattened2DPointBy3DMatrix (animation::Point const &p, + glm::mat4 const &matrix) + { + namespace agd = animation::geometry::dimension; + + glm::vec4 t (matrix * glm::vec4 (agd::get <0> (p), agd::get <1> (p), 0.0, 1.0)); + return animation::Vector4D (t[0], t[1], t[2], t[3]); + } + } +} diff --git a/animation/wobbly/wobbly_internal.h b/animation/wobbly/wobbly_internal.h index 77d847a..850f2cf 100644 --- a/animation/wobbly/wobbly_internal.h +++ b/animation/wobbly/wobbly_internal.h @@ -1268,6 +1268,7 @@ wobbly::SpringMesh::CalculateForces (double springConstant) const }; } +// To make this faster inline animation::Point wobbly::BezierMesh::DeformUnitCoordsToMeshSpace (Point const &normalized) const { @@ -1296,27 +1297,68 @@ wobbly::BezierMesh::DeformUnitCoordsToMeshSpace (Point const &normalized) const double const one_u_pow2 = one_u * one_u; double const one_v_pow2 = one_v * one_v; - long double const uCoefficients[] = + double const uCoefficients[] = { - one_u * one_u * one_u, + one_u_pow2 * one_u, three_u * one_u_pow2, 3 * u_pow2 * one_u, u_pow2 * u }; - long double const vCoefficients[] = + double const vCoefficients[] = { - one_v * one_v * one_v, + one_v_pow2 * one_v, three_v * one_v_pow2, 3 * v_pow2 * one_v, v_pow2 * v }; - double x = 0.0; - double y = 0.0; + double const coefficients[] = + { + uCoefficients[0] * vCoefficients[0], + uCoefficients[0] * vCoefficients[0], + uCoefficients[0] * vCoefficients[1], + uCoefficients[0] * vCoefficients[1], + uCoefficients[0] * vCoefficients[2], + uCoefficients[0] * vCoefficients[2], + uCoefficients[0] * vCoefficients[3], + uCoefficients[0] * vCoefficients[3], + uCoefficients[1] * vCoefficients[0], + uCoefficients[1] * vCoefficients[0], + uCoefficients[1] * vCoefficients[1], + uCoefficients[1] * vCoefficients[1], + uCoefficients[1] * vCoefficients[2], + uCoefficients[1] * vCoefficients[2], + uCoefficients[1] * vCoefficients[3], + uCoefficients[1] * vCoefficients[3], + uCoefficients[2] * vCoefficients[0], + uCoefficients[2] * vCoefficients[0], + uCoefficients[2] * vCoefficients[1], + uCoefficients[2] * vCoefficients[1], + uCoefficients[2] * vCoefficients[2], + uCoefficients[2] * vCoefficients[2], + uCoefficients[2] * vCoefficients[3], + uCoefficients[2] * vCoefficients[3], + uCoefficients[3] * vCoefficients[0], + uCoefficients[3] * vCoefficients[0], + uCoefficients[3] * vCoefficients[1], + uCoefficients[3] * vCoefficients[1], + uCoefficients[3] * vCoefficients[2], + uCoefficients[3] * vCoefficients[2], + uCoefficients[3] * vCoefficients[3], + uCoefficients[3] * vCoefficients[3] + }; + + double pos[config::Width + config::Height] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + + for (size_t i = 0; i < config::Width * config::Height; ++i) + { + pos[(i * 2) % 8] += coefficients[i * 2] * mPoints[i * 2]; + pos[(i * 2 + 1) % 8] += coefficients[i * 2 + 1] * mPoints[i * 2 + 1]; + } /* This will access the point matrix in a linear fashion for - * cache-efficiency */ + * cache-efficiency */ /* for (size_t j = 0; j < config::Height; ++j) { for (size_t i = 0; i < config::Width; ++i) @@ -1324,10 +1366,13 @@ wobbly::BezierMesh::DeformUnitCoordsToMeshSpace (Point const &normalized) const size_t const xIdx = j * 2 * config::Width + i * 2; size_t const yIdx = j * 2 * config::Width + i * 2 + 1; - x += uCoefficients[j] * vCoefficients[i] * mPoints[xIdx]; - y += uCoefficients[j] * vCoefficients[i] * mPoints[yIdx]; + pos[i * 2] += uCoefficients[j] * vCoefficients[i] * mPoints[xIdx]; + pos[i * 2 + 1] += uCoefficients[j] * vCoefficients[i] * mPoints[yIdx]; } - } + }*/ + + double x = pos[0] + pos[2] + pos[4] + pos[6]; + double y = pos[1] + pos[3] + pos[5] + pos[7]; Point absolutePosition (x, y); return absolutePosition; diff --git a/animation/zoom/meson.build b/animation/zoom/meson.build new file mode 100644 index 0000000..5c2413e --- /dev/null +++ b/animation/zoom/meson.build @@ -0,0 +1,32 @@ +# animation/zoom/meson.build +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Build the libanimation library (zoom animation component). + +zoom_sources = files([ + 'zoom.cpp' +]) + +zoom_headers = files([ + 'zoom.h' +]) + +animation_sources += zoom_sources +animation_headers += zoom_headers + +install_headers(zoom_headers, subdir: join_paths(animation_headers_subdir, 'zoom')) diff --git a/animation/zoom/zoom.cpp b/animation/zoom/zoom.cpp new file mode 100644 index 0000000..5023477 --- /dev/null +++ b/animation/zoom/zoom.cpp @@ -0,0 +1,223 @@ +/* + * animation/zoom/zoom.cpp + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Animation that causes a surface to zoom from one rectangle to another. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace agd = animation::geometry::dimension; +namespace abc = animation::box_calculation; +namespace am = animation::math; +namespace atc = animation::transform_calculation; +namespace az = animation::zoom; + +namespace +{ + animation::Box EnsureNonZeroArea (animation::Box const &box) + { + auto x1 = agd::get <0> (box.topLeft()); + auto y1 = agd::get <1> (box.topLeft()); + auto x2 = agd::get <0> (box.bottomRight()); + auto y2 = agd::get <1> (box.bottomRight()); + + return animation::Box (animation::Point (x1, y1), + animation::Point (x2 == x1 ? x2 + 1 : x2, + y2 == y1 ? y2 + 1 : y2)); + } + + /* This is a simple affine transformation that goes from + * one box to another. + * + * The animation runs "from" some source point "to" some target point + * which is the surface' "natural" position within the scene (eg, if + * there were a scene graph, the "to" position forms its co-ordinates + * and its geometry). + * + * For instance, if the window was minimizing, then the animation would + * have to be run in reverse, "from" the minimized point "to" the unminimized + * point, but stepping backwards. */ + void ComputeZoomTransform (glm::mat4 &transform, + float progress, + animation::Box const &from, + animation::Box const &to) + { + animation::Point const &fromTopLeft (from.topLeft ()); + animation::Point const &fromBottomRight (from.bottomRight ()); + animation::Point const &toTopLeft (to.topLeft ()); + animation::Point const &toBottomRight (to.bottomRight ()); + + auto fromWidth = agd::get <0> (fromBottomRight) - agd::get <0> (fromTopLeft); + auto fromHeight = agd::get <1> (fromBottomRight) - agd::get <1> (fromTopLeft); + + auto toWidth = agd::get <0> (toBottomRight) - agd::get <0> (toTopLeft); + auto toHeight = agd::get <1> (toBottomRight) - agd::get <1> (toTopLeft); + + animation::Point fromCenter (abc::ComputeBoxCenter (from)); + animation::Point toCenter (abc::ComputeBoxCenter (to)); + animation::Point toCenterOffset (abc::ComputeBoxCenterOffset (to)); + + /* Translate backwards from "to" to "from" according to the inverse of progress + * (eg, at progress == 1.0, there will be no translation from the natural point + * but at progress == 0.0 we translate all the way back from "to" to "from"). */ + animation::Point translation ((agd::get <0> (fromCenter) - agd::get <0> (toCenter)) * (1.0f - progress), + (agd::get <1> (fromCenter) - agd::get <1> (toCenter)) * (1.0f - progress)); + + /* Interpolate between the source and destination width */ + animation::Point scaleFactor (((toWidth * progress) + (fromWidth * (1.0 - progress))) / + toWidth, + ((toHeight * progress) + (fromHeight * (1.0 - progress))) / + toHeight); + + /* Remember that transforamtions are done back to front when postmultiplying. + * + * So we translate to the center first, then scale, then undo the translation + * then translate to the correct point along the animation. */ + auto centerMat = glm::translate (glm::mat4 (1.0), + glm::vec3 (-1.0 * agd::get <0> (toCenterOffset), + -1.0 * agd::get <1> (toCenterOffset), + 0.0)); + auto scaleMat = glm::scale (glm::mat4 (1.0), + glm::vec3 (agd::get <0> (scaleFactor), + agd::get <1> (scaleFactor), + 1.0)); + auto invCenterMat = glm::translate (glm::mat4 (1.0), + glm::vec3 (agd::get <0> (toCenterOffset), + agd::get <1> (toCenterOffset), + 0.0)); + auto translationMat = glm::translate (glm::mat4 (1.0), + glm::vec3 (agd::get <0> (translation), + agd::get <1> (translation), + 0.0)); + + transform = translationMat * invCenterMat * scaleMat * centerMat; + } +} + +namespace animation +{ + namespace zoom + { + struct ZoomAnimation::Private + { + Private (animation::BoxQuery ::Shared const &source, + animation::BoxQuery ::Shared const &target, + animation::stepper::Stepper::Shared const &stepper); + + animation::BoxQuery ::Shared sourceQuery; + animation::BoxQuery ::Shared targetQuery; + + glm::mat4 transform; + float progress; + + animation::stepper::Stepper::Shared stepper; + }; + + ZoomAnimation::Private::Private (animation::BoxQuery ::Shared const &sourceQuery, + animation::BoxQuery ::Shared const &targetQuery, + animation::stepper::Stepper::Shared const &stepper) : + sourceQuery (sourceQuery), + targetQuery (targetQuery), + transform (glm::mat4 (1.0)), + progress ((*stepper) (0)), + stepper (stepper) + { + } + } +} + +std::array +az::ZoomAnimation::Extremes (std::array const &corners) const +{ + return std::array { + atc::TransformFlattened2DPointBy3DMatrix (corners[0], priv->transform), + atc::TransformFlattened2DPointBy3DMatrix (corners[1], priv->transform), + atc::TransformFlattened2DPointBy3DMatrix (corners[2], priv->transform), + atc::TransformFlattened2DPointBy3DMatrix (corners[3], priv->transform) + }; +} + +float +az::ZoomAnimation::Progress () const +{ + return priv->progress; +} + +bool +az::ZoomAnimation::Step (unsigned int ms) +{ + priv->progress = am::clamp ((*priv->stepper) (ms), 0.0f, 1.0f); + + auto from = EnsureNonZeroArea (priv->sourceQuery->Geometry ()); + auto to = EnsureNonZeroArea (priv->targetQuery->Geometry ()); + + ComputeZoomTransform (priv->transform, + priv->progress, + from, + to); + + return priv->progress != 0.0f && priv->progress != 1.0f; +} + +float * const +az::ZoomAnimation::Matrix () const +{ + return glm::value_ptr (priv->transform); +} + +ANIMATION_DEFINE_PROPERTY (az::ZoomAnimation, Source, animation::BoxQuery ::Shared, priv->sourceQuery) +ANIMATION_DEFINE_PROPERTY (az::ZoomAnimation, Target, animation::BoxQuery ::Shared, priv->targetQuery) +ANIMATION_DEFINE_PROPERTY (az::ZoomAnimation, Stepper, animation::stepper::Stepper::Shared, priv->stepper) + +az::ZoomAnimation::ZoomAnimation (animation::BoxQuery ::Shared const &from, + animation::BoxQuery ::Shared const &to, + animation::stepper::Stepper::Shared const &stepper) : + priv (new az::ZoomAnimation::Private (from, to, stepper)) +{ + /* Taking a size-zero step ensures that our matrix is + * in sync with the animation parameters we just set. */ + Step (0); +} + +az::ZoomAnimation::ZoomAnimation () : + ZoomAnimation (animation::MakeStaticBoxQuery (animation::Box (animation::Point (0, 0), + animation::Point (1, 1))), + animation::MakeStaticBoxQuery (animation::Box (animation::Point (0, 0), + animation::Point (1, 1))), + animation::stepper::Linear (1)) +{ +} + +az::ZoomAnimation::~ZoomAnimation () +{ +} diff --git a/animation/zoom/zoom.h b/animation/zoom/zoom.h new file mode 100644 index 0000000..07d8849 --- /dev/null +++ b/animation/zoom/zoom.h @@ -0,0 +1,62 @@ +/* + * animation/zoom/zoom.h + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Animation that causes a surface to zoom from one rectangle to another. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#pragma once + +namespace animation +{ + namespace zoom + { + class ZoomAnimation : + public animation::transform::TransformAnimation + { + public: + + ZoomAnimation (); + ZoomAnimation (animation::BoxQuery ::Shared const &source, + animation::BoxQuery ::Shared const &target, + animation::stepper::Stepper::Shared const &stepper); + ~ZoomAnimation (); + + float * const Matrix () const; + float Progress () const; + bool Step (unsigned int ms); + std::array Extremes (std::array const &corners) const; + + ANIMATION_DECLARE_PROPERTY (ZoomAnimation, Source, animation::BoxQuery ::Shared) + ANIMATION_DECLARE_PROPERTY (ZoomAnimation, Target, animation::BoxQuery ::Shared) + ANIMATION_DECLARE_PROPERTY (ZoomAnimation, Stepper, animation::stepper::Stepper::Shared) + + private: + + struct Private; + std::unique_ptr priv; + }; + } +} diff --git a/compositor-plugins/gnome-shell/extension.js b/compositor-plugins/gnome-shell/extension.js new file mode 100644 index 0000000..6f8ef90 --- /dev/null +++ b/compositor-plugins/gnome-shell/extension.js @@ -0,0 +1,775 @@ +/* + * /tests/js/extension.js + * + * Copyright (C) 2019 Sam Spilsbury. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Shell extension which implements libanimation to provide nice window + * animations. + */ + +const Animation = imports.gi.Animation; +const AnimationClutter = imports.gi.AnimationClutter; +const Clutter = imports.gi.Clutter; +const Gio = imports.gi.Gio; +const GObject = imports.gi.GObject; +const Meta = imports.gi.Meta; +const Main = imports.ui.main; + +const settings = new Gio.Settings({ + schema: 'org.gnome.shell.extensions.animation' +}); + +const BoxWrapper = GObject.registerClass({ + Properties: { + 'box': GObject.ParamSpec.boxed('box', + 'AnimationBox', + 'AnimationBox to animate to (static)', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, + Animation.Box) + } +}, class BoxWrapper extends Animation.BoxQuery { + _init(props) { + super._init(props); + this.update(this.box); + } +}); + +const bindObjectPropsToSettings = (animation, props, settings) => { + Object.keys(props).forEach(key => { + settings.bind(props[key], + animation, + key, + Gio.SettingsBindFlags.GET) + }); + + return animation; +}; + +const ZoomAnimation = GObject.registerClass({ +}, class ZoomAnimation extends AnimationClutter.AffineEffect { + constructor(props) { + super(props); + this._enabledId = 0; + } + + _startAnimation(animation, done) { + this.transform_animation = animation; + this.enabled = true; + + this._enabledId = this.connect('notify::enabled', () => { + if (!this.enabled) { + done(); + this.disconnect(this._enabledId); + this._enabledId = 0; + } + }); + } + + activate(event, detail, done) { + let [x, y] = this.actor.get_position(); + let [width, height] = this.actor.get_size(); + + switch(event) { + case 'open': + this._startAnimation(new Animation.ZoomAnimation({ + source: new AnimationClutter.ActorBoxQuery({ + actor: this.actor + }), + target: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: detail.pointer[0], + y: detail.pointer[1] + }), + bottom_right: new Animation.Vector({ + x: detail.pointer[0], + y: detail.pointer[1] + }) + }) + }), + stepper: bindObjectPropsToSettings(new Animation.LinearStepper(), { + length: 'zoom-length' + }, settings) + }), done); + return true; + case 'close': + this._startAnimation(new Animation.ZoomAnimation({ + source: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: detail.pointer[0], + y: detail.pointer[1] + }), + bottom_right: new Animation.Vector({ + x: detail.pointer[0], + y: detail.pointer[1] + }) + }) + }), + target: new AnimationClutter.ActorBoxQuery({ + actor: this.actor + }), + stepper: new Animation.ReverseStepper({ + base_stepper: bindObjectPropsToSettings(new Animation.LinearStepper({}), { + length: 'zoom-length' + }, settings) + }) + }), done); + return true; + case 'minimize': + this._startAnimation(new Animation.ZoomAnimation({ + source: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: detail.icon.x, + y: detail.icon.y + }), + bottom_right: new Animation.Vector({ + x: detail.icon.x + detail.icon.width, + y: detail.icon.y + detail.icon.height + }) + }) + }), + target: new AnimationClutter.ActorBoxQuery({ + actor: this.actor + }), + stepper: new Animation.ReverseStepper({ + base_stepper: bindObjectPropsToSettings(new Animation.LinearStepper({}), { + length: 'zoom-length' + }, settings) + }) + }), done); + return true; + case 'unminimize': + this._startAnimation(new Animation.ZoomAnimation({ + target: new AnimationClutter.ActorBoxQuery({ + actor: this.actor + }), + source: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: detail.icon.x, + y: detail.icon.y + }), + bottom_right: new Animation.Vector({ + x: detail.icon.x + detail.icon.width, + y: detail.icon.y + detail.icon.height + }) + }) + }), + stepper: bindObjectPropsToSettings(new Animation.LinearStepper({}), { + length: 'zoom-length' + }, settings) + }), done); + return true; + default: + return false; + } + } + + remove() { + if (this.actor) { + this.actor.remove_effect(this); + } + + this.set_enabled(false); + } +}); + +const BounceAnimation = GObject.registerClass({ +}, class BounceAnimation extends AnimationClutter.AffineEffect { + constructor(params) { + super(params); + this._enabledId = 0; + } + + _startAnimation(animation, done) { + this.transform_animation = animation; + this.enabled = true; + + this._enabledId = this.connect('notify::enabled', () => { + if (!this.enabled) { + done(); + this.disconnect(this._enabledId); + this._enabledId = 0; + } + }); + } + + activate(event, detail, done) { + let [x, y] = this.actor.get_position(); + let [width, height] = this.actor.get_size(); + + switch (event) { + case 'open': + this._startAnimation(bindObjectPropsToSettings(new Animation.BounceAnimation({ + target: new AnimationClutter.ActorBoxQuery({ + actor: this.actor + }), + stepper: bindObjectPropsToSettings(new Animation.LinearStepper({}), { + length: 'bounce-length' + }, settings) + }), { + initial_scale: 'bounce-initial-scale', + maximum_scale: 'bounce-maximum-scale', + n_bounce: 'bounce-n-bounce', + }, settings), done); + return true; + case 'close': + this._startAnimation(bindObjectPropsToSettings(new Animation.BounceAnimation({ + target: new AnimationClutter.ActorBoxQuery({ + actor: this.actor + }), + stepper: new Animation.ReverseStepper({ + base_stepper: bindObjectPropsToSettings(new Animation.LinearStepper({}), { + length: 'bounce-length' + }, settings) + }) + }), { + initial_scale: 'bounce-initial-scale', + maximum_scale: 'bounce-maximum-scale', + n_bounce: 'bounce-n-bounce', + }, settings), done); + return true; + default: + return false; + } + } + + remove() { + if (this.actor) { + this.actor.remove_effect(this); + } + + this.set_enabled(false); + } +}); + + // A boune animation as attached to an actor. +const GlideAnimation = GObject.registerClass({ +}, class GlideAnimation extends AnimationClutter.AffineEffect { + constructor(params) { + super(params); + this._enabledId = 0; + } + + _startAnimation(animation, done) { + this.transform_animation = animation; + this.enabled = true; + + this._enabledId = this.connect('notify::enabled', () => { + if (!this.enabled) { + done(); + this.disconnect(this._enabledId); + this._enabledId = 0; + } + }); + } + + activate(event, detail, done) { + let [x, y] = this.actor.get_position(); + let [width, height] = this.actor.get_size(); + + switch (event) { + case 'open': + this._startAnimation(bindObjectPropsToSettings(new Animation.GlideAnimation({ + viewport: new AnimationClutter.ActorBoxQuery({ + actor: global.get_stage() + }), + target: new AnimationClutter.ActorBoxQuery({ + actor: this.actor + }), + stepper: bindObjectPropsToSettings(new Animation.LinearStepper({}), { + length: 'glide-length' + }, settings) + }), { + x_rotation_angle_degrees: 'glide-x-rotation-angle-degrees', + y_rotation_angle_degrees: 'glide-y-rotation-angle-degrees', + x_axis_location_unit: 'glide-x-axis-location-unit', + y_axis_location_unit: 'glide-y-axis-location-unit', + }, settings), done); + return true; + case 'close': + this._startAnimation(bindObjectPropsToSettings(new Animation.GlideAnimation({ + viewport: new AnimationClutter.ActorBoxQuery({ + actor: global.get_stage() + }), + target: new AnimationClutter.ActorBoxQuery({ + actor: this.actor + }), + stepper: new Animation.ReverseStepper({ + base_stepper: bindObjectPropsToSettings(new Animation.LinearStepper({}), { + length: 'glide-length' + }, settings) + }) + }), { + x_rotation_angle_degrees: 'glide-x-rotation-angle-degrees', + y_rotation_angle_degrees: 'glide-y-rotation-angle-degrees', + x_axis_location_unit: 'glide-x-axis-location-unit', + y_axis_location_unit: 'glide-y-axis-location-unit', + }, settings), done); + return true; + default: + return false; + } + } + + remove() { + if (this.actor) { + this.actor.remove_effect(this); + } + } +}); + +const MetaWindowOffsetActorBoxQuery = GObject.registerClass({ + Properties: { + 'actor': GObject.ParamSpec.object('actor', + 'ClutterActor', + 'The ClutterActor', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, + Clutter.Actor), + 'window': GObject.ParamSpec.object('window', + 'MetaWindow', + 'The MetaWindow', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, + Meta.Window) + } +}, class MetaWindowOffsetActorBoxQuery extends Animation.BoxQuery { + _init(params) { + super._init(params); + + this.actor.connect('notify::x', () => this._updateGeometry()); + this.actor.connect('notify::y', () => this._updateGeometry()); + this.actor.connect('notify::width', () => this._updateGeometry()); + this.actor.connect('notify::height', () => this._updateGeometry()); + + this.window.connect('position-changed', () => this._updateGeometry()); + this.window.connect('size-changed', () => this._updateGeometry()); + + this._updateGeometry(); + } + + _updateGeometry() { + let [x, y] = this.actor.get_position(); + let [width, height] = this.actor.get_size(); + let actorGeometry = this.actor.get_meta_window().get_frame_rect(); + + // Need to know the difference between the window size with shadows + // and without shadows in order to offset the window box correctly. + let offsetX = Math.ceil((width - actorGeometry.width) / 2); + let offsetY = Math.ceil((height - actorGeometry.height) / 2); + + let box = { + x1: x - offsetX, + y1: y - offsetY, + x2: x - offsetX + width, + y2: y - offsetY + height + }; + + this.update(new Animation.Box({ + top_left: new Animation.Vector({ x: box.x1, y: box.y1 }), + bottom_right: new Animation.Vector({ x: box.x2, y: box.y2 }) + })); + } +}); + +// MagicLampAnimation +// +// A magic lamp animation as attached to an actor. +const MagicLampAnimation = GObject.registerClass({ +}, class MagicLampAnimation extends AnimationClutter.GridEffect { + constructor(params) { + super(params); + this._enabledId = 0; + } + + _startAnimation(animation, done) { + this.grid_animation = animation; + this.x_tiles = 2; + this.y_tiles = 100; + this.enabled = true; + + this._enabledId = this.connect('notify::enabled', () => { + if (!this.enabled) { + done(); + this.disconnect(this._enabledId); + this._enabledId = 0; + } + }); + } + + activate(event, detail, done) { + let [x, y] = this.actor.get_position(); + let [width, height] = this.actor.get_size(); + let actorGeometry = this.actor.get_meta_window().get_frame_rect(); + + // Need to know the difference between the window size with shadows + // and without shadows in order to offset the window box correctly. + let offsetX = Math.ceil((width - actorGeometry.width) / 2); + let offsetY = Math.ceil((height - actorGeometry.height) / 2); + + let box = { + x1: x - offsetX, + y1: y - offsetY, + x2: x - offsetX + width, + y2: y - offsetY + height + }; + + switch (event) { + case 'minimize': + this._startAnimation(bindObjectPropsToSettings(new Animation.MagicLampAnimation({ + source: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: detail.icon.x, + y: detail.icon.y + }), + bottom_right: new Animation.Vector({ + x: detail.icon.x + detail.icon.width, + y: detail.icon.y + detail.icon.height + }) + }) + }), + target: new MetaWindowOffsetActorBoxQuery({ + actor: this.actor, + window: this.actor.get_meta_window() + }), + resolution: new Animation.Vector({ x: 2, y: 100 }), + stepper: new Animation.ReverseStepper({ + base_stepper: bindObjectPropsToSettings(new Animation.LinearStepper({}), { + length: 'magiclamp-length' + }, settings) + }) + }), { + bend_factor: 'magiclamp-bend-factor', + offset_factor: 'magiclamp-offset-factor', + stretch_factor: 'magiclamp-stretch-factor', + deform_speed_factor: 'magiclamp-deform-speed-factor', + }, settings), done); + return true; + case 'unminimize': + this._startAnimation(bindObjectPropsToSettings(new Animation.MagicLampAnimation({ + source: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: detail.icon.x, + y: detail.icon.y + }), + bottom_right: new Animation.Vector({ + x: detail.icon.x + detail.icon.width, + y: detail.icon.y + detail.icon.height + }) + }) + }), + target: new MetaWindowOffsetActorBoxQuery({ + actor: this.actor, + window: this.actor.get_meta_window() + }), + resolution: new Animation.Vector({ x: 2, y: 100 }), + stepper: bindObjectPropsToSettings(new Animation.LinearStepper({}), { + length: 'magiclamp-length' + }, settings) + }), { + bend_factor: 'magiclamp-bend-factor', + offset_factor: 'magiclamp-offset-factor', + stretch_factor: 'magiclamp-stretch-factor', + deform_speed_factor: 'magiclamp-deform-speed-factor', + }, settings), done); + return true; + default: + return false; + } + } + + remove() { + if (this.actor) { + this.actor.remove_effect(this); + } + + this.set_enabled(false); + } +}); + +const WobblyEffect = GObject.registerClass({ +}, class WobblyEffect extends AnimationClutter.WobblyEffect { + constructor(params) { + super(params); + bindObjectPropertiesToSettings(this, { + object_movement_range: 'wobbly-object-movement-range', + spring_k: 'wobbly-spring-k', + friction: 'wobbly-friction', + slowdown_factor: 'wobbly-slowdown-factor' + }, settings); + } + + activate(event, detail) { + switch (event) { + case 'move': + detail.grabbed ? this._grabbedByMouse() : this._ungrabbedByMouse(); + return true; + default: + return false; + } + } + + remove() { + if (this.actor) { + this.actor.remove_effect(this); + this.actor = null; + } + } + + _grabbedByMouse() { + let position = global.get_pointer(); + let actor = this.get_actor(); + this.grab(position[0], position[1]); + + this._lastPosition = actor.get_position(); + this._positionChangedId = + actor.metaWindow.connect('position-changed', (window) => { + let actor = window.get_compositor_private() + let position = actor.get_position(); + let dx = position[0] - this._lastPosition[0]; + let dy = position[1] - this._lastPosition[1]; + + this.move_by(dx, dy); + this._lastPosition = position; + }); + } + + _ungrabbedByMouse() { + // Only continue if we have an active grab and change notification + // on movement + if (!this._positionChangedId) + return; + + let actor = this.get_actor(); + this.ungrab(); + + actor.disconnect(this._positionChangedId); + this._positionChangedId = null; + } +}); + + +function init() { +} + +let connections = { +}; + +function disable() { + Object.keys(connections).forEach(k => { + let [signal, object] = connections[k]; + object.disconnect(signal); + delete connections[k]; + }); +} + +const ANIMATION_NAME_TO_CLASS = { + 'bounce': BounceAnimation, + 'zoom': ZoomAnimation, + 'magiclamp': MagicLampAnimation, + 'glide': GlideAnimation, +} + +const DEFAULT_EFFECTS = { + 'open': 'glide', + 'close': 'glide', + 'minimize': 'zoom', + 'unminimize': 'zoom', + 'move': 'none' +} + +function createAnimationForEvent(eventName) { + let effectName = settings.get_string(`${eventName}-effect`); + + if (effectName === 'default') { + effectName = DEFAULT_EFFECTS[eventName]; + } + + return [new ANIMATION_NAME_TO_CLASS[effectName]({}), effectName]; +} + +function enable() { + let wm = global.window_manager; + connections['minimize'] = [wm.connect_after('minimize', (shellwm, actor) => { + let [hasIcon, icon] = actor.meta_window.get_icon_geometry(); + + if (!hasIcon) + return; + + // Remove all existing tweens and minimize animations first first + if (Main.wm._removeEffect(Main.wm._minimizing, actor)) { + actor.remove_all_transitions(); + actor.set_pivot_point(0, 0); + actor.scale_y = 1; + actor.scale_x = 1; + actor.translation_y = 0; + actor.translation_x = 0; + actor.opacity = 255; + } + + // Use libanimation to run an animation + let [animation, effectName] = createAnimationForEvent('minimize'); + actor.add_effect_with_name(`animation::${effectName}`, animation); + animation.activate('minimize', { + // Guaranteed to work, since the minimize effect is + // only available if this window has an icon + icon: icon, + }, () => { + shellwm.completed_minimize(actor); + actor.remove_effect(animation); + }); + }), wm]; + connections['unminimize'] = [wm.connect_after('unminimize', (shellwm, actor) => { + let [hasIcon, icon] = actor.meta_window.get_icon_geometry(); + + if (!hasIcon) + return; + + // Remove all existing tweens and minimize animations first first + if (Main.wm._removeEffect(Main.wm._unminimizing, actor)) { + actor.remove_all_transitions(); + actor.set_pivot_point(0, 0); + actor.scale_y = 1; + actor.scale_x = 1; + actor.translation_y = 0; + actor.translation_x = 0; + actor.opacity = 255; + + // Also undo actor translation and return to frame rect + let rect = actor.meta_window.get_frame_rect(); + actor.set_position(rect.x, rect.y); + } + + // Use libanimation to run an animation + let [animation, effectName] = createAnimationForEvent('unminimize'); + actor.add_effect_with_name(`animation::${effectName}`, animation); + animation.activate('unminimize', { + // Guaranteed to work, since the minimize effect is + // only available if this window has an icon + icon: icon, + }, () => { + shellwm.completed_unminimize(actor); + actor.remove_effect(animation); + }); + }), wm]; + connections['map'] = [wm.connect_after('map', (shellwm, actor) => { + let types = [Meta.WindowType.NORMAL, + Meta.WindowType.DIALOG, + Meta.WindowType.MODAL_DIALOG]; + if (!Main.wm._shouldAnimateActor(actor, types) || actor._windowType != Meta.WindowType.NORMAL) { + // We weren't going to animate this actor anyway + return; + } + + // Remove all existing tweens and open animations first + if (Main.wm._removeEffect(Main.wm._mapping, actor)) { + actor.remove_all_transitions(); + actor.set_pivot_point(0, 0); + actor.scale_y = 1; + actor.scale_x = 1; + actor.translation_y = 0; + actor.translation_x = 0; + actor.opacity = 0; + } + + // Use libanimation to run an animation + let [animation, effectName] = createAnimationForEvent('open'); + actor.add_effect_with_name(`animation::${effectName}`, animation); + animation.activate('open', {}, () => { + shellwm.completed_map(actor); + actor.remove_effect(animation); + }); + }), wm]; + connections['destroy'] = [wm.connect_after('destroy', (shellwm, actor) => { + let types = [Meta.WindowType.NORMAL, + Meta.WindowType.DIALOG, + Meta.WindowType.MODAL_DIALOG]; + if (!Main.wm._shouldAnimateActor(actor, types) || + (actor._windowType && actor._windowType != Meta.WindowType.NORMAL)) { + // We weren't going to animate this actor anyway + return; + } + + // Remove all existing tweens and destroy animations first + if (Main.wm._removeEffect(Main.wm._destroying, actor)) { + actor.remove_all_transitions(); + actor.set_pivot_point(0, 0); + actor.scale_y = 1; + actor.scale_x = 1; + actor.translation_y = 0; + actor.translation_x = 0; + actor.opacity = 255; + } + + // Use libanimation to run an animation + let [animation, effectName] = createAnimationForEvent('close'); + actor.add_effect_with_name(`animation::${effectName}`, animation); + animation.activate('close', {}, () => { + shellwm.completed_destroy(actor); + // Can't remove the effect since the actor is already gone + }); + }), wm]; + + connections['grab-op-begin'] = [global.display.connect('grab-op-begin', (display, screen, window, op) => { + // Occassionally, window can be null, in cases where grab-op-begin + // was emitted on a window from shell-toolkit. Ignore these grabs. + if (!window) + return; + + if (window.is_override_redirect() || + op != Meta.GrabOp.MOVING) + return; + + // Right now we only have the wobbly effect for moving windows, + // so we just check directly if we were enabled. + if (settings.get_string('move-effect') !== 'wobbly') + return; + + let actor = window.get_compositor_private(); + + log(`Grab ${actor.x} ${actor.y} ${actor.width} ${actor.height}`); + + if (!actor._wobblyEffect) { + actor._wobblyEffect = new WobblyEffect({}); + actor.add_effect_with_name('animation::wobbly', actor._wobblyEffect); + } + + actor._wobblyEffect.activate('move', { + grabbed: true + }); + }), global.display]; + + connections['grab-op-end'] = [global.display.connect('grab-op-end', (display, screen, window, op) => { + // Occassionally, window can be null, in cases where grab-op-begin + // was emitted on a window from shell-toolkit. Ignore these grabs. + if (!window) + return; + + let actor = window.get_compositor_private(); + + if (!actor._wobblyEffect) { + return; + } + + actor._wobblyEffect.activate('move', { + grabbed: false + }); + }), global.display]; +} diff --git a/compositor-plugins/gnome-shell/meson.build b/compositor-plugins/gnome-shell/meson.build new file mode 100644 index 0000000..6f285a3 --- /dev/null +++ b/compositor-plugins/gnome-shell/meson.build @@ -0,0 +1,44 @@ +# /compositor-plugins/gnome-shell/meson.build +# +# Install the GNOME-Shell extension. +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +datadir = get_option('datadir') + +extension_name = 'animation@endlessm.com' + +extension_files = files([ + 'extension.js', + 'metadata.json', + 'stylesheet.css' +]) + +schemas_files = files([ + 'org.gnome.shell.extensions.animation.gschema.xml' +]) + +install_data( + schemas_files, + install_dir: join_paths(datadir, 'glib-2.0', 'schemas') +) + +install_data( + extension_files, + install_dir: join_paths(datadir, 'gnome-shell', 'extensions', extension_name) +) + diff --git a/compositor-plugins/gnome-shell/metadata.json b/compositor-plugins/gnome-shell/metadata.json new file mode 100644 index 0000000..9f7ea28 --- /dev/null +++ b/compositor-plugins/gnome-shell/metadata.json @@ -0,0 +1,6 @@ +{ + "name": "Animations", + "description": "Use libanimation to provide nice window animations", + "uuid": "animation@endlessm.com", + "shell-version": ["3.32.2"] +} diff --git a/compositor-plugins/gnome-shell/org.gnome.shell.extensions.animation.gschema.xml b/compositor-plugins/gnome-shell/org.gnome.shell.extensions.animation.gschema.xml new file mode 100644 index 0000000..fae1354 --- /dev/null +++ b/compositor-plugins/gnome-shell/org.gnome.shell.extensions.animation.gschema.xml @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 'default' + + Open Window Effect + + + Animation to use when opening a window. + + + + 'default' + + Close Window Effect + + + Animation to use when closing a window. + + + + 'default' + + Minimize Window Effect + + + Animation to use when minimize a window. + + + + 'default' + + Unminimize Window Effect + + + Animation to use when unminimize a window. + + + + 'default' + + Move Window Effect + + + Animation to use when moving a window. + + + + + 200 + + Glide animation length + + + How long the glide animation lasts + + + + + -0.3 + + Initial distance + + + Initial distance away from the camera + + + + + -20.0 + + X Rotation Angle + + + Angle to rotate around the x-axis + + + + + 0.0 + + Y Rotation Angle + + + Angle to rotate around the y-axis + + + + + 0.2 + + X Axis Location + + + Unit co-ordinates for X axis location + + + + + 200 + + Magic Lamp animation length + + + How long the Magic Lamp animation lasts + + + + + 10.0 + + Bend Factor + + + How much to bend the window during the animation + + + + + 0.5 + + Offset Factor + + + How much the waves curve during the animation + + + + + 0.45 + + Stretch Factor + + + How much the window stretches during the animation + + + + + 2.3 + + Deform Speed Factor + + + How quickly to deform the window during the animation + + + + + 0.5 + + Y Axis Location + + + Unit co-ordinates for Y axis location + + + + + 200 + + Zoom animation length + + + How long the zoom animation lasts + + + + + 200 + + Bounce animation length + + + How long the bounce animation lasts + + + + + 0.7 + + Initial scale + + + Initial scale of the surface being bounced + + + + + 1.3 + + Maximum scale + + + Maximum scale of the surface being bounced + + + + + 2 + + Number of Bounces + + + How many times the window oscillates from its original position + + + + + 8.0 + + Wobbly effect Spring Constant + + + Springiness of wobbly effect + + + + + 3.0 + + Wobbly effect Friction + + + Friction of wobbly effect + + + + + 1.0 + + Wobbly effect slowdown factor + + + Slowdown factor of wobbly effect (1.0 being normal speed) + + + + + 100.0 + + Wobbly effect object movement range + + + How much objects are allowed to move in the mesh. A higher range + allows for a more pronounced effect. + + + + diff --git a/compositor-plugins/gnome-shell/stylesheet.css b/compositor-plugins/gnome-shell/stylesheet.css new file mode 100644 index 0000000..9a2a332 --- /dev/null +++ b/compositor-plugins/gnome-shell/stylesheet.css @@ -0,0 +1,9 @@ + +.helloworld-label { + font-size: 36px; + font-weight: bold; + color: #ffffff; + background-color: rgba(10,10,10,0.7); + border-radius: 5px; + padding: .5em; +} diff --git a/compositor-plugins/meson.build b/compositor-plugins/meson.build new file mode 100644 index 0000000..02c22a6 --- /dev/null +++ b/compositor-plugins/meson.build @@ -0,0 +1,22 @@ +# /compositor-plugins/meson.build +# +# Install compositor plugins. +# +# Copyright (C) 2017, 2018 Endless Mobile, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +subdir('gnome-shell') + diff --git a/debian/control b/debian/control index cd1086b..46e3444 100644 --- a/debian/control +++ b/debian/control @@ -10,6 +10,7 @@ Build-Depends: debhelper (>= 8.0.0), libgirepository1.0-dev, libglib2.0-dev, libgtest-dev, + libmutter-4-dev, meson Standards-Version: 3.9.4 Section: libs @@ -51,7 +52,7 @@ Description: 2D Surfaces Animations (GLib API) headers Replaces: libwobbly-glib-dev (<= 0.3.2) Package: gir1.2-animation-glib-0 -Section: non-free/libs +Section: libs Architecture: any Depends: ${gir:Depends}, ${misc:Depends}, @@ -60,3 +61,40 @@ Depends: ${gir:Depends}, Description: 2D Surfaces Animations library (GObject Introspection) files 2D Surfaces Animations library (GObject Introspection) files Replaces: gir1.2-libwobbly-glib0 (<= 0.3.2) + +Package: libanimation-clutter0 +Section: libs +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends}, + libanimation-glib0 (= ${binary:Version}) +Description: 2D Surfaces Animations library (Clutter Implementation API) + 2D Surfaces Animations library + +Package: libanimation-clutter-dev +Section: libs +Architecture: any +Depends: libanimation-clutter0 (= ${binary:Version}), +Description: 2D Surfaces Animations (Clutter Implementation API) headers + 2D Surfaces Animations (Clutter Implementation API) headers + +Package: gir1.2-animation-clutter-0 +Section: libs +Architecture: any +Depends: ${gir:Depends}, + ${misc:Depends}, + ${shlibs:Depends}, + libanimation-clutter0 (= ${binary:Version}) +Description: 2D Surfaces Animations library, Clutter Implementation (GObject Introspection) files + 2D Surfaces Animations library, Clutter Implementation (GObject Introspection) files + +Package: gnome-shell-extension-animation +Section: libs +Architecture: any +Depends: ${gir:Depends}, + ${misc:Depends}, + ${shlibs:Depends}, + libanimation-clutter0 (= ${binary:Version}), + gir1.2-animation-clutter-0, + gnome-shell +Description: GNOME-Shell extension using 2D Surface Animation Library to provide window animations. diff --git a/debian/gir1.2-animation-clutter-0.install b/debian/gir1.2-animation-clutter-0.install new file mode 100644 index 0000000..d3811bc --- /dev/null +++ b/debian/gir1.2-animation-clutter-0.install @@ -0,0 +1 @@ +usr/lib/*/girepository-1.0/AnimationClutter-0.typelib diff --git a/debian/gnome-shell-extension-animation.install b/debian/gnome-shell-extension-animation.install new file mode 100644 index 0000000..abb1dd6 --- /dev/null +++ b/debian/gnome-shell-extension-animation.install @@ -0,0 +1,5 @@ +usr/share/glib-2.0/schemas/org.gnome.shell.extensions.animation.gschema.xml +usr/share/gnome-shell/extensions/animation@endlessm.com/extension.js +usr/share/gnome-shell/extensions/animation@endlessm.com/metadata.json +usr/share/gnome-shell/extensions/animation@endlessm.com/stylesheet.css + diff --git a/debian/libanimation-clutter-dev.install b/debian/libanimation-clutter-dev.install new file mode 100644 index 0000000..b182e31 --- /dev/null +++ b/debian/libanimation-clutter-dev.install @@ -0,0 +1,4 @@ +usr/include/animation-clutter/* +usr/lib/*/libanimation-clutter.so +usr/lib/*/pkgconfig/libanimation-clutter-0.pc +usr/share/gir-1.0/AnimationClutter-0.gir diff --git a/debian/libanimation-clutter0.install b/debian/libanimation-clutter0.install new file mode 100644 index 0000000..70887f7 --- /dev/null +++ b/debian/libanimation-clutter0.install @@ -0,0 +1 @@ +usr/lib/*/libanimation-clutter.so.* diff --git a/matchers/mathematical_model_matcher.h b/matchers/mathematical_model_matcher.h index f3f74e1..01fc3f9 100644 --- a/matchers/mathematical_model_matcher.h +++ b/matchers/mathematical_model_matcher.h @@ -445,6 +445,59 @@ namespace animation namespace t = ::testing; namespace agd = ::animation::geometry::dimension; + template + struct AlmostEqTrait + { + typedef T ValueType; + + template + static bool apply (T const &lhs, + T const &rhs, + Comparator &&comparator) + { + return comparator (lhs, rhs); + } + }; + + template + class AlmostEqMatcher : + public t::MatcherInterface + { + public: + + AlmostEqMatcher (T const &value, float tolerance) : + value (value), + tolerance (tolerance) + { + } + + bool MatchAndExplain (T const &x, + t::MatchResultListener *listener) const + { + using namespace std::placeholders; + auto func = animation::testing::close_at_tolerance ::ValueType, typename AlmostEqTrait ::ValueType, typename AlmostEqTrait ::ValueType>; + return AlmostEqTrait ::apply (x, + value, + std::bind (func, _1, _2, tolerance)); + } + + void DescribeTo (std::ostream *os) const + { + *os << "almost equal to " << value << " (at tolerance " << tolerance << ")"; + } + + private: + + T value; + float tolerance; + }; + + template + t::Matcher AlmostEq (T const &value, float tolerance) + { + return t::MakeMatcher (new AlmostEqMatcher (value, tolerance)); + } + template class GeometricallyEqualMatcher : public t::MatcherInterface diff --git a/meson.build b/meson.build index aeb4227..e4044d5 100644 --- a/meson.build +++ b/meson.build @@ -18,11 +18,11 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -project('libanimation', 'cpp', +project('libanimation', ['c', 'cpp'], version: '0.0.0', default_options : ['cpp_std=c++17'], license: 'LGPL2+', - meson_version: '>= 0.40.0') + meson_version: '>= 0.42.0') gtest_dep = dependency('gtest', required: false) gtest_main_dep = dependency('gtest', main: true, required: false) @@ -36,9 +36,12 @@ if not gtest_dep.found() or not gtest_main_dep.found() or not gmock_dep.found() endif animation_inc = include_directories('.') +glm_inc = include_directories(join_paths('animation', 'third_party', 'glm')) tests_inc = include_directories('tests') subdir('animation') subdir('animation-glib') +subdir('animation-clutter') +subdir('compositor-plugins') subdir('matchers') subdir('tests') diff --git a/tests/bounce/bounce_animation_test.cpp b/tests/bounce/bounce_animation_test.cpp new file mode 100644 index 0000000..b75425b --- /dev/null +++ b/tests/bounce/bounce_animation_test.cpp @@ -0,0 +1,217 @@ +/* + * tests/animation/bounce_animation_test.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Tests for the "bounce" animation. + */ +#include // for size_t +#include // for bind, __bind, _1 + +#include +#include +#include + +#include // for AtLeast +#include // for FunctionMocker, etc +#include // for AnythingMatcher, etc +#include // for EXPECT_CALL, etc +#include // for TEST_F, Test, Types, etc + +#include +#include +#include +#include +#include + +#include + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Eq; +using ::testing::Test; + +namespace ab = animation::bounce; +namespace abc = animation::box_calculation; +namespace agd = animation::geometry::dimension; +namespace as = animation::stepper; + +namespace +{ + template + decltype(auto) BQ (animation::Box &&box) + { + return animation::MakeStaticBoxQuery (std::move (box)); + } + + template + decltype(auto) BQ (animation::Box const &box) + { + return animation::MakeStaticBoxQuery (box); + } + + TEST (BounceAnimation, AnimationIncompleteBeforeLengthTimesteps) + { + ab::BounceAnimation anim (0.7f, + 1.5f, + 1, + BQ (animation::Box (animation::Point (0, 0), + animation::Point (100, 100))), + as::Linear (200)); + + EXPECT_TRUE (anim.Step (199)); + } + + TEST (BounceAnimation, AnimationCompleteAtLengthTimesteps) + { + ab::BounceAnimation anim (0.7f, + 1.5f, + 1, + BQ (animation::Box (animation::Point (0, 0), + animation::Point (100, 100))), + as::Linear (200)); + + EXPECT_FALSE (anim.Step (200)); + } + + TEST (BounceAnimation, AnimationStopsAtTargetBox) + { + auto target = animation::Box (animation::Point (100, 100), + animation::Point (200, 200)); + ab::BounceAnimation anim (0.7f, + 1.5f, + 1, + BQ (target), + as::Linear (200)); + + anim.Step (200); + + /* Apply to a shape at (0, 0) which is translated to (100, 100) */ + glm::mat4 translation (glm::translate (glm::mat4 (1.0), glm::vec3 (100, 100, 0))); + glm::vec4 tl (0, 0, 0, 1); + glm::vec4 br (100, 100, 0, 1); + + /* Apply transformation matrix to vectors */ + glm::mat4 transformation (glm::make_mat4x4 (anim.Matrix ())); + + /* Should finish at target box */ + EXPECT_THAT (translation * transformation * tl, + Eq (glm::vec4 (agd::get <0> (target.topLeft ()), + agd::get <1> (target.topLeft ()), + 0, + 1))); + EXPECT_THAT (translation * transformation * br, + Eq (glm::vec4 (agd::get <0> (target.bottomRight ()), + agd::get <1> (target.bottomRight ()), + 0, + 1))); + } + + TEST (BounceAnimation, AnimationStartsAtTargetBoxScaledByInitialScale) + { + auto target = animation::Box (animation::Point (100, 100), + animation::Point (200, 200)); + auto width = 100.0f; + auto height = 100.0f; + auto initialScale = 0.5f; + ab::BounceAnimation anim (initialScale, + 1.5f, + 1, + BQ (target), + as::Linear (200)); + + animation::Point boxCenter (abc::ComputeBoxCenter (target)); + + animation::Box scaledBox ( + animation::Point (agd::get <0> (boxCenter) - (width / 2.0f) * initialScale, + agd::get <1> (boxCenter) - (height / 2.0f) * initialScale), + animation::Point (agd::get <0> (boxCenter) + (width / 2.0f) * initialScale, + agd::get <1> (boxCenter) + (height / 2.0f) * initialScale) + ); + + /* Apply to a shape at (0, 0) which is translated to (100, 100) */ + glm::mat4 translation (glm::translate (glm::mat4 (1.0), glm::vec3 (100, 100, 0))); + glm::vec4 tl (0, 0, 0, 1); + glm::vec4 br (100, 100, 0, 1); + + /* Apply transformation matrix to vectors */ + glm::mat4 transformation (glm::make_mat4x4 (anim.Matrix ())); + + /* Should finish at target box */ + EXPECT_THAT (translation * transformation * tl, + Eq (glm::vec4 (agd::get <0> (scaledBox.topLeft ()), + agd::get <1> (scaledBox.topLeft ()), + 0, + 1))); + EXPECT_THAT (translation * transformation * br, + Eq (glm::vec4 (agd::get <0> (scaledBox.bottomRight ()), + agd::get <1> (scaledBox.bottomRight ()), + 0, + 1))); + } + + TEST (BounceAnimation, AtHalfwayPointBounceIsAtHighestAttenuatedScale) + { + auto target = animation::Box (animation::Point (100, 100), + animation::Point (200, 200)); + auto width = 100.0f; + auto height = 100.0f; + auto initialScale = 0.5f; + auto maximumScale = 1.5f; + auto progress = 0.5; + auto scaleFloor = (1.0f - initialScale) * progress; + + /* At 0.5f progress, we are at the top of the sine wave, so + * add that to the scale floor. */ + auto expectedScale = scaleFloor + (maximumScale - initialScale); + ab::BounceAnimation anim (initialScale, + maximumScale, + 1, + BQ (target), + as::Linear (200)); + anim.Step (100); + + animation::Point boxCenter (abc::ComputeBoxCenter (target)); + + animation::Box scaledBox ( + animation::Point (agd::get <0> (boxCenter) - (width / 2.0f) * expectedScale, + agd::get <1> (boxCenter) - (height / 2.0f) * expectedScale), + animation::Point (agd::get <0> (boxCenter) + (width / 2.0f) * expectedScale, + agd::get <1> (boxCenter) + (height / 2.0f) * expectedScale) + ); + + /* Apply to a shape at (0, 0) which is translated to (100, 100) */ + glm::mat4 translation (glm::translate (glm::mat4 (1.0), glm::vec3 (100, 100, 0))); + glm::vec4 tl (0, 0, 0, 1); + glm::vec4 br (100, 100, 0, 1); + + /* Apply transformation matrix to vectors */ + glm::mat4 transformation (glm::make_mat4x4 (anim.Matrix ())); + + /* Should finish at target box */ + EXPECT_THAT (translation * transformation * tl, + Eq (glm::vec4 (agd::get <0> (scaledBox.topLeft ()), + agd::get <1> (scaledBox.topLeft ()), + 0, + 1))); + EXPECT_THAT (translation * transformation * br, + Eq (glm::vec4 (agd::get <0> (scaledBox.bottomRight ()), + agd::get <1> (scaledBox.bottomRight ()), + 0, + 1))); + } +} diff --git a/tests/glide/glide_animation_test.cpp b/tests/glide/glide_animation_test.cpp new file mode 100644 index 0000000..c809929 --- /dev/null +++ b/tests/glide/glide_animation_test.cpp @@ -0,0 +1,198 @@ +/* + * tests/wobbly/glide_animation_test.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Tests for the "glide" animation. + */ +#include // for size_t +#include // for bind, __bind, _1 + +#include +#include +#include + +#include // for AtLeast +#include // for FunctionMocker, etc +#include // for AnythingMatcher, etc +#include // for EXPECT_CALL, etc +#include // for TEST_F, Test, Types, etc + +#include +#include +#include + +#include +#include + +#include + +using ::animation::matchers::AlmostEq; +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Eq; +using ::testing::Not; +using ::testing::Test; + +namespace agd = animation::geometry::dimension; +namespace ag = animation::glide; +namespace as = animation::stepper; + +namespace animation +{ + namespace matchers + { + template <> + struct AlmostEqTrait + { + typedef float ValueType; + + template + static bool apply (glm::vec4 const &lhs, + glm::vec4 const &rhs, + Comparator &&comparator) + { + return comparator (lhs[0], rhs[0]) && + comparator (lhs[1], rhs[1]) && + comparator (lhs[2], rhs[2]) && + comparator (lhs[3], rhs[3]); + } + }; + } +} + +namespace +{ + template + decltype(auto) BQ (animation::Box &&box) + { + return animation::MakeStaticBoxQuery (std::move (box)); + } + + template + decltype(auto) BQ (animation::Box const &box) + { + return animation::MakeStaticBoxQuery (box); + } + + static const animation::Box MockScreenGeometry = animation::Box ( + animation::Point(0, 0), + animation::Point(1000, 100) + ); + + TEST (GlideAnimation, AnimationIncompleteBeforeLengthTimesteps) + { + ag::GlideAnimation anim (0.5f, + 0.0f, + 20.0f, + 0.5f, + 0.0f, + BQ (MockScreenGeometry), + BQ (animation::Box (animation::Point (0, 0), + animation::Point (100, 100))), + as::Linear (200)); + + EXPECT_TRUE (anim.Step (199)); + } + + TEST (GlideAnimation, AnimationCompleteAtLengthTimesteps) + { + ag::GlideAnimation anim (0.5f, + 0.0f, + 20.0f, + 0.5f, + 0.0f, + BQ (MockScreenGeometry), + BQ (animation::Box (animation::Point (0, 0), + animation::Point (100, 100))), + as::Linear (200)); + + EXPECT_FALSE (anim.Step (200)); + } + + TEST (GlideAnimation, AnimationStopsAtTargetBox) + { + auto target = animation::Box (animation::Point (100, 100), + animation::Point (200, 200)); + ag::GlideAnimation anim (0.5f, + 0.0f, + 20.0f, + 0.5f, + 0.0f, + BQ (MockScreenGeometry), + BQ (target), + as::Linear (200)); + + anim.Step (200); + + /* Apply to a shape at (0, 0) which is translated to (100, 100) */ + glm::mat4 translation (glm::translate (glm::mat4 (1.0), glm::vec3 (100, 100, 0))); + glm::vec4 tl (translation * glm::vec4 (0, 0, 0, 1)); + glm::vec4 br (translation * glm::vec4 (100, 100, 0, 1)); + + /* Apply transformation matrix to vectors */ + glm::mat4 transformation (glm::make_mat4x4 (anim.Matrix ())); + + /* Should finish at target box */ + EXPECT_THAT (transformation * tl, + AlmostEq (glm::vec4 (agd::get <0> (target.topLeft ()), + agd::get <1> (target.topLeft ()), + 0, + 1), + 0.002)); + EXPECT_THAT (transformation * br, + AlmostEq (glm::vec4 (agd::get <0> (target.bottomRight ()), + agd::get <1> (target.bottomRight ()), + 0, + 1), + 0.002)); + } + + TEST (GlideAnimation, AnimationDoesNotStartAtTargetBox) + { + auto target = animation::Box (animation::Point (100, 100), + animation::Point (200, 200)); + ag::GlideAnimation anim (0.5f, + 0.0f, + 20.0f, + 0.5f, + 0.0f, + BQ (MockScreenGeometry), + BQ (target), + as::Linear (200)); + + /* Apply to a shape at (0, 0) which is translated to (100, 100) */ + glm::mat4 translation (glm::translate (glm::mat4 (1.0), glm::vec3 (100, 100, 0))); + glm::vec4 tl (translation * glm::vec4 (0, 0, 0, 1)); + glm::vec4 br (translation * glm::vec4 (100, 100, 0, 1)); + + /* Apply transformation matrix to vectors */ + glm::mat4 transformation (glm::make_mat4x4 (anim.Matrix ())); + + /* Should finish at target box */ + EXPECT_THAT (transformation * tl, + Not (Eq (glm::vec4 (agd::get <0> (target.topLeft ()), + agd::get <1> (target.topLeft ()), + 0, + 1)))); + EXPECT_THAT (transformation * br, + Not (Eq (glm::vec4 (agd::get <0> (target.bottomRight ()), + agd::get <1> (target.bottomRight ()), + 0, + 1)))); + } +} diff --git a/tests/glm_ostream_operators.h b/tests/glm_ostream_operators.h new file mode 100644 index 0000000..c3e7832 --- /dev/null +++ b/tests/glm_ostream_operators.h @@ -0,0 +1,66 @@ +/* + * tests/glm_ostream_operators.h + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * A simple helper to output the contents of glm types when + * tests fail. + */ +#pragma once + +#include +#include // for operator<<, setprecision +#include // for ostream +#include // for basic_ostream, char_traits, etc + +namespace +{ + namespace detail + { + template + struct ArrayDimensionPrinter + { + static std::ostream & Apply (std::ostream &lhs, + T const &array) + { + ArrayDimensionPrinter ::Apply (lhs, array); + return lhs << ", " << array[D]; + } + }; + + template + struct ArrayDimensionPrinter + { + static std::ostream & Apply (std::ostream &lhs, + T const &array) + { + return lhs << array[0]; + } + }; + } +} + +namespace glm +{ + inline std::ostream & operator<< (std::ostream &lhs, + glm::vec4 const &vector) + { + lhs << "vec4("; + ::detail::ArrayDimensionPrinter ::Apply (lhs, vector); + return lhs << ")"; + } +} diff --git a/tests/js/common.js b/tests/js/common.js new file mode 100644 index 0000000..597c5d3 --- /dev/null +++ b/tests/js/common.js @@ -0,0 +1,52 @@ +/* + * /tests/js/common.js + * + * Helper functions for javascript tests for animations. + * + * Copyright (C) 2019 Sam Spilsbury. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +const { Animation, GLib, GObject } = imports.gi; + +var BoxWrapper = GObject.registerClass({ + Properties: { + 'box': GObject.ParamSpec.boxed('box', + 'AnimationBox', + 'AnimationBox to animate to (static)', + GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, + Animation.Box) + } +}, class BoxWrapper extends Animation.BoxQuery { + _init(props) { + super._init(props); + this.update(this.box); + } +}); + +function multiplyMatrixVector(mat, vec) { + return [ + mat[0] * vec[0] + mat[4] * vec[1] + mat[8] * vec[2] + mat[12] * vec[3], + mat[1] * vec[0] + mat[5] * vec[1] + mat[9] * vec[2] + mat[13] * vec[3], + mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2] + mat[14] * vec[3], + mat[3] * vec[0] + mat[7] * vec[1] + mat[11] * vec[2] + mat[15] * vec[3] + ] +} + +function applyCallerTranslation(vector, x, y) { + return [vector[0] + x, vector[1] + y, 0, 1]; +} + diff --git a/tests/js/meson.build b/tests/js/meson.build new file mode 100644 index 0000000..5f3b021 --- /dev/null +++ b/tests/js/meson.build @@ -0,0 +1,51 @@ +# tests/js/meson.build +# +# Meson build file for the libanimation library javascript tests. +# +# Copyright (C) 2019 Sam Spilsbury +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +javascript_tests = [ + 'testBounceAnimation.js', + 'testGlideAnimation.js', + 'testMagicLampAnimation.js', + 'testStepper.js', + 'testZoomAnimation.js' +] + +gjs = find_program('gjs', required: false) +jasmine = find_program('jasmine', required: false) +include_path = '@0@:@1@'.format(meson.source_root(), meson.build_root()) +built_library_path = join_paths(meson.build_root(), 'animation-glib') + +tests_environment = environment() +tests_environment.set('GJS_PATH', include_path) +tests_environment.prepend('GI_TYPELIB_PATH', built_library_path) +tests_environment.prepend('LD_LIBRARY_PATH', built_library_path) +tests_environment.prepend('DYLD_LIBRARY_PATH', built_library_path) + +if gjs.found() and jasmine.found() + foreach test_file : javascript_tests + test(test_file, + gjs, + args: [ + jasmine.path(), + '--verbose', + join_paths(meson.current_source_dir(), test_file) + ], + env: tests_environment) + endforeach +endif diff --git a/tests/js/testBounceAnimation.js b/tests/js/testBounceAnimation.js new file mode 100644 index 0000000..dc752d6 --- /dev/null +++ b/tests/js/testBounceAnimation.js @@ -0,0 +1,70 @@ +/* + * /tests/js/testBounceAnimation.js + * + * Copyright (C) 2019 Sam Spilsbury . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Tests for the JavaScript Binding to the Bounce Animation. + */ + +const { Animation } = imports.gi; +const { BoxWrapper, applyCallerTranslation, multiplyMatrixVector } = imports.common; + +describe('Animation', function() { + describe('BounceAnimation', function() { + let animation; + + beforeEach(function() { + animation = new Animation.BounceAnimation({ + initial_scale: 0.7, + maximum_scale: 1.5, + n_bounce: 1, + target: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: 0, + y: 0, + }), + bottom_right: new Animation.Vector({ + x: 100, + y: 100, + }) + }), + }), + stepper: new Animation.LinearStepper({ + length: 200 + }), + }); + }); + + it('has properties that were set on construction', function() { + expect(animation.initial_scale).toBeCloseTo(0.7); + expect(animation.maximum_scale).toBeCloseTo(1.5); + expect(animation.n_bounce).toEqual(1); + expect(animation.stepper.length).toEqual(200); + }); + + it('stops at the target box', function() { + animation.step(200); + + expect(multiplyMatrixVector(animation.matrix(), [0, 0, 0, 1])).toEqual([0, 0, 0, 1]); + }); + + it('starts with the target scale', function() { + expect(multiplyMatrixVector(animation.matrix(), [0, 0, 0, 1])).toEqual([15, 15, 0, 1]); + }); + }); +}); diff --git a/tests/js/testGlideAnimation.js b/tests/js/testGlideAnimation.js new file mode 100644 index 0000000..253d000 --- /dev/null +++ b/tests/js/testGlideAnimation.js @@ -0,0 +1,93 @@ +/* + * /tests/js/testGlideAnimation.js + * + * Tests for the JavaScript Binding to the Glide Animation. + * + * Copyright (C) 2019 Sam Spilsbury. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +const { Animation } = imports.gi; +const { BoxWrapper, applyCallerTranslation, multiplyMatrixVector } = imports.common; + +describe('Animation', function() { + describe('GlideAnimation', function() { + let animation; + + beforeEach(function() { + animation = new Animation.GlideAnimation({ + initial_distance: 0.5, + x_rotation_angle_degrees: 0.0, + y_rotation_angle_degrees: 20.0, + x_axis_location_unit: 0.5, + y_axis_location_unit: 0.0, + viewport: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ x: 0, y: 0 }), + bottom_right: new Animation.Vector({ x: 1000, y: 100 }) + }) + }), + target: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: 0, + y: 0, + }), + bottom_right: new Animation.Vector({ + x: 100, + y: 100, + }), + }), + }), + stepper: new Animation.LinearStepper({ + length: 200, + }) + }); + }); + + it('has expected properties set on construction', function() { + expect(animation.initial_distance).toEqual(0.5); + expect(animation.x_rotation_angle_degrees).toEqual(0.0); + expect(animation.y_rotation_angle_degrees).toEqual(20.0); + expect(animation.x_axis_location_unit).toEqual(0.5); + expect(animation.y_axis_location_unit).toEqual(0.0); + expect(animation.stepper.length).toEqual(200); + }); + + it('animates to correct position after length timesteps', function() { + expect(animation.step(200)).toBeFalsy(); + + // Note that the animation is applied taking the translation the caller + // would have already applied into account. Since the size does not change, + // at the end of the animation we are in the same position as the caller + // would be. + let target = [100, 100, 0, 1] + multiplyMatrixVector(animation.matrix(), [100, 100, 0, 1]).forEach(function(x, i) { + expect(x).toBeCloseTo(target[i]); + }); + }); + + it('starts in the correct position', function() { + expect(animation.step(200)).toBeFalsy(); + + // Note that the animation is applied taking the translation the caller + // would have already applied into account. Since the size does not change, + // at the end of the animation we are in the same position as the caller + // would be. + expect(multiplyMatrixVector(animation.matrix(), [100, 100, 0, 1])).not.toEqual([100, 100, 0, 1]); + }); + }); +}); diff --git a/tests/js/testMagicLampAnimation.js b/tests/js/testMagicLampAnimation.js new file mode 100644 index 0000000..949e7d2 --- /dev/null +++ b/tests/js/testMagicLampAnimation.js @@ -0,0 +1,107 @@ +/* + * /tests/js/testMagicLampAnimation.js + * + * Tests for the JavaScript Binding to the Zoom Animation. + * + * Copyright (C) 2019 Sam Spilsbury. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +const { Animation } = imports.gi; +const { BoxWrapper } = imports.common; + +describe('Animation', function() { + describe('MagicLampAnimation', function() { + let animation; + let bendFactor = 10; + let offsetFactor = 0.5; + let stretchFactor = 0.45; + let deformationSpeedFactor = 2.3; + let resolution = new Animation.Vector({ x: 4, y: 4 }); + let source = new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: 100, + y: 100, + }), + bottom_right: new Animation.Vector({ + x: 150, + y: 150, + }), + }), + }); + let target = new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: 0, + y: 0, + }), + bottom_right: new Animation.Vector({ + x: 100, + y: 100, + }), + }), + }); + + beforeEach(function() { + animation = new Animation.MagicLampAnimation({ + source: source, + target: target, + resolution: resolution, + bend_factor: bendFactor, + offset_factor: offsetFactor, + stretch_factor: stretchFactor, + deform_speed_factor: deformationSpeedFactor, + stepper: new Animation.LinearStepper({ + length: 200, + }), + }); + }); + + it('has expected properties set on construction', function() { + expect(animation.resolution.x).toEqual(4); + expect(animation.resolution.y).toEqual(4); + expect(animation.bend_factor).toBeCloseTo(bendFactor); + expect(animation.stretch_factor).toBeCloseTo(stretchFactor); + expect(animation.deform_speed_factor).toBeCloseTo(deformationSpeedFactor); + expect(animation.stepper.length).toEqual(200); + }); + + it('animates to correct position after length timesteps', function() { + expect(animation.step(200)).toBeFalsy(); + + let corners = [ + new Animation.Vector({ x: 0, y: 0 }), + new Animation.Vector({ x: 100, y: 0 }), + new Animation.Vector({ x: 0, y: 100 }), + new Animation.Vector({ x: 100, y: 100 }), + ]; + let extremes = animation.extremes (corners); + + expect(extremes[0].x).toBeCloseTo(0); + expect(extremes[0].y).toBeCloseTo(0); + + expect(extremes[1].x).toBeCloseTo(100); + expect(extremes[1].y).toBeCloseTo(0); + + expect(extremes[2].x).toBeCloseTo(0); + expect(extremes[2].y).toBeCloseTo(100); + + expect(extremes[3].x).toBeCloseTo(100); + expect(extremes[3].y).toBeCloseTo(100); + }); + }); +}); diff --git a/tests/js/testStepper.js b/tests/js/testStepper.js new file mode 100644 index 0000000..ccd496b --- /dev/null +++ b/tests/js/testStepper.js @@ -0,0 +1,48 @@ +/* + * /tests/js/testStepper.js + * + * Copyright (C) 2019 Sam Spilsbury . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Tests for the JavaScript Binding to the Stepper. + */ + +const { Animation } = imports.gi; +const { BoxWrapper, applyCallerTranslation, multiplyMatrixVector } = imports.common; + +describe('Animation', function() { + describe('LinearStepper', function() { + let stepper; + + beforeEach(function() { + stepper = new Animation.LinearStepper({ + length: 200 + }); + }); + + it('has properties that were set on construction', function() { + expect(stepper.length).toEqual(200); + }); + + it('stops at 1.0 on the last step', function() { + expect(stepper.step(200)).toEqual(1.0); + }); + + it('behaves linearly', function() { + expect(stepper.step(100)).toEqual(0.5); + }); + }); +}); diff --git a/tests/js/testZoomAnimation.js b/tests/js/testZoomAnimation.js new file mode 100644 index 0000000..b253927 --- /dev/null +++ b/tests/js/testZoomAnimation.js @@ -0,0 +1,130 @@ +/* + * /tests/js/testZoomAnimation.js + * + * Tests for the JavaScript Binding to the Zoom Animation. + * + * Copyright (C) 2019 Sam Spilsbury. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +const { Animation } = imports.gi; +const { BoxWrapper, applyCallerTranslation, multiplyMatrixVector } = imports.common; + +describe('Animation', function() { + describe('ZoomAnimation', function() { + let animation; + + beforeEach(function() { + animation = new Animation.ZoomAnimation({ + source: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: 100, + y: 100, + }), + bottom_right: new Animation.Vector({ + x: 200, + y: 200, + }), + }), + }), + target: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: 200, + y: 200, + }), + bottom_right: new Animation.Vector({ + x: 300, + y: 300, + }), + }), + }), + stepper: new Animation.LinearStepper({ + length: 10, + }) + }); + }); + + it('has expected properties set on construction', function() { + expect(animation.stepper.length).toEqual(10); + }); + + it('animates to correct position after length timesteps', function() { + expect(animation.step(10)).toBeFalsy(); + + // Note that the animation is applied taking the translation the caller + // would have already applied into account. Since the size does not change, + // at the end of the animation we are in the same position as the caller + // would be. + expect(multiplyMatrixVector(animation.matrix(), [200, 200, 0, 1])).toEqual([200, 200, 0, 1]); + }); + + describe('when scaling', function() { + beforeEach(function() { + animation = new Animation.ZoomAnimation({ + source: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: 100, + y: 100, + }), + bottom_right: new Animation.Vector({ + x: 150, + y: 150, + }), + }), + }), + target: new BoxWrapper({ + box: new Animation.Box({ + top_left: new Animation.Vector({ + x: 200, + y: 200, + }), + bottom_right: new Animation.Vector({ + x: 300, + y: 300, + }), + }), + }), + stepper: new Animation.LinearStepper({ + length: 10, + }) + }); + }); + + it('animates and scales to correct position after length timesteps', function() { + expect(animation.step(10)).toBeFalsy(); + + // Take into account caller translation and size (we translated to (200, 200), (250, 250)), + // meaning that and the end of the animation, nothing changes. + expect(multiplyMatrixVector(animation.matrix(), [250, 250, 0, 1])).toEqual([250, 250, 0, 1]); + }); + + it('is scaled down at the beginning of the animation', function() { + // We need to scale down at the beginning of the animation, given that the + // caller size is already 100x100 + expect(applyCallerTranslation(multiplyMatrixVector(animation.matrix(), [0, 0, 0, 1]), 200, 200)).toEqual([100, 100, 0, 1]); + expect(applyCallerTranslation(multiplyMatrixVector(animation.matrix(), [100, 100, 0, 1]), 200, 200)).toEqual([150, 150, 0, 1]); + }); + }); + + it('starts in the correct position', function() { + // Take into account caller translation, we need to apply a negative offset + expect(multiplyMatrixVector(animation.matrix(), [200, 200, 0, 1])).toEqual([100, 100, 0, 1]); + }); + }); +}); diff --git a/tests/magiclamp/magiclamp_animation_test.cpp b/tests/magiclamp/magiclamp_animation_test.cpp new file mode 100644 index 0000000..24e69ee --- /dev/null +++ b/tests/magiclamp/magiclamp_animation_test.cpp @@ -0,0 +1,251 @@ +/* + * tests/wobbly/magiclamp_animation_test.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Tests for the "magic lamp" animation. + */ +#include // for size_t +#include // for bind, __bind, _1 + +#include +#include +#include + +#include // for AtLeast +#include // for FunctionMocker, etc +#include // for AnythingMatcher, etc +#include // for EXPECT_CALL, etc +#include // for TEST_F, Test, Types, etc + +#include +#include +#include + +#include +#include +#include +#include +#include + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Eq; +using ::testing::Test; + +namespace am = animation::matchers; +namespace aml = animation::magiclamp; +namespace as = animation::stepper; + +namespace animation +{ + namespace matchers + { + template <> + struct AlmostEqTrait + { + typedef double ValueType; + + template + static bool apply (animation::Point const &lhs, + animation::Point const &rhs, + Comparator &&comparator) + { + return comparator (agd::get <0> (lhs), agd::get <0> (rhs)) && + comparator (agd::get <1> (lhs), agd::get <1> (rhs)); + } + }; + } +} + +namespace +{ + template + decltype(auto) BQ (animation::Box &&box) + { + return animation::MakeStaticBoxQuery (std::move (box)); + } + + template + decltype(auto) BQ (animation::Box const &box) + { + return animation::MakeStaticBoxQuery (box); + } + + static animation::geometry::PointModel const Resolution (4, 4); + static double const BendFactor = 10; + static double const OffsetFactor = 0.5; + static double const StretchFactor = 0.45; + static double const DeformationSpeedFactor = 2.3; + + TEST (MagicLampAnimation, AnimationIncompleteBeforeLengthTimesteps) + { + aml::MagicLampAnimation anim (BQ (animation::Box (animation::Point (100, 100), + animation::Point (150, 150))), + BQ (animation::Box (animation::Point (0, 0), + animation::Point (100, 100))), + Resolution, + BendFactor, + OffsetFactor, + StretchFactor, + DeformationSpeedFactor, + as::Linear (200)); + + EXPECT_TRUE (anim.Step (199)); + } + + TEST (MagicLampAnimation, AnimationCompleteAfterLengthTimesteps) + { + aml::MagicLampAnimation anim (BQ (animation::Box (animation::Point (100, 100), + animation::Point (150, 150))), + BQ (animation::Box (animation::Point (0, 0), + animation::Point (100, 100))), + Resolution, + BendFactor, + OffsetFactor, + StretchFactor, + DeformationSpeedFactor, + as::Linear (200)); + + EXPECT_FALSE (anim.Step (200)); + } + + TEST (MagicLampAnimation, StartsAtCorrectPosition) + { + aml::MagicLampAnimation anim (BQ (animation::Box (animation::Point (100, 100), + animation::Point (150, 150))), + BQ (animation::Box (animation::Point (0, 0), + animation::Point (100, 100))), + Resolution, + BendFactor, + OffsetFactor, + StretchFactor, + DeformationSpeedFactor, + as::Linear (200)); + + std::array corners = {{ + animation::Point (0, 0), + animation::Point (100, 0), + animation::Point (0, 100), + animation::Point (100, 100) + }}; + std::array extremes = anim.Extremes (corners); + + EXPECT_THAT (extremes[0], am::Eq (animation::Vector4D (100, 100, 0, 1))); + EXPECT_THAT (extremes[1], am::Eq (animation::Vector4D (150, 100, 0, 1))); + EXPECT_THAT (extremes[2], am::Eq (animation::Vector4D (100, 150, 0, 1))); + EXPECT_THAT (extremes[3], am::Eq (animation::Vector4D (150, 150, 0, 1))); + } + + TEST (MagicLampAnimation, CorrectLinearInterpolatedVertexDeformation) + { + std::vector clientGrid; + clientGrid.reserve(128); + + double const x = 0; + double const y = 0; + double const width = 100; + double const height = 100; + + double const iconX = 100; + double const iconY = 100; + double const iconWidth = 50; + double const iconHeight = 50; + + aml::MagicLampAnimation anim (BQ (animation::Box (animation::Point (iconX, iconY), + animation::Point (iconX + iconWidth, iconY + iconHeight))), + BQ (animation::Box (animation::Point (x, y), + animation::Point (width, height))), + Resolution, + BendFactor, + OffsetFactor, + StretchFactor, + DeformationSpeedFactor, + as::Linear (200)); + + size_t const gridWidth = 4; + size_t const gridHeight = 100; + + for (size_t i = 0; i < gridWidth; ++i) + { + for (size_t j = 0; j < gridHeight; ++j) + { + EXPECT_THAT (anim.DeformUVToModelSpace (animation::Point (i / static_cast (gridWidth - 1), + j / static_cast (gridHeight - 1))), + am::AlmostEq (animation::Point (iconX + (iconWidth / (gridWidth - 1)) * i, + iconY + (iconHeight / (gridHeight - 1)) * j), + 0.002)); + } + } + } + + TEST (MagicLampAnimation, AnimatesToCorrectPositionAfterLengthTimesteps) + { + aml::MagicLampAnimation anim (BQ (animation::Box (animation::Point (100, 100), + animation::Point (150, 150))), + BQ (animation::Box (animation::Point (0, 0), + animation::Point (100, 100))), + Resolution, + BendFactor, + OffsetFactor, + StretchFactor, + DeformationSpeedFactor, + as::Linear (200)); + anim.Step (200); + + std::array corners = {{ + animation::Point (0, 0), + animation::Point (100, 0), + animation::Point (0, 100), + animation::Point (100, 100) + }}; + std::array extremes = anim.Extremes (corners); + + EXPECT_THAT (extremes[0], am::Eq (animation::Vector4D (0, 0, 0, 1))); + EXPECT_THAT (extremes[1], am::Eq (animation::Vector4D (100, 0, 0, 1))); + EXPECT_THAT (extremes[2], am::Eq (animation::Vector4D (0, 100, 0, 1))); + EXPECT_THAT (extremes[3], am::Eq (animation::Vector4D (100, 100, 0, 1))); + } + + TEST (MagicLampAnimation, AnimatesToCorrectPositionAfterLengthTimestepsReverse) + { + aml::MagicLampAnimation anim (BQ (animation::Box (animation::Point (100, 100), + animation::Point (150, 150))), + BQ (animation::Box (animation::Point (0, 0), + animation::Point (100, 100))), + Resolution, + BendFactor, + OffsetFactor, + StretchFactor, + DeformationSpeedFactor, + as::Reverse (as::Linear (200))); + anim.Step (200); + + std::array corners = {{ + animation::Point (0, 0), + animation::Point (100, 0), + animation::Point (0, 100), + animation::Point (100, 100) + }}; + std::array extremes = anim.Extremes (corners); + + EXPECT_THAT (extremes[0], am::Eq (animation::Vector4D (100, 100, 0, 1))); + EXPECT_THAT (extremes[1], am::Eq (animation::Vector4D (150, 100, 0, 1))); + EXPECT_THAT (extremes[2], am::Eq (animation::Vector4D (100, 150, 0, 1))); + EXPECT_THAT (extremes[3], am::Eq (animation::Vector4D (150, 150, 0, 1))); + } +} diff --git a/tests/meson.build b/tests/meson.build index 9d59aa9..33fbeb3 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,4 +1,4 @@ -# /matchers/meson.build +# tests/meson.build # # Copyright (C) 2017, 2018 Endless Mobile, Inc. # @@ -18,8 +18,14 @@ # # Build the libanimation unit tests. +subdir('js') + animation_test_sources = [ + 'bounce/bounce_animation_test.cpp', + 'glide/glide_animation_test.cpp', + 'glm_ostream_operators.h', 'ostream_point_operator.h', + 'magiclamp/magiclamp_animation_test.cpp', 'wobbly/anchor_test.cpp', 'wobbly/constrainment_test.cpp', 'wobbly/euler_integration_test.cpp', @@ -27,7 +33,8 @@ animation_test_sources = [ 'wobbly/mesh_interpolation_test.cpp', 'wobbly/model_test.cpp', 'wobbly/point_test.cpp', - 'wobbly/spring_test.cpp' + 'wobbly/spring_test.cpp', + 'zoom/zoom_animation_test.cpp' ] glib = dependency('glib-2.0') @@ -47,7 +54,7 @@ animation_test_executable = executable( animation_dep, animation_glib_dep ], - include_directories: [ tests_inc ] + include_directories: [ glm_inc, tests_inc ] ) test('animation_test', animation_test_executable) diff --git a/tests/ostream_point_operator.h b/tests/ostream_point_operator.h index a2cac5b..9705f2f 100644 --- a/tests/ostream_point_operator.h +++ b/tests/ostream_point_operator.h @@ -54,5 +54,21 @@ namespace animation agd::assign (point, p); return lhs << point; } + + inline std::ostream & + operator<< (std::ostream &lhs, Vector4D const &p) + { + namespace agd = animation::geometry::dimension; + + return lhs << std::setprecision (10) + << "x: " + << agd::get <0> (p) + << " y: " + << agd::get <1> (p) + << " z: " + << agd::get <2> (p) + << " w: " + << agd::get <3> (p); + } } } diff --git a/tests/wobbly/mesh_interpolation_test.cpp b/tests/wobbly/mesh_interpolation_test.cpp index f839e20..8f7803b 100644 --- a/tests/wobbly/mesh_interpolation_test.cpp +++ b/tests/wobbly/mesh_interpolation_test.cpp @@ -206,6 +206,17 @@ namespace WithTolerance (10e-5))); } + TEST_F (BezierMesh, Performance) + { + double x = 0; + for (int i = 0; i < 100000000; ++i) { + auto const p = mesh.DeformUnitCoordsToMeshSpace (animation::Point(i / 100000000.0, i / 100000.0)); + x += agd::get <0> (p); + } + + EXPECT_EQ (x, 1.0); + } + /* Its somewhat difficult to test the non-linear case, considering * that the bezier patch evaluation is actually an interpolation * between four different parabolic functions. For now just check diff --git a/tests/zoom/zoom_animation_test.cpp b/tests/zoom/zoom_animation_test.cpp new file mode 100644 index 0000000..9ed8161 --- /dev/null +++ b/tests/zoom/zoom_animation_test.cpp @@ -0,0 +1,175 @@ +/* + * tests/wobbly/zoom_animation_test.cpp + * + * Copyright 2018 Endless Mobile, Inc. + * + * libanimation is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * libanimation is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with eos-companion-app-service. If not, see + * . + * + * Tests for the "zoom" animation. + */ +#include // for size_t +#include // for bind, __bind, _1 +#include + +#include +#include +#include + +#include // for AtLeast +#include // for FunctionMocker, etc +#include // for AnythingMatcher, etc +#include // for EXPECT_CALL, etc +#include // for TEST_F, Test, Types, etc + +#include +#include +#include + +#include +#include +#include +#include +#include + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Eq; +using ::testing::Test; + +namespace am = animation::matchers; +namespace as = animation::stepper; +namespace az = animation::zoom; + +namespace +{ + template + decltype(auto) BQ (animation::Box &&box) + { + return animation::MakeStaticBoxQuery (std::move (box)); + } + + TEST (ZoomAnimation, AnimationIncompleteBeforeLengthTimesteps) + { + az::ZoomAnimation anim (BQ (animation::Box (animation::Point (0, 0), + animation::Point (100, 100))), + BQ (animation::Box (animation::Point (100, 100), + animation::Point (200, 200))), + as::Linear (200)); + + EXPECT_TRUE (anim.Step (199)); + } + + TEST (ZoomAnimation, AnimationCompleteAfterLengthTimesteps) + { + az::ZoomAnimation anim (BQ (animation::Box (animation::Point (0, 0), + animation::Point (100, 100))), + BQ (animation::Box (animation::Point (100, 100), + animation::Point (200, 200))), + as::Linear (200)); + + EXPECT_FALSE (anim.Step (200)); + } + + TEST (ZoomAnimation, AnimatesToCorrectPositionAfterLengthTimesteps) + { + az::ZoomAnimation anim (BQ (animation::Box (animation::Point (100, 100), + animation::Point (200, 200))), + BQ (animation::Box (animation::Point (200, 200), + animation::Point (300, 300))), + as::Linear (200)); + anim.Step (200); + + /* Apply to a shape at (0, 0) */ + glm::vec4 tl (0, 0, 0, 1); + glm::vec4 br (100, 100, 0, 1); + + /* Apply transformation matrix to vectors, with existing + * translation to scene position */ + glm::mat4 translation (glm::translate (glm::mat4 (1.0), glm::vec3 (200, 200, 0))); + glm::mat4 transformation (glm::make_mat4x4 (anim.Matrix ())); + + /* Should be about halfway */ + EXPECT_THAT (translation * transformation * tl, Eq (translation * tl)); + EXPECT_THAT (translation * transformation * br, Eq (translation * br)); + } + + TEST (ZoomAnimation, AnimatesToCorrectReversePositionAfterLengthTimesteps) + { + az::ZoomAnimation anim (BQ (animation::Box (animation::Point (100, 100), + animation::Point (150, 150))), + BQ (animation::Box (animation::Point (200, 200), + animation::Point (300, 300))), + as::Reverse (as::Linear (200))); + anim.Step (200); + + /* Apply to a shape at (0, 0) */ + glm::vec4 tl (0, 0, 0, 1); + glm::vec4 br (100, 100, 0, 1); + + /* Apply transformation matrix to vectors, with existing + * translation to scene position */ + glm::mat4 translation (glm::translate (glm::mat4 (1.0), glm::vec3 (200, 200, 0))); + glm::mat4 transformation (glm::make_mat4x4 (anim.Matrix ())); + + EXPECT_THAT (translation * transformation * tl, Eq (glm::vec4 (100, 100, 0, 1))); + EXPECT_THAT (translation * transformation * br, Eq (glm::vec4 (150, 150, 0, 1))); + } + + TEST (ZoomAnimation, StartsAtCorrectPositionAtZeroTimesteps) + { + az::ZoomAnimation anim (BQ (animation::Box (animation::Point (100, 100), + animation::Point (200, 200))), + BQ (animation::Box (animation::Point (200, 200), + animation::Point (300, 300))), + as::Linear (200)); + + /* Apply to a shape at (0, 0) */ + glm::vec4 tl (0, 0, 0, 1); + glm::vec4 br (100, 100, 0, 1); + + /* Apply transformation matrix to vectors, with existing + * translation to scene position */ + glm::mat4 translation (glm::translate (glm::mat4 (1.0), glm::vec3 (200, 200, 0))); + glm::mat4 transformation (glm::make_mat4x4 (anim.Matrix ())); + + /* Should be about halfway */ + EXPECT_THAT (translation * transformation * tl, Eq (glm::vec4 (100, 100, 0, 1))); + EXPECT_THAT (translation * transformation * br, Eq (glm::vec4 (200, 200, 0, 1))); + } + + TEST (ZoomAnimation, AtCorrectPositionAfterHalfwayPoint) + { + az::ZoomAnimation anim (BQ (animation::Box (animation::Point (100, 100), + animation::Point (200, 200))), + BQ (animation::Box (animation::Point (200, 200), + animation::Point (300, 300))), + as::Linear (200)); + + anim.Step (100); + + /* Apply to a shape at (0, 0) */ + glm::vec4 tl (0, 0, 0, 1); + glm::vec4 br (100, 100, 0, 1); + + /* Apply transformation matrix to vectors, with existing + * translation to scene position */ + glm::mat4 translation (glm::translate (glm::mat4 (1.0), glm::vec3 (200, 200, 0))); + glm::mat4 transformation (glm::make_mat4x4 (anim.Matrix ())); + + /* Should be about halfway */ + EXPECT_THAT (translation * transformation * tl, Eq (glm::vec4 (150, 150, 0, 1))); + EXPECT_THAT (translation * transformation * br, Eq (glm::vec4 (250, 250, 0, 1))); + } +}