blob: 191d661b953d67071e675c9f04d2ffdebbd263ef [file] [log] [blame]
#include "rive/constraints/scrolling/elastic_scroll_physics.hpp"
#include "math.h"
using namespace rive;
Vec2D ElasticScrollPhysics::advance(float elapsedSeconds)
{
float advanceX =
m_physicsX != nullptr ? m_physicsX->advance(elapsedSeconds) : 0.0f;
float advanceY =
m_physicsY != nullptr ? m_physicsY->advance(elapsedSeconds) : 0.0f;
bool isRunningX = m_physicsX != nullptr && m_physicsX->isRunning();
bool isRunningY = m_physicsY != nullptr && m_physicsY->isRunning();
if (!isRunningX && !isRunningY)
{
reset();
}
return Vec2D(advanceX, advanceY);
}
Vec2D ElasticScrollPhysics::clamp(Vec2D range, Vec2D value)
{
float clampX =
m_physicsX != nullptr ? m_physicsX->clamp(range.x, value.x) : 0.0f;
float clampY =
m_physicsY != nullptr ? m_physicsY->clamp(range.y, value.y) : 0.0f;
return Vec2D(clampX, clampY);
}
void ElasticScrollPhysics::run(Vec2D range,
Vec2D value,
std::vector<Vec2D> snappingPoints)
{
Super::run(range, value, snappingPoints);
std::vector<float> xPoints;
std::vector<float> yPoints;
for (auto pt : snappingPoints)
{
xPoints.push_back(pt.x);
yPoints.push_back(pt.y);
}
if (m_physicsX != nullptr)
{
m_physicsX->run(m_acceleration.x, range.x, value.x, xPoints);
}
if (m_physicsY != nullptr)
{
m_physicsY->run(m_acceleration.y, range.y, value.y, yPoints);
}
}
void ElasticScrollPhysics::prepare(DraggableConstraintDirection dir)
{
Super::prepare(dir);
if (dir == DraggableConstraintDirection::horizontal ||
dir == DraggableConstraintDirection::all)
{
m_physicsX = new ElasticScrollPhysicsHelper(friction(),
speedMultiplier(),
elasticFactor());
}
if (dir == DraggableConstraintDirection::vertical ||
dir == DraggableConstraintDirection::all)
{
m_physicsY = new ElasticScrollPhysicsHelper(friction(),
speedMultiplier(),
elasticFactor());
}
}
void ElasticScrollPhysics::reset()
{
m_physicsX = nullptr;
m_physicsY = nullptr;
}
float ElasticScrollPhysicsHelper::advance(float elapsedSeconds)
{
if (m_speed != 0)
{
m_current += m_speed * elapsedSeconds;
auto friction = m_friction;
if (m_current < m_runRange)
{
friction *= 4;
}
else if (m_current > 0)
{
friction *= 4;
}
m_speed += -m_speed * std::min(1.0f, elapsedSeconds * friction);
if (abs(m_speed) < 5)
{
m_speed = 0;
if (m_current < m_runRange)
{
m_target = m_runRange;
}
else if (m_current > 0)
{
m_target = 0;
}
else
{
m_target = m_current;
}
}
return m_current;
}
auto diff = m_target - m_current;
if (abs(diff) < 0.1)
{
m_current = m_target;
m_isRunning = false;
}
else
{
m_current += diff * std::min(1.0f, elapsedSeconds * 15.0f);
}
return m_current;
}
float ElasticScrollPhysicsHelper::clamp(float range, float value)
{
if (value < range)
{
return range - pow(-(value - range), m_elasticFactor);
}
else if (value > 0)
{
return pow(value, m_elasticFactor);
}
return value;
}
void ElasticScrollPhysicsHelper::run(float acceleration,
float range,
float value,
std::vector<float> snappingPoints)
{
m_isRunning = true;
m_runRange = range;
if (abs(acceleration) > 100)
{
m_speed = acceleration * 0.16f * 0.16f * 0.1f * m_speedMultiplier;
}
else
{
m_speed = 0;
}
if (value < range)
{
m_target = range;
}
else if (value > 0)
{
m_target = 0;
}
else
{
m_target = value;
}
m_current = value;
if (!snappingPoints.empty())
{
float endTarget = -(m_current + m_speed / m_friction);
float closest = std::numeric_limits<float>::max();
float snapTarget = 0;
for (auto snap : snappingPoints)
{
float diff = std::abs(snap - endTarget);
if (diff < closest)
{
closest = diff;
snapTarget = snap;
}
}
m_speed = -(snapTarget + m_current) * m_friction;
}
}