1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

Animation: Add juce_animation module

This commit is contained in:
attila 2022-11-01 11:15:39 +01:00 committed by Anthony Nicholls
parent bc6295d7b5
commit bc3600cde8
22 changed files with 2731 additions and 4 deletions

View file

@ -273,6 +273,7 @@ StringArray getJUCEModules() noexcept
static StringArray juceModuleIds = static StringArray juceModuleIds =
{ {
"juce_analytics", "juce_analytics",
"juce_animation",
"juce_audio_basics", "juce_audio_basics",
"juce_audio_devices", "juce_audio_devices",
"juce_audio_formats", "juce_audio_formats",

View file

@ -34,6 +34,7 @@ juce_add_modules(
INSTALL_PATH "include/JUCE-${JUCE_VERSION}/modules" INSTALL_PATH "include/JUCE-${JUCE_VERSION}/modules"
ALIAS_NAMESPACE juce ALIAS_NAMESPACE juce
juce_analytics juce_analytics
juce_animation
juce_audio_basics juce_audio_basics
juce_audio_devices juce_audio_devices
juce_audio_formats juce_audio_formats

View file

@ -0,0 +1,234 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
class Animator::Impl
{
public:
virtual ~Impl() = default;
virtual double getDurationMs() const
{
return 0.0;
}
void start()
{
shouldStart = true;
shouldComplete = false;
}
void complete()
{
shouldComplete = true;
}
Animator::Status update (double timestampMs)
{
if (std::exchange (shouldStart, false))
{
onStart (timestampMs);
running = true;
}
if (! running)
return Animator::Status::idle;
const auto status = internalUpdate (timestampMs);
if (status != Animator::Status::finished && ! shouldComplete)
return status;
shouldComplete = false;
running = false;
onComplete();
return Animator::Status::finished;
}
virtual bool isComplete() const { return shouldComplete; }
virtual void onStart (double timeStampMs) = 0;
virtual void onComplete() = 0;
virtual Animator::Status internalUpdate (double timestampMs) = 0;
bool shouldStart = false, shouldComplete = false, running = false;
};
//==============================================================================
Animator::Animator (std::shared_ptr<Impl> ai)
: ptr (std::move (ai))
{
jassert (ptr != nullptr);
}
double Animator::getDurationMs() const
{
return ptr->getDurationMs();
}
void Animator::start() const
{
ptr->start();
}
void Animator::complete() const
{
ptr->complete();
}
Animator::Status Animator::update (double timestampMs) const
{
return ptr->update (timestampMs);
}
bool Animator::isComplete() const
{
return ptr->isComplete();
}
//==============================================================================
#if JUCE_UNIT_TESTS
struct AnimatorTests : public UnitTest
{
AnimatorTests()
: UnitTest ("Animator", UnitTestCategories::gui)
{
}
struct TestEvents
{
enum class TestEventType
{
animatorStarted,
animatorEnded
};
struct TestEvent
{
TestEvent (TestEventType e, int a) : eventType (e), animatorId (a) {}
TestEventType eventType;
int animatorId;
};
void started (int animatorId)
{
events.emplace_back (TestEventType::animatorStarted, animatorId);
}
void ended (int animatorId)
{
events.emplace_back (TestEventType::animatorEnded, animatorId);
}
bool animatorStartedBeforeAnotherStarted (int animator, int before)
{
for (const auto& event : events)
{
if (event.animatorId == before && event.eventType == TestEventType::animatorStarted)
return false;
if (event.animatorId == animator && event.eventType == TestEventType::animatorStarted)
return true;
}
return false;
}
bool animatorStartedBeforeAnotherEnded (int animator, int before)
{
for (const auto& event : events)
{
if (event.animatorId == before && event.eventType == TestEventType::animatorEnded)
return false;
if (event.animatorId == animator && event.eventType == TestEventType::animatorStarted)
return true;
}
return false;
}
private:
std::vector<TestEvent> events;
};
void runTest() override
{
beginTest ("AnimatorSet starts end ends Animators in the right order");
{
TestEvents events;
auto createTestAnimator = [&events] (int animatorId)
{
return ValueAnimatorBuilder {}
.withOnStartCallback ([&events, animatorId]
{
events.started (animatorId);
return [] (auto) {};
})
.withOnCompleteCallback ([&events, animatorId]
{
events.ended (animatorId);
})
.build();
};
auto stage1 = AnimatorSetBuilder { createTestAnimator (1) };
stage1.followedBy (createTestAnimator (2));
stage1.togetherWith (createTestAnimator (3));
Animator animator = stage1.build();
animator.start();
for (double timeMs = 0.0; animator.update (timeMs) != Animator::Status::finished; timeMs += 16.667)
;
expect (events.animatorStartedBeforeAnotherEnded (1, 2));
expect (! events.animatorStartedBeforeAnotherEnded (2, 1));
expect (events.animatorStartedBeforeAnotherEnded (3, 1));
expect (events.animatorStartedBeforeAnotherStarted (3, 2));
}
}
};
static AnimatorTests animatorTests;
#endif
} // namespace juce

View file

@ -0,0 +1,210 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Wrapper class for managing the lifetime of all the different animator kinds created through the
builder classes.
It uses reference counting. If you copy an Animator the resulting object will refer to the same
underlying instance, and the underlying instance is guaranteed to remain valid for as long as
you have an Animator object referencing it.
An Animator object can be registered with the AnimatorUpdater, which only stores a weak
reference to the underlying instance. If an AnimatorUpdater references the underlying instance
and it becomes deleted due to all Animator objects being deleted, the updater will automatically
remove it from its queue, so manually removing it is not required.
@see ValueAnimatorBuilder, AnimatorSetBuilder, AnimatorUpdater, VBlankAnimatorUpdater
@tags{Animations}
*/
class JUCE_API Animator
{
public:
class Impl;
/** The state of an Animator that determines how AnimatorUpdaters and other Animators will
interact with it.
*/
enum class Status
{
/** The Animator is idle and its state is not progressing even if it is attached to an
AnimatorUpdater.
*/
idle,
/** The Animator is active and its state is progressing whenever its update function is
called.
*/
inProgress,
/** The Animator finished its run and its onCompletion callback may be called. It requires
no further calls to its update function.
*/
finished
};
/** @internal
Constructor. Used by the builder classes.
@see ValueAnimatorBuilder, AnimatorSetBuilder
*/
explicit Animator (std::shared_ptr<Impl>);
/** Returns the total animation duration in milliseconds. */
double getDurationMs() const;
/** Marks the Animator ready for starting. You must call this function to allow the Animator to
move out of the idle state.
After calling this function the Animator's on start callback will be executed at the next
update immediately followed by the first call to it's update function.
You can call this function before or after adding the Animator to an AnimatorUpdater. Until
start() is called the Animator will just sit idly in the updater's queue.
*/
void start() const;
/** Marks the Animator ready to be completed. ValueAnimators will be completed automatically
when they reach a progress >= 1.0 unless they are infinitely running. AnimatorSets will also
complete on their own when all of their constituent Animators complete.
Using this function you can fast track the completion of an Animator. After calling this
function isComplete will return true, and it's guaranteed that you will receive an update
callback with a progress value of 1.0. After this the onComplete callback will be executed.
*/
void complete() const;
/** Called periodically for active Animators by the AnimatorUpdater classes. The passed in
timestamp must be monotonically increasing. This allows the underlying Animator
to follow its progression towards completion.
While you can call this function in special circumstances, you will generally want an
AnimatorUpdater to do it. Using the VBlankAnimatorUpdater ensures that update is called in
sync with the monitor's vertical refresh resulting in smooth animations.
@see AnimatorUpdater, VBlankAnimatorUpdater
*/
Status update (double timestampMs) const;
/** Returns true if the Animator has reached the point of completion either because complete()
has been called on it, or in case of the ValueAnimator, if it reached a progress of >= 1.0.
You typically don't need to call this function, because in any case a completed Animator
will receive an update callback with a progress value of 1.0 and following that the
on complete callback will be called.
*/
bool isComplete() const;
/** Comparison function used by the implementation to store Animators in ordered collections.
It can also be used to determine equality of Animator objects based on whether they
reference the same underlying implementation.
*/
struct JUCE_API Compare
{
/** Comparison function. */
bool operator() (const Animator& a, const Animator& b) const { return a.ptr < b.ptr; }
};
/** @internal
Stores a weak reference to the Animator's underlying implementation. Animator objects store
a strong reference to the implementation, so it won't be deleted as long as an Animator
object references it. Instead of copying the Animator, you can use makeWeak() to create a
weak reference, which will not prevent deletion of the underlying implementation, but allows
you to create a strong reference using the lock function for as long as the underlying
object is alive.
This class is used by the AnimatorUpdater, and it's unlikely you will need to use it
directly.
@see AnimatorUpdater, VBlankAnimatorUpdater
*/
class JUCE_API Weak
{
public:
/** Constructor used by the Animator implementation. To obtain a weak reference use
Animator::makeWeak().
*/
Weak() = default;
/** Constructor used by the Animator implementation. To obtain a weak reference use
Animator::makeWeak().
*/
explicit Weak (std::shared_ptr<Impl> p) : ptr (p), originalPtr (p.get()) {}
/** If the referenced Animator implementation object still exists it returns an Animator
object storing a strong reference to it.
If the implementation object was deleted it returns a nullopt.
*/
std::optional<Animator> lock() const
{
if (const auto l = ptr.lock())
return Animator { l };
return {};
}
/** @internal
Used internally for storing the reference in a std::map.
*/
void* getKey() const
{
return originalPtr;
}
private:
std::weak_ptr<Impl> ptr;
Impl* originalPtr{};
};
/** @internal
Returns a weak reference to the underlying implementation. You can use Weak::lock() to
obtain a strong reference as long as the underlying object has not been deleted.
*/
Weak makeWeak() const { return Weak { ptr }; }
private:
std::shared_ptr<Impl> ptr;
};
} // namespace juce

View file

@ -0,0 +1,291 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
class DelayAnimator : public Animator::Impl
{
public:
double getDurationMs() const override { return delayMs; }
static Animator build (double delayMsIn, std::function<void()> callbackIn = nullptr)
{
return Animator { rawToUniquePtr (new DelayAnimator (delayMsIn, std::move (callbackIn))) };
}
private:
DelayAnimator (double delayMsIn, std::function<void()> callbackIn)
: onCompletion (std::move (callbackIn)),
delayMs (delayMsIn)
{
}
Animator::Status internalUpdate (double timestampMs) override
{
if (timestampMs - startedAtMs >= delayMs)
return Animator::Status::finished;
return Animator::Status::inProgress;
}
void onStart (double timeMs) override { startedAtMs = timeMs; }
void onComplete() override { NullCheckedInvocation::invoke (onCompletion); }
std::function<void()> onCompletion;
double startedAtMs = 0.0, delayMs = 0.0;
};
//==============================================================================
struct AnimatorSetData
{
explicit AnimatorSetData (Animator root)
: roots { root }, entries { { root, {} } } {}
struct Entry
{
std::optional<Animator> parent; // If no parent, this is the root node
std::set<Animator, Animator::Compare> children;
};
auto getRoots() const { return roots; }
auto getChildren (const Animator& a) const
{
if (const auto iter = entries.find (a); iter != entries.end())
return iter->second.children;
return std::set<Animator, Animator::Compare>();
}
std::set<Animator, Animator::Compare> roots;
std::map<Animator, Entry, Animator::Compare> entries;
std::function<double (double)> timeTransform;
};
class AnimatorSet : public Animator::Impl
{
public:
explicit AnimatorSet (AnimatorSetData dataIn) : data (std::move (dataIn)) {}
double getDurationMs() const override
{
const auto roots = data.getRoots();
return getMaxDuration (roots.begin(), roots.end());
}
private:
void onStart (double timestampMs) override
{
startedAtMs = timestampMs;
active = data.getRoots();
for (const auto& i : active)
i.start();
}
void onComplete() override {}
Animator::Status internalUpdate (double timestampMs) override
{
const auto internalTimestampMs = [&]
{
if (data.timeTransform == nullptr)
return timestampMs;
return data.timeTransform (timestampMs - startedAtMs);
}();
if (isComplete())
{
for (auto i : active)
i.complete();
while (updateAnimatorSet (internalTimestampMs) != Animator::Status::finished)
{
}
return Animator::Status::finished;
}
return updateAnimatorSet (internalTimestampMs);
}
Animator::Status updateAnimatorSet (double timestampMs)
{
std::set<Animator, Animator::Compare> animatorsToRemove;
const auto currentlyActive = active;
for (const auto& animator : currentlyActive)
{
const auto status = animator.update (timestampMs);
if (status == Animator::Status::finished)
{
animatorsToRemove.insert (animator);
for (const auto& j : data.getChildren (animator))
{
j.start();
if (isComplete())
j.complete();
active.insert (j);
}
}
}
for (auto i : animatorsToRemove)
active.erase (i);
return active.empty() ? Animator::Status::finished : Animator::Status::inProgress;
}
template <typename It>
double getMaxDuration (It begin, It end) const
{
return std::accumulate (begin, end, 0.0, [this] (const auto acc, const auto& anim)
{
const auto children = data.getChildren (anim);
return std::max (acc, anim.getDurationMs() + getMaxDuration (children.begin(), children.end()));
});
}
const AnimatorSetData data;
std::set<Animator, Animator::Compare> active;
double startedAtMs = 0.0;
};
//==============================================================================
struct AnimatorSetBuilder::AnimatorSetBuilderState
{
explicit AnimatorSetBuilderState (Animator animator)
: data (std::move (animator)) {}
bool valid = true;
AnimatorSetData data;
};
AnimatorSetBuilder::AnimatorSetBuilder (Animator startingAnimator)
: AnimatorSetBuilder (startingAnimator,
std::make_shared<AnimatorSetBuilderState> (startingAnimator))
{}
AnimatorSetBuilder::AnimatorSetBuilder (double delayMs)
: AnimatorSetBuilder (DelayAnimator::build (delayMs, nullptr))
{}
AnimatorSetBuilder::AnimatorSetBuilder (std::function<void()> cb)
: AnimatorSetBuilder (DelayAnimator::build (0.0, std::move (cb)))
{}
AnimatorSetBuilder::AnimatorSetBuilder (std::shared_ptr<AnimatorSetBuilderState> dataIn)
: cursor (*dataIn->data.getRoots().begin()), state (std::move (dataIn))
{}
AnimatorSetBuilder::AnimatorSetBuilder (Animator cursorIn, std::shared_ptr<AnimatorSetBuilderState> dataIn)
: cursor (cursorIn), state (std::move (dataIn))
{}
AnimatorSetBuilder AnimatorSetBuilder::togetherWith (Animator animator)
{
add (state->data.entries.at (cursor).parent, animator);
return { animator, state };
}
AnimatorSetBuilder AnimatorSetBuilder::followedBy (Animator animator)
{
add (cursor, animator);
return { animator, state };
}
void AnimatorSetBuilder::add (std::optional<Animator> parent, Animator child)
{
state->data.entries.emplace (child, AnimatorSetData::Entry { parent, {} });
if (parent.has_value())
state->data.entries.at (*parent).children.insert (child);
else
state->data.roots.insert (child);
}
Animator AnimatorSetBuilder::build()
{
if (state == nullptr)
{
/* If you're hitting this assertion, you've already used this AnimatorSetBuilder to build an
AnimatorSet.
To create another AnimatorSet you need to use another AnimatorSetBuilder independently
created with the AnimatorSetBuilder (Animator) constructor.
*/
jassertfalse;
return ValueAnimatorBuilder{}.build();
}
auto animator = Animator { std::make_unique<AnimatorSet> (std::move (state->data)) };
state = nullptr;
return animator;
}
AnimatorSetBuilder AnimatorSetBuilder::followedBy (double delayMs)
{
return followedBy (DelayAnimator::build (delayMs, nullptr));
}
AnimatorSetBuilder AnimatorSetBuilder::followedBy (std::function<void()> cb)
{
return followedBy (DelayAnimator::build (0.0, std::move (cb)));
}
AnimatorSetBuilder AnimatorSetBuilder::togetherWith (double delayMs)
{
return togetherWith (DelayAnimator::build (delayMs, nullptr));
}
AnimatorSetBuilder AnimatorSetBuilder::togetherWith (std::function<void()> cb)
{
return togetherWith (DelayAnimator::build (0.0, std::move (cb)));
}
AnimatorSetBuilder AnimatorSetBuilder::withTimeTransform (std::function<double (double)> timeTransformIn)
{
state->data.timeTransform = std::move (timeTransformIn);
return *this;
}
} // namespace juce

View file

@ -0,0 +1,143 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A builder class that can be used to construct an Animator wrapping an AnimatorSet
implementation. It allows you to compose larger, complex animations by executing multiple
constituent Animator instances in a coordinated manner. It essentially builds an Animator
with an execution graph referencing other Animators.
Unlike ValueAnimatorBuilder, objects of AnimatorSetBuilder returned by its member functions
reference the same underlying, modifiable builder instance. For this reason build() can be
called only once on an underlying builder instance. This is to allow you to attach Animators to
different points of the execution graph.
E.g. to have two functions followed by different amounts of delay, each followed by another
function you would write the following.
@code
// Both objects reference the same execution graph, but also refer to different Animators in it
auto builderReferencingFirst = AnimatorSetBuilder { firstFunction };
auto builderReferencingSecond = builderReferencingFirst.togetherWith (secondFunction);
builderReferencingFirst.followedBy (200).followedBy (thirdFunction);
builderReferencingSecond.followedBy (500).followedBy (fourthFunction);
// You could use any one of the builder objects that refer to the same execution graph
auto animator = builderReferencingFirst.build();
@endcode
@tags{Animations}
*/
class JUCE_API AnimatorSetBuilder
{
public:
/** Creates a new builder instance specifying the startingAnimator as the first Animator that is
started.
*/
explicit AnimatorSetBuilder (Animator startingAnimator);
/** Creates a builder with an empty starting animation that completes after delayMs.
*/
explicit AnimatorSetBuilder (double delayMs);
/** Creates a builder with a starting animation that completes at the first update and executes
the provided callback function.
*/
explicit AnimatorSetBuilder (std::function<void()> cb);
/** Adds an Animator to the execution graph that will start executing at the same time as the
Animator provided last to this builder object.
*/
AnimatorSetBuilder togetherWith (Animator animator);
/** Adds an empty Animator to the execution graph that will start executing at the same time as
the Animator provided last to this builder object, and completes in delayMs.
*/
AnimatorSetBuilder togetherWith (double delayMs);
/** Adds an empty Animator to the execution graph that will start executing at the same time as
the Animator provided last to this builder object, completes upon its first update, and
executes the provided callback.
*/
AnimatorSetBuilder togetherWith (std::function<void()> cb);
/** Adds an Animator to the execution graph that will start executing after the Animator
provided last to this builder object completes.
*/
AnimatorSetBuilder followedBy (Animator animator);
/** Adds an empty Animator to the execution graph that will start executing after the Animator
provided last to this builder object
*/
AnimatorSetBuilder followedBy (double delayMs);
/** Adds an empty Animator to the execution graph that will start executing after the Animator
provided last to this builder object, completes upon its first update, and executes the
provided callback.
*/
AnimatorSetBuilder followedBy (std::function<void()> cb);
/** Specifies a time transformation function that the built Animator should utilise, allowing
accelerating and decelerating the entire set of Animators.
The provided function should be monotonically increasing.
*/
AnimatorSetBuilder withTimeTransform (std::function<double (double)> transform);
/** Builds an Animator that executes the previously described and parameterised execution graph.
This function should only be called once for every AnimatorSetBuilder created by its public
constructor.
*/
Animator build();
private:
struct AnimatorSetBuilderState;
explicit AnimatorSetBuilder (std::shared_ptr<AnimatorSetBuilderState> dataIn);
AnimatorSetBuilder (Animator cursorIn, std::shared_ptr<AnimatorSetBuilderState> dataIn);
void add (std::optional<Animator> parent, Animator child);
Animator cursor;
std::shared_ptr<AnimatorSetBuilderState> state;
};
} // namespace juce

View file

@ -0,0 +1,100 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
void AnimatorUpdater::addAnimator (const Animator& animator)
{
addAnimator (animator, nullptr);
}
void AnimatorUpdater::addAnimator (const Animator& animator, std::function<void()> onComplete)
{
Entry entry { animator.makeWeak(), std::move (onComplete) };
animators[entry.animator.getKey()] = std::move (entry);
}
void AnimatorUpdater::removeAnimator (const Animator& animator)
{
if (auto it = animators.find (animator.makeWeak().getKey()); it != animators.end())
{
if (it == currentIterator)
{
++currentIterator;
iteratorServiced = false;
}
animators.erase (it);
}
}
void AnimatorUpdater::update()
{
if (reentrancyGuard)
{
// If this is hit, one of the animators is trying to update itself
// recursively. This is a bad idea! Inspect the callstack to find the
// cause of the problem.
jassertfalse;
return;
}
const ScopedValueSetter setter { reentrancyGuard, true };
const auto timestampMs = Time::getMillisecondCounterHiRes();
for (currentIterator = animators.begin(); currentIterator != animators.end();)
{
auto& current = *currentIterator;
if (const auto locked = current.second.animator.lock())
{
iteratorServiced = true;
if (locked->update (timestampMs) == Animator::Status::finished)
NullCheckedInvocation::invoke (current.second.onComplete);
if (iteratorServiced && currentIterator != animators.end())
++currentIterator;
}
else
{
currentIterator = animators.erase (currentIterator);
}
}
}
} // namespace juce

View file

@ -0,0 +1,107 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Helper class to update several animators at once, without owning or otherwise extending the
lifetimes of those animators.
The intended use case is to register Animators with an updater as opposed to separately calling
Animator::update() on each of them. Calling update() then will update all registered Animators.
In case an Animator's underlying implementation is deleted (all Animator objects that were
strongly referencing it were deleted) it is automatically removed by the AnimatorUpdater.
If you want to update all your Animators in sync with the display refresh you will probably want
to use the VBlankAnimatorUpdater.
The order in which Animator::update() functions are called for registered Animators is not
specified, as Animators should be implemented in a way where it doesn't matter.
@see VBlankAnimatorUpdater
@tags{Animations}
*/
class JUCE_API AnimatorUpdater
{
public:
/** Registers an Animator with the updater.
*/
void addAnimator (const Animator& animator);
/** Registers an Animator with the updater and specifies a callback to be called upon the
completion of the Animator.
This callback can be used for cleanup purposes e.g.
@code
animatorUpdater.addAnimator (someComponentPtr->getAnimator(),
[&someComponentPtr] { someComponentPtr.reset(); });
@endcode
*/
void addAnimator (const Animator& animator, std::function<void()> onComplete);
/** Removes an Animator
*/
void removeAnimator (const Animator& animator);
/** Calls Animator::update() for all registered Animators that are still alive. References to
deleted Animators are removed.
*/
void update();
private:
struct JUCE_API Entry
{
Entry() = default;
Entry(Animator::Weak animatorIn, std::function<void()> onCompleteIn)
: animator (animatorIn),
onComplete (std::move (onCompleteIn))
{}
Animator::Weak animator;
std::function<void()> onComplete;
};
std::map<void*, Entry> animators;
std::map<void*, Entry>::iterator currentIterator;
bool iteratorServiced = false;
bool reentrancyGuard = false;
};
} // namespace juce

View file

@ -0,0 +1,156 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
std::function<float (float)> Easings::createCubicBezier (float x1, float y1, float x2, float y2)
{
// The x axis represents time, it's important this always stays in the range 0 - 1
jassert (isPositiveAndNotGreaterThan (x1, 1.0f));
jassert (isPositiveAndNotGreaterThan (x2, 1.0f));
chromium::gfx::CubicBezier cubicBezier { (double) x1, (double) y1, (double) x2, (double) y2 };
return [bezier = std::move (cubicBezier)] (float v) { return (float) bezier.Solve (v); };
}
std::function<float (float)> Easings::createCubicBezier (Point<float> controlPoint1,
Point<float> controlPoint2)
{
return createCubicBezier (controlPoint1.getX(),
controlPoint1.getY(),
controlPoint2.getX(),
controlPoint2.getY());
}
std::function<float (float)> Easings::createEase()
{
const static auto f = createCubicBezier (0.25f, 0.1f, 0.25f, 1.0f);
return f;
}
std::function<float (float)> Easings::createEaseIn()
{
const static auto f = createCubicBezier (0.42f, 0.0f, 1.0f, 1.0f);
return f;
}
std::function<float (float)> Easings::createEaseOut()
{
const static auto f = createCubicBezier (0.0f, 0.0f, 0.58f, 1.0f);;
return f;
}
std::function<float (float)> Easings::createEaseInOut()
{
const static auto f = createCubicBezier (0.42f, 0.0f, 0.58f, 1.0f);
return f;
}
std::function<float (float)> Easings::createLinear()
{
return [] (auto x){ return x; };
}
std::function<float (float)> Easings::createEaseOutBack()
{
const static auto f = createCubicBezier (0.34f, 1.56f, 0.64f, 1.0f);
return f;
}
std::function<float (float)> Easings::createEaseInOutCubic()
{
const static auto f = createCubicBezier (0.65f, 0.0f, 0.35f, 1.0f);
return f;
}
std::function<float (float)> Easings::createSpring (const SpringEasingOptions& options)
{
return [=] (float v)
{
const auto t = std::clamp (v, 0.0f, 1.0f);
const auto omega = 2.0f * MathConstants<float>::pi * options.getFrequency();
const auto physicalValue = 1.0f - std::exp (-options.getAttenuation() * t) * std::cos (omega * t);
const auto squish = 1.0f / options.getExtraAttenuationRange();
const auto shift = 1.0f - options.getExtraAttenuationRange();
const auto weight = std::clamp (std::pow (squish * (std::max (t - shift, 0.0f)), 2.0f), 0.0f, 1.0f);
return weight + (1.0f - weight) * physicalValue;
};
}
std::function<float (float)> Easings::createBounce (int numBounces)
{
jassert (numBounces >= 0);
numBounces = std::max (0, numBounces);
const auto alpha = std::pow (0.05f, 1.0f / (float) numBounces);
const auto fallTime = [] (float h)
{
return std::sqrt (2.0f * h);
};
std::vector<float> bounceTimes;
bounceTimes.reserve ((size_t) (numBounces + 1));
bounceTimes.push_back (fallTime (1.0f));
for (int i = 1; i < numBounces + 1; ++i)
bounceTimes.push_back (bounceTimes.back() + 2.0f * fallTime (std::pow (alpha, (float) i)));
for (auto& bounce : bounceTimes)
bounce /= bounceTimes.back();
return [alpha, times = std::move (bounceTimes)] (float v)
{
v = std::clamp (v, 0.0f, 1.0f);
const auto boundIt = std::lower_bound (times.begin(), times.end(), v);
if (boundIt == times.end())
return 1.0f;
const auto i = (size_t) std::distance (times.begin(), boundIt);
const auto height = i == 0 ? 1.0f : std::pow (alpha, (float) i);
const auto center = i == 0 ? 0.0f : (times[i] + times[i - 1]) / 2.0f;
const auto distToZero = i == 0 ? times[i] : (times[i] - times[i - 1]) / 2.0f;
return 1.0f - height * (1.0f - std::pow (1.0f / distToZero * (v - center), 2.0f));
};
}
std::function<float (float)> Easings::createOnOffRamp()
{
return [] (float x) { return 1.0f - std::abs (2.0f * (x - 0.5f)); };
}
} // namespace juce

View file

@ -0,0 +1,232 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** A selection of options available for customising a spring style easing function. */
class SpringEasingOptions
{
public:
/** Specifies the number of oscillations the easing would undergo.
This also affects the speed of the movement.
@see getFrequency, withAttenuation, withExtraAttenuationRange
*/
[[nodiscard]] auto withFrequency (float newFrequency) const
{
return withMember (*this, &SpringEasingOptions::frequency, newFrequency);
}
/** Affects how quickly the oscillations die down.
@see getAttenuation, withFrequency, withExtraAttenuationRange
*/
[[nodiscard]] auto withAttenuation (float newAttenuation) const
{
return withMember (*this, &SpringEasingOptions::attenuation, newAttenuation);
}
/** Specifies the input value at which an extra non-physical attenuation begins to be applied.
The value must be in the range [0.05f, 0.98f].
This ensures that the easing always reaches an output value of 1.0f when the input value is
1.0f. If the attenuation is set sufficiently high this won't have a visible effect.
@see getExtraAttenuationRange, withFrequency, withAttenuation
*/
[[nodiscard]] auto withExtraAttenuationRange (float newExtraAttenuationRange) const
{
return withMember (*this, &SpringEasingOptions::extraAttenuationRange, std::clamp (newExtraAttenuationRange, 0.05f, 0.98f));
}
/** Returns the value specified by withFrequency.
If no value was specified the default value is 3.0f.
@see withFrequency
*/
[[nodiscard]] auto getFrequency() const { return frequency; }
/** Returns the value specified by withAttenuation.
If no value was specified the default value is 3.0f.
@see withAttenuation
*/
[[nodiscard]] auto getAttenuation() const { return attenuation; }
/** Returns the value specified by withExtraAttenuationRange
If no value was specified the default value is 0.25f.
@see withExtraAttenuationRange
*/
[[nodiscard]] auto getExtraAttenuationRange() const { return extraAttenuationRange; }
private:
float frequency = 3.0f;
float attenuation = 3.0f;
float extraAttenuationRange = 0.25f;
};
//==============================================================================
/**
Holds a number of easing functions that you can pass into ValueAnimatorBuilder::withEasing to
transform the linear progression of animations.
Using createSpring() for example would transform a rigid movement into one that is reminiscent
of a weight attached to a spring.
For examples of all the easing functions see the AnimationEasingDemo in the DemoRunner.
@tags{Animations}
*/
struct Easings
{
/** Returns a cubic Bezier function with the control points (x1, y1), (x2, y2). These points are
the two middle points of a cubic Bezier function's four control points, the first and last
being (0, 0), and (1, 1).
*/
static std::function<float (float)> createCubicBezier (float x1, float y1, float x2, float y2);
/** Returns a cubic Bezier function with two control points. These points are the two middle
points of a cubic Bezier function's four control points, the first and last being (0, 0),
and (1, 1).
*/
static std::function<float (float)> createCubicBezier (Point<float> controlPoint1,
Point<float> controlPoint2);
/** Returns the easing function createCubicBezier (0.25f, 0.1f, 0.25f, 1.0f). This indicates
that the interpolation starts slowly, accelerates sharply, and then slows gradually towards
the end. It is similar to createEaseInOut(), though it accelerates more sharply at the
beginning.
This is equivalent to using the "ease" keyword when specifying a timing-function in CSS.
This is the default easing used by the ValueAnimatorBuilder class if no other easing
function is specified.
@see createCubicBezier, createEaseIn, createEaseOut, createEaseInOut
*/
static std::function<float (float)> createEase();
/** Returns the easing function createCubicBezier (0.42f, 0.0f, 1.0f, 1.0f). This indicates that
the interpolation starts slowly, then progressively speeds up until the end, at which point
it stops abruptly.
This is equivalent to using the "ease-in" keyword when specifying a timing-function in CSS.
@see createCubicBezier, createEase, createEaseOut, createEaseInOut
*/
static std::function<float (float)> createEaseIn();
/** Returns the easing function createCubicBezier (0.0f, 0.0f, 0.58f, 1.0f). This indicates that
the interpolation starts abruptly and then progressively slows down towards the end.
This is equivalent to using the "ease-out" keyword when specifying a timing-function in CSS.
@see createCubicBezier, createEase, createEaseIn, createEaseInOut, createEaseOutBack
*/
static std::function<float (float)> createEaseOut();
/** Returns the easing function createCubicBezier (0.34f, 1.56f, 0.64f, 1.0f). This indicates
that the interpolation starts abruptly, quickly decelerating before overshooting the target
value by approximately 10% and changing direction to slowly head back towards the target
value.
Like createSpring() this will overshoot causing it to return float values exceeding 1.0f.
This is equivalent to easeOutBack as specified on https://easings.net/#easeOutBack.
@see createCubicBezier, createEaseOutBack, createSpring
*/
static std::function<float (float)> createEaseOutBack();
/** Returns the easing function createCubicBezier (0.42f, 0.0f, 0.58f, 1.0f). This indicates
that the interpolation starts slowly, speeds up, and then slows down towards the end. At the
beginning, it behaves like createEaseIn(); at the end, it behaves like createEaseOut().
This is equivalent to using the "ease-in-out" keyword when specifying a timing-function in
CSS.
@see createCubicBezier, createEase, createEaseIn, createEaseInOut
*/
static std::function<float (float)> createEaseInOut();
/** Returns the easing function createCubicBezier (0.65f, 0.0f, 0.35f, 1.0f). This indicates
that the interpolation starts slowly, speeds up, and then slows down towards the end. It
behaves similar to createEaseInOut() but is more exaggerated and has a more symmetrical
curve.
This is equivalent to easeInOutCubic as specified on https://easings.net/#easeInOutCubic.
@see createCubicBezier, createEaseInOut
*/
static std::function<float (float)> createEaseInOutCubic();
/** Returns an easing function with a constant rate of interpolation, with no change in the rate
of progress throughout the duration (that is, no acceleration or deceleration).
*/
static std::function<float (float)> createLinear();
/** Returns an easing function that behaves like a spring with a weight attached.
Like createEaseOutBack() this might overshoot causing it to return float values exceeding
1.0f.
@see createEaseOutBack
*/
static std::function<float (float)> createSpring (const SpringEasingOptions& options = {});
/** Returns an easing function that behaves like a bouncy ball dropped on the ground.
The function will bounce numBounces times on the input range of [0, 1] before coming to
stop, each bounce is less pronounced than the previous.
This is equivalent to easeInOutCubic as specified on https://easings.net/#easeOutBounce.
*/
static std::function<float (float)> createBounce (int numBounces = 3);
/** Returns an easing function that reaches 1.0f when the input value is 0.5f, before returning
to 0.0f when the input values reaches 1.0f.
This is useful for making a repeating pulsation.
*/
static std::function<float (float)> createOnOffRamp();
};
} // namespace juce

View file

@ -0,0 +1,131 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Helper class for using linear interpolation between a begin and an end value.
The ValueType could be any numerical type, or a std::tuple containing numerical types. This
class is mainly intended to be used with the latter.
This way you can interpolate multiple values by supplying a single float value, which you can
access in an Animator's value change callback.
E.g.
@code
const auto boundsToTuple = [] (auto b)
{
return std::make_tuple (b.getX(), b.getY(), b.getWidth(), b.getHeight());
};
const auto begin = boundsToTuple (component.getBoundsInParent());
const auto end = boundsToTuple (targetBounds);
const auto limits = makeAnimationLimits (begin, end);
// This is the value change callback of an Animator, where you will transition a Component from
// one bounds to the next. See the AnimatorsDemo for a more detailed example.
const auto valueChanged = [&component, limits] (auto v)
{
const auto [x, y, w, h] = limits.lerp (v);
component.setBounds (x, y, w, h);
};
@endcode
@see ValueAnimatorBuilder::ValueChangedCallback
@tags{Animations}
*/
template <typename ValueType>
class JUCE_API StaticAnimationLimits
{
public:
/** Constructor. You can use it to interpolate between a 0 initialised numerical value or tuple
and the provided end state.
*/
explicit StaticAnimationLimits (const ValueType& endIn)
: StaticAnimationLimits ({}, endIn) {}
/** Constructor. Creates an object that will interpolate between the two provided beginning and
end states. The ValueType can be a numerical type or a std::tuple containing numerical
types.
*/
StaticAnimationLimits (const ValueType& beginIn, const ValueType& endIn)
: begin (beginIn), end (endIn) {}
/** Evaluation operator. Returns a value that is a linear interpolation of the beginning and end
state. It's a shorthand for the lerp() function.
*/
ValueType operator() (float value) const
{
return lerp (value);
}
/** Returns a value that is a linear interpolation of the beginning and end state.
*/
ValueType lerp (float value) const
{
using namespace detail::ArrayAndTupleOps;
if constexpr (std::is_integral_v<ValueType>)
return (ValueType) std::round ((float) begin + ((float) (end - begin) * value));
else
return (ValueType) (begin + ((end - begin) * value));
}
private:
ValueType begin{}, end{};
};
/** Creates an instance of StaticAnimationLimits, deducing ValueType from
the function argument.
*/
template <typename ValueType>
StaticAnimationLimits<ValueType> makeAnimationLimits (const ValueType& end)
{
return StaticAnimationLimits<ValueType> (end);
}
/** Creates an instance of StaticAnimationLimits, deducing ValueType from
the function arguments.
*/
template <typename ValueType>
StaticAnimationLimits<ValueType> makeAnimationLimits (const ValueType& begin, const ValueType& end)
{
return StaticAnimationLimits<ValueType> (begin, end);
}
} // namespace juce

View file

@ -0,0 +1,59 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
/**
Similar to AnimatorUpdater, but automatically calls update() whenever the screen refreshes.
@tags{Animations}
*/
class JUCE_API VBlankAnimatorUpdater : private AnimatorUpdater
{
public:
/** Constructs a VBlankAnimatorUpdater that is synchronised to the refresh rate of the monitor
that the provided Component is being displayed on.
*/
explicit VBlankAnimatorUpdater (Component* c) : vBlankAttachment (c, [this] { update(); })
{
}
using AnimatorUpdater::addAnimator, AnimatorUpdater::removeAnimator;
private:
VBlankAttachment vBlankAttachment;
};
} // namespace juce

View file

@ -0,0 +1,112 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
class ValueAnimator : public Animator::Impl
{
public:
explicit ValueAnimator (ValueAnimatorBuilder optionsIn) : options (std::move (optionsIn)) {}
auto getValue() const
{
using namespace detail::ArrayAndTupleOps;
return options.getEasing() == nullptr ? getProgress() : options.getEasing() (getProgress());
}
float getProgress() const
{
if (isComplete())
return 1.0f;
return timeBasedProgress;
}
/** Returns the time in milliseconds that it takes for the progress to go from 0.0 to 1.0.
This is the value returned even if the Animator is infinitely running.
*/
double getDurationMs() const override
{
return options.getDurationMs();
}
bool isComplete() const override
{
return Animator::Impl::isComplete()
|| (! options.isInfinitelyRunning() && timeBasedProgress >= 1.0f);
}
private:
Animator::Status internalUpdate (double timestampMs) override
{
timeBasedProgress = (float) ((timestampMs - startedAtMs) / options.getDurationMs());
NullCheckedInvocation::invoke (onValueChanged, getValue());
if (! options.isInfinitelyRunning())
return getProgress() >= 1.0 ? Animator::Status::finished : Animator::Status::inProgress;
return Animator::Status::inProgress;
}
void onStart (double timeMs) override
{
startedAtMs = timeMs;
timeBasedProgress = 0.0f;
if (auto fn = options.getOnStartWithValueChanged())
onValueChanged = fn();
}
void onComplete() override
{
NullCheckedInvocation::invoke (options.getOnComplete());
}
double startedAtMs = 0.0;
float timeBasedProgress = 0.0f;
const ValueAnimatorBuilder options;
ValueAnimatorBuilder::ValueChangedCallback onValueChanged;
};
//==============================================================================
Animator ValueAnimatorBuilder::build() const& { return Animator { std::make_unique<ValueAnimator> (*this) }; }
Animator ValueAnimatorBuilder::build() && { return Animator { std::make_unique<ValueAnimator> (std::move (*this)) }; }
} // namespace juce

View file

@ -0,0 +1,276 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A builder class that can be used to construct an Animator wrapping a ValueAnimator
implementation.
Every ValueAnimatorBuilder object is immutable, and every "with..." function returns a new
object. Each object can be used independently and as many times as required to build an Animator
object.
Calling build() multiple times will return an independent Animator object referencing a new
instance of the underlying implementation. Such Animator objects don't affect each other's
lifetime. The copy of an Animator object however shares ownership with the object that it was
copied from.
You can treat ValueAnimatorBuilder instances as disposable objects that are only needed until
you call build() on them. You can then store only the returned Animator object, which can be
started and completed multiple times as needed.
All functions beginning with "with..." are optional and can be used to affect the created
Animator's behaviour by overriding defaults.
@tags{Animations}
*/
class JUCE_API ValueAnimatorBuilder
{
public:
/** The type of the value change callback. The float parameter is related to the time parameter
passed to Animator::update(). The update function is typically called by an AnimatorUpdater.
The parameter will have a value of 0.0 during the first call of the value change callback,
and it will reach 1.0 when the time passed equals the duration of the Animator. This however
can be changed if an EasingFn is also specified. Correctly written easing functions should
preserve the 0.0 and 1.0 start and end values, but intermittent values can fall outside the
range of [0.0, 1.0].
After a call with a progress value of 1.0, the on complete callback will be called and the
updates will stop, unless the Animator is infinitely running, in which case the progress
will go beyond 1.0 and on complete will not be called until after Animator::complete() has
been called.
A value change callback is optional. If you want to use one, you need to create it inside
your on start callback that you must pass to withOnStartCallback().
@see withOnStartCallback, withDurationMs, withEasing, runningInfinitely
*/
using ValueChangedCallback = std::function<void (float)>;
/** The type of the on start callback. It can be used to do any initialisation necessary at the
start of an animation, then it must return a ValueChangedCallback.
The ValueChangedCallback is called during every update of the Animator after the on start
callback and before the on complete callback.
The on start callback is called during the first update that the Animator receives after
Animator::start() has been called. Immediately after the on start callback the first call to
the value changed callback is made.
@see ValueChangedCallback
*/
using OnStartReturningValueChangedCallback = std::function<ValueChangedCallback()>;
/** The type of an optional easing function that can be passed to the withEasing() builder
function.
If this function is specified it will be called with the progress value going from 0.0 to
1.0 (and beyond in case of an infinitely running Animator), that would otherwise be passed
directly to the value change callback. The return value is then passed to the on value
change callback instead.
This function can be used to change the linear progression of the animation and create
effects like rubber band like motion.
@see ValueChangedCallback
*/
using EasingFn = std::function<float (float)>;
/** Use this function to specify an optional on start callback.
Alternatively you can use the withOnStartReturningValueChangedCallback function that allows
you to return the ValueChangedCallback from inside your on start callback.
You can only use either withOnStartCallback() or withOnStartReturningValueChangedCallback().
*/
[[nodiscard]] ValueAnimatorBuilder withOnStartCallback (std::function<void()> onStartCallback) const
{
return with (&ValueAnimatorBuilder::onStartReturningValueChanged,
[start = std::move (onStartCallback), previous = onStartReturningValueChanged]
{
NullCheckedInvocation::invoke (start);
return previous != nullptr ? previous() : nullptr;
});
}
/** Use this function to specify an optional on change callback.
Alternatively you can use the withOnStartReturningValueChangedCallback function that allows
you to return the ValueChangedCallback from inside your on start callback.
You can only use either withValueChangedCallback or withOnStartReturningValueChangedCallback.
@see OnStartReturningValueChangedCallback
*/
[[nodiscard]] ValueAnimatorBuilder withValueChangedCallback (ValueChangedCallback valueChangedCallback) const
{
return with (&ValueAnimatorBuilder::onStartReturningValueChanged,
[changed = std::move (valueChangedCallback), previous = onStartReturningValueChanged]
{
return [changed, previousChanged = previous != nullptr ? previous() : nullptr] (float x)
{
NullCheckedInvocation::invoke (previousChanged, x);
NullCheckedInvocation::invoke (changed, x);
};
});
}
/** Use this function to specify an optional on start callback.
The return value of the provided function is a ValueChangedCallback. This allows you to
construct a new ValueChangedCallback on every on start event, capturing state that is also
constructed at the time of starting.
If you don't need to return a new ValueChangedCallback on every animation start, you can use
the simpler variants withOnStartCallback and withValueChangedCallback. However you cannot
use those functions together with this one.
@see OnStartReturningValueChangedCallback, withOnStartCallback, withValueChangedCallback
*/
[[nodiscard]] ValueAnimatorBuilder withOnStartReturningValueChangedCallback (OnStartReturningValueChangedCallback value) const
{
return with (&ValueAnimatorBuilder::onStartReturningValueChanged, std::move (value));
}
/** Use this function to optionally specify an on complete callback. This function will be
called after the Animator reached a progress value >= 1.0, or in the case of an
infinitely running animation, if Animator::complete() has been called.
*/
[[nodiscard]] ValueAnimatorBuilder withOnCompleteCallback (std::function<void()> value) const
{
return with (&ValueAnimatorBuilder::onComplete, std::move (value));
}
/** Use this function to specify the time it takes for the Animator to reach a progress of 1.0.
The default value is 300 ms.
A progress of 1.0 will be reached after this time elapses even if the Animator is infinitely
running.
*/
[[nodiscard]] ValueAnimatorBuilder withDurationMs (double durationMsIn) const
{
return with (&ValueAnimatorBuilder::durationMs, durationMsIn);
}
/** Supply a function that transforms the linear progression of time.
@see EasingFn
*/
[[nodiscard]] ValueAnimatorBuilder withEasing (EasingFn fn) const
{
return with (&ValueAnimatorBuilder::easing, std::move (fn));
}
/** This function specifies that the Animator will keep running even after its progress > 1.0
and its on complete function will not be called until Animator::complete() is called.
*/
[[nodiscard]] ValueAnimatorBuilder runningInfinitely() const
{
return with (&ValueAnimatorBuilder::infinitelyRunning, true);
}
//==============================================================================
/** Getter function used by the corresponding Animator implementation.
*/
auto& getOnComplete() const
{
return onComplete;
}
/** Getter function used by the corresponding Animator implementation.
*/
auto& getOnStartWithValueChanged() const
{
return onStartReturningValueChanged;
}
/** Getter function used by the corresponding Animator implementation.
*/
auto getDurationMs() const
{
return durationMs;
}
/** Getter function used by the corresponding Animator implementation.
*/
auto isInfinitelyRunning() const
{
return infinitelyRunning;
}
/** Getter function used by the corresponding Animator implementation.
*/
auto& getEasing() const
{
return easing;
}
/** The build() function will instantiate a new underlying implementation with the specified
parameters and return an Animator object referencing it. Calling build() multiple times
will return unrelated Animator objects, that reference separate underlying implementation
instances.
*/
Animator build() const&;
/** The build() function will instantiate a new underlying implementation with the specified
parameters and return an Animator object referencing it. Calling build() multiple times
will return unrelated Animator objects, that reference separate underlying implementation
instances.
This overload will be called on rvalue handles.
*/
Animator build() &&;
private:
//==============================================================================
template <typename Member, typename Value>
ValueAnimatorBuilder with (Member&& member, Value&& value) const noexcept
{
auto copy = *this;
copy.*member = std::forward<Value> (value);
return copy;
}
OnStartReturningValueChangedCallback onStartReturningValueChanged;
std::function<void()> onComplete;
double durationMs = 300.0;
bool infinitelyRunning = false;
EasingFn easing = Easings::createEase();
};
} // namespace juce

View file

@ -0,0 +1,272 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cubic_bezier.h"
#include <algorithm>
#include <cmath>
#include <limits>
namespace gfx {
namespace {
const int kMaxNewtonIterations = 4;
} // namespace
static const double kBezierEpsilon = 1e-7;
double CubicBezier::ToFinite(double value) {
// TODO(crbug.com/1275541): We can clamp this in numeric operation helper
// function like ClampedNumeric.
if (std::isinf(value)) {
if (value > 0)
return std::numeric_limits<double>::max();
return std::numeric_limits<double>::lowest();
}
return value;
}
CubicBezier::CubicBezier(double p1x, double p1y, double p2x, double p2y) {
InitCoefficients(p1x, p1y, p2x, p2y);
InitGradients(p1x, p1y, p2x, p2y);
InitRange(p1y, p2y);
InitSpline();
}
CubicBezier::CubicBezier(const CubicBezier& other) = default;
void CubicBezier::InitCoefficients(double p1x,
double p1y,
double p2x,
double p2y) {
// Calculate the polynomial coefficients, implicit first and last control
// points are (0,0) and (1,1).
cx_ = 3.0 * p1x;
bx_ = 3.0 * (p2x - p1x) - cx_;
ax_ = 1.0 - cx_ - bx_;
cy_ = ToFinite(3.0 * p1y);
by_ = ToFinite(3.0 * (p2y - p1y) - cy_);
ay_ = ToFinite(1.0 - cy_ - by_);
#ifndef NDEBUG
// Bezier curves with x-coordinates outside the range [0,1] for internal
// control points may have multiple values for t for a given value of x.
// In this case, calls to SolveCurveX may produce ambiguous results.
monotonically_increasing_ = p1x >= 0 && p1x <= 1 && p2x >= 0 && p2x <= 1;
#endif
}
void CubicBezier::InitGradients(double p1x,
double p1y,
double p2x,
double p2y) {
// End-point gradients are used to calculate timing function results
// outside the range [0, 1].
//
// There are four possibilities for the gradient at each end:
// (1) the closest control point is not horizontally coincident with regard to
// (0, 0) or (1, 1). In this case the line between the end point and
// the control point is tangent to the bezier at the end point.
// (2) the closest control point is coincident with the end point. In
// this case the line between the end point and the far control
// point is tangent to the bezier at the end point.
// (3) both internal control points are coincident with an endpoint. There
// are two special case that fall into this category:
// CubicBezier(0, 0, 0, 0) and CubicBezier(1, 1, 1, 1). Both are
// equivalent to linear.
// (4) the closest control point is horizontally coincident with the end
// point, but vertically distinct. In this case the gradient at the
// end point is Infinite. However, this causes issues when
// interpolating. As a result, we break down to a simple case of
// 0 gradient under these conditions.
if (p1x > 0)
start_gradient_ = p1y / p1x;
else if (!p1y && p2x > 0)
start_gradient_ = p2y / p2x;
else if (!p1y && !p2y)
start_gradient_ = 1;
else
start_gradient_ = 0;
if (p2x < 1)
end_gradient_ = (p2y - 1) / (p2x - 1);
else if (p2y == 1 && p1x < 1)
end_gradient_ = (p1y - 1) / (p1x - 1);
else if (p2y == 1 && p1y == 1)
end_gradient_ = 1;
else
end_gradient_ = 0;
}
// This works by taking taking the derivative of the cubic bezier, on the y
// axis. We can then solve for where the derivative is zero to find the min
// and max distance along the line. We the have to solve those in terms of time
// rather than distance on the x-axis
void CubicBezier::InitRange(double p1y, double p2y) {
range_min_ = 0;
range_max_ = 1;
if (0 <= p1y && p1y < 1 && 0 <= p2y && p2y <= 1)
return;
const double epsilon = kBezierEpsilon;
// Represent the function's derivative in the form at^2 + bt + c
// as in sampleCurveDerivativeY.
// (Technically this is (dy/dt)*(1/3), which is suitable for finding zeros
// but does not actually give the slope of the curve.)
const double a = 3.0 * ay_;
const double b = 2.0 * by_;
const double c = cy_;
// Check if the derivative is constant.
if (std::abs(a) < epsilon && std::abs(b) < epsilon)
return;
// Zeros of the function's derivative.
double t1 = 0;
double t2 = 0;
if (std::abs(a) < epsilon) {
// The function's derivative is linear.
t1 = -c / b;
} else {
// The function's derivative is a quadratic. We find the zeros of this
// quadratic using the quadratic formula.
double discriminant = b * b - 4 * a * c;
if (discriminant < 0)
return;
double discriminant_sqrt = sqrt(discriminant);
t1 = (-b + discriminant_sqrt) / (2 * a);
t2 = (-b - discriminant_sqrt) / (2 * a);
}
double sol1 = 0;
double sol2 = 0;
// If the solution is in the range [0,1] then we include it, otherwise we
// ignore it.
// An interesting fact about these beziers is that they are only
// actually evaluated in [0,1]. After that we take the tangent at that point
// and linearly project it out.
if (0 < t1 && t1 < 1)
sol1 = SampleCurveY(t1);
if (0 < t2 && t2 < 1)
sol2 = SampleCurveY(t2);
range_min_ = std::min({range_min_, sol1, sol2});
range_max_ = std::max({range_max_, sol1, sol2});
}
void CubicBezier::InitSpline() {
double delta_t = 1.0 / (CUBIC_BEZIER_SPLINE_SAMPLES - 1);
for (int i = 0; i < CUBIC_BEZIER_SPLINE_SAMPLES; i++) {
spline_samples_[i] = SampleCurveX(i * delta_t);
}
}
double CubicBezier::GetDefaultEpsilon() {
return kBezierEpsilon;
}
double CubicBezier::SolveCurveX(double x, double epsilon) const {
jassert (x >= 0.0);
jassert (x <= 1.0);
double t0;
double t1;
double t2 = x;
double x2;
double d2;
int i;
#ifndef NDEBUG
jassert (monotonically_increasing_);
#endif
// Linear interpolation of spline curve for initial guess.
double delta_t = 1.0 / (CUBIC_BEZIER_SPLINE_SAMPLES - 1);
for (i = 1; i < CUBIC_BEZIER_SPLINE_SAMPLES; i++) {
if (x <= spline_samples_[i]) {
t1 = delta_t * i;
t0 = t1 - delta_t;
t2 = t0 + (t1 - t0) * (x - spline_samples_[i - 1]) /
(spline_samples_[i] - spline_samples_[i - 1]);
break;
}
}
// Perform a few iterations of Newton's method -- normally very fast.
// See https://en.wikipedia.org/wiki/Newton%27s_method.
double newton_epsilon = std::min(kBezierEpsilon, epsilon);
for (i = 0; i < kMaxNewtonIterations; i++) {
x2 = SampleCurveX(t2) - x;
if (fabs(x2) < newton_epsilon)
return t2;
d2 = SampleCurveDerivativeX(t2);
if (fabs(d2) < kBezierEpsilon)
break;
t2 = t2 - x2 / d2;
}
if (fabs(x2) < epsilon)
return t2;
// Fall back to the bisection method for reliability.
while (t0 < t1) {
x2 = SampleCurveX(t2);
if (fabs(x2 - x) < epsilon)
return t2;
if (x > x2)
t0 = t2;
else
t1 = t2;
t2 = (t1 + t0) * .5;
}
// Failure.
return t2;
}
double CubicBezier::Solve(double x) const {
return SolveWithEpsilon(x, kBezierEpsilon);
}
double CubicBezier::SlopeWithEpsilon(double x, double epsilon) const {
x = std::clamp(x, 0.0, 1.0);
double t = SolveCurveX(x, epsilon);
double dx = SampleCurveDerivativeX(t);
double dy = SampleCurveDerivativeY(t);
// TODO(crbug.com/1275534): We should clamp NaN to a proper value.
// Please see the issue for detail.
if (!dx && !dy)
return 0;
return ToFinite(dy / dx);
}
double CubicBezier::Slope(double x) const {
return SlopeWithEpsilon(x, kBezierEpsilon);
}
double CubicBezier::GetX1() const {
return cx_ / 3.0;
}
double CubicBezier::GetY1() const {
return cy_ / 3.0;
}
double CubicBezier::GetX2() const {
return (bx_ + cx_) / 3.0 + GetX1();
}
double CubicBezier::GetY2() const {
return (by_ + cy_) / 3.0 + GetY1();
}
} // namespace gfx

View file

@ -0,0 +1,109 @@
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_GFX_GEOMETRY_CUBIC_BEZIER_H_
#define UI_GFX_GEOMETRY_CUBIC_BEZIER_H_
namespace gfx {
#define CUBIC_BEZIER_SPLINE_SAMPLES 11
class CubicBezier {
public:
CubicBezier(double p1x, double p1y, double p2x, double p2y);
CubicBezier(const CubicBezier& other);
CubicBezier& operator=(const CubicBezier&) = delete;
double SampleCurveX(double t) const {
// `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
// The x values are in the range [0, 1]. So it isn't needed toFinite
// clamping.
// https://drafts.csswg.org/css-easing-1/#funcdef-cubic-bezier-easing-function-cubic-bezier
return ((ax_ * t + bx_) * t + cx_) * t;
}
double SampleCurveY(double t) const {
return ToFinite(((ay_ * t + by_) * t + cy_) * t);
}
double SampleCurveDerivativeX(double t) const {
return (3.0 * ax_ * t + 2.0 * bx_) * t + cx_;
}
double SampleCurveDerivativeY(double t) const {
return ToFinite(
ToFinite(ToFinite(3.0 * ay_) * t + ToFinite(2.0 * by_)) * t + cy_);
}
static double GetDefaultEpsilon();
// Given an x value, find a parametric value it came from.
// x must be in [0, 1] range. Doesn't use gradients.
double SolveCurveX(double x, double epsilon) const;
// Evaluates y at the given x with default epsilon.
double Solve(double x) const;
// Evaluates y at the given x. The epsilon parameter provides a hint as to the
// required accuracy and is not guaranteed. Uses gradients if x is
// out of [0, 1] range.
double SolveWithEpsilon(double x, double epsilon) const {
if (x < 0.0)
return ToFinite(0.0 + start_gradient_ * x);
if (x > 1.0)
return ToFinite(1.0 + end_gradient_ * (x - 1.0));
return SampleCurveY(SolveCurveX(x, epsilon));
}
// Returns an approximation of dy/dx at the given x with default epsilon.
double Slope(double x) const;
// Returns an approximation of dy/dx at the given x.
// Clamps x to range [0, 1].
double SlopeWithEpsilon(double x, double epsilon) const;
// These getters are used rarely. We reverse compute them from coefficients.
// See CubicBezier::InitCoefficients. The speed has been traded for memory.
double GetX1() const;
double GetY1() const;
double GetX2() const;
double GetY2() const;
// Gets the bezier's minimum y value in the interval [0, 1].
double range_min() const { return range_min_; }
// Gets the bezier's maximum y value in the interval [0, 1].
double range_max() const { return range_max_; }
private:
void InitCoefficients(double p1x, double p1y, double p2x, double p2y);
void InitGradients(double p1x, double p1y, double p2x, double p2y);
void InitRange(double p1y, double p2y);
void InitSpline();
static double ToFinite(double value);
double ax_;
double bx_;
double cx_;
double ay_;
double by_;
double cy_;
double start_gradient_;
double end_gradient_;
double range_min_;
double range_max_;
double spline_samples_[CUBIC_BEZIER_SPLINE_SAMPLES];
#ifndef NDEBUG
// Guard against attempted to solve for t given x in the event that the curve
// may have multiple values for t for some values of x in [0, 1].
bool monotonically_increasing_;
#endif
};
} // namespace gfx
#endif // UI_GFX_GEOMETRY_CUBIC_BEZIER_H_

View file

@ -0,0 +1,141 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#ifndef DOXYGEN
//==============================================================================
/** The contents of this namespace are used to implement Animator and should not
be used elsewhere. Their interfaces (and existence) are liable to change!
*/
namespace juce::detail::ArrayAndTupleOps
{
template <typename, typename = void>
constexpr auto hasTupleSize = false;
template <typename T>
constexpr auto hasTupleSize<T, std::void_t<decltype (std::tuple_size<T>::value)>> = true;
static_assert (! hasTupleSize<float>);
static_assert (hasTupleSize<std::tuple<float, float>>);
static_assert (hasTupleSize<std::array<float, 5>>);
template <typename A, typename B, typename Op, size_t... Ix, std::enable_if_t<hasTupleSize<B>, int> = 0>
constexpr auto& assignOpImpl (A& a, const B& b, Op&& op, std::index_sequence<Ix...>)
{
(op (std::get<Ix> (a), std::get<Ix> (b)), ...);
return a;
}
template <typename A, typename B, typename Op, size_t... Ix, std::enable_if_t<! hasTupleSize<B>, int> = 0>
constexpr auto& assignOpImpl (A& a, const B& b, Op&& op, std::index_sequence<Ix...>)
{
(op (std::get<Ix> (a), b), ...);
return a;
}
template <typename A, typename B, typename Op, std::enable_if_t<hasTupleSize<A>, int> = 0>
constexpr auto& assignOpImpl (A& a, const B& b, Op&& op)
{
return assignOpImpl (a, b, std::forward<Op> (op), std::make_index_sequence<std::tuple_size_v<A>>());
}
template <typename A, typename B, std::enable_if_t<hasTupleSize<A>, int> = 0>
constexpr auto& operator+= (A& a, const B& b)
{
return assignOpImpl (a, b, [] (auto& x, auto y)
{
using Tx = std::remove_reference_t<decltype (x)>;
using Ty = std::remove_reference_t<decltype (y)>;
if constexpr (std::is_integral_v<Tx> && std::is_floating_point_v<Ty>)
x = (Tx) std::round ((Ty) x + y);
else
x += y;
});
}
template <typename A, typename B, std::enable_if_t<hasTupleSize<A>, int> = 0>
constexpr auto& operator-= (A& a, const B& b)
{
return assignOpImpl (a, b, [] (auto& x, auto y)
{
using Tx = std::remove_reference_t<decltype (x)>;
using Ty = std::remove_reference_t<decltype (y)>;
if constexpr (std::is_integral_v<Tx> && std::is_floating_point_v<Ty>)
x = (Tx) std::round ((Ty) x - y);
else
x -= y;
});
}
template <typename A, typename B, std::enable_if_t<hasTupleSize<A>, int> = 0>
constexpr auto& operator*= (A& a, const B& b)
{
return assignOpImpl (a, b, [] (auto& x, auto y)
{
using Tx = std::remove_reference_t<decltype (x)>;
using Ty = std::remove_reference_t<decltype (y)>;
if constexpr (std::is_integral_v<Tx> && std::is_floating_point_v<Ty>)
x = (Tx) std::round ((Ty) x * y);
else
x *= y;
});
}
template <typename A, typename B, std::enable_if_t<hasTupleSize<A>, int> = 0>
constexpr auto& operator/= (A& a, const B& b)
{
return assignOpImpl (a, b, [] (auto& x, auto y)
{
using Tx = std::remove_reference_t<decltype (x)>;
using Ty = std::remove_reference_t<decltype (y)>;
if constexpr (std::is_integral_v<Tx> && std::is_floating_point_v<Ty>)
x = (Tx) std::round ((Ty) x / y);
else
x /= y;
});
}
template <typename A, typename B, std::enable_if_t<hasTupleSize<A>, int> = 0> constexpr auto operator+ (const A& a, const B& b) { A copy { a }; return copy += b; }
template <typename A, typename B, std::enable_if_t<hasTupleSize<A>, int> = 0> constexpr auto operator- (const A& a, const B& b) { A copy { a }; return copy -= b; }
template <typename A, typename B, std::enable_if_t<hasTupleSize<A>, int> = 0> constexpr auto operator* (const A& a, const B& b) { A copy { a }; return copy *= b; }
template <typename A, typename B, std::enable_if_t<hasTupleSize<A>, int> = 0> constexpr auto operator/ (const A& a, const B& b) { A copy { a }; return copy /= b; }
static_assert (std::tuple (1.0f, 5.0) + 3.0f == std::tuple (4.0f, 8.0));
static_assert (std::tuple (1.0f, 5.0) - 1.0f == std::tuple (0.0f, 4.0));
static_assert (std::tuple (1, 2, 3) * std::tuple (4, 5, 6) == std::tuple (4, 10, 18));
} // namespace juce::detail::ArrayAndTupleOps
#endif

View file

@ -0,0 +1,70 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
#ifdef JUCE_ANIMATION_H_INCLUDED
/* When you add this cpp file to your project, you mustn't include it in a file where you've
already included any other headers - just put it inside a file on its own, possibly with your config
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
header files that the compiler may be using.
*/
#error "Incorrect use of JUCE cpp file"
#endif
//==============================================================================
#include "juce_animation.h"
//==============================================================================
namespace chromium
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4701 6001)
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wfloat-conversion",
"-Wfloat-equal",
"-Wconditional-uninitialized")
#include "detail/chromium/cubic_bezier.h"
#include "detail/chromium/cubic_bezier.cc"
JUCE_END_IGNORE_WARNINGS_MSVC
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
} // namespace chromium
//==============================================================================
#include "animation/juce_Animator.cpp"
#include "animation/juce_AnimatorSetBuilder.cpp"
#include "animation/juce_AnimatorUpdater.cpp"
#include "animation/juce_Easings.cpp"
#include "animation/juce_ValueAnimatorBuilder.cpp"

View file

@ -0,0 +1,76 @@
/*
==============================================================================
This file is part of the JUCE framework.
Copyright (c) Raw Material Software Limited
JUCE is an open source framework subject to commercial or open source
licensing.
By downloading, installing, or using the JUCE framework, or combining the
JUCE framework with any other source code, object code, content or any other
copyrightable work, you agree to the terms of the JUCE End User Licence
Agreement, and all incorporated terms including the JUCE Privacy Policy and
the JUCE Website Terms of Service, as applicable, which will bind you. If you
do not agree to the terms of these agreements, we will not license the JUCE
framework to you, and you must discontinue the installation or download
process and cease use of the JUCE framework.
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
Or:
You may also use this code under the terms of the AGPLv3:
https://www.gnu.org/licenses/agpl-3.0.en.html
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this module, and is read by
the Projucer to automatically generate project code that uses it.
For details about the syntax and how to create or use a module, see the
JUCE Module Format.md file.
BEGIN_JUCE_MODULE_DECLARATION
ID: juce_animation
vendor: juce
version: 7.0.9
name: JUCE Animation classes
description: Classes for defining and handling animations.
website: http://www.juce.com/juce
license: GPL/Commercial
minimumCppStandard: 17
dependencies: juce_gui_basics
END_JUCE_MODULE_DECLARATION
*******************************************************************************/
#pragma once
#define JUCE_ANIMATION_H_INCLUDED
#include <juce_gui_basics/juce_gui_basics.h>
//==============================================================================
#include "detail/juce_ArrayAndTupleOps.h"
//==============================================================================
#include "animation/juce_Animator.h"
#include "animation/juce_AnimatorSetBuilder.h"
#include "animation/juce_AnimatorUpdater.h"
#include "animation/juce_Easings.h"
#include "animation/juce_StaticAnimationLimits.h"
#include "animation/juce_ValueAnimatorBuilder.h"
#include "animation/juce_VBlankAnimatorUpdater.h"

View file

@ -259,14 +259,17 @@ public:
*/ */
Component* findComponentAt (Point<int> screenPosition) const; Component* findComponentAt (Point<int> screenPosition) const;
/** The Desktop object has a ComponentAnimator instance which can be used for performing /** The ComponentAnimator has been superseded, it is now recommended you use the Animator
class in the juce_animation module.
The Desktop object has a ComponentAnimator instance which can be used for performing
your animations. your animations.
Having a single shared ComponentAnimator object makes it more efficient when multiple Having a single shared ComponentAnimator object makes it more efficient when multiple
components are being moved around simultaneously. It's also more convenient than having components are being moved around simultaneously. It's also more convenient than having
to manage your own instance of one. to manage your own instance of one.
@see ComponentAnimator @see Animator, ComponentAnimator
*/ */
ComponentAnimator& getAnimator() noexcept { return animator; } ComponentAnimator& getAnimator() noexcept { return animator; }

View file

@ -37,6 +37,9 @@ namespace juce
//============================================================================== //==============================================================================
/** /**
This class has been superseded, it is now recommended you use the Animator
class in the juce_animation module.
Animates a set of components, moving them to a new position and/or fading their Animates a set of components, moving them to a new position and/or fading their
alpha levels. alpha levels.
@ -54,7 +57,7 @@ namespace juce
The class is a ChangeBroadcaster and sends a notification when any components The class is a ChangeBroadcaster and sends a notification when any components
start or finish being animated. start or finish being animated.
@see Desktop::getAnimator @see Animator, Desktop::getAnimator
@tags{GUI} @tags{GUI}
*/ */

View file

@ -39,7 +39,7 @@ namespace juce
Helper class to synchronise Component updates to the vertical blank event of the display that Helper class to synchronise Component updates to the vertical blank event of the display that
the Component is presented on. This is useful when animating the Component's contents. the Component is presented on. This is useful when animating the Component's contents.
@tags{GUI} @tags{GUI, Animations}
*/ */
class JUCE_API VBlankAttachment final : public ComponentPeer::VBlankListener, class JUCE_API VBlankAttachment final : public ComponentPeer::VBlankListener,
public ComponentListener public ComponentListener