Giter VIP home page Giter VIP logo

choreograph's Introduction

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.

choreograph's People

Contributors

andrewfb avatar notlion avatar sansumbrella avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

choreograph's Issues

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

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?

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.

Control fps

How the way we can control fps?
Example to ensure the animation looks smooth at ~60fps

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 );

Clean up example project

Name files based on the actual examples shown, remove unused files, simplify the sample base class.

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]

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.

Add CMakeLists.txt

Integration is by copying the files, wouldn't it be better to provide a CMakeLists.txt to deal with messy parts that are compilation and headers ?

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.

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

Consider removing copy-ctor from Output

Currently it lies about the constness of its argument. Not lying would cause potentially confusing errors if someone stored Outputs directly in a std::vector.

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();

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.

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>.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.