It solves all my C++ animation problems
-- me
Use rtl::var<T>
#include <rtl/var.h>
...
rtl::var<float> bar = 2.0f;
rtl::var<float> foo = 3.0f;
rtl::var<float> foobar = [&](){ return foo() + bar(); };
printf("foobar = %f\n", foobar());
foo = 2.0f;
printf("foobar = %f\n", foobar());
bar = 1.0f;
printf("foobar = %f\n", foobar());RTL uses the rtl::clock class as a source of time, and if no instance is explicitly provided for rtl::var and rtl::animator they will use whatever the thread static rtl::clock::current_clock points to.
This clock is by default pointing to the thread static rtl::clock::default_clock. However, we need to update this clock every frame by calling rtl::clock::default_clock.adjust() with the current time in seconds:
#include <rtl/clock.h>
...
rtl:clock::default_clock.adjust(get_time_in_seconds()); Reacting to value changes
With rtl::clock set up, you can now use rtl::animator to react to changes.
Even if multiple dependencies change the animator callback is run at max once per frame, avoiding wrong intermediate states.
#include <rtl/animator.h>
...
rtl::animator foo_printer = [&]()
{
printf("foo = %f\n", foo());
};
rtl::animator bar_printer = [&]()
{
printf("bar = %f\n", bar());
};
rtl::animator foobar_printer = [&]()
{
printf("foobar = %f\n", foobar());
};
foo = 3.0f; // prints nothing yet
bar = 2.0f; // prints nothing yet
// later..
rtl:clock::default_clock.adjust(get_time_in_seconds()); // prints foo = 3, bar = 2, foobar = 5
bar = 3.0f; // prints nothing again
rtl:clock::default_clock.adjust(get_time_in_seconds()); // prints bar = 3, foobar = 6Triggering animations
foo.animate(1.0f, 0.5, &rtl::easing::linear, [&]()
{
foo.animate(0.0f, 0.5, &rtl::easing::linear, [&]()
{
printf("animation complete!\n");
});
});Again note that rtl::clock::frame_clock.adjust() must have been called at least once before triggering any animations.
While it is often easier to just use an internal rtl::var<T> or rtl::animator, RTL can be extended at a lower level through the graph API.
Adding new value producers
-
Include relevant header
#include <rtl/graph/dependency.h>
-
Make an
rtl::dependencydependency my_dependency;
-
When evaluated, notify the current visitor
my_dependency.visit();
-
When changed, invalidate dependants
my_dependency.invalidate();
Adding new value consumers
-
Include relevant headers
#include <rtl/graph/dependant.h> #include <rtl/graph/visitor.h>
-
Implement
rtl::invalidatable, e.g. usingrtl::invalidator#include <rtl/invalidator.h> invalidator my_invalidator = []() { // one of the dependencies was invalidated ... };
-
Make an
rtl::dependantdependant my_dependant(&my_invalidator); -
Guard any calls to reactive functions with an instance of
rtl::visitor{ visitor my_visitor(&my_dependant); // evaluate dependencies here ... }
Inteded for single-threaded use
MIT-licence here