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:
parent
bc6295d7b5
commit
bc3600cde8
22 changed files with 2731 additions and 4 deletions
|
|
@ -273,6 +273,7 @@ StringArray getJUCEModules() noexcept
|
|||
static StringArray juceModuleIds =
|
||||
{
|
||||
"juce_analytics",
|
||||
"juce_animation",
|
||||
"juce_audio_basics",
|
||||
"juce_audio_devices",
|
||||
"juce_audio_formats",
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ juce_add_modules(
|
|||
INSTALL_PATH "include/JUCE-${JUCE_VERSION}/modules"
|
||||
ALIAS_NAMESPACE juce
|
||||
juce_analytics
|
||||
juce_animation
|
||||
juce_audio_basics
|
||||
juce_audio_devices
|
||||
juce_audio_formats
|
||||
|
|
|
|||
234
modules/juce_animation/animation/juce_Animator.cpp
Normal file
234
modules/juce_animation/animation/juce_Animator.cpp
Normal 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
|
||||
210
modules/juce_animation/animation/juce_Animator.h
Normal file
210
modules/juce_animation/animation/juce_Animator.h
Normal 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
|
||||
291
modules/juce_animation/animation/juce_AnimatorSetBuilder.cpp
Normal file
291
modules/juce_animation/animation/juce_AnimatorSetBuilder.cpp
Normal 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
|
||||
143
modules/juce_animation/animation/juce_AnimatorSetBuilder.h
Normal file
143
modules/juce_animation/animation/juce_AnimatorSetBuilder.h
Normal 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
|
||||
100
modules/juce_animation/animation/juce_AnimatorUpdater.cpp
Normal file
100
modules/juce_animation/animation/juce_AnimatorUpdater.cpp
Normal 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
|
||||
107
modules/juce_animation/animation/juce_AnimatorUpdater.h
Normal file
107
modules/juce_animation/animation/juce_AnimatorUpdater.h
Normal 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
|
||||
156
modules/juce_animation/animation/juce_Easings.cpp
Normal file
156
modules/juce_animation/animation/juce_Easings.cpp
Normal 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
|
||||
232
modules/juce_animation/animation/juce_Easings.h
Normal file
232
modules/juce_animation/animation/juce_Easings.h
Normal 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
|
||||
131
modules/juce_animation/animation/juce_StaticAnimationLimits.h
Normal file
131
modules/juce_animation/animation/juce_StaticAnimationLimits.h
Normal 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
|
||||
|
|
@ -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
|
||||
112
modules/juce_animation/animation/juce_ValueAnimatorBuilder.cpp
Normal file
112
modules/juce_animation/animation/juce_ValueAnimatorBuilder.cpp
Normal 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
|
||||
276
modules/juce_animation/animation/juce_ValueAnimatorBuilder.h
Normal file
276
modules/juce_animation/animation/juce_ValueAnimatorBuilder.h
Normal 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
|
||||
272
modules/juce_animation/detail/chromium/cubic_bezier.cc
Normal file
272
modules/juce_animation/detail/chromium/cubic_bezier.cc
Normal 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
|
||||
109
modules/juce_animation/detail/chromium/cubic_bezier.h
Normal file
109
modules/juce_animation/detail/chromium/cubic_bezier.h
Normal 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_
|
||||
141
modules/juce_animation/detail/juce_ArrayAndTupleOps.h
Normal file
141
modules/juce_animation/detail/juce_ArrayAndTupleOps.h
Normal 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
|
||||
70
modules/juce_animation/juce_animation.cpp
Normal file
70
modules/juce_animation/juce_animation.cpp
Normal 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"
|
||||
76
modules/juce_animation/juce_animation.h
Normal file
76
modules/juce_animation/juce_animation.h
Normal 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"
|
||||
|
|
@ -259,14 +259,17 @@ public:
|
|||
*/
|
||||
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.
|
||||
|
||||
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
|
||||
to manage your own instance of one.
|
||||
|
||||
@see ComponentAnimator
|
||||
@see Animator, ComponentAnimator
|
||||
*/
|
||||
ComponentAnimator& getAnimator() noexcept { return animator; }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
alpha levels.
|
||||
|
||||
|
|
@ -54,7 +57,7 @@ namespace juce
|
|||
The class is a ChangeBroadcaster and sends a notification when any components
|
||||
start or finish being animated.
|
||||
|
||||
@see Desktop::getAnimator
|
||||
@see Animator, Desktop::getAnimator
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace juce
|
|||
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.
|
||||
|
||||
@tags{GUI}
|
||||
@tags{GUI, Animations}
|
||||
*/
|
||||
class JUCE_API VBlankAttachment final : public ComponentPeer::VBlankListener,
|
||||
public ComponentListener
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue