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 =
|
static StringArray juceModuleIds =
|
||||||
{
|
{
|
||||||
"juce_analytics",
|
"juce_analytics",
|
||||||
|
"juce_animation",
|
||||||
"juce_audio_basics",
|
"juce_audio_basics",
|
||||||
"juce_audio_devices",
|
"juce_audio_devices",
|
||||||
"juce_audio_formats",
|
"juce_audio_formats",
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ juce_add_modules(
|
||||||
INSTALL_PATH "include/JUCE-${JUCE_VERSION}/modules"
|
INSTALL_PATH "include/JUCE-${JUCE_VERSION}/modules"
|
||||||
ALIAS_NAMESPACE juce
|
ALIAS_NAMESPACE juce
|
||||||
juce_analytics
|
juce_analytics
|
||||||
|
juce_animation
|
||||||
juce_audio_basics
|
juce_audio_basics
|
||||||
juce_audio_devices
|
juce_audio_devices
|
||||||
juce_audio_formats
|
juce_audio_formats
|
||||||
|
|
|
||||||
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;
|
Component* findComponentAt (Point<int> screenPosition) const;
|
||||||
|
|
||||||
/** The Desktop object has a ComponentAnimator instance which can be used for performing
|
/** The ComponentAnimator has been superseded, it is now recommended you use the Animator
|
||||||
|
class in the juce_animation module.
|
||||||
|
|
||||||
|
The Desktop object has a ComponentAnimator instance which can be used for performing
|
||||||
your animations.
|
your animations.
|
||||||
|
|
||||||
Having a single shared ComponentAnimator object makes it more efficient when multiple
|
Having a single shared ComponentAnimator object makes it more efficient when multiple
|
||||||
components are being moved around simultaneously. It's also more convenient than having
|
components are being moved around simultaneously. It's also more convenient than having
|
||||||
to manage your own instance of one.
|
to manage your own instance of one.
|
||||||
|
|
||||||
@see ComponentAnimator
|
@see Animator, ComponentAnimator
|
||||||
*/
|
*/
|
||||||
ComponentAnimator& getAnimator() noexcept { return animator; }
|
ComponentAnimator& getAnimator() noexcept { return animator; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ namespace juce
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/**
|
/**
|
||||||
|
This class has been superseded, it is now recommended you use the Animator
|
||||||
|
class in the juce_animation module.
|
||||||
|
|
||||||
Animates a set of components, moving them to a new position and/or fading their
|
Animates a set of components, moving them to a new position and/or fading their
|
||||||
alpha levels.
|
alpha levels.
|
||||||
|
|
||||||
|
|
@ -54,7 +57,7 @@ namespace juce
|
||||||
The class is a ChangeBroadcaster and sends a notification when any components
|
The class is a ChangeBroadcaster and sends a notification when any components
|
||||||
start or finish being animated.
|
start or finish being animated.
|
||||||
|
|
||||||
@see Desktop::getAnimator
|
@see Animator, Desktop::getAnimator
|
||||||
|
|
||||||
@tags{GUI}
|
@tags{GUI}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ namespace juce
|
||||||
Helper class to synchronise Component updates to the vertical blank event of the display that
|
Helper class to synchronise Component updates to the vertical blank event of the display that
|
||||||
the Component is presented on. This is useful when animating the Component's contents.
|
the Component is presented on. This is useful when animating the Component's contents.
|
||||||
|
|
||||||
@tags{GUI}
|
@tags{GUI, Animations}
|
||||||
*/
|
*/
|
||||||
class JUCE_API VBlankAttachment final : public ComponentPeer::VBlankListener,
|
class JUCE_API VBlankAttachment final : public ComponentPeer::VBlankListener,
|
||||||
public ComponentListener
|
public ComponentListener
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue