229 lines
4.6 KiB
C++
229 lines
4.6 KiB
C++
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "GesturePinch.hpp"
|
|
|
|
namespace OpenVulkano::Input
|
|
{
|
|
namespace
|
|
{
|
|
constexpr float MIN_DISTANCE = 2;
|
|
constexpr float MIN_DISTANCE2 = MIN_DISTANCE * MIN_DISTANCE;
|
|
constexpr float START_DISTANCE = 400;
|
|
constexpr uint32_t REQUIRED_TOUCHES = 2;
|
|
}
|
|
|
|
void GesturePinch::TryStart()
|
|
{
|
|
uint32_t dragCount = 0, existingActive = 0;
|
|
|
|
for (const auto& t : m_pendingTouches)
|
|
{
|
|
if (t.isDrag) dragCount++;
|
|
if (t.active) existingActive++;
|
|
}
|
|
|
|
if (dragCount >= REQUIRED_TOUCHES)
|
|
{
|
|
if (!IsActive() && !ResolveConflicts()) return;
|
|
|
|
uint32_t activeCount = existingActive;
|
|
|
|
for (auto& t : m_pendingTouches)
|
|
{
|
|
if (t.isDrag && !t.active && activeCount < REQUIRED_TOUCHES)
|
|
{
|
|
t.active = true;
|
|
++activeCount;
|
|
}
|
|
}
|
|
|
|
m_paused = false;
|
|
|
|
if (!IsActive())
|
|
{
|
|
SetActive(true);
|
|
float distance = CalculateDistance();
|
|
if (distance < START_DISTANCE)
|
|
{
|
|
SetActive(false);
|
|
return;
|
|
}
|
|
|
|
m_initialDistance = distance;
|
|
|
|
m_pinchInfo.scale = 0;
|
|
m_pinchInfo.position = CalculateCenter();
|
|
|
|
OnPinchStarted.NotifyAll(this, m_pinchInfo);
|
|
|
|
m_lastDistance = m_initialDistance;
|
|
}
|
|
else
|
|
{
|
|
OnPinchMoved.NotifyAll(this, m_pinchInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
float GesturePinch::LinearLength(const std::vector<Math::Vector2f>& points)
|
|
{
|
|
if (points.size() < 2) return 0;
|
|
auto start = points.begin();
|
|
auto finish = start + 1;
|
|
float sum = 0;
|
|
|
|
while (finish != points.end())
|
|
{
|
|
Math::Vector2f displacement = *start - *finish;
|
|
sum += Math::Utils::length(displacement);
|
|
start = finish++;
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
float GesturePinch::CalculateDistance() const
|
|
{
|
|
if (m_pendingTouches.size() >= REQUIRED_TOUCHES && IsActive() && !m_paused)
|
|
{
|
|
std::vector<Math::Vector2f> positions;
|
|
for (const auto& t : m_pendingTouches)
|
|
{
|
|
if (t.active) positions.push_back(t.currentPosition);
|
|
}
|
|
return LinearLength(positions);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Math::Vector2f GesturePinch::CalculateCenter() const
|
|
{
|
|
Math::Vector2f center(0);
|
|
if (m_pendingTouches.size() >= REQUIRED_TOUCHES && IsActive() && !m_paused)
|
|
{
|
|
int count = 0;
|
|
for (const auto& t : m_pendingTouches)
|
|
{
|
|
if (t.active)
|
|
{
|
|
center += t.currentPosition;
|
|
count++;
|
|
}
|
|
}
|
|
if (count)
|
|
{
|
|
center /= count;
|
|
}
|
|
}
|
|
return center;
|
|
}
|
|
|
|
void GesturePinch::Cancel()
|
|
{
|
|
for (auto& t : m_pendingTouches)
|
|
{
|
|
t.active = false;
|
|
}
|
|
SetActive(false);
|
|
m_paused = false;
|
|
OnPinchEnded.NotifyAll(this, m_pinchInfo);
|
|
m_pinchInfo.position = {};
|
|
m_pinchInfo.scale = 0.0f;
|
|
}
|
|
|
|
void GesturePinch::TouchDown(const Touch& touch)
|
|
{
|
|
TouchInfo ti;
|
|
ti.touchId = touch.GetId();
|
|
ti.currentPosition = touch.GetPosition();
|
|
ti.initialPosition = touch.GetPosition();
|
|
ti.delta = touch.GetPosition() - touch.GetLastPosition();
|
|
|
|
m_pendingTouches.push_back(ti);
|
|
}
|
|
|
|
void GesturePinch::TouchMoved(const Touch& touch)
|
|
{
|
|
if (!m_pendingTouches.empty())
|
|
{
|
|
bool isPending = false;
|
|
bool isActive = false;
|
|
bool oneIsDrag = false;
|
|
for (auto& t : m_pendingTouches)
|
|
{
|
|
if (touch.GetId() == t.touchId)
|
|
{
|
|
t.currentPosition = touch.GetPosition();
|
|
t.delta = touch.GetPosition() - t.initialPosition;
|
|
|
|
Math::Vector2f displacement = t.currentPosition - t.initialPosition;
|
|
|
|
if (Math::Utils::length2(displacement) > MIN_DISTANCE2)
|
|
{
|
|
t.isDrag = true;
|
|
oneIsDrag = true;
|
|
}
|
|
|
|
if (t.active) isActive = true;
|
|
|
|
isPending = true;
|
|
}
|
|
}
|
|
|
|
|
|
if (oneIsDrag)
|
|
{
|
|
// Just to activate both cursors
|
|
for (auto& t : m_pendingTouches)
|
|
{
|
|
t.isDrag = true;
|
|
}
|
|
}
|
|
|
|
if (isPending)
|
|
{
|
|
if (!IsActive() || m_paused)
|
|
{
|
|
TryStart();
|
|
}
|
|
else if (isActive)
|
|
{
|
|
float newDistance = CalculateDistance();
|
|
|
|
if (m_lastDistance == 0) m_lastDistance = newDistance;
|
|
|
|
m_pinchInfo.scale = (newDistance - m_initialDistance);
|
|
m_pinchInfo.position = CalculateCenter();
|
|
|
|
OnPinchMoved.NotifyAll(this, m_pinchInfo);
|
|
|
|
m_lastDistance = newDistance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GesturePinch::TouchUp(const Touch& touch)
|
|
{
|
|
if (!m_pendingTouches.empty())
|
|
{
|
|
for (auto touchIter = m_pendingTouches.begin(); touchIter != m_pendingTouches.end(); touchIter++)
|
|
{
|
|
if (touch.GetId() == touchIter->touchId)
|
|
{
|
|
if (touchIter->active) m_paused = true;
|
|
|
|
m_pendingTouches.erase(touchIter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_pendingTouches.empty() && IsActive()) Cancel();
|
|
}
|
|
}
|
|
}
|