From 649951728a1d00e3267318ccf47a45dc479966d3 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Thu, 23 Aug 2018 06:25:51 +0800 Subject: [PATCH 01/19] geometry: Add operator== for point types If we know that two points are going to be of the same type, it is convenient to be able to compare them with the == operator as opposed to having to use agd::equals --- animation/geometry_traits.h | 5 +++++ 1 file changed, 5 insertions(+) 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); + } } } From 978e5d06658bf26fffec46f9d7f8663256f61c14 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 5 Aug 2018 08:27:45 +0800 Subject: [PATCH 02/19] animation: Add box type The "box" type represents a simple perpendicular quadrilateral in 2D space given by its top left and bottom right co-ordinates (eg, x1, y1, x2, y2). --- animation-glib/box.cpp | 40 ++++++++++++++++++++++++++++++++++++++ animation-glib/box.h | 39 +++++++++++++++++++++++++++++++++++++ animation-glib/meson.build | 10 ++++++++-- 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 animation-glib/box.cpp create mode 100644 animation-glib/box.h 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/meson.build b/animation-glib/meson.build index fb997bb..50b5fc5 100644 --- a/animation-glib/meson.build +++ b/animation-glib/meson.build @@ -20,8 +20,14 @@ api_version = '0' -animation_glib_toplevel_headers = ['vector.h'] -animation_glib_toplevel_introspectable_sources = ['vector.cpp'] +animation_glib_toplevel_headers = [ + 'box.h', + 'vector.h' +] +animation_glib_toplevel_introspectable_sources = [ + 'box.cpp', + 'vector.cpp' +] animation_glib_introspectable_sources = [] animation_glib_private_sources = [] From 3399640325b98c00d37c3cf4b81190f889e78e88 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Wed, 22 Aug 2018 12:04:49 +0800 Subject: [PATCH 03/19] tests: Add ostream operator for Vector4D This is necessary in order to use it with some of the testing operators (like agd::Equals or agd::AlmostEq), since those testing operators use operator<< in order to print out the contents of the type in the event of a matching failure. --- tests/ostream_point_operator.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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); + } } } From b518947b6256460a529b99ae361de10f31261aa5 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sat, 4 Aug 2018 23:22:18 +0800 Subject: [PATCH 04/19] build: Add header-dependency on glm We're going to do affine transformations and glm is a particularly well respected library for things like that. Note that the dependency is a submodule, so you'll need to fetch and pull submodules before building. --- .gitmodules | 3 +++ animation/meson.build | 2 +- animation/third_party/glm | 1 + meson.build | 1 + tests/meson.build | 2 +- 5 files changed, 7 insertions(+), 2 deletions(-) create mode 160000 animation/third_party/glm 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/animation/meson.build b/animation/meson.build index 0a7d758..78df2a8 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -31,7 +31,7 @@ animation_lib = shared_library( animation_sources, soversion: api_version, install: true, - include_directories: [ animation_inc ] + include_directories: [ animation_inc, glm_inc ] ) animation_dep = declare_dependency( 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/meson.build b/meson.build index aeb4227..dd3cb05 100644 --- a/meson.build +++ b/meson.build @@ -36,6 +36,7 @@ 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') diff --git a/tests/meson.build b/tests/meson.build index 9d59aa9..3811d9e 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -47,7 +47,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) From 3f0138023cdb1ead1e488077db039c6d9db77591 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Tue, 21 Aug 2018 12:33:46 +0800 Subject: [PATCH 05/19] animation: Add concept of steppers Steppers encapsulate some progress change within the animation. The typical case is linear stepping, which just interpolates from 0 to 1. However, another case is reverse stepping which is composed by 1.0f - linear (ms). Future steppers can be added to add all sorts of easing parameters. https://phabricator.endlessm.com/T23543 --- animation-glib/meson.build | 19 +- animation-glib/stepper/linear.cpp | 151 ++++++++++++++++ animation-glib/stepper/linear.h | 36 ++++ animation-glib/stepper/meson.build | 42 +++++ animation-glib/stepper/reverse.cpp | 173 ++++++++++++++++++ animation-glib/stepper/reverse.h | 36 ++++ animation-glib/stepper/stepper-holder.cpp | 196 +++++++++++++++++++++ animation-glib/stepper/stepper-holder.h | 36 ++++ animation-glib/stepper/stepper-wrapper.cpp | 160 +++++++++++++++++ animation-glib/stepper/stepper-wrapper.h | 36 ++++ animation-glib/stepper/stepper.cpp | 49 ++++++ animation-glib/stepper/stepper.h | 44 +++++ animation/meson.build | 1 + animation/stepper/linear.h | 41 +++++ animation/stepper/meson.build | 31 ++++ animation/stepper/reverse.h | 42 +++++ animation/stepper/stepper.h | 35 ++++ 17 files changed, 1119 insertions(+), 9 deletions(-) create mode 100644 animation-glib/stepper/linear.cpp create mode 100644 animation-glib/stepper/linear.h create mode 100644 animation-glib/stepper/meson.build create mode 100644 animation-glib/stepper/reverse.cpp create mode 100644 animation-glib/stepper/reverse.h create mode 100644 animation-glib/stepper/stepper-holder.cpp create mode 100644 animation-glib/stepper/stepper-holder.h create mode 100644 animation-glib/stepper/stepper-wrapper.cpp create mode 100644 animation-glib/stepper/stepper-wrapper.h create mode 100644 animation-glib/stepper/stepper.cpp create mode 100644 animation-glib/stepper/stepper.h create mode 100644 animation/stepper/linear.h create mode 100644 animation/stepper/meson.build create mode 100644 animation/stepper/reverse.h create mode 100644 animation/stepper/stepper.h diff --git a/animation-glib/meson.build b/animation-glib/meson.build index 50b5fc5..0d7f4ba 100644 --- a/animation-glib/meson.build +++ b/animation-glib/meson.build @@ -20,24 +20,25 @@ api_version = '0' -animation_glib_toplevel_headers = [ +animation_glib_toplevel_headers = files([ 'box.h', 'vector.h' -] -animation_glib_toplevel_introspectable_sources = [ +]) +animation_glib_toplevel_introspectable_sources = files([ 'box.cpp', 'vector.cpp' -] +]) -animation_glib_introspectable_sources = [] -animation_glib_private_sources = [] -animation_glib_headers = [] +animation_glib_introspectable_sources = files([]) +animation_glib_private_sources = files([]) +animation_glib_headers = files([]) animation_glib_headers_subdir = 'animation-glib' +subdir('stepper') subdir('wobbly') -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_headers += animation_glib_toplevel_headers install_headers(animation_glib_toplevel_headers, subdir: animation_glib_headers_subdir) diff --git a/animation-glib/stepper/linear.cpp b/animation-glib/stepper/linear.cpp new file mode 100644 index 0000000..a2411b0 --- /dev/null +++ b/animation-glib/stepper/linear.cpp @@ -0,0 +1,151 @@ +/* + * 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 + +namespace as = animation::stepper; + +struct _AnimationLinearStepper +{ + GObject parent_instance; +}; + +typedef struct _AnimationLinearStepperPrivate +{ +} AnimationLinearStepperPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (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) +{ + switch (prop_id) + { + case PROP_LENGTH: + 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) +{ + switch (prop_id) + { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static GObject * +animation_linear_stepper_constructor (GType type, + unsigned int n_construct_params, + GObjectConstructParam *construct_params) +{ + const char * const wanted_properties[] = { + "length", + NULL + }; + g_autoptr(GHashTable) properties_table = + static_hash_table_of_values_for_specs (wanted_properties, + construct_params, + n_construct_params); + + auto stepper = as::Linear (ForwardFromValueHT (properties_table, g_value_get_uint, "length")); + + replace_named_pointer_prop_in_construct_params (construct_params, + n_construct_params, + "stepper", + &stepper); + + return G_OBJECT_CLASS (animation_linear_stepper_parent_class)->constructor (type, + n_construct_params, + construct_params); +} + +static void +animation_linear_stepper_init (AnimationLinearStepper *model) +{ +} + +static void +animation_linear_stepper_class_init (AnimationLinearStepperClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = animation_linear_stepper_constructor; + 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_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + 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..8eb30b1 --- /dev/null +++ b/animation-glib/stepper/meson.build @@ -0,0 +1,42 @@ +# /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-wrapper.cpp' +]) + +stepper_headers = files([ + 'linear.h', + 'reverse.h', + 'stepper.h', + 'stepper-holder.h', + 'stepper-wrapper.h' +]) + +animation_glib_introspectable_sources += stepper_introspectable_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..30f7a4f --- /dev/null +++ b/animation-glib/stepper/reverse.cpp @@ -0,0 +1,173 @@ +/* + * 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 + +namespace as = animation::stepper; + +struct _AnimationReverseStepper +{ + GObject parent_instance; +}; + +typedef struct _AnimationReverseStepperPrivate +{ + AnimationStepper *base_stepper; +} AnimationReverseStepperPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (AnimationReverseStepper, + animation_reverse_stepper, + ANIMATION_TYPE_STEPPER_HOLDER) + +enum { + PROP_0, + PROP_BASE_STEPPER, + NPROPS +}; + +static GParamSpec *animation_reverse_stepper_properties[NPROPS]; + +static void +animation_reverse_stepper_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationReverseStepper *stepper = ANIMATION_REVERSE_STEPPER (object); + AnimationReverseStepperPrivate *priv = + reinterpret_cast (animation_reverse_stepper_get_instance_private (stepper)); + + switch (prop_id) + { + case PROP_BASE_STEPPER: + priv->base_stepper = ANIMATION_STEPPER (g_value_dup_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) +{ + AnimationReverseStepper *stepper = ANIMATION_REVERSE_STEPPER (object); + AnimationReverseStepperPrivate *priv = + reinterpret_cast (animation_reverse_stepper_get_instance_private (stepper)); + + switch (prop_id) + { + case PROP_BASE_STEPPER: + g_value_set_object (value, G_OBJECT (priv->base_stepper)); + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_reverse_stepper_dispose (GObject *object) +{ + AnimationReverseStepper *stepper = ANIMATION_REVERSE_STEPPER (object); + AnimationReverseStepperPrivate *priv = + reinterpret_cast (animation_reverse_stepper_get_instance_private (stepper)); + + g_clear_object (&priv->base_stepper); +} + +static GObject * +animation_reverse_stepper_constructor (GType type, + unsigned int n_construct_params, + GObjectConstructParam *construct_params) +{ + const char * const wanted_properties[] = { + "base-stepper", + NULL + }; + g_autoptr(GHashTable) properties_table = + static_hash_table_of_values_for_specs (wanted_properties, + construct_params, + n_construct_params); + + auto stepper = as::Reverse (ForwardFromValueHT (properties_table, + animation_stepper_from_gvalue, + "base-stepper")); + + replace_named_pointer_prop_in_construct_params (construct_params, + n_construct_params, + "stepper", + &stepper); + + return G_OBJECT_CLASS (animation_reverse_stepper_parent_class)->constructor (type, + n_construct_params, + construct_params); +} + +static void +animation_reverse_stepper_init (AnimationReverseStepper *model) +{ +} + +static void +animation_reverse_stepper_class_init (AnimationReverseStepperClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = animation_reverse_stepper_constructor; + 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.cpp b/animation-glib/stepper/stepper-holder.cpp new file mode 100644 index 0000000..d51db15 --- /dev/null +++ b/animation-glib/stepper/stepper-holder.cpp @@ -0,0 +1,196 @@ +/* + * 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 *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)) + +enum { + PROP_0, + PROP_STEPPER, + NPROPS +}; + +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); +} + +template +static T * +copy_ptr_if_set (T *ptr) +{ + if (ptr != nullptr) + return new T (*ptr); + + return nullptr; +} + +static void +animation_stepper_holder_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + AnimationStepperHolder *holder = ANIMATION_STEPPER_HOLDER (object); + AnimationStepperHolderPrivate *priv = + reinterpret_cast (animation_stepper_holder_get_instance_private (holder)); + + switch (prop_id) + { + case PROP_STEPPER: + /* We need to copy-construct here as the property is + * not transfer full. */ + priv->stepper = copy_ptr_if_set (reinterpret_cast (g_value_get_pointer (value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +animation_stepper_holder_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + AnimationStepperHolder *holder = ANIMATION_STEPPER_HOLDER (object); + AnimationStepperHolderPrivate *priv = + reinterpret_cast (animation_stepper_holder_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_holder_finalize (GObject *object) +{ + AnimationStepperHolder *holder = ANIMATION_STEPPER_HOLDER (object); + AnimationStepperHolderPrivate *priv = + reinterpret_cast (animation_stepper_holder_get_instance_private (holder)); + + delete priv->stepper; + priv->stepper = nullptr; + + G_OBJECT_CLASS (animation_stepper_holder_parent_class)->finalize (object); +} + +static GObject * +animation_stepper_holder_constructor (GType type, + unsigned int n_construct_params, + GObjectConstructParam *construct_params) +{ + auto defaultStepperHolder = as::Linear (300); + + /* Check the passed stepper_holder prop to ensure that it is set. If not, + * then we need to set it to some sensible default value. */ + for (unsigned int i = 0; i < n_construct_params; ++i) + { + if (g_strcmp0 (construct_params[i].pspec->name, "stepper") == 0) + { + if (g_value_get_pointer (construct_params[i].value) == nullptr) + g_value_set_pointer (construct_params[i].value, &defaultStepperHolder); + } + } + + return G_OBJECT_CLASS (animation_stepper_holder_parent_class)->constructor (type, + n_construct_params, + construct_params); +} + +static void +animation_stepper_holder_init (AnimationStepperHolder *stepper_holder) +{ +} + +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->constructor = animation_stepper_holder_constructor; + object_class->get_property = animation_stepper_holder_get_property; + object_class->set_property = animation_stepper_holder_set_property; + object_class->finalize = animation_stepper_holder_finalize; + + g_object_class_override_property (object_class, + PROP_STEPPER, + "stepper"); +} + +/** + * animation_stepper_holder_new: (skip): + * @interface: A pointer to an interlying stepper implementation. + * + * Create a new #AnimationStepperHolder, an implementation of + * #AnimationStepper which copies the underlying stepper when it + * is constructed (thus resetting its internal state, as copying + * a C++ lambda default-constructs its closure). + * + * Returns: (transfer full): A new #AnimationStepper with the underlying + * stepper copied. + */ +AnimationStepper * +animation_stepper_holder_new (gpointer interface) +{ + return ANIMATION_STEPPER (g_object_new (ANIMATION_TYPE_STEPPER_HOLDER, "stepper", interface, NULL)); +} diff --git a/animation-glib/stepper/stepper-holder.h b/animation-glib/stepper/stepper-holder.h new file mode 100644 index 0000000..967ce28 --- /dev/null +++ b/animation-glib/stepper/stepper-holder.h @@ -0,0 +1,36 @@ +/* + * 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) + +AnimationStepper * animation_stepper_holder_new (gpointer interface); + +G_END_DECLS diff --git a/animation-glib/stepper/stepper-wrapper.cpp b/animation-glib/stepper/stepper-wrapper.cpp new file mode 100644 index 0000000..830b0bd --- /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)); + 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)); + + delete priv->stepper; + 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..714f4a2 --- /dev/null +++ b/animation-glib/stepper/stepper.cpp @@ -0,0 +1,49 @@ +/* + * 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) +{ + g_object_interface_install_property ((gpointer) iface, + g_param_spec_pointer ("stepper", + "Internal Interface", + "Internal C++ interface that this class wraps", + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY))); +} 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/meson.build b/animation/meson.build index 78df2a8..69b493b 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -24,6 +24,7 @@ animation_sources = [] animation_headers = [] animation_headers_subdir = 'animation' +subdir('stepper') subdir('wobbly') animation_lib = shared_library( diff --git a/animation/stepper/linear.h b/animation/stepper/linear.h new file mode 100644 index 0000000..a18096d --- /dev/null +++ b/animation/stepper/linear.h @@ -0,0 +1,41 @@ +/* + * 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 + +#pragma once + +namespace animation +{ + namespace stepper + { + inline std::function Linear (float length) { + float progress = 0.0; + + return [=](unsigned int ms) mutable -> float { + progress += ms / length; + return progress; + }; + } + } +} diff --git a/animation/stepper/meson.build b/animation/stepper/meson.build new file mode 100644 index 0000000..771d9f1 --- /dev/null +++ b/animation/stepper/meson.build @@ -0,0 +1,31 @@ +# /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' +]) + +animation_headers += stepper_headers + +install_headers(stepper_headers, subdir: join_paths(animation_headers_subdir, 'stepper')) diff --git a/animation/stepper/reverse.h b/animation/stepper/reverse.h new file mode 100644 index 0000000..c9896b2 --- /dev/null +++ b/animation/stepper/reverse.h @@ -0,0 +1,42 @@ +/* + * 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 reverse stepping function. Takes another stepping + * function and inverts its output. + */ + +#include + +#include + +#pragma once + +namespace animation +{ + namespace stepper + { + inline std::function Reverse (Stepper &&step) { + Stepper localStepper (std::move (step)); + + return [=](unsigned int ms) mutable -> float { + return 1.0f - localStepper (ms); + }; + } + } +} diff --git a/animation/stepper/stepper.h b/animation/stepper/stepper.h new file mode 100644 index 0000000..bcedac6 --- /dev/null +++ b/animation/stepper/stepper.h @@ -0,0 +1,35 @@ +/* + * 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 + { + typedef std::function Stepper; + } +} From e27a100ef25598ed2a4551b99bb2b0f9bb2e203d Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Tue, 21 Aug 2018 12:36:16 +0800 Subject: [PATCH 06/19] animation: Add 4D vector type Right now we only have 2D vector types, but some consumers like clutter want bounding volumes in a four dimensional space, so we need a type to represent such bounding volumes. --- animation-glib/meson.build | 12 ++- animation-glib/vector4d-internal.h | 99 ++++++++++++++++++++++ animation-glib/vector4d.cpp | 39 +++++++++ animation-glib/vector4d.h | 39 +++++++++ animation/geometry.h | 131 +++++++++++++++++++++++++++++ 5 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 animation-glib/vector4d-internal.h create mode 100644 animation-glib/vector4d.cpp create mode 100644 animation-glib/vector4d.h diff --git a/animation-glib/meson.build b/animation-glib/meson.build index 0d7f4ba..56833e4 100644 --- a/animation-glib/meson.build +++ b/animation-glib/meson.build @@ -26,7 +26,17 @@ animation_glib_toplevel_headers = files([ ]) animation_glib_toplevel_introspectable_sources = files([ 'box.cpp', - 'vector.cpp' + 'vector.cpp', + 'vector.h', + 'vector4d.h' +]) +animation_glib_toplevel_private_headers = files([ + 'vector4d-internal.h' +]) +animation_glib_toplevel_introspectable_sources = files([ + 'box.cpp', + 'vector.cpp', + 'vector4d.cpp' ]) animation_glib_introspectable_sources = files([]) 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/geometry.h b/animation/geometry.h index 51413d8..29b5dfc 100644 --- a/animation/geometry.h +++ b/animation/geometry.h @@ -367,12 +367,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 ; From ea4df2615f6e255f2212b7b99b3dc1dec7695897 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 2 Sep 2018 14:32:48 +0800 Subject: [PATCH 07/19] animation: Make sure to install geometry headers --- animation/meson.build | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/animation/meson.build b/animation/meson.build index 69b493b..a778a73 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -27,6 +27,13 @@ animation_headers_subdir = 'animation' subdir('stepper') subdir('wobbly') +animation_toplevel_headers = files([ + 'geometry.h', + 'geometry_traits.h' +]) + +animation_headers += animation_toplevel_headers + animation_lib = shared_library( 'animation', animation_sources, @@ -49,3 +56,5 @@ pkg.generate( libraries: animation_lib, install_dir: join_paths(get_option('libdir'), 'pkgconfig') ) + +install_headers(animation_toplevel_headers, subdir: animation_headers_subdir) From 13f3dd6ffc70c16e1b2699a272adf94ebfa4aa71 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 5 Aug 2018 08:28:35 +0800 Subject: [PATCH 08/19] animation: Add a TransformAnimation base class We'll do some affine transformations based on this one. There's three main methods to override here: - Step() -> Take a single step in the animation by a given number of milliseconds. Returns true if more steps need to be taken, false otherwise - Matrix() -> Get the transformation matrix for the animated surface at this point in the animation. - Extremes() -> Get the four points specifying the 3D bounding volume for this surface, usually transforming the co-ordinates of the surface in scene-relative co-ordinates that have been passed in. --- animation-glib/meson.build | 1 + animation-glib/transform/meson.build | 34 +++++ animation-glib/transform/transform.cpp | 202 +++++++++++++++++++++++++ animation-glib/transform/transform.h | 49 ++++++ animation/meson.build | 4 +- animation/transform/meson.build | 13 ++ animation/transform/transform.h | 45 ++++++ animation/transform_calculation.h | 39 +++++ 8 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 animation-glib/transform/meson.build create mode 100644 animation-glib/transform/transform.cpp create mode 100644 animation-glib/transform/transform.h create mode 100644 animation/transform/meson.build create mode 100644 animation/transform/transform.h create mode 100644 animation/transform_calculation.h diff --git a/animation-glib/meson.build b/animation-glib/meson.build index 56833e4..c11161e 100644 --- a/animation-glib/meson.build +++ b/animation-glib/meson.build @@ -45,6 +45,7 @@ animation_glib_headers = files([]) animation_glib_headers_subdir = 'animation-glib' subdir('stepper') +subdir('transform') subdir('wobbly') animation_glib_introspectable_sources += animation_glib_toplevel_introspectable_sources diff --git a/animation-glib/transform/meson.build b/animation-glib/transform/meson.build new file mode 100644 index 0000000..6a64bff --- /dev/null +++ b/animation-glib/transform/meson.build @@ -0,0 +1,34 @@ +# /animation/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..190fc9d --- /dev/null +++ b/animation-glib/transform/transform.cpp @@ -0,0 +1,202 @@ +/* + * 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: + * @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]); +} + +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..58a3be8 --- /dev/null +++ b/animation-glib/transform/transform.h @@ -0,0 +1,49 @@ +/* + * 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); + +G_END_DECLS diff --git a/animation/meson.build b/animation/meson.build index a778a73..c78e68c 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -25,11 +25,13 @@ animation_headers = [] animation_headers_subdir = 'animation' subdir('stepper') +subdir('transform') subdir('wobbly') animation_toplevel_headers = files([ 'geometry.h', - 'geometry_traits.h' + 'geometry_traits.h', + 'transform_calculation.h' ]) animation_headers += animation_toplevel_headers diff --git a/animation/transform/meson.build b/animation/transform/meson.build new file mode 100644 index 0000000..eb9d690 --- /dev/null +++ b/animation/transform/meson.build @@ -0,0 +1,13 @@ +# /animation/wobbly/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]); + } + } +} From 1132322d7f54e7b48a9b79c7c11d75e8e540eb46 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Mon, 13 Aug 2018 14:04:20 +0800 Subject: [PATCH 09/19] animation: Add box_calculation inline functions We'll use these later for the affine transformation animations to calculate box offsets. Usually we want to compute where the center of a box is in both absolute co-ordinates and relative co-ordinates. ComputeBoxCenter will do it in absolute co-ordinates and ComputeBoxCenterOffset in co-ordinates relative to the top left corner of the box. --- animation/box_calculation.h | 70 +++++++++++++++++++++++++++++++++++++ animation/meson.build | 1 + 2 files changed, 71 insertions(+) create mode 100644 animation/box_calculation.h 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/meson.build b/animation/meson.build index c78e68c..1012f55 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -29,6 +29,7 @@ subdir('transform') subdir('wobbly') animation_toplevel_headers = files([ + 'box_calculation.h', 'geometry.h', 'geometry_traits.h', 'transform_calculation.h' From 073adbcfd5e329373900bd970c6cd5e53957ecad Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Mon, 13 Aug 2018 14:04:45 +0800 Subject: [PATCH 10/19] animation: Add math inline helper functions We'll use these later when implementing linear progress based animations. Right now we only have clamp(), used to ensure that the given value sits between some bounds. --- animation/math.h | 33 +++++++++++++++++++++++++++++++++ animation/meson.build | 1 + 2 files changed, 34 insertions(+) create mode 100644 animation/math.h 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 1012f55..4194e7e 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -32,6 +32,7 @@ animation_toplevel_headers = files([ 'box_calculation.h', 'geometry.h', 'geometry_traits.h', + 'math.h', 'transform_calculation.h' ]) From 8e3f8f5bc2665984d4dc7986c664febbad7d27b1 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 2 Sep 2018 00:58:43 +0800 Subject: [PATCH 11/19] animation: Add property.h header These are just some simple macros to define ABI safe property get/set functions. They are accessed by calling the correct overload, for instance, to set properties: animation.Target(target).Source(source) And to get properties: auto const &target = animation.Target(); --- animation/meson.build | 1 + animation/property.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 animation/property.h diff --git a/animation/meson.build b/animation/meson.build index 4194e7e..f76897d 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -33,6 +33,7 @@ animation_toplevel_headers = files([ 'geometry.h', 'geometry_traits.h', 'math.h', + 'property.h', 'transform_calculation.h' ]) diff --git a/animation/property.h b/animation/property.h new file mode 100644 index 0000000..b622dba --- /dev/null +++ b/animation/property.h @@ -0,0 +1,43 @@ +/* + * 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; \ + } + From dba9acd004fdb62b31dce40f969298f0e4395883 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Mon, 13 Aug 2018 12:48:37 +0800 Subject: [PATCH 12/19] animation-glib: Add constructor helpers These helpers are used to quickly append a new "interface" construction prop to the existing construction properties and make it easy to lookup the props inline. Basically, we need these helpers because we are wrapping some C++ interface implementation in a GObject. GObject doesn't require all the properties to be set at construction time, whereas constructing the C++ interface implementation does. So we need to collect all the properties in the constructor and then construct the underlying C++ interface there. Doing this by looking over all the construct properties and assigning values would be cumbersome, especially doing it for every effect implementation. Instead, we put all the construct properties into a hash table (hashed by the property name) and then fetch them in O(1) using the InterfaceConstructor template. This template constructs the "Interface" type (the type passed should be the implementation of the interface you want to construct) and variadically takes in all the construction parameters. Use ForwardValueFromHT to extract a typed value from the given property name from its corresponding GValue in the construct properties hash table. For instance, supposing that MyType's constructor is defined as follows: MyType::MyType (float bar, animation::Point const &baz) { ... } You would use the following in the constructor override for the GObject wrapper: const char *interesting_props = { "bar", "baz", NULL }; g_autoptr(GHashTable) props_ht = static_hash_table_of_values_for_specs (interesting_props, construct_params, n_construct_params); auto *interface = InterfaceConstructor ::construct ( ForwardValueFromHT (props_ht, g_value_get_float, "bar"), ForwardValueFromHT (props_ht, animation_point_from_gvalue, "baz") ); g_auto(GValue) interface_value = G_VALUE_INIT; unsigned int new_n_construct_params = 0; GObjectConstructParam *new_construct_params = append_interface_prop_to_construct_params (construct_params, n_construct_params, object_class, &interface_value, interface, &new_n_construct_params); object_class->constructor (type, new_construct_params, new_n_construct_params); --- animation-glib/constructor-helpers.cpp | 156 +++++++++++++++++++++++ animation-glib/constructor-helpers.h | 163 +++++++++++++++++++++++++ animation-glib/meson.build | 7 ++ 3 files changed, 326 insertions(+) create mode 100644 animation-glib/constructor-helpers.cpp create mode 100644 animation-glib/constructor-helpers.h diff --git a/animation-glib/constructor-helpers.cpp b/animation-glib/constructor-helpers.cpp new file mode 100644 index 0000000..41af673 --- /dev/null +++ b/animation-glib/constructor-helpers.cpp @@ -0,0 +1,156 @@ +/* + * 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" + +GHashTable * +static_hash_table_of_values_for_specs (const char * const *wanted_properties, + GObjectConstructParam *construct_params, + unsigned int n_construct_params) +{ + g_autoptr(GHashTable) ht = g_hash_table_new (g_str_hash, g_str_equal); + + /* Insert all the properties we intend to + * have in the hash table, with NULL as + * their value. */ + for (const char * const *iter = wanted_properties; *iter != NULL; ++iter) + g_hash_table_insert (ht, + (gpointer) (*iter), + NULL); + + /* Now go through all the construct params and insert + * their value pointer into the hash table. */ + for (unsigned int i = 0; i < n_construct_params; ++i) + { + GParamSpec *pspec = construct_params[i].pspec; + GValue *value = construct_params[i].value; + + if (g_hash_table_contains (ht, pspec->name)) + g_hash_table_insert (ht, + (gpointer) pspec->name, + value); + } + + return reinterpret_cast (g_steal_pointer (&ht)); +} + +/** + * 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 (); +} diff --git a/animation-glib/constructor-helpers.h b/animation-glib/constructor-helpers.h new file mode 100644 index 0000000..b20f8a6 --- /dev/null +++ b/animation-glib/constructor-helpers.h @@ -0,0 +1,163 @@ +/* + * 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 + +G_BEGIN_DECLS + +inline GValue * +lookup_gvalue (GHashTable *ht, const char *key) +{ + return reinterpret_cast (g_hash_table_lookup (ht, key)); +} + +GHashTable * +static_hash_table_of_values_for_specs (const char * const *wanted_properties, + GObjectConstructParam *construct_params, + unsigned int n_construct_params); + +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 +inline animation::Box +animation_box_from_gvalue (GValue *value) +{ + AnimationBox *boxed_box = reinterpret_cast (g_value_get_boxed (value)); + + if (boxed_box == nullptr) + return animation::Box (animation::Point (0, 0), + animation::Point (1, 1)); + + return animation::Box (animation::Point (boxed_box->top_left.x, + boxed_box->top_left.y), + animation::Point (boxed_box->bottom_right.x, + boxed_box->bottom_right.y)); +} + +inline animation::Point +animation_point_from_gvalue (GValue *value) +{ + AnimationVector *boxed_point = reinterpret_cast (g_value_get_boxed (value)); + + if (boxed_point == nullptr) + return animation::geometry::PointModel (0, 0); + + return animation::Point (boxed_point->x, boxed_point->y); +} + +inline animation::geometry::PointModel +animation_point_size_t_from_gvalue (GValue *value) +{ + AnimationVector *boxed_point = reinterpret_cast (g_value_get_boxed (value)); + + if (boxed_point == nullptr) + return animation::geometry::PointModel (0, 0); + + return animation::geometry::PointModel (boxed_point->x, boxed_point->y); +} + +inline animation::stepper::Stepper +animation_stepper_from_gvalue (GValue *value) +{ + static const unsigned int DefaultAnimationLength = 300; + + AnimationStepper *stepper = + reinterpret_cast (g_value_get_object (value)); + animation::stepper::Stepper *stepper_ptr = NULL; + + if (stepper != nullptr) + { + g_object_get (G_OBJECT (stepper), "stepper", &stepper_ptr, NULL); + return *stepper_ptr; + } + + return animation::stepper::Linear (DefaultAnimationLength); +} + +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/meson.build b/animation-glib/meson.build index c11161e..00b7000 100644 --- a/animation-glib/meson.build +++ b/animation-glib/meson.build @@ -31,8 +31,12 @@ animation_glib_toplevel_introspectable_sources = files([ 'vector4d.h' ]) animation_glib_toplevel_private_headers = files([ + 'constructor-helpers.h', 'vector4d-internal.h' ]) +animation_glib_toplevel_private_sources = files([ + 'constructor-helpers.cpp' +]) animation_glib_toplevel_introspectable_sources = files([ 'box.cpp', 'vector.cpp', @@ -40,6 +44,7 @@ animation_glib_toplevel_introspectable_sources = files([ ]) 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' @@ -49,7 +54,9 @@ subdir('transform') subdir('wobbly') 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) From b5a2b258ed7f7df0d155a0c80ccccf7a37f594fd Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Mon, 13 Aug 2018 14:00:34 +0800 Subject: [PATCH 13/19] tests: Add glm_ostream_operators.h This will be useful for printing out the contents of matrices when tests fail. Also necessary if we want to use comparison matchers with them. --- tests/glm_ostream_operators.h | 66 +++++++++++++++++++++++++++++++++++ tests/meson.build | 1 + 2 files changed, 67 insertions(+) create mode 100644 tests/glm_ostream_operators.h 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/meson.build b/tests/meson.build index 3811d9e..f8600f0 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -19,6 +19,7 @@ # Build the libanimation unit tests. animation_test_sources = [ + 'glm_ostream_operators.h', 'ostream_point_operator.h', 'wobbly/anchor_test.cpp', 'wobbly/constrainment_test.cpp', From f96b9fab0304a8725785846a0465e97c12d99dce Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Tue, 21 Aug 2018 12:40:13 +0800 Subject: [PATCH 14/19] matchers: Add AlmostEqMatcher This matcher allows the verify that one type instance is "almost equal" to another type instance. The way that is done is that the type must implement the animation::matcher::AlmostEqTrait type trait and define the apply() method. --- matchers/mathematical_model_matcher.h | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) 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 From a5c58fcd5caf32e0736dad22fe758d2cdd3fe6e1 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 5 Aug 2018 08:30:04 +0800 Subject: [PATCH 15/19] animation: Add zoom animation 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. --- animation-glib/meson.build | 1 + animation-glib/zoom/meson.build | 18 ++ animation-glib/zoom/zoom.cpp | 295 +++++++++++++++++++++++++++++ animation-glib/zoom/zoom.h | 50 +++++ animation/meson.build | 1 + animation/zoom/meson.build | 34 ++++ animation/zoom/zoom.cpp | 211 +++++++++++++++++++++ animation/zoom/zoom.h | 60 ++++++ tests/meson.build | 3 +- tests/zoom/zoom_animation_test.cpp | 167 ++++++++++++++++ 10 files changed, 839 insertions(+), 1 deletion(-) create mode 100644 animation-glib/zoom/meson.build create mode 100644 animation-glib/zoom/zoom.cpp create mode 100644 animation-glib/zoom/zoom.h create mode 100644 animation/zoom/meson.build create mode 100644 animation/zoom/zoom.cpp create mode 100644 animation/zoom/zoom.h create mode 100644 tests/zoom/zoom_animation_test.cpp diff --git a/animation-glib/meson.build b/animation-glib/meson.build index 00b7000..c179374 100644 --- a/animation-glib/meson.build +++ b/animation-glib/meson.build @@ -52,6 +52,7 @@ animation_glib_headers_subdir = 'animation-glib' subdir('stepper') subdir('transform') subdir('wobbly') +subdir('zoom') animation_glib_introspectable_sources += animation_glib_toplevel_introspectable_sources animation_glib_private_sources += animation_glib_toplevel_private_sources diff --git a/animation-glib/zoom/meson.build b/animation-glib/zoom/meson.build new file mode 100644 index 0000000..c206a40 --- /dev/null +++ b/animation-glib/zoom/meson.build @@ -0,0 +1,18 @@ +# /animation/zoom/meson.build +# +# Build the libanimation library (zoom animation component). +# +# See /LICENCE.md for Copyright information. + +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..d986c5f --- /dev/null +++ b/animation-glib/zoom/zoom.cpp @@ -0,0 +1,295 @@ +/* + * 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 agd = animation::geometry::dimension; +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_FROM, + PROP_TO, + PROP_STEPPER, + NPROPS +}; + +static GParamSpec *animation_zoom_animation_props [NPROPS] = { NULL, }; + +void +animation_zoom_animation_set_from (AnimationZoomAnimation *animation, + AnimationBox box) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->From (animation::Box (animation::Point (box.top_left.x, + box.top_left.y), + animation::Point (box.bottom_right.x, + box.bottom_right.y))); +} + +/** + * animation_glide_animation_get_from: + * @animation: An #AnimationZoomAnimation + * @out_box: (out caller-allocates): Return location for an #AnimationBox to the source + * + * Get the source box for this animation. + */ +void +animation_zoom_animation_get_from (AnimationZoomAnimation *animation, + AnimationBox *out_box) +{ + g_return_if_fail (out_box != NULL); + + auto box = LookupTypedInterfaceProp (G_OBJECT (animation))->From (); + + 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 ()); +} + + +/** + * animation_zoom_animation_get_to: + * @animation: An #AnimationZoomAnimation + * @out_box: (out caller-allocates): Return location for an #AnimationBox to the target + * + * Get the source box for this animation. + */ +void +animation_zoom_animation_get_to (AnimationZoomAnimation *animation, + AnimationBox *out_box) +{ + g_return_if_fail (out_box != NULL); + + auto box = LookupTypedInterfaceProp (G_OBJECT (animation))->To (); + + 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 ()); +} + +void +animation_zoom_animation_set_stepper (AnimationZoomAnimation *animation, + AnimationStepper *stepper) +{ + animation::stepper::Stepper *stepper_ptr = nullptr; + g_object_get (stepper, "stepper", (gpointer) &stepper_ptr, NULL); + + LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper (*stepper_ptr); +} + +/** + * 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 animation_stepper_wrapper_new ((gpointer) &stepper); +} + +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_FROM: + { + AnimationBox *box = reinterpret_cast (g_value_get_boxed (value)); + + if (box != nullptr) + animation_zoom_animation_set_from (zoom_animation, *box); + } + break; + case PROP_TO: + break; + case PROP_STEPPER: + animation_zoom_animation_set_stepper (zoom_animation, reinterpret_cast (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_FROM: + { + AnimationBox box; + animation_zoom_animation_get_from (zoom_animation, &box); + + g_value_set_boxed (value, (gpointer) &box); + } + break; + case PROP_TO: + { + AnimationBox box; + animation_zoom_animation_get_to (zoom_animation, &box); + + g_value_set_boxed (value, (gpointer) &box); + } + break; + case PROP_STEPPER: + g_value_take_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) +{ + replace_named_pointer_prop_in_construct_params_if_null (construct_params, + n_construct_params, + "stepper", + g_value_get_object, + g_value_set_object, + []() -> gpointer { + return animation_linear_stepper_new (300); + }); + + const char * const wanted_properties[] = { + "from", + "to", + "stepper", + NULL + }; + g_autoptr(GHashTable) properties_table = + static_hash_table_of_values_for_specs (wanted_properties, + construct_params, + n_construct_params); + + auto *interface = + static_cast (InterfaceConstructor ::construct ( + ForwardFromValueHT (properties_table, animation_box_from_gvalue, "from"), + ForwardFromValueHT (properties_table, animation_box_from_gvalue, "to"), + ForwardFromValueHT (properties_table, animation_stepper_from_gvalue, "stepper") + )); + + replace_interface_prop_in_construct_params (construct_params, + n_construct_params, + g_steal_pointer (&interface)); + + return G_OBJECT_CLASS (animation_zoom_animation_parent_class)->constructor (type, + n_construct_params, + construct_params); +} + +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->get_property = animation_zoom_animation_get_property; + object_class->set_property = animation_zoom_animation_set_property; + + animation_zoom_animation_props[PROP_FROM] = + g_param_spec_boxed ("from", + "From Box", + "Box that we are animating from", + ANIMATION_TYPE_BOX, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_zoom_animation_props[PROP_TO] = + g_param_spec_boxed ("to", + "To Box", + "Box that we are animating to", + ANIMATION_TYPE_BOX, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + 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: + * @from: The #AnimationBox that we are animating from. + * @to: The #AnimationBox that we are animating to (current location). + * @stepper: The stepper to use. + * + * Returns: (transfer full): A new #AnimationZoomAnimation. + */ +AnimationZoomAnimation * +animation_zoom_new (const AnimationBox *current, + const AnimationBox *from, + const AnimationBox *to, + AnimationStepper *stepper) +{ + return ANIMATION_ZOOM_ANIMATION (g_object_new (ANIMATION_TYPE_ZOOM_ANIMATION, + "from", from, + "to", to, + "stepper", stepper, + NULL)); +} diff --git a/animation-glib/zoom/zoom.h b/animation-glib/zoom/zoom.h new file mode 100644 index 0000000..47f4cd0 --- /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 +#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_from (AnimationZoomAnimation *animation, + AnimationBox box); +void animation_zoom_animation_get_from (AnimationZoomAnimation *animation, + AnimationBox *out_box); + +void animation_zoom_animation_get_to (AnimationZoomAnimation *animation, + AnimationBox *out_box); + +void animation_zoom_animation_set_stepper (AnimationZoomAnimation *animation, + AnimationStepper *stepper); +AnimationStepper * animation_zoom_animation_get_stepper (AnimationZoomAnimation *animation); + +AnimationZoomAnimation * animation_zoom_new (const AnimationBox *from, + const AnimationBox *to, + AnimationStepper *stepper); + +G_END_DECLS diff --git a/animation/meson.build b/animation/meson.build index f76897d..ff6b8db 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -27,6 +27,7 @@ animation_headers_subdir = 'animation' subdir('stepper') subdir('transform') subdir('wobbly') +subdir('zoom') animation_toplevel_headers = files([ 'box_calculation.h', diff --git a/animation/zoom/meson.build b/animation/zoom/meson.build new file mode 100644 index 0000000..598b4b6 --- /dev/null +++ b/animation/zoom/meson.build @@ -0,0 +1,34 @@ +# /animation/zoom/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 (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..42f8b92 --- /dev/null +++ b/animation/zoom/zoom.cpp @@ -0,0 +1,211 @@ +/* + * 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 + +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); + + animation::Point invScaleFactor (1.0 / agd::get <0> (scaleFactor), + 1.0 / agd::get <1> (scaleFactor)); + + /* 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::Box const &from, + animation::Box const &to, + animation::stepper::Stepper const &stepper); + + animation::Box from; + animation::Box to; + + glm::mat4 transform; + float progress; + + animation::stepper::Stepper stepper; + }; + + ZoomAnimation::Private::Private (animation::Box const &from, + animation::Box const &to, + animation::stepper::Stepper const &stepper) : + from (EnsureNonZeroArea (from)), + to (EnsureNonZeroArea (to)), + transform (glm::mat4 (1.0)), + progress (stepper (0)), + stepper (stepper) + { + ComputeZoomTransform (transform, + progress, + this->from, + this->to); + } + } +} + +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); + + ComputeZoomTransform (priv->transform, + priv->progress, + priv->from, + priv->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, From, animation::Box , priv->from) +ANIMATION_DEFINE_READONLY_PROPERTY (az::ZoomAnimation, To, animation::Box , priv->to) +ANIMATION_DEFINE_PROPERTY (az::ZoomAnimation, Stepper, animation::stepper::Stepper, priv->stepper) + +az::ZoomAnimation::ZoomAnimation (animation::Box const &from, + animation::Box const &to, + animation::stepper::Stepper const &stepper) : + priv (new az::ZoomAnimation::Private (from, to, stepper)) +{ +} + +az::ZoomAnimation::~ZoomAnimation () +{ +} diff --git a/animation/zoom/zoom.h b/animation/zoom/zoom.h new file mode 100644 index 0000000..39328c1 --- /dev/null +++ b/animation/zoom/zoom.h @@ -0,0 +1,60 @@ +/* + * 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 + +#pragma once + +namespace animation +{ + namespace zoom + { + class ZoomAnimation : + public animation::transform::TransformAnimation + { + public: + + ZoomAnimation (animation::Box const &from, + animation::Box const &to, + animation::stepper::Stepper 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, From, animation::Box ) + ANIMATION_DECLARE_READONLY_PROPERTY (ZoomAnimation, To, animation::Box ) + ANIMATION_DECLARE_PROPERTY (ZoomAnimation, Stepper, animation::stepper::Stepper) + + private: + + struct Private; + std::unique_ptr priv; + }; + } +} diff --git a/tests/meson.build b/tests/meson.build index f8600f0..8f8c00b 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -28,7 +28,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') diff --git a/tests/zoom/zoom_animation_test.cpp b/tests/zoom/zoom_animation_test.cpp new file mode 100644 index 0000000..b81d8d5 --- /dev/null +++ b/tests/zoom/zoom_animation_test.cpp @@ -0,0 +1,167 @@ +/* + * 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 "wobbly" spring model. + */ +#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 + +using ::testing::_; +using ::testing::AtLeast; +using ::testing::Eq; +using ::testing::Test; + +namespace am = animation::matchers; +namespace as = animation::stepper; +namespace az = animation::zoom; + +namespace +{ + TEST (ZoomAnimation, AnimationIncompleteBeforeLengthTimesteps) + { + az::ZoomAnimation anim (animation::Box (animation::Point (0, 0), + animation::Point (100, 100)), + animation::Box (animation::Point (100, 100), + animation::Point (200, 200)), + as::Linear (200)); + + EXPECT_TRUE (anim.Step (199)); + } + + TEST (ZoomAnimation, AnimationCompleteAfterLengthTimesteps) + { + az::ZoomAnimation anim (animation::Box (animation::Point (0, 0), + animation::Point (100, 100)), + animation::Box (animation::Point (100, 100), + animation::Point (200, 200)), + as::Linear (200)); + + EXPECT_FALSE (anim.Step (200)); + } + + TEST (ZoomAnimation, AnimatesToCorrectPositionAfterLengthTimesteps) + { + az::ZoomAnimation anim (animation::Box (animation::Point (100, 100), + animation::Point (200, 200)), + 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 (animation::Box (animation::Point (100, 100), + animation::Point (150, 150)), + 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 (animation::Box (animation::Point (100, 100), + animation::Point (200, 200)), + 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 (animation::Box (animation::Point (100, 100), + animation::Point (200, 200)), + 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))); + } +} From ddade3c6926f396fd6ba89ac11532b275d5a6dd0 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sun, 12 Aug 2018 22:49:50 +0800 Subject: [PATCH 16/19] animation: Add bounce animation 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. --- animation-glib/bounce/bounce.cpp | 349 +++++++++++++++++++++++++ animation-glib/bounce/bounce.h | 61 +++++ animation-glib/bounce/meson.build | 18 ++ animation-glib/meson.build | 1 + animation/bounce/bounce.cpp | 218 +++++++++++++++ animation/bounce/bounce.h | 65 +++++ animation/bounce/meson.build | 34 +++ animation/meson.build | 1 + tests/bounce/bounce_animation_test.cpp | 204 +++++++++++++++ tests/meson.build | 1 + 10 files changed, 952 insertions(+) create mode 100644 animation-glib/bounce/bounce.cpp create mode 100644 animation-glib/bounce/bounce.h create mode 100644 animation-glib/bounce/meson.build create mode 100644 animation/bounce/bounce.cpp create mode 100644 animation/bounce/bounce.h create mode 100644 animation/bounce/meson.build create mode 100644 tests/bounce/bounce_animation_test.cpp diff --git a/animation-glib/bounce/bounce.cpp b/animation-glib/bounce/bounce.cpp new file mode 100644 index 0000000..72e06d1 --- /dev/null +++ b/animation-glib/bounce/bounce.cpp @@ -0,0 +1,349 @@ +/* + * 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 + +namespace agd = animation::geometry::dimension; +namespace ab = animation::bounce; +namespace as = animation::stepper; +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, }; + +float +animation_bounce_animation_get_initial_scale (AnimationBounceAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->InitialScale (); +} + +void +animation_bounce_animation_set_initial_scale (AnimationBounceAnimation *animation, + float initial_scale) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->InitialScale (initial_scale); +} + +float +animation_bounce_animation_get_maximum_scale (AnimationBounceAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->MaximumScale (); +} + +void +animation_bounce_animation_set_maximum_scale (AnimationBounceAnimation *animation, + float 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); +} + +/** + * animation_bounce_animation_get_target: + * @animation: An #AnimationBounceAnimation + * @out_box: (out caller-allocates): Return location for an #AnimationBox + * + * Get the box representing the animation target. + */ +void +animation_bounce_animation_get_target (AnimationBounceAnimation *animation, + AnimationBox *out_box) +{ + g_return_if_fail (out_box != nullptr); + + animation::Box const &box = + LookupTypedInterfaceProp (G_OBJECT (animation))->Target (); + + 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 ()); +} + +void +animation_bounce_animation_set_stepper (AnimationBounceAnimation *animation, + AnimationStepper *stepper) +{ + animation::stepper::Stepper *stepper_ptr = nullptr; + g_object_get (stepper, "stepper", (gpointer) &stepper_ptr, NULL); + + LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper (*stepper_ptr); +} + +/** + * animation_bounce_animation_get_stepper: + * @animation: An #AnimationBounceAnimation + * + * Returns: (transfer full): Get the stepper for this #AnimationBounceAnimation + */ +AnimationStepper * +animation_bounce_animation_get_stepper (AnimationBounceAnimation *animation) +{ + auto const &stepper (LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper ()); + + return animation_stepper_wrapper_new ((gpointer) &stepper); +} + +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_float (value)); + break; + case PROP_MAXIMUM_SCALE: + animation_bounce_animation_set_maximum_scale (ANIMATION_BOUNCE_ANIMATION (object), + g_value_get_float (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: + /* No-op here to handle the constructor */ + 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_float (value, animation_bounce_animation_get_initial_scale (bounce_animation)); + break; + case PROP_MAXIMUM_SCALE: + g_value_set_float (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: + { + AnimationBox target; + animation_bounce_animation_get_target (bounce_animation, &target); + + g_value_set_boxed (value, &target); + break; + } + case PROP_STEPPER: + g_value_take_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) +{ + replace_named_pointer_prop_in_construct_params_if_null (construct_params, + n_construct_params, + "stepper", + g_value_get_object, + g_value_set_object, + []() -> gpointer { + return animation_linear_stepper_new (300); + }); + + const char * const wanted_properties[] = { + "initial-scale", + "maximum-scale", + "n-bounce", + "target", + "stepper", + NULL + }; + g_autoptr(GHashTable) properties_table = + static_hash_table_of_values_for_specs (wanted_properties, + construct_params, + n_construct_params); + + auto *interface = + static_cast (InterfaceConstructor ::construct ( + ForwardFromValueHT (properties_table, g_value_get_float, "initial-scale"), + ForwardFromValueHT (properties_table, g_value_get_float, "maximum-scale"), + ForwardFromValueHT (properties_table, g_value_get_uint, "n-bounce"), + ForwardFromValueHT (properties_table, animation_box_from_gvalue, "target"), + ForwardFromValueHT (properties_table, animation_stepper_from_gvalue, "stepper") + )); + + replace_interface_prop_in_construct_params (construct_params, + n_construct_params, + g_steal_pointer (&interface)); + + return G_OBJECT_CLASS (animation_bounce_animation_parent_class)->constructor (type, + n_construct_params, + construct_params); +} + +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->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_float ("initial-scale", + "Initial Scale", + "The initial scale of the animation", + 0.1f, + 1.0f, + 0.7f, + static_cast (G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + animation_bounce_animation_props[PROP_MAXIMUM_SCALE] = + g_param_spec_float ("maximum-scale", + "Maximum Scale", + "The maximum scale of the animation", + 1.0f, + 3.0f, + 1.2f, + 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_boxed ("target", + "Target Box", + "Box that we are animating to", + ANIMATION_TYPE_BOX, + static_cast (G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + + 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 #AnimationBox that we are animating to. + * @stepper: The #AnimationStepper of the animation. + * + * Returns: (transfer full): A new #AnimationBounceAnimation. + */ +AnimationBounceAnimation * +animation_bounce_new (float initial_scale, + float maximum_scale, + unsigned int n_bounce, + const AnimationBox *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..684f44f --- /dev/null +++ b/animation-glib/bounce/bounce.h @@ -0,0 +1,61 @@ +/* + * 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 + +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) + +float animation_bounce_animation_get_initial_scale (AnimationBounceAnimation *animation); +void animation_bounce_animation_set_initial_scale (AnimationBounceAnimation *animation, + float initial_scale); + +float animation_bounce_animation_get_maximum_scale (AnimationBounceAnimation *animation); +void animation_bounce_animation_set_maximum_scale (AnimationBounceAnimation *animation, + float 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_get_target (AnimationBounceAnimation *animation, + AnimationBox *out_box); + +void animation_bounce_animation_set_stepper (AnimationBounceAnimation *animation, + AnimationStepper *stepper); +AnimationStepper * animation_bounce_animation_get_stepper (AnimationBounceAnimation *animation); + +AnimationBounceAnimation * animation_bounce_new (float initial_scale, + float maximum_scale, + unsigned int n_bounce, + const AnimationBox *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..0c47361 --- /dev/null +++ b/animation-glib/bounce/meson.build @@ -0,0 +1,18 @@ +# /animation/bounce/meson.build +# +# Build the libanimation library (bounce animation component). +# +# See /LICENCE.md for Copyright information. + +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/meson.build b/animation-glib/meson.build index c179374..56d4d04 100644 --- a/animation-glib/meson.build +++ b/animation-glib/meson.build @@ -49,6 +49,7 @@ animation_glib_private_sources = files([]) animation_glib_headers = files([]) animation_glib_headers_subdir = 'animation-glib' +subdir('bounce') subdir('stepper') subdir('transform') subdir('wobbly') diff --git a/animation/bounce/bounce.cpp b/animation/bounce/bounce.cpp new file mode 100644 index 0000000..95b2d64 --- /dev/null +++ b/animation/bounce/bounce.cpp @@ -0,0 +1,218 @@ +/* + * 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 + +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::Box const &target, + animation::stepper::Stepper const &stepper); + + float initialScale; + float maximumScale; + unsigned int nBounce; + animation::Box target; + + glm::mat4 transform; + float progress; + + animation::stepper::Stepper stepper; + }; + + BounceAnimation::Private::Private (float initialScale, + float maximumScale, + unsigned int nBounce, + animation::Box const &target, + animation::stepper::Stepper const &stepper) : + initialScale (initialScale), + maximumScale (maximumScale), + nBounce (nBounce), + target (target), + transform (glm::mat4 (1.0)), + progress (stepper (0)), + stepper (stepper) + { + ComputeBounceTransform (transform, + progress, + initialScale, + maximumScale, + nBounce, + target); + } + } +} + +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); + + ComputeBounceTransform (priv->transform, + priv->progress, + priv->initialScale, + priv->maximumScale, + priv->nBounce, + priv->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, priv->stepper) +ANIMATION_DEFINE_READONLY_PROPERTY (ab::BounceAnimation, Target, animation::Box , priv->target) + +ab::BounceAnimation::BounceAnimation (float initialScale, + float maximumScale, + unsigned int nBounce, + animation::Box const &target, + animation::stepper::Stepper const &stepper) : + priv (new ab::BounceAnimation::Private (initialScale, + maximumScale, + nBounce, + target, + stepper)) +{ +} + +ab::BounceAnimation::~BounceAnimation () +{ +} diff --git a/animation/bounce/bounce.h b/animation/bounce/bounce.h new file mode 100644 index 0000000..58d079d --- /dev/null +++ b/animation/bounce/bounce.h @@ -0,0 +1,65 @@ +/* + * 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 + +#pragma once + +namespace animation +{ + namespace bounce + { + class BounceAnimation : + public animation::transform::TransformAnimation + { + public: + + BounceAnimation (float initialScale, + float maximumScale, + unsigned int nBounce, + animation::Box const &target, + animation::stepper::Stepper 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) + ANIMATION_DECLARE_READONLY_PROPERTY (BounceAnimation, Target, animation::Box ) + + 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..4fccbad --- /dev/null +++ b/animation/bounce/meson.build @@ -0,0 +1,34 @@ +# /animation/bounce/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 (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/meson.build b/animation/meson.build index ff6b8db..bad9883 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -24,6 +24,7 @@ animation_sources = [] animation_headers = [] animation_headers_subdir = 'animation' +subdir('bounce') subdir('stepper') subdir('transform') subdir('wobbly') diff --git a/tests/bounce/bounce_animation_test.cpp b/tests/bounce/bounce_animation_test.cpp new file mode 100644 index 0000000..88558f5 --- /dev/null +++ b/tests/bounce/bounce_animation_test.cpp @@ -0,0 +1,204 @@ +/* + * tests/wobbly/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 + +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 +{ + TEST (BounceAnimation, AnimationIncompleteBeforeLengthTimesteps) + { + ab::BounceAnimation anim (0.7f, + 1.5f, + 1, + 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, + 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, + 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, + 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, + 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/meson.build b/tests/meson.build index 8f8c00b..2ad5c76 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -19,6 +19,7 @@ # Build the libanimation unit tests. animation_test_sources = [ + 'bounce/bounce_animation_test.cpp', 'glm_ostream_operators.h', 'ostream_point_operator.h', 'wobbly/anchor_test.cpp', From 6657b6b2a4f3f401a85c6a5c534a77baa088c94c Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Sat, 1 Sep 2018 23:46:41 +0800 Subject: [PATCH 17/19] animation: Add glide animation 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. --- animation-glib/glide/glide.cpp | 404 +++++++++++++++++++++++++++ animation-glib/glide/glide.h | 67 +++++ animation-glib/glide/meson.build | 18 ++ animation-glib/meson.build | 1 + animation/glide/glide.cpp | 241 ++++++++++++++++ animation/glide/glide.h | 70 +++++ animation/glide/meson.build | 34 +++ animation/meson.build | 1 + tests/glide/glide_animation_test.cpp | 181 ++++++++++++ tests/meson.build | 1 + 10 files changed, 1018 insertions(+) create mode 100644 animation-glib/glide/glide.cpp create mode 100644 animation-glib/glide/glide.h create mode 100644 animation-glib/glide/meson.build create mode 100644 animation/glide/glide.cpp create mode 100644 animation/glide/glide.h create mode 100644 animation/glide/meson.build create mode 100644 tests/glide/glide_animation_test.cpp diff --git a/animation-glib/glide/glide.cpp b/animation-glib/glide/glide.cpp new file mode 100644 index 0000000..525f959 --- /dev/null +++ b/animation-glib/glide/glide.cpp @@ -0,0 +1,404 @@ +/* + * animation-glib/glide/glide.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 "glide" animation. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace agd = animation::geometry::dimension; +namespace ag = animation::glide; +namespace at = animation::transform; + +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_SCREEN_WIDTH, + PROP_TARGET, + PROP_STEPPER, + NPROPS +}; + +static GParamSpec *animation_glide_animation_props [NPROPS] = { NULL, }; + +void +animation_glide_animation_set_initial_distance (AnimationGlideAnimation *animation, + float initial_distance) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->InitialDistance (initial_distance); +} + +float +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, + float x_rotation_angle_degrees) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->XRotationAngleDegrees (x_rotation_angle_degrees); +} + +float +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, + float y_rotation_angle_degrees) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->YRotationAngleDegrees (y_rotation_angle_degrees); +} + +float +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, + float x_axis_location_unit) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->XAxisLocationUnit (x_axis_location_unit); +} + +float +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, + float y_axis_location_unit) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->YAxisLocationUnit (y_axis_location_unit); +} + +float +animation_glide_animation_get_y_axis_location_unit (AnimationGlideAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->YAxisLocationUnit (); +} + +void +animation_glide_animation_set_stepper (AnimationGlideAnimation *animation, + AnimationStepper *stepper) +{ + animation::stepper::Stepper *stepper_ptr = nullptr; + g_object_get (stepper, "stepper", (gpointer) &stepper_ptr, NULL); + + LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper (*stepper_ptr); +} + +/** + * 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 animation_stepper_wrapper_new ((gpointer) &stepper); +} + +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_float (value)); + break; + case PROP_X_ROTATION_ANGLE_DEGREES: + animation_glide_animation_set_x_rotation_angle_degrees (glide_animation, g_value_get_float (value)); + break; + case PROP_Y_ROTATION_ANGLE_DEGREES: + animation_glide_animation_set_y_rotation_angle_degrees (glide_animation, g_value_get_float (value)); + break; + case PROP_X_AXIS_LOCATION_UNIT: + animation_glide_animation_set_x_axis_location_unit (glide_animation, g_value_get_float (value)); + break; + case PROP_Y_AXIS_LOCATION_UNIT: + animation_glide_animation_set_y_axis_location_unit (glide_animation, g_value_get_float (value)); + break; + case PROP_SCREEN_WIDTH: + /* Not writable, except on construction */ + break; + case PROP_TARGET: + /* Not writable, except on construction */ + 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_float (value, animation_glide_animation_get_initial_distance (glide_animation)); + break; + case PROP_X_ROTATION_ANGLE_DEGREES: + g_value_set_float (value, animation_glide_animation_get_x_rotation_angle_degrees (glide_animation)); + break; + case PROP_Y_ROTATION_ANGLE_DEGREES: + g_value_set_float (value, animation_glide_animation_get_y_rotation_angle_degrees (glide_animation)); + break; + case PROP_X_AXIS_LOCATION_UNIT: + g_value_set_float (value, animation_glide_animation_get_x_axis_location_unit (glide_animation)); + break; + case PROP_Y_AXIS_LOCATION_UNIT: + g_value_set_float (value, animation_glide_animation_get_y_axis_location_unit (glide_animation)); + break; + case PROP_SCREEN_WIDTH: + /* Not readable */ + break; + case PROP_TARGET: + /* Not readable */ + break; + case PROP_STEPPER: + g_value_take_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) +{ + replace_named_pointer_prop_in_construct_params_if_null (construct_params, + n_construct_params, + "stepper", + g_value_get_object, + g_value_set_object, + []() -> gpointer { + return animation_linear_stepper_new (300); + }); + + + const char * const wanted_properties[] = { + "initial-distance", + "x-rotation-angle-degrees", + "y-rotation-angle-degrees", + "x-axis-location-unit", + "y-axis-location-unit", + "screen-width", + "target", + "stepper", + NULL + }; + g_autoptr(GHashTable) properties_table = + static_hash_table_of_values_for_specs (wanted_properties, + construct_params, + n_construct_params); + + auto *interface = + static_cast (InterfaceConstructor ::construct ( + ForwardFromValueHT (properties_table, g_value_get_float, "initial-distance"), + ForwardFromValueHT (properties_table, g_value_get_float, "x-rotation-angle-degrees"), + ForwardFromValueHT (properties_table, g_value_get_float, "y-rotation-angle-degrees"), + ForwardFromValueHT (properties_table, g_value_get_float, "x-axis-location-unit"), + ForwardFromValueHT (properties_table, g_value_get_float, "y-axis-location-unit"), + ForwardFromValueHT (properties_table, g_value_get_uint, "screen-width"), + ForwardFromValueHT (properties_table, animation_box_from_gvalue, "target"), + ForwardFromValueHT (properties_table, animation_stepper_from_gvalue, "stepper") + )); + + replace_interface_prop_in_construct_params (construct_params, + n_construct_params, + g_steal_pointer (&interface)); + + return G_OBJECT_CLASS (animation_glide_animation_parent_class)->constructor (type, + n_construct_params, + construct_params); +} + +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->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_float ("initial-distance", + "Initial Distance", + "The initial distance away from the camera", + -1.0f, + 1.0f, + -0.3f, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_X_ROTATION_ANGLE_DEGREES] = + g_param_spec_float ("x-rotation-angle-degrees", + "X Rotation Angle Degrees", + "Number of degrees on the X axis to rotate", + -360.0f, + 360.0f, + 0.0f, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_Y_ROTATION_ANGLE_DEGREES] = + g_param_spec_float ("y-rotation-angle-degrees", + "Y Rotation Angle Degrees", + "Number of degrees on the Y axis to rotate", + -360.0f, + 360.0f, + 0.0f, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_X_AXIS_LOCATION_UNIT] = + g_param_spec_float ("x-axis-location-unit", + "X Axis Location Unit", + "Unit-coordinates of where the X axis is on the surface", + 0.0f, + 1.0f, + 0.2f, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_Y_AXIS_LOCATION_UNIT] = + g_param_spec_float ("y-axis-location-unit", + "Y Axis Location Unit", + "Unit-coordinates of where the Y axis is on the surface", + 0.0f, + 1.0f, + 0.5f, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_glide_animation_props[PROP_SCREEN_WIDTH] = + g_param_spec_uint ("screen-width", + "Screen Width", + "Width of the screen in pixels", + 1, + G_MAXUINT, + 1, + static_cast (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + animation_glide_animation_props[PROP_TARGET] = + g_param_spec_boxed ("target", + "Target Box", + "Box that we are animating to", + ANIMATION_TYPE_BOX, + static_cast (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + 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 #AnimationBox that we are animating to. + * @length: The length of the animation. + * + * Returns: (transfer full): A new #AnimationGlideAnimation. + */ +AnimationGlideAnimation * +animation_glide_new (float initial_distance, + float x_rotation_angle_degrees, + float y_rotation_angle_degrees, + float x_axis_location_unit, + float y_axis_location_unit, + unsigned int screen_width, + const AnimationBox *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..2b2fbb7 --- /dev/null +++ b/animation-glib/glide/glide.h @@ -0,0 +1,67 @@ +/* + * animation-glib/glide/glide.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 "glide" animation. + */ +#pragma once + +#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, + float initial_distance); +float animation_glide_animation_get_initial_distance (AnimationGlideAnimation *animation); + +void animation_glide_animation_set_x_rotation_angle_degrees (AnimationGlideAnimation *animation, + float x_rotation_angle_degrees); +float animation_glide_animation_get_x_rotation_angle_degrees (AnimationGlideAnimation *animation); + +void animation_glide_animation_set_y_rotation_angle_degrees (AnimationGlideAnimation *animation, + float y_rotation_angle_degrees); +float animation_glide_animation_get_y_rotation_angle_degrees (AnimationGlideAnimation *animation); + +void animation_glide_animation_set_x_axis_location_unit (AnimationGlideAnimation *animation, + float x_axis_location_unit); + +float animation_glide_animation_get_x_axis_location_unit (AnimationGlideAnimation *animation); +void animation_glide_animation_set_y_axis_location_unit (AnimationGlideAnimation *animation, + float y_axis_location_unit); + +float animation_glide_animation_get_y_axis_location_unit (AnimationGlideAnimation *animation); + +void animation_glide_animation_set_stepper (AnimationGlideAnimation *animation, + AnimationStepper *stepper); +AnimationStepper * animation_glide_animation_get_stepper (AnimationGlideAnimation *animation); + +AnimationGlideAnimation * animation_glide_new (float initial_distance, + float x_rotation_angle_degrees, + float y_rotation_angle_degrees, + float x_axis_location_unit, + float y_axis_location_unit, + unsigned int screen_width, + const AnimationBox *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..d5f1026 --- /dev/null +++ b/animation-glib/glide/meson.build @@ -0,0 +1,18 @@ +# /animation/glide/meson.build +# +# Build the libanimation library (glide animation component). +# +# See /LICENCE.md for Copyright information. + +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/meson.build b/animation-glib/meson.build index 56d4d04..62a674d 100644 --- a/animation-glib/meson.build +++ b/animation-glib/meson.build @@ -50,6 +50,7 @@ animation_glib_headers = files([]) animation_glib_headers_subdir = 'animation-glib' subdir('bounce') +subdir('glide') subdir('stepper') subdir('transform') subdir('wobbly') diff --git a/animation/glide/glide.cpp b/animation/glide/glide.cpp new file mode 100644 index 0000000..f4e4d49 --- /dev/null +++ b/animation/glide/glide.cpp @@ -0,0 +1,241 @@ +/* + * animation/glide/glide.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 glide onto screen, gently + * following an attenuating sine wave. + */ + +#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, + unsigned int screenWidth, + animation::Box const &target, + animation::stepper::Stepper const &stepper); + + float initialDistance; + float xRotationAngleDegrees; + float yRotationAngleDegrees; + float xAxisLocationUnit; + float yAxisLocationUnit; + unsigned int screenWidth; + animation::Box target; + + glm::mat4 transform; + float progress; + + animation::stepper::Stepper stepper; + }; + + GlideAnimation::Private::Private (float initialDistance, + float xRotationAngleDegrees, + float yRotationAngleDegrees, + float xAxisLocationUnit, + float yAxisLocationUnit, + unsigned int screenWidth, + animation::Box const &target, + animation::stepper::Stepper const &stepper) : + initialDistance (initialDistance), + xRotationAngleDegrees (xRotationAngleDegrees), + yRotationAngleDegrees (yRotationAngleDegrees), + xAxisLocationUnit (xAxisLocationUnit), + yAxisLocationUnit (yAxisLocationUnit), + screenWidth (screenWidth), + target (target), + transform (glm::mat4 (1.0)), + progress (stepper (0)), + stepper (stepper) + { + ComputeGlideTransform (transform, + progress, + initialDistance, + xRotationAngleDegrees, + yRotationAngleDegrees, + xAxisLocationUnit, + yAxisLocationUnit, + screenWidth, + target); + } + } +} + +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); + + ComputeGlideTransform (priv->transform, + priv->progress, + priv->initialDistance, + priv->xRotationAngleDegrees, + priv->yRotationAngleDegrees, + priv->xAxisLocationUnit, + priv->yAxisLocationUnit, + priv->screenWidth, + priv->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, priv->stepper) + +ag::GlideAnimation::GlideAnimation (float initialDistance, + float xRotationAngleDegrees, + float yRotationAngleDegrees, + float xAxisLocationUnit, + float yAxisLocationUnit, + unsigned int screenWidth, + animation::Box const &target, + animation::stepper::Stepper const &stepper) : + priv (new ag::GlideAnimation::Private (initialDistance, + xRotationAngleDegrees, + yRotationAngleDegrees, + xAxisLocationUnit, + yAxisLocationUnit, + screenWidth, + target, + stepper)) +{ +} + +ag::GlideAnimation::~GlideAnimation () +{ +} diff --git a/animation/glide/glide.h b/animation/glide/glide.h new file mode 100644 index 0000000..96a0d15 --- /dev/null +++ b/animation/glide/glide.h @@ -0,0 +1,70 @@ +/* + * animation/glide/glide.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 glide onto screen, gently + * following an attenuating sine wave. + */ + +#include +#include + +#include +#include +#include +#include + +#pragma once + +namespace animation +{ + namespace glide + { + class GlideAnimation : + public animation::transform::TransformAnimation + { + public: + + GlideAnimation (float initialDistance, + float xRotationAngleDegrees, + float yRotationAngleDegrees, + float xAxisLocationUnit, + float yAxisLocationUnit, + unsigned int screenWidth, + animation::Box const &target, + animation::stepper::Stepper 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, Stepper, animation::stepper::Stepper) + + 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..5cf6104 --- /dev/null +++ b/animation/glide/meson.build @@ -0,0 +1,34 @@ +# /animation/glide/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 (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/meson.build b/animation/meson.build index bad9883..c13ca9a 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -25,6 +25,7 @@ animation_headers = [] animation_headers_subdir = 'animation' subdir('bounce') +subdir('glide') subdir('stepper') subdir('transform') subdir('wobbly') diff --git a/tests/glide/glide_animation_test.cpp b/tests/glide/glide_animation_test.cpp new file mode 100644 index 0000000..1359ead --- /dev/null +++ b/tests/glide/glide_animation_test.cpp @@ -0,0 +1,181 @@ +/* + * 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 + +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 +{ + static const unsigned int MockScreenWidth = 1000; + + TEST (GlideAnimation, AnimationIncompleteBeforeLengthTimesteps) + { + ag::GlideAnimation anim (0.5f, + 0.0f, + 20.0f, + 0.5f, + 0.0f, + 1000, + 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, + 1000, + 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, + 1000, + 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, + 1000, + 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/meson.build b/tests/meson.build index 2ad5c76..b4111aa 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -20,6 +20,7 @@ animation_test_sources = [ 'bounce/bounce_animation_test.cpp', + 'glide/glide_animation_test.cpp', 'glm_ostream_operators.h', 'ostream_point_operator.h', 'wobbly/anchor_test.cpp', From ad83058d7f1f0be24be56f9c06c0cc5f167931e7 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Wed, 22 Aug 2018 06:50:02 +0800 Subject: [PATCH 18/19] grid: Add GridAnimation interface The GridAnimation interface represents transformations that are done on a grid of vertices that a surface is subdivided into. The transformation can be any arbitrary function. Implementations must override three functions: - Step() -> Take a single step on the function according to the number of milliseconds passed. If more steps are needed to complete the animation, return true, otherwise return false. - DeformUVToModelSpace() - Given some co-ordinates between (0, 0) and (1, 1), deform the co-ordinates into model space, that is the space in the scene of the surface as if the function were applied. - Extremes() - Get the geometry of a 2D bounding plane totally encompassing the deformed surface. --- animation-glib/grid/grid.cpp | 235 ++++++++++++++++++++++++++++++++ animation-glib/grid/grid.h | 54 ++++++++ animation-glib/grid/meson.build | 32 +++++ animation-glib/meson.build | 1 + animation/grid/grid.h | 46 +++++++ animation/grid/meson.build | 13 ++ animation/meson.build | 1 + 7 files changed, 382 insertions(+) create mode 100644 animation-glib/grid/grid.cpp create mode 100644 animation-glib/grid/grid.h create mode 100644 animation-glib/grid/meson.build create mode 100644 animation/grid/grid.h create mode 100644 animation/grid/meson.build diff --git a/animation-glib/grid/grid.cpp b/animation-glib/grid/grid.cpp new file mode 100644 index 0000000..2f4881d --- /dev/null +++ b/animation-glib/grid/grid.cpp @@ -0,0 +1,235 @@ +/* + * 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: + * @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]); +} + +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..06afb1b --- /dev/null +++ b/animation-glib/grid/grid.h @@ -0,0 +1,54 @@ +/* + * 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); + +G_END_DECLS diff --git a/animation-glib/grid/meson.build b/animation-glib/grid/meson.build new file mode 100644 index 0000000..d84b1fe --- /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/meson.build b/animation-glib/meson.build index 62a674d..81bfcda 100644 --- a/animation-glib/meson.build +++ b/animation-glib/meson.build @@ -51,6 +51,7 @@ animation_glib_headers_subdir = 'animation-glib' subdir('bounce') subdir('glide') +subdir('grid') subdir('stepper') subdir('transform') subdir('wobbly') 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..bfd9662 --- /dev/null +++ b/animation/grid/meson.build @@ -0,0 +1,13 @@ +# /animation/grid/meson.build +# +# Build the libanimation library (grid animation base class component). +# +# See /LICENCE.md for Copyright information. + +grid_headers = files([ + 'grid.h' +]) + +animation_headers += grid_headers + +install_headers(grid_headers, subdir: join_paths(animation_headers_subdir, 'grid')) diff --git a/animation/meson.build b/animation/meson.build index c13ca9a..52569a6 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -26,6 +26,7 @@ animation_headers_subdir = 'animation' subdir('bounce') subdir('glide') +subdir('grid') subdir('stepper') subdir('transform') subdir('wobbly') From 4e6c16dc67bf7c9a55cbb4e4ce95a34ecc839d58 Mon Sep 17 00:00:00 2001 From: Sam Spilsbury Date: Wed, 22 Aug 2018 09:30:50 +0800 Subject: [PATCH 19/19] animation: Add magic lamp animation 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. 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. https://phabricator.endlessm.com/T23697 --- animation-glib/magiclamp/magiclamp.cpp | 459 +++++++++++++++++++ animation-glib/magiclamp/magiclamp.h | 71 +++ animation-glib/magiclamp/meson.build | 18 + animation-glib/meson.build | 1 + animation/magiclamp/magiclamp.cpp | 329 +++++++++++++ animation/magiclamp/magiclamp.h | 72 +++ animation/magiclamp/meson.build | 34 ++ animation/meson.build | 1 + tests/magiclamp/magiclamp_animation_test.cpp | 238 ++++++++++ tests/meson.build | 1 + 10 files changed, 1224 insertions(+) create mode 100644 animation-glib/magiclamp/magiclamp.cpp create mode 100644 animation-glib/magiclamp/magiclamp.h create mode 100644 animation-glib/magiclamp/meson.build create mode 100644 animation/magiclamp/magiclamp.cpp create mode 100644 animation/magiclamp/magiclamp.h create mode 100644 animation/magiclamp/meson.build create mode 100644 tests/magiclamp/magiclamp_animation_test.cpp diff --git a/animation-glib/magiclamp/magiclamp.cpp b/animation-glib/magiclamp/magiclamp.cpp new file mode 100644 index 0000000..eb90b42 --- /dev/null +++ b/animation-glib/magiclamp/magiclamp.cpp @@ -0,0 +1,459 @@ +/* + * animation-glib/magiclamp/magiclamp.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 "magiclamp" animation. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ag = animation::grid; +namespace agd = animation::geometry::dimension; +namespace aml = animation::magiclamp; + +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_source (AnimationMagicLampAnimation *animation, + AnimationBox box) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->Source (animation::Box (animation::Point (box.top_left.x, + box.top_left.y), + animation::Point (box.bottom_right.x, + box.bottom_right.y))); +} + +/** + * animation_magiclamp_animation_get_source: + * @animation: An #AnimationMagicLampAnimation + * @out_box: (out caller-allocates): Return location for an #AnimationBox to the source + * + * Get the source box for this animation. + */ +void +animation_magiclamp_animation_get_source (AnimationMagicLampAnimation *animation, + AnimationBox *out_box) +{ + g_return_if_fail (out_box != NULL); + + auto box = LookupTypedInterfaceProp (G_OBJECT (animation))->Source (); + + 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 ()); +} + + +/** + * animation_magiclamp_animation_get_target: + * @animation: An #AnimationMagicLampAnimation + * @out_box: (out caller-allocates): Return location for an #AnimationBox to the target + * + * Get the source box for this animation. + */ +void +animation_magiclamp_animation_get_target (AnimationMagicLampAnimation *animation, + AnimationBox *out_box) +{ + g_return_if_fail (out_box != NULL); + + auto box = LookupTypedInterfaceProp (G_OBJECT (animation))->Target (); + + 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 ()); +} + +void +animation_magiclamp_animation_set_bend_factor (AnimationMagicLampAnimation *animation, + float bend_factor) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->BendFactor (bend_factor); +} + +float +animation_magiclamp_animation_get_bend_factor (AnimationMagicLampAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->BendFactor (); +} + +void +animation_magiclamp_animation_set_offset_factor (AnimationMagicLampAnimation *animation, + float offset_factor) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->OffsetFactor (offset_factor); +} + +float +animation_magiclamp_animation_get_offset_factor (AnimationMagicLampAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->OffsetFactor (); +} + +void +animation_magiclamp_animation_set_stretch_factor (AnimationMagicLampAnimation *animation, + float stretch_factor) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->StretchFactor (stretch_factor); +} + +float +animation_magiclamp_animation_get_stretch_factor (AnimationMagicLampAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->StretchFactor (); +} + +void +animation_magiclamp_animation_set_deform_speed_factor (AnimationMagicLampAnimation *animation, + float deform_speed_factor) +{ + LookupTypedInterfaceProp (G_OBJECT (animation))->DeformSpeedFactor (deform_speed_factor); +} + +float +animation_magiclamp_animation_get_deform_speed_factor (AnimationMagicLampAnimation *animation) +{ + return LookupTypedInterfaceProp (G_OBJECT (animation))->DeformSpeedFactor (); +} + +void +animation_magiclamp_animation_set_stepper (AnimationMagicLampAnimation *animation, + AnimationStepper *stepper) +{ + animation::stepper::Stepper *stepper_ptr = nullptr; + g_object_get (stepper, "stepper", (gpointer) &stepper_ptr, NULL); + + LookupTypedInterfaceProp (G_OBJECT (animation))->Stepper (*stepper_ptr); +} + +/** + * 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 animation_stepper_wrapper_new ((gpointer) &stepper); +} + +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: + { + AnimationBox *box = reinterpret_cast (g_value_get_boxed (value)); + + if (box != nullptr) + animation_magiclamp_animation_set_source (magiclamp_animation, *box); + } + break; + case PROP_TARGET: + /* Not writable, except on construction */ + break; + case PROP_RESOLUTION: + /* Not writable, except on construction */ + break; + case PROP_BEND_FACTOR: + animation_magiclamp_animation_set_bend_factor (magiclamp_animation, g_value_get_float (value)); + break; + case PROP_OFFSET_FACTOR: + animation_magiclamp_animation_set_offset_factor (magiclamp_animation, g_value_get_float (value)); + break; + case PROP_STRETCH_FACTOR: + animation_magiclamp_animation_set_stretch_factor (magiclamp_animation, g_value_get_float (value)); + break; + case PROP_DEFORM_SPEED_FACTOR: + animation_magiclamp_animation_set_deform_speed_factor (magiclamp_animation, g_value_get_float (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: + { + AnimationBox box; + + animation_magiclamp_animation_get_source (magiclamp_animation, &box); + g_value_set_boxed (value, (gpointer) &box); + } + break; + case PROP_TARGET: + { + AnimationBox box; + + animation_magiclamp_animation_get_target (magiclamp_animation, &box); + g_value_set_boxed (value, (gpointer) &box); + } + break; + case PROP_BEND_FACTOR: + g_value_set_float (value, animation_magiclamp_animation_get_bend_factor (magiclamp_animation)); + break; + case PROP_STRETCH_FACTOR: + g_value_set_float (value, animation_magiclamp_animation_get_stretch_factor (magiclamp_animation)); + break; + case PROP_OFFSET_FACTOR: + g_value_set_float (value, animation_magiclamp_animation_get_offset_factor (magiclamp_animation)); + break; + case PROP_DEFORM_SPEED_FACTOR: + g_value_set_float (value, animation_magiclamp_animation_get_deform_speed_factor (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) +{ + replace_named_pointer_prop_in_construct_params_if_null (construct_params, + n_construct_params, + "stepper", + g_value_get_object, + g_value_set_object, + []() -> gpointer { + return animation_linear_stepper_new (300); + }); + + /* Need to have a grid resolution */ + replace_named_pointer_prop_in_construct_params_if_null (construct_params, + n_construct_params, + "resolution", + g_value_get_boxed, + (AnimationConstructorHelpersGValueSetPointerFunc) g_value_set_boxed, + []() -> gpointer { + AnimationVector *v = g_new0 (AnimationVector, 1); + + v->x = 10.0; + v->y = 10.0; + + return (gpointer) v; + }); + + const char * const wanted_properties[] = { + "source", + "target", + "resolution", + "bend-factor", + "offset-factor", + "stretch-factor", + "deform-speed-factor", + "stepper", + NULL + }; + g_autoptr(GHashTable) properties_table = + static_hash_table_of_values_for_specs (wanted_properties, + construct_params, + n_construct_params); + + auto *interface = + static_cast (InterfaceConstructor ::construct ( + ForwardFromValueHT (properties_table, animation_box_from_gvalue, "source"), + ForwardFromValueHT (properties_table, animation_box_from_gvalue, "target"), + ForwardFromValueHT (properties_table, animation_point_size_t_from_gvalue, "resolution"), + ForwardFromValueHT (properties_table, g_value_get_float, "bend-factor"), + ForwardFromValueHT (properties_table, g_value_get_float, "offset-factor"), + ForwardFromValueHT (properties_table, g_value_get_float, "stretch-factor"), + ForwardFromValueHT (properties_table, g_value_get_float, "deform-speed-factor"), + ForwardFromValueHT (properties_table, animation_stepper_from_gvalue, "stepper") + )); + + replace_interface_prop_in_construct_params (construct_params, + n_construct_params, + g_steal_pointer (&interface)); + + return G_OBJECT_CLASS (animation_magiclamp_animation_parent_class)->constructor (type, + n_construct_params, + construct_params); +} + +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->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_boxed ("source", + "Source Box", + "Box that we are animating from", + ANIMATION_TYPE_BOX, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + animation_magiclamp_animation_props[PROP_TARGET] = + g_param_spec_boxed ("target", + "Target Box", + "Box that we are animating to", + ANIMATION_TYPE_BOX, + static_cast (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + animation_magiclamp_animation_props[PROP_RESOLUTION] = + g_param_spec_boxed ("resolution", + "Resolution", + "Grid Resolution", + ANIMATION_TYPE_VECTOR, + static_cast (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + animation_magiclamp_animation_props[PROP_BEND_FACTOR] = + g_param_spec_float ("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_float ("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_float ("stretch-factor", + "Stretch Factor", + "How much the window should stretch when animating", + 0.2, + 1.0, + 0.45, + static_cast (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + animation_magiclamp_animation_props[PROP_DEFORM_SPEED_FACTOR] = + g_param_spec_float ("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 #AnimationBox that we are animating from. + * @target_box: The #AnimationBox 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 AnimationBox *source_box, + const AnimationBox *target_box, + const AnimationVector *resolution, + float bend_factor, + float offset_factor, + float stretch_factor, + float 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, + "stepper", stepper, + NULL)); +} diff --git a/animation-glib/magiclamp/magiclamp.h b/animation-glib/magiclamp/magiclamp.h new file mode 100644 index 0000000..ec59f99 --- /dev/null +++ b/animation-glib/magiclamp/magiclamp.h @@ -0,0 +1,71 @@ +/* + * animation-glib/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 + * . + * + * 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_source (AnimationMagicLampAnimation *animation, + AnimationBox box); +void animation_magiclamp_animation_get_source (AnimationMagicLampAnimation *animation, + AnimationBox *out_box); + +void animation_magiclamp_animation_get_target (AnimationMagicLampAnimation *animation, + AnimationBox *out_box); + +void animation_magiclamp_animation_set_bend_factor (AnimationMagicLampAnimation *animation, + float bend_factor); +float animation_magiclamp_animation_get_bend_factor (AnimationMagicLampAnimation *animation); + +void animation_magiclamp_animation_set_offset_factor (AnimationMagicLampAnimation *animation, + float offset_factor); +float animation_magiclamp_animation_get_offset_factor (AnimationMagicLampAnimation *animation); + +void animation_magiclamp_animation_set_stretch_factor (AnimationMagicLampAnimation *animation, + float stretch_factor); +float animation_magiclamp_animation_get_stretch_factor (AnimationMagicLampAnimation *animation); + +void animation_magiclamp_animation_set_deform_speed_factor (AnimationMagicLampAnimation *animation, + float deform_speed_factor); +float animation_magiclamp_animation_get_deform_speed_factor (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, + float bend_factor, + float offset_factor, + float stretch_factor, + float 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..ba8e089 --- /dev/null +++ b/animation-glib/magiclamp/meson.build @@ -0,0 +1,18 @@ +# /animation/magiclamp/meson.build +# +# Build the libanimation library (magiclamp animation component). +# +# See /LICENCE.md for Copyright information. + +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 81bfcda..a34de9f 100644 --- a/animation-glib/meson.build +++ b/animation-glib/meson.build @@ -53,6 +53,7 @@ subdir('bounce') subdir('glide') subdir('grid') subdir('stepper') +subdir('magiclamp') subdir('transform') subdir('wobbly') subdir('zoom') diff --git a/animation/magiclamp/magiclamp.cpp b/animation/magiclamp/magiclamp.cpp new file mode 100644 index 0000000..ea9f42d --- /dev/null +++ b/animation/magiclamp/magiclamp.cpp @@ -0,0 +1,329 @@ +/* + * animation/magiclamp/magiclamp.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 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::Box const &source, + animation::Box const &target, + ag::PointModel const &resolution, + float bendFactor, + float offsetFactor, + float stretchFactor, + float deformSpeedFactor, + animation::stepper::Stepper const &stepper); + + animation::Box source; + animation::Box target; + + std::vector grid; + ag::PointModel gridResolution; + + float bendFactor; + float offsetFactor; + float stretchFactor; + float deformSpeedFactor; + + float progress; + + animation::stepper::Stepper stepper; + }; + + MagicLampAnimation::Private::Private (animation::Box const &source, + animation::Box const &target, + ag::PointModel const &resolution, + float bendFactor, + float offsetFactor, + float stretchFactor, + float deformSpeedFactor, + animation::stepper::Stepper const &stepper) : + source (source), + target (target), + grid (InitializeGridObjects (resolution)), + gridResolution (resolution), + bendFactor (bendFactor), + offsetFactor (offsetFactor), + stretchFactor (stretchFactor), + deformSpeedFactor (deformSpeedFactor), + progress (stepper (0)), + stepper (stepper) + { + MoveGridVerticallyTowardsTargetWithXSigmoidDeformation (source, + target, + grid, + gridResolution, + bendFactor, + offsetFactor, + stretchFactor, + deformSpeedFactor, + progress); + } + } +} + +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); + + MoveGridVerticallyTowardsTargetWithXSigmoidDeformation (priv->source, + priv->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::Box , priv->source) +ANIMATION_DEFINE_PROPERTY (aml::MagicLampAnimation, Target, animation::Box , priv->target) +ANIMATION_DEFINE_PROPERTY (aml::MagicLampAnimation, GridResolution, animation::geometry::PointModel , priv->gridResolution) +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, priv->stepper) + +aml::MagicLampAnimation::MagicLampAnimation (animation::Box const &source, + animation::Box const &target, + ag::PointModel const &resolution, + float bendFactor, + float offsetFactor, + float stretchFactor, + float deformSpeedFactor, + animation::stepper::Stepper const &stepper) : + priv (new aml::MagicLampAnimation::Private (source, + target, + resolution, + bendFactor, + offsetFactor, + stretchFactor, + deformSpeedFactor, + stepper)) +{ +} + +aml::MagicLampAnimation::~MagicLampAnimation () +{ +} diff --git a/animation/magiclamp/magiclamp.h b/animation/magiclamp/magiclamp.h new file mode 100644 index 0000000..97e1e42 --- /dev/null +++ b/animation/magiclamp/magiclamp.h @@ -0,0 +1,72 @@ +/* + * 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 + +#pragma once + +namespace animation +{ + namespace magiclamp + { + class MagicLampAnimation : + public animation::grid::GridAnimation + { + public: + + MagicLampAnimation (animation::Box const &source, + animation::Box const &target, + animation::geometry::PointModel const &resolution, + float bendFactor, + float offsetFactor, + float stretchFactor, + float deformSpeedFactor, + animation::stepper::Stepper 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::Box ) + ANIMATION_DECLARE_PROPERTY (MagicLampAnimation, Target, animation::Box ) + 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) + + 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..c1196ce --- /dev/null +++ b/animation/magiclamp/meson.build @@ -0,0 +1,34 @@ +# /animation/magiclamp/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 (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/meson.build b/animation/meson.build index 52569a6..06fb36c 100644 --- a/animation/meson.build +++ b/animation/meson.build @@ -27,6 +27,7 @@ animation_headers_subdir = 'animation' subdir('bounce') subdir('glide') subdir('grid') +subdir('magiclamp') subdir('stepper') subdir('transform') subdir('wobbly') diff --git a/tests/magiclamp/magiclamp_animation_test.cpp b/tests/magiclamp/magiclamp_animation_test.cpp new file mode 100644 index 0000000..d22ecf2 --- /dev/null +++ b/tests/magiclamp/magiclamp_animation_test.cpp @@ -0,0 +1,238 @@ +/* + * 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 "wobbly" spring model. + */ +#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 + +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 +{ + 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 (animation::Box (animation::Point (100, 100), + animation::Point (150, 150)), + 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 (animation::Box (animation::Point (100, 100), + animation::Point (150, 150)), + 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 (animation::Box (animation::Point (100, 100), + animation::Point (150, 150)), + 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 (animation::Box (animation::Point (iconX, iconY), + animation::Point (iconX + iconWidth, iconY + iconHeight)), + 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 (animation::Box (animation::Point (100, 100), + animation::Point (150, 150)), + 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 (animation::Box (animation::Point (100, 100), + animation::Point (150, 150)), + 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 b4111aa..0ae505c 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -23,6 +23,7 @@ animation_test_sources = [ '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',