A simple, modern C++ animation and timing library.

Overview

Choreograph

A simple, modern C++ animation and timing library.
v0.4.0

Choreograph is designed to describe motion. With it, you compose motion Phrases into Sequences that can be used to animate arbitrary properties on a Timeline.

Basic Usage

Simple things are simple. You can animate a variable through a sequence of values in Choreograph by creating a sequence of ramps.

// Create a Sequence that is applied to a variable.
timeline.apply( &variable )
  .then<RampTo>( value, 1.0f, EaseInOutQuad() )
  .then<RampTo>( other_value, 0.5f );

Choreograph also provides a range of more sophisticated phrases, including procedures and accumulators. These allow you to do things like combine a left-to-right ramp with a sine-wave procedure. The code shown below is elaborated in the Oscillator sample.

  // Create a procedural phrase that generates a vertical sine wave.
  auto bounce = makeProcedure<vec2>( 2.0, [] ( Time t, Time duration ) {
    return vec2( 0, sin( easeInOutQuad(t) * 6 * M_PI ) * 100.0f );
  } );

  // Create a ramp phrase that moves from left-to-right.
  auto slide = makeRamp( vec2( x1, 0 ), vec2( x2, 0 ), 2.0f, EaseInOutCubic() );

  // Combine the slide and bounce phrases using an AccumulatePhrase.
  float center_y = app::getWindowHeight() / 2.0f;
  auto bounceAndSlide = makeAccumulator<vec2>( vec2( 0, center_y ), bounce, slide );

  timeline.apply( &output, bounceAndSlide );

The code above produces the motion of the purple circle below:

Oscillator Sample

Read the concepts section below and see the projects in Samples/ for more ideas on how to use Choreograph in your own projects.

Concepts

Choreograph breaks the task of animation into two parts: motion description and motion application. Description of motion is handled by Phrases and Sequences. The application of motion is handled by Timelines that manage TimelineItems: Motions (Sequences bound to an Output) and Cues (Functions bound to a point in time).

Phrase, Sequence

A Phrase defines a change in value over time, like "bounce up and down for three seconds."" The built-in phrase RampTo linearly interpolates a value over time. Other built-in Phrases include Hold, which holds a value for an amount of time, and LoopPhrase, which takes an existing Phrase and repeats it a number of times. It is simple to add your own Phrases by extending Phrase<T>.

A Sequence is a collection of Phrases. Sequences are the primary object you manipulate to build animations. Choreograph’s method-chaining syntax allows you to build up sophisticated Sequences by telling them to do one Phrase then another. Sequences can compose other Sequences, too, by either wrapping the Sequence in a Phrase or duplicating the other’s Phrases.

Sequences and Phrases have duration but no concept of playback or the current time. They interpolate values within their duration and clamp values before zero and after their duration.

diagram-phrase-sequence

Motion and Output

A Motion connects a Sequence to an Output. Motions have a sense of starting, finishing, and updating, as well as knowing where in time they currently are.

Outputs are values that can safely be animated with a Motion. The Output<T> template wraps any type so that it can communicate with the Motion applied to it about its lifetime. If either the Motion or the Output goes out of scope, the animation on that Output will stop.

diagram-motion

Generally, you will not create Motions directly, but will receive an interface to them from a Timeline.

// Outputs can safely be animated by a choreograph::Timeline
choreograph::Output<vec3> target;
choreograph::Timeline timeline;
// Create a Motion with a Connection to target and modify
// the Motion’s underlying Sequence.
timeline.apply( &target )
  .then<Hold>( vec3( 1.0 ), 1.0 )
  .then<RampTo>( vec3( 100 ), 3.0 );
timeline.step( 1.0 / 60.0 );

Motions can also be connected to raw pointers using the Timeline::applyRaw() family of methods. If you go this route, you need to be very careful about object lifetime and memory management. The Motion has no way to know if the raw pointer becomes invalid, and the raw pointer won’t know anything about the Motion.

If you cannot wrap your animation target in an Output template, consider creating a Sequence and assigning its value to your target manually. That way you aren’t storing raw pointers in a timeline.

// Recommended approach to animating non-Output types
vec3 target;
// Create a Sequence with initial value from target.
auto sequence = Sequence<vec3>( target.value() )
  .then<Hold>( 1.0, 1.0 )
  .then<RampTo>( vec3( 100 ), 3.0 );
target = sequence.getValue( animationTime );

Cues

Cues are functions that are called at a certain point in time. You can add them to a Timeline to trigger events in the future. They are useful for changing application state that isn’t strictly animatable. The sample application uses cues to manage transitions between each of the samples.

You can use a Control to manage the lifetime of your Cue. Controls are also available for Motions, and can be used to manage Motions affecting raw pointers.

// Controls let you cancel the Cue.
auto control = timeline.cue( [] { "do something..."; }, 10.0f ).getControl();
// Stop the cue from firing by calling cancel().
control.cancel();

// Scoped controls cancel timeline items when they fall out of scope.
// They are handy to store at the same scope as any variables captured by reference in your lambda.
auto scoped_control = timeline.cue( [] { "do something else..."; }, 10.0f ).getScopedControl();

Timeline

Timelines manage a collection of TimelineItems (Motions, Cues, &c). They provide a straightforward interface for connecting Sequences to Outputs and for building up Sequences in-place.

When you create a Motion with a Timeline, you receive a MotionOptions object that provides an interface to manipulate the underlying Sequence as well as the Motion.

diagram-timeline

Since Motions know where they are in time, the Timeline controlling them doesn’t need to. While that may seem strange, this allows us to keep timelines running in perpetuity without worrying about running out of numerical precision. The largest number we ever need to keep precise is the duration of our longest Sequence and its corresponding Motion. Also, I find it feels natural that new Motions always start at time zero.

Building and running

Include the headers in your search path and add the .cpp files to your project (drag them in) and everything should just work. If you are working with Cinder, you can create a new project with Tinderbox and include Choreograph as a block.

Dependencies

Choreograph itself has no third-party dependencies.

You do need a modern C++ compiler, since Choreograph takes advantage of a number of C++11 features. Choreograph is known to work with Apple LLVM 6.0 (Clang 600), and Visual Studio 2013.

Building the Tests

Tests are built and run with the projects inside the tests/ directory. There are test projects for Xcode 6 and Visual Studio 2013. Choreograph’s tests use the Catch framework, a single-header library that is included in the tests/ directory.

Choreograph_test has no linker dependencies, but will try to include vector types from Cinder if INCLUDE_CINDER_HEADERS is true. Including the Cinder headers enables a handful of additional tests, notably those covering the separable component easing of RampToN.

Benchmarks_test relies on the Cinder library. It uses Cinder’s Timer class to measure performance. Benchmarks_test also runs a rough performance comparison between choreograph::Timeline and cinder::Timeline.

Building the Samples

Choreograph’s samples use Cinder for system interaction and graphics display. Any recent version of Cinder's glNext branch should work. Clone Choreograph to your blocks directory to have the sample project work out of the box.

Samples are run from the projects inside the Samples directory. Projects to build the samples exist for iOS and OSX using Xcode and for Windows Desktop using Visual Studio 2013. These are more of a work in progress than the rest of the library.

Interpolating Special Types

If you are using Cinder, Choreograph will automatically include a specialization header so quaternions will be slerped. Where relevant, Phrases also accept an optional interpolation function parameter so you can customize them for other types.

History

The original motivation for having a library like Choreograph came from past experience with Flash tweening libraries like Greensock’s TweenMax. They made programmatic animations art-directable, which was a huge boon for doing work on a team.

The first version of Choreograph served as a proof-of-concept for what eventually became Timeline in libCinder. Cinder’s timeline is an excellent, production-ready tweening option.

The current iteration of Choreograph is redesigned from whole cloth. The library concepts, outlined above, allow for a smaller, more expressive API than what is possible with a tween-centric system. As a bonus, Choreograph’s new design also outperforms Cinder’s timeline in the included benchmarks for creating and running animations.

Comments
  • Make a consistent TimelineItem cancellation scheme.

    Make a consistent TimelineItem cancellation scheme.

    Similar to what we have now for Cues. On destruction, associated TimelineItem will be considered invalid/canceled. Could use to make raw pointer animations potentially safe. Auto-wire scope to Output's lifetime when given a ch::Output type.

    Any way to bring along the source with an Output on std::move, though? Or do we still need the pointer back to the Connection?

    Names: CancellationControlRef ConnectionControlRef TImelineItemControlRef

    opened by sansumbrella 5
  • Ability to animate via setter?

    Ability to animate via setter?

    Is there currently a mechanism to invoke a setter or provide an arbitrary lambda to do the actual value application when a value changes?

    This is useful/valuable for animating properties on objects that need to set a flag or invalidate something when values change (such as visual elements that need to be repainted on a given frame of animation or some such).

    Similarly is it feasible to specify a getter to pull the current "value" rather than a flat property?

    opened by CoreyDotCom 4
  • TimelineItem::cancel() and composable Timelines

    TimelineItem::cancel() and composable Timelines

    Streamlines cancellation of TimelineItems. MotionGroups are a facade around a Timeline, so you can simply compose Timelines. Keep in mind that Timelines and unique_ptrs are move-only objects.

    opened by sansumbrella 4
  • Interface for a sampling approach?

    Interface for a sampling approach?

    The recommended interface to the timeline object is the dt step. If Choreograph were to be used to derive say audio envelopes, it might need to be called in batches because hundreds to thousands of samples might need to be generated per render quantum. At the moment, that would require jumping through to the lambda to get to the phrase, for each sample. It would be nice to have, for efficiency, a batch sampling mechanism, that would be along the lines of

    timeline.jumpToTime(t); timeline.sample(dt, 128);

    That would need an iterating, version of phrases though.

    Anyway, just a thought.

    opened by meshula 2
  • Make Choreograph Header-Only

    Make Choreograph Header-Only

    Consider making the library header-only so user can reliably specify things like the Time type through a macro. Having any files of Choreograph that are compiled prevents the user guaranteeing that they can specify the time type before its first inclusion.

    Enhancement wontfix 
    opened by sansumbrella 2
  • Sequence Slicing

    Sequence Slicing

    Manipulate Sequences with more panache at runtime. Blow away the past if your motion feels like it. Cut to the future sooner if you like. Motions hold onto their Sequence by name, so we don't worry about stepping on our partner's toes.

    opened by sansumbrella 2
  • Default startValue and Cue point timing updates

    Default startValue and Cue point timing updates

    Thanks for starting sc-Choreograph. Coming from AS3 dev, I was looking for a simple tweening engine for cinder and stumbled upon sc-Choregraph. I thought it's a really good place to start and decided to use and understand it better. We used it extensively in one of our recent projects but changed two little handy things:

    • when you add a tween without specifying a start value, it's now using the value the target object has at the start of the tween and not 0.
    • you can add a cue point at any time now. (new Cue( action, mCurrentTime+atTime ))

    Hope you find this changes as useful as we do. Thanks for sharing your work. Daniel.

    opened by dscheibel 2
  • Error on macos - clang

    Error on macos - clang

    Hello,

    This error shows compiling Choreograph on AppleClang 13.1.6.1. I know it is a build error, but I wonder if you have come across it.

    /Users/Choreograph/Choreograph/source/src/choreograph/Sequence.hpp:42:59: error: a space is required between consecutive right angle brackets (use '> >')
    using SequencePhraseRef = std::shared_ptr<SequencePhrase<T>>;
    
    /Users/Choreograph/Choreograph/source/src/choreograph/Motion.hpp:168:5: warning: 'auto' type specifier is a C++11 extension [-Wc++11-extensions]
    
    opened by kmilo9999 1
  • Composable Timelines

    Composable Timelines

    Remove MotionGroup item in favor of Timeline being a timelineitem. Organize tests into files.

    Todo before merge:

    • [x] Test on Windows.
    • [x] Use timeline instead of motiongroup in samples.
    opened by sansumbrella 1
  • make_unique is not supported in Xcode

    make_unique is not supported in Xcode

    Bizarrely, this is the one C++1y/14 thing that works in VS2013 and not Xcode.

    Will either add a conditional function definition in detail or switch back to unique_ptr( new T );

    opened by sansumbrella 1
  • Motion callback simplification

    Motion callback simplification

    Motion callbacks no longer pass through the current Motion reference. This simplifies the common use case.

    If you need a reference to the motion, capture it or the output object in your lambda using output.inputPtr().

    opened by sansumbrella 0
  • Use a chrono::duration type as underlying time representation

    Use a chrono::duration type as underlying time representation

    As demonstrated by the date library, std::chrono operations compile down to simple numeric operations without any extra type information. This means calculations with duration<double, seconds::period> are the same as calculations with double.

    As a benefit, we would get explicit temporal information in our timestep.

      .rampTo(10.0f, Seconds(1.5))
      .rampTo(20.0f, Seconds(1.0) + minutes(2));
    

    On a current project, I have a constexpr converting chrono types to double to pass through to Choreograph. Will be nice to skip that extra writing. Will, however, require explicitly defining the incoming time type.

    opened by sansumbrella 1
  • Explore Channels as underlying animation paradigm

    Explore Channels as underlying animation paradigm

    Channels: stream of values with interpolation Players: playhead referring to a channel Connectors: timeline item connecting a player to an output

    Working on this in the exp_channel branch.

    Would like to be able to write something like:

    channel = Channel<float>{ {1.0f, 0.0}, {Linear}, {10.0f, 2.0}, {Bezier, 0.2, 0.4, 0.8, 1.0}, {20.0f, 3.0} };
    player = Player(channel);
    value = player.step(dt);
    values = player.sample(128, dt);
    
    Channel<float> x, y, z;
    …
    player = Player<vec3>(x, y, z);
    vec3 previous = player.value();
    vec3 value = player.step(dt);
    
    struct Transform {
      float x, y, z;
      quat orientation;
    };
    
    Channel<quat> orientation;
    player = Player<Transform>(x, y, z, orientation);
    Transform xf = player.value();
    
    opened by sansumbrella 1
  • Enable including only Output

    Enable including only Output

    For some types, clang will error out if only choreograph/Output.h is included. Including Choreograph.h fixes the issue, but it would be nice to get to the bottom of it.

    Implicit instantiation of undefined template choreograph::Motion<glm::quat>.

    opened by sansumbrella 0
  • Performance benchmarks

    Performance benchmarks

    Recorded March 29, 2015 for future comparison.

    master 71154c2 2.3 GHz Intel Core i7 Early 2011 MBP

    Sequence Creation and Slicing
    =============================
    [Creating Medium Sequence] .......................   0.005960ms
    [Creating Medium Sequence rhs Assignment] ........   0.003994ms
    [Creating Huge Sequence] ......................... 172.978997ms
    [Slicing Medium Sequence] ........................   0.002980ms
    [Slicing Huge Sequence] ..........................   0.946999ms
    
    Basic Timing for 50000 Motions
    ==============================
    [Creating Motions with Callbacks in place] .......  16.393006ms
    [60 Motion Steps (1sec at 60Hz)] ................. 174.609005ms
    [Disconnecting Motions] ..........................   2.493024ms
    [Step to remove disconnected Motions] ............  22.778034ms
    [Creating Motions from existing Sequence] ........  10.605991ms
    [60 Motion Steps (1sec at 60Hz)] ................. 137.154996ms
    
    Comparative Performance: many targets, short animations.
    ========================================================
    [Choreograph Create Average] .....................   2.072752ms
    [Cinder Create Average] ..........................   3.174737ms
    [Choreograph Step Average] ....................... 161.833778ms
    [Cinder Step Average] ............................ 255.035758ms
    [Create Performance (Choreograph / Cinder)] ......   0.652889
    [Step Performance (Choreograph / Cinder)] ........   0.634553
    
    Comparative Performance: single target, long animation.
    =======================================================
    [Choreograph Create Average] .....................   1.004770ms
    [Cinder Create Average] .......................... 195.051253ms
    [Choreograph Step Average] .......................  11.369735ms
    [Cinder Step Average] ............................  99.682257ms
    [Create Performance (Choreograph / Cinder)] ......   0.005151
    [Step Performance (Choreograph / Cinder)] ........   0.114060
    

    c++11 80b233c 3.6GHz i7-4790 Windows 7

    Sequence Creation and Slicing
    =============================
    [Creating Medium Sequence] .......................   0.007396ms
    [Creating Medium Sequence rhs Assignment] ........   0.007964ms
    [Creating Huge Sequence] ......................... 125.167679ms
    [Slicing Medium Sequence] ........................   0.002844ms
    [Slicing Huge Sequence] ..........................   0.558646ms
    
    Basic Timing for 50000 Motions
    ==============================
    [Creating Motions with Callbacks in place] .......  14.910213ms
    [60 Motion Steps (1sec at 60Hz)] .................  95.661847ms
    [Disconnecting Motions] ..........................   0.764014ms
    [Step to remove disconnected Motions] ............   6.410774ms
    [Creating Motions from existing Sequence] ........   9.174990ms
    [60 Motion Steps (1sec at 60Hz)] .................  84.630298ms
    
    Comparative Performance: many targets, short animations.
    ========================================================
    [Choreograph Create Average] .....................   1.787439ms
    [Cinder Create Average] ..........................   3.202045ms
    [Choreograph Step Average] ....................... 111.521388ms
    [Cinder Step Average] ............................ 150.351401ms
    [Create Performance (Choreograph / Cinder)] ......   0.558218
    [Step Performance (Choreograph / Cinder)] ........   0.741738
    
    Comparative Performance: single target, long animation.
    =======================================================
    [Choreograph Create Average] .....................   0.856102ms
    [Cinder Create Average] .......................... 119.149152ms
    [Choreograph Step Average] .......................   6.777350ms
    [Cinder Step Average] ............................  49.646096ms
    [Create Performance (Choreograph / Cinder)] ......   0.007185
    [Step Performance (Choreograph / Cinder)] ........   0.136513
    
    Metadata 
    opened by sansumbrella 3
  • Make MotionGroup semantics simpler

    Make MotionGroup semantics simpler

    Working with MotionGroup is not fun. Come up with an alternative. Best-seeming solution is the one from previous iterations of Choreograph: make Timeline a TimelineItem.

    ~~Instead of wrapping own timeline, wrap items that are on its parent timeline. Might make it possible to append the grouped animations more simply.~~

    ~~At present, to append a MotionGroup after another one that moves the same output variables, we must use the first group's finishFn to trigger the second group creation. Not a huge deal, but potentially confusing since the groups have their own timelines.~~

    Enhancement 
    opened by sansumbrella 6
Owner
David Wicks
David Wicks
The FastLED library for colored LED animation on Arduino.

The FastLED library for colored LED animation on Arduino. Please direct questions/requests for help to the FastLED Reddit community: http://fastled.io/r We'd like to use github "issues" just for tracking library bugs / enhancements.

FastLED 5.6k Dec 31, 2022
Card_detect and show animation

Collector 收集者 Card_detect and show animation 在全志D1上玩卡牌识别播放动画 项目来源于Kirin。其项目创意及初衷见帖下方评论:https://bbs.aw-ol.com/topic/223/ 具体实现过程也在帖子中 ?? ?? ?? 视频连接: 名字由

BedRock 9 Sep 8, 2022
Dynamic Animation and Robotics Toolkit

Build Status Item Status Build Status API Documentation Coverage Static Analysis Resources Visit the DART website for more information Gallery Install

DART: Dynamic Animation and Robotics Toolkit 765 Jan 1, 2023
Digital rain animation gif with glow squeezed into a raspberry pi pico and pimoroni pico-display

pico-display-matrix Digital rain animation gif with glow squeezed into a raspberry pi pico and pimoroni pico-display or how to actually use all Flash

null 32 Sep 10, 2022
A custom OLED animation for QMK keyboards

superloop This animation is made for 32x128 pixel 1-bit OLED displays. Info This repository is a lightweight clone of the crkdb:default QMK configurat

hexcowboy 28 Nov 28, 2022
qmk based oled bongo cat animation

oledbongocat qmk based oled bongo cat animation step 1: append OLED enable and WPM enable to your rules.mk (see last lines of my rules.mk) step 2: in

null 94 Dec 31, 2022
OpenToonz - An open-source full-featured 2D animation creation software

OpenToonz 日本語 What is OpenToonz? OpenToonz is a 2D animation software published by DWANGO. It is based on Toonz Studio Ghibli Version, originally deve

OpenToonz 3.7k Jan 2, 2023
Simple, fully external, smart, fast, JSON-configurated, feature-rich Windows x86 DLL Memory Dumper with Code Generation. Written in Modern C++.

altdumper Simple, fully external, smart, fast, JSON-configurated, feature-rich Windows x86 DLL Memory Dumper with Code Generation. Written in Modern C

cristei 14 Sep 9, 2022
MyOwnBricks - A library for building your own sensors and devices compatible with the modern LEGO PoweredUp system.

English version (See at the end for the French version) MyOwnBricks MyOwnBricks is a library for building your own sensors and devices compatible with

null 5 Sep 26, 2022
Digital Signal Processing Library and Audio Toolbox for the Modern Synthesist.

Digital Signal Processing Library and Audio Toolbox for the Modern Synthesist. Attention This library is still under development! Read the docs and ch

everdrone 81 Nov 25, 2022
Vritual Traffic Cabinet - a modern C++ library for virtualization of traffic control cabinet in adherence with NEMA-TS and ATC Standards.

Virtual Traffic Cabinet A modern C++ library (C++20) for the virtualization of traffic cabinet in adherence with NEMA-TS1, NEMA-TS2, 170, Caltrans 232

Wuping Xin 3 Nov 22, 2022
SSD1306 library and simple graphics core library based on Adafruit GFX Library.

Raspberry Pico SSD1306 + GFX Library Based on Adafruit GFX Library https://github.com/adafruit/Adafruit-GFX-Library Usage Hardware Connect your SSD130

Marcin Bober 31 Sep 1, 2022
A Minimal, Header only Modern c++ library for terminal goodies 💄✨

rang Colors for your Terminal. Windows Demo Example usage #include "rang.hpp" using namespace std; using namespace rang; int main() { cout << "P

Abhinav Gauniyal 1.3k Dec 30, 2022
A modern C++ tweening library

Tweeny Tweeny is an inbetweening library designed for the creation of complex animations for games and other beautiful interactive software. It levera

Leonardo Guilherme de Freitas 578 Dec 22, 2022
A modern ESM build of the Potrace library for use in the browser.

ESM Potrace Wasm A modern ESM build of the Potrace library for use in the browser. Installation npm install --save esm-potrace-wasm Usage import potra

Thomas Steiner 40 Nov 5, 2022
Modern transactional key-value/row storage library.

Sophia is advanced transactional MVCC key-value/row storage library. How does it differ from other storages? Sophia is RAM-Disk hybrid storage. It is

Dmitry Simonenko 1.8k Dec 15, 2022
Gaming meets modern C++ - a fast and reliable entity component system (ECS) and much more

EnTT is a header-only, tiny and easy to use library for game programming and much more written in modern C++. Among others, it's used in Minecraft by

Michele Caini 7.6k Dec 30, 2022
A simple implementation of a parser and its use to calculate simple mathematical expressions

Calculator C Parser A simple implementation of a parser and its use to calculate simple mathematical expressions I haven't written a detailed descript

Romes 14 Nov 8, 2021