You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
835 lines
20 KiB
C++
835 lines
20 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
|
|
#include "EditorDefs.h"
|
|
|
|
#include "SplineCtrl.h"
|
|
|
|
// Qt
|
|
#include <QPainter>
|
|
#include <QPainterPath>
|
|
#include <QToolTip>
|
|
|
|
// Editor
|
|
#include "TimelineCtrl.h"
|
|
|
|
|
|
#define MIN_TIME_EPSILON 0.01f
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
CSplineCtrl::CSplineCtrl(QWidget* parent)
|
|
: QWidget(parent)
|
|
{
|
|
m_nActiveKey = -1;
|
|
m_nHitKeyIndex = -1;
|
|
m_nKeyDrawRadius = 3;
|
|
m_bTracking = false;
|
|
m_pSpline = nullptr;
|
|
m_gridX = 10;
|
|
m_gridY = 10;
|
|
m_fMinTime = -1;
|
|
m_fMaxTime = 1;
|
|
m_fMinValue = -1;
|
|
m_fMaxValue = 1;
|
|
m_fTooltipScaleX = 1;
|
|
m_fTooltipScaleY = 1;
|
|
m_bLockFirstLastKey = false;
|
|
m_pTimelineCtrl = nullptr;
|
|
|
|
m_bSelectedKeys.reserve(0);
|
|
|
|
m_fTimeMarker = -10;
|
|
|
|
setMouseTracking(true);
|
|
}
|
|
|
|
CSplineCtrl::~CSplineCtrl()
|
|
{
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CSplineCtrl message handlers
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::resizeEvent([[maybe_unused]] QResizeEvent* event)
|
|
{
|
|
m_rcSpline = rect();
|
|
|
|
if (m_pTimelineCtrl)
|
|
{
|
|
QRect rct = m_rcSpline;
|
|
rct.setHeight(20);
|
|
m_rcSpline.setTop(rct.bottom() + 1);
|
|
m_pTimelineCtrl->setGeometry(rct);
|
|
}
|
|
|
|
m_rcSpline.adjust(2, 2, -2, -2);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
QPoint CSplineCtrl::KeyToPoint(int nKey)
|
|
{
|
|
if (nKey >= 0)
|
|
{
|
|
return TimeToPoint(m_pSpline->GetKeyTime(nKey));
|
|
}
|
|
return QPoint(0, 0);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
QPoint CSplineCtrl::TimeToPoint(float time)
|
|
{
|
|
QPoint point;
|
|
point.setX(static_cast<int>((time - m_fMinTime) * (m_rcSpline.width() / (m_fMaxTime - m_fMinTime)) + m_rcSpline.left()));
|
|
float val = 0;
|
|
if (m_pSpline)
|
|
{
|
|
m_pSpline->InterpolateFloat(time, val);
|
|
}
|
|
point.setY(static_cast<int>((floor((m_fMaxValue - val) * (m_rcSpline.height() / (m_fMaxValue - m_fMinValue)) + 0.5f) + m_rcSpline.top())));
|
|
return point;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::PointToTimeValue(const QPoint& point, float& time, float& value)
|
|
{
|
|
time = XOfsToTime(point.x());
|
|
float t = float(m_rcSpline.bottom() - point.y()) / m_rcSpline.height();
|
|
value = LERP(m_fMinValue, m_fMaxValue, t);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
float CSplineCtrl::XOfsToTime(int x)
|
|
{
|
|
// m_fMinTime to m_fMaxTime time range.
|
|
float t = float(x - m_rcSpline.left()) / m_rcSpline.width();
|
|
return LERP(m_fMinTime, m_fMaxTime, t);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
QPoint CSplineCtrl::XOfsToPoint(int x)
|
|
{
|
|
return TimeToPoint(XOfsToTime(x));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::paintEvent(QPaintEvent* event)
|
|
{
|
|
QPainter painter(this);
|
|
|
|
if (m_pSpline)
|
|
{
|
|
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
|
|
}
|
|
|
|
{
|
|
if (m_TimeUpdateRect != event->rect())
|
|
{
|
|
painter.fillRect(event->rect(), QColor(140, 140, 140));
|
|
|
|
//Draw Grid
|
|
DrawGrid(&painter);
|
|
|
|
//Draw Keys and Curve
|
|
if (m_pSpline)
|
|
{
|
|
DrawSpline(&painter);
|
|
DrawKeys(&painter);
|
|
}
|
|
}
|
|
m_TimeUpdateRect = QRect();
|
|
}
|
|
DrawTimeMarker(&painter);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::DrawGrid(QPainter* painter)
|
|
{
|
|
QPen pOldPen = painter->pen();
|
|
|
|
int cx = m_rcSpline.width();
|
|
int cy = m_rcSpline.height();
|
|
|
|
QPen pen(QColor(90, 90, 90), 1, Qt::DotLine);
|
|
pen.setCosmetic(true);
|
|
painter->setPen(pen);
|
|
|
|
//Draw Vertical Grid Lines
|
|
for (int y = 1; y < m_gridX; y++)
|
|
{
|
|
painter->drawLine(m_rcSpline.left() + y * cx / m_gridX, m_rcSpline.top() + cy, m_rcSpline.left() + y * cx / m_gridX, m_rcSpline.top());
|
|
}
|
|
|
|
//Draw Horizontal Grid Lines
|
|
for (int x = 1; x < m_gridY; x++)
|
|
{
|
|
painter->drawLine(m_rcSpline.left(), m_rcSpline.top() + x * cy / m_gridY, m_rcSpline.left() + cx, m_rcSpline.top() + x * cy / m_gridY);
|
|
}
|
|
|
|
painter->setPen(QColor(75, 75, 75));
|
|
|
|
painter->drawLine(m_rcSpline.left() + (m_gridX / 2) * cx / m_gridX, m_rcSpline.top() + cy, m_rcSpline.left() + (m_gridX / 2) * cx / m_gridX, m_rcSpline.top() + 0);
|
|
painter->drawLine(m_rcSpline.left() + 0, m_rcSpline.left() + (m_gridY / 2) * cy / m_gridY, m_rcSpline.left() + cx, m_rcSpline.left() + (m_gridY / 2) * cy / m_gridY);
|
|
|
|
painter->drawRect(m_rcSpline);
|
|
|
|
painter->setPen(pOldPen);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::DrawSpline(QPainter* painter)
|
|
{
|
|
//Draw Curve
|
|
// create and select a thick, white pen
|
|
const QPen pOldPen = painter->pen();
|
|
painter->setPen(QColor(128, 255, 128));
|
|
|
|
const QRect rcClip = painter->hasClipping() ? painter->clipBoundingRect().intersected(m_rcSpline).toRect() : m_rcSpline;
|
|
|
|
bool bFirst = true;
|
|
QPainterPath path;
|
|
for (int x = rcClip.left(); x < rcClip.right(); x++)
|
|
{
|
|
QPoint pt = XOfsToPoint(x);
|
|
if (!bFirst)
|
|
{
|
|
path.lineTo(pt);
|
|
}
|
|
else
|
|
{
|
|
path.moveTo(pt);
|
|
bFirst = false;
|
|
}
|
|
}
|
|
painter->drawPath(path);
|
|
|
|
// Put back the old objects
|
|
painter->setPen(pOldPen);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::DrawKeys(QPainter* painter)
|
|
{
|
|
if (!m_pSpline)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// create and select a white pen
|
|
const QPen pOldPen = painter->pen();
|
|
painter->setPen(Qt::black);
|
|
|
|
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
|
|
|
|
for (int i = 0; i < m_pSpline->GetKeyCount(); i++)
|
|
{
|
|
float time = m_pSpline->GetKeyTime(i);
|
|
const QPoint pt = TimeToPoint(time);
|
|
|
|
QColor clr(220, 220, 0);
|
|
if (m_bSelectedKeys[i])
|
|
{
|
|
clr = QColor(255, 0, 0);
|
|
}
|
|
const QBrush pOldBrush = painter->brush();
|
|
painter->setBrush(clr);
|
|
|
|
// Draw this key.
|
|
painter->drawRect(QRect(QPoint(pt.x() - m_nKeyDrawRadius, pt.y() - m_nKeyDrawRadius), QPoint(pt.x() + m_nKeyDrawRadius - 1, pt.y() + m_nKeyDrawRadius - 1)));
|
|
|
|
painter->setBrush(pOldBrush);
|
|
}
|
|
|
|
painter->setPen(pOldPen);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::DrawTimeMarker(QPainter* painter)
|
|
{
|
|
painter->setPen(QColor(255, 0, 255));
|
|
const QPoint pt = TimeToPoint(m_fTimeMarker);
|
|
painter->drawLine(pt.x(), m_rcSpline.top() + 1, pt.x(), m_rcSpline.bottom() - 1);
|
|
}
|
|
|
|
void CSplineCtrl::UpdateToolTip()
|
|
{
|
|
if (m_nHitKeyIndex >= 0 && m_pSpline)
|
|
{
|
|
float time = m_pSpline->GetKeyTime(m_nHitKeyIndex);
|
|
float val;
|
|
m_pSpline->GetKeyValueFloat(m_nHitKeyIndex, val);
|
|
int cont_s = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_IN_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2;
|
|
int cont_d = (m_pSpline->GetKeyFlags(m_nHitKeyIndex) >> SPLINE_KEY_TANGENT_OUT_SHIFT) & SPLINE_KEY_TANGENT_LINEAR ? 1 : 2;
|
|
|
|
const QString tipText = tr("%1, %2, [%3|%4").arg(time * m_fTooltipScaleX, 3, 'f').arg(val * m_fTooltipScaleY, 3, 'f').arg(cont_s).arg(cont_d);
|
|
QToolTip::showText(QCursor::pos(), tipText, this);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//Mouse Message Handlers
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::mousePressEvent(QMouseEvent* event)
|
|
{
|
|
switch (event->button())
|
|
{
|
|
case Qt::LeftButton:
|
|
OnLButtonDown(event->pos(), event->modifiers());
|
|
break;
|
|
case Qt::RightButton:
|
|
OnRButtonDown(event->pos(), event->modifiers());
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CSplineCtrl::mouseReleaseEvent(QMouseEvent* event)
|
|
{
|
|
switch (event->button())
|
|
{
|
|
case Qt::LeftButton:
|
|
OnLButtonUp(event->pos(), event->modifiers());
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CSplineCtrl::OnLButtonDown([[maybe_unused]] const QPoint& point, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
|
|
{
|
|
if (m_bTracking)
|
|
{
|
|
return;
|
|
}
|
|
if (!m_pSpline)
|
|
{
|
|
return;
|
|
}
|
|
|
|
setFocus();
|
|
|
|
switch (m_hitCode)
|
|
{
|
|
case HIT_KEY:
|
|
StartTracking();
|
|
SetActiveKey(m_nHitKeyIndex);
|
|
break;
|
|
|
|
case HIT_SPLINE:
|
|
// Cycle the spline slope of the nearest key.
|
|
ToggleKeySlope(m_nHitKeyIndex, m_nHitKeyDist);
|
|
SetActiveKey(-1);
|
|
break;
|
|
|
|
case HIT_NOTHING:
|
|
SetActiveKey(-1);
|
|
break;
|
|
}
|
|
update();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::OnRButtonDown([[maybe_unused]] const QPoint& point, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
|
|
{
|
|
setFocus();
|
|
|
|
if (!m_pSpline)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::mouseDoubleClickEvent(QMouseEvent* event)
|
|
{
|
|
if (!m_pSpline || event->button() != Qt::LeftButton)
|
|
{
|
|
return;
|
|
}
|
|
|
|
switch (m_hitCode)
|
|
{
|
|
case HIT_NOTHING:
|
|
{
|
|
int iIndex = InsertKey(event->pos());
|
|
SetActiveKey(iIndex);
|
|
|
|
update();
|
|
}
|
|
break;
|
|
case HIT_KEY:
|
|
{
|
|
RemoveKey(m_nHitKeyIndex);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::mouseMoveEvent(QMouseEvent* event)
|
|
{
|
|
OnSetCursor();
|
|
|
|
if (!m_pSpline)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_bTracking)
|
|
{
|
|
TrackKey(event->pos());
|
|
UpdateToolTip();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::OnLButtonUp([[maybe_unused]] const QPoint& point, [[maybe_unused]] Qt::KeyboardModifiers modifiers)
|
|
{
|
|
if (!m_pSpline)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_bTracking)
|
|
{
|
|
StopTracking();
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::SetActiveKey(int nIndex)
|
|
{
|
|
ClearSelection();
|
|
|
|
// Activate New Key
|
|
if (nIndex >= 0)
|
|
{
|
|
m_bSelectedKeys[nIndex] = true;
|
|
}
|
|
m_nActiveKey = nIndex;
|
|
update();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::SetSpline(ISplineInterpolator* pSpline, bool bRedraw)
|
|
{
|
|
if (pSpline != m_pSpline)
|
|
{
|
|
m_pSpline = pSpline;
|
|
}
|
|
|
|
ValidateSpline();
|
|
ClearSelection();
|
|
|
|
if (bRedraw)
|
|
{
|
|
update();
|
|
}
|
|
}
|
|
|
|
void CSplineCtrl::ValidateSpline()
|
|
{
|
|
// Add initial control points (will be serialised only if edited).
|
|
if (m_pSpline->GetKeyCount() == 0)
|
|
{
|
|
m_pSpline->InsertKeyFloat(0.f, 1.f);
|
|
m_pSpline->InsertKeyFloat(1.f, 1.f);
|
|
m_pSpline->Update();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
ISplineInterpolator* CSplineCtrl::GetSpline()
|
|
{
|
|
return m_pSpline;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::OnSetCursor()
|
|
{
|
|
const QPoint point = mapFromGlobal(QCursor::pos());
|
|
|
|
const int hitKey = m_nHitKeyIndex;
|
|
|
|
switch (HitTest(point))
|
|
{
|
|
case HIT_SPLINE:
|
|
{
|
|
setCursor(CMFCUtils::LoadCursor(IDC_ARRWHITE));
|
|
} break;
|
|
case HIT_KEY:
|
|
{
|
|
setCursor(CMFCUtils::LoadCursor(IDC_ARRBLCK));
|
|
} break;
|
|
default:
|
|
unsetCursor();
|
|
break;
|
|
}
|
|
|
|
if (m_bTracking)
|
|
{
|
|
m_nHitKeyIndex = hitKey;
|
|
}
|
|
|
|
if (m_pSpline)
|
|
{
|
|
if (m_nHitKeyIndex >= 0)
|
|
{
|
|
UpdateToolTip();
|
|
}
|
|
else if (!m_bTracking)
|
|
{
|
|
QToolTip::hideText();
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::keyPressEvent(QKeyEvent* event)
|
|
{
|
|
bool bProcessed = false;
|
|
|
|
if (m_nActiveKey != -1 && m_pSpline)
|
|
{
|
|
switch (event->key())
|
|
{
|
|
case Qt::Key_Space:
|
|
{
|
|
ToggleKeySlope(m_nActiveKey, 0);
|
|
bProcessed = true;
|
|
} break;
|
|
case Qt::Key_Delete:
|
|
{
|
|
RemoveKey(m_nActiveKey);
|
|
bProcessed = true;
|
|
} break;
|
|
case Qt::Key_Up:
|
|
{
|
|
CUndo undo("Move Spline Key");
|
|
QPoint point = KeyToPoint(m_nActiveKey);
|
|
point.ry() -= 1;
|
|
emit beforeChange();
|
|
TrackKey(point);
|
|
bProcessed = true;
|
|
} break;
|
|
case Qt::Key_Down:
|
|
{
|
|
CUndo undo("Move Spline Key");
|
|
QPoint point = KeyToPoint(m_nActiveKey);
|
|
point.ry() += 1;
|
|
emit beforeChange();
|
|
TrackKey(point);
|
|
bProcessed = true;
|
|
} break;
|
|
case Qt::Key_Left:
|
|
{
|
|
CUndo undo("Move Spline Key");
|
|
QPoint point = KeyToPoint(m_nActiveKey);
|
|
point.rx() -= 1;
|
|
emit beforeChange();
|
|
TrackKey(point);
|
|
bProcessed = true;
|
|
} break;
|
|
case Qt::Key_Right:
|
|
{
|
|
CUndo undo("Move Spline Key");
|
|
QPoint point = KeyToPoint(m_nActiveKey);
|
|
point.ry() += 1;
|
|
emit beforeChange();
|
|
TrackKey(point);
|
|
bProcessed = true;
|
|
} break;
|
|
|
|
default:
|
|
break; //do nothing
|
|
}
|
|
|
|
update();
|
|
}
|
|
|
|
event->setAccepted(bProcessed);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
CSplineCtrl::EHitCode CSplineCtrl::HitTest(const QPoint& point)
|
|
{
|
|
if (!m_pSpline)
|
|
{
|
|
return HIT_NOTHING;
|
|
}
|
|
|
|
float time, val;
|
|
PointToTimeValue(point, time, val);
|
|
|
|
m_nHitKeyIndex = -1;
|
|
m_nHitKeyDist = 0xFFFF;
|
|
m_hitCode = HIT_NOTHING;
|
|
|
|
QPoint splinePt = TimeToPoint(time);
|
|
if (abs(splinePt.y() - point.y()) < 4)
|
|
{
|
|
m_hitCode = HIT_SPLINE;
|
|
for (int i = 0; i < m_pSpline->GetKeyCount(); i++)
|
|
{
|
|
const QPoint splinePt2 = TimeToPoint(m_pSpline->GetKeyTime(i));
|
|
if (abs(point.x() - splinePt2.x()) < abs(m_nHitKeyDist))
|
|
{
|
|
m_nHitKeyIndex = i;
|
|
m_nHitKeyDist = point.x() - splinePt2.x();
|
|
}
|
|
}
|
|
if (abs(m_nHitKeyDist) < 4)
|
|
{
|
|
m_hitCode = HIT_KEY;
|
|
}
|
|
}
|
|
|
|
return m_hitCode;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::StartTracking()
|
|
{
|
|
m_bTracking = true;
|
|
|
|
GetIEditor()->BeginUndo();
|
|
|
|
emit beforeChange();
|
|
|
|
setCursor(CMFCUtils::LoadCursor(IDC_ARRBLCKCROSS));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::TrackKey(const QPoint& p)
|
|
{
|
|
int nKey = m_nHitKeyIndex;
|
|
|
|
QPoint point = p;
|
|
|
|
if (nKey >= 0)
|
|
{
|
|
float time, val;
|
|
|
|
// Editing time & value.
|
|
Limit(point.rx(), m_rcSpline.left(), m_rcSpline.right());
|
|
Limit(point.ry(), m_rcSpline.top(), m_rcSpline.bottom());
|
|
PointToTimeValue(point, time, val);
|
|
|
|
int i;
|
|
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
|
|
{
|
|
// Switch to next key.
|
|
if (fabs(m_pSpline->GetKeyTime(i) - time) < MIN_TIME_EPSILON)
|
|
{
|
|
if (i != nKey)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_pSpline->SetKeyValueFloat(nKey, val);
|
|
if ((nKey != 0 && nKey != m_pSpline->GetKeyCount() - 1) || !m_bLockFirstLastKey)
|
|
{
|
|
m_pSpline->SetKeyTime(nKey, time);
|
|
}
|
|
else if (m_bLockFirstLastKey)
|
|
{
|
|
int first = 0;
|
|
int last = m_pSpline->GetKeyCount() - 1;
|
|
if (nKey == first)
|
|
{
|
|
m_pSpline->SetKeyValueFloat(last, val);
|
|
}
|
|
else if (nKey == last)
|
|
{
|
|
m_pSpline->SetKeyValueFloat(first, val);
|
|
}
|
|
}
|
|
|
|
m_pSpline->Update();
|
|
emit change();
|
|
if (m_updateCallback)
|
|
{
|
|
m_updateCallback(this);
|
|
}
|
|
|
|
update();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::StopTracking()
|
|
{
|
|
if (!m_bTracking)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetIEditor()->AcceptUndo("Spline Move");
|
|
|
|
m_bTracking = false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::RemoveKey(int nKey)
|
|
{
|
|
if (!m_pSpline)
|
|
{
|
|
return;
|
|
}
|
|
if (nKey)
|
|
{
|
|
if (m_bLockFirstLastKey)
|
|
{
|
|
if (nKey == 0 || nKey == m_pSpline->GetKeyCount() - 1)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
CUndo undo("Remove Spline Key");
|
|
|
|
emit beforeChange();
|
|
m_nActiveKey = -1;
|
|
m_nHitKeyIndex = -1;
|
|
if (m_pSpline)
|
|
{
|
|
m_pSpline->RemoveKey(nKey);
|
|
m_pSpline->Update();
|
|
ValidateSpline();
|
|
}
|
|
emit change();
|
|
if (m_updateCallback)
|
|
{
|
|
m_updateCallback(this);
|
|
}
|
|
update();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
int CSplineCtrl::InsertKey(const QPoint& point)
|
|
{
|
|
CUndo undo("Spline Insert Key");
|
|
|
|
float time, val;
|
|
PointToTimeValue(point, time, val);
|
|
|
|
int i;
|
|
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
|
|
{
|
|
// Skip if any key already have time that is very close.
|
|
if (fabs(m_pSpline->GetKeyTime(i) - time) < MIN_TIME_EPSILON)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
emit beforeChange();
|
|
|
|
m_pSpline->InsertKeyFloat(time, val);
|
|
m_pSpline->Update();
|
|
ClearSelection();
|
|
update();
|
|
|
|
emit change();
|
|
if (m_updateCallback)
|
|
{
|
|
m_updateCallback(this);
|
|
}
|
|
|
|
for (i = 0; i < m_pSpline->GetKeyCount(); i++)
|
|
{
|
|
// Find key with added time.
|
|
if (m_pSpline->GetKeyTime(i) == time)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void CSplineCtrl::ToggleKeySlope(int nIndex, int nDir)
|
|
{
|
|
if (nIndex >= 0)
|
|
{
|
|
int flags = m_pSpline->GetKeyFlags(nIndex);
|
|
if (nDir <= 0)
|
|
{
|
|
// Toggle left side.
|
|
flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_IN_SHIFT;
|
|
}
|
|
if (nDir >= 0)
|
|
{
|
|
// Toggle right side.
|
|
flags ^= SPLINE_KEY_TANGENT_LINEAR << SPLINE_KEY_TANGENT_OUT_SHIFT;
|
|
}
|
|
m_pSpline->SetKeyFlags(nIndex, flags);
|
|
m_pSpline->Update();
|
|
emit change();
|
|
if (m_updateCallback)
|
|
{
|
|
m_updateCallback(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::ClearSelection()
|
|
{
|
|
m_nActiveKey = -1;
|
|
if (m_pSpline)
|
|
{
|
|
m_bSelectedKeys.resize(m_pSpline->GetKeyCount());
|
|
}
|
|
for (int i = 0; i < (int)m_bSelectedKeys.size(); i++)
|
|
{
|
|
m_bSelectedKeys[i] = false;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::SetTimeMarker(float fTime)
|
|
{
|
|
if (!m_pSpline)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (fTime == m_fTimeMarker)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Erase old first.
|
|
QPoint pt0 = TimeToPoint(m_fTimeMarker);
|
|
QPoint pt1 = TimeToPoint(fTime);
|
|
QRect rc(QPoint(pt0.x(), m_rcSpline.top()), QPoint(pt1.x(), m_rcSpline.bottom()));
|
|
rc = rc.normalized();
|
|
rc.adjust(-5, 0, 5, 0);
|
|
rc = rc.intersected(m_rcSpline);
|
|
|
|
m_TimeUpdateRect = rc;
|
|
update(rc);
|
|
|
|
m_fTimeMarker = fTime;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void CSplineCtrl::SetTimelineCtrl(TimelineWidget* pTimelineCtrl)
|
|
{
|
|
m_pTimelineCtrl = pTimelineCtrl;
|
|
if (m_pTimelineCtrl)
|
|
{
|
|
m_pTimelineCtrl->setParent(this);
|
|
}
|
|
}
|
|
|
|
#include <Controls/moc_SplineCtrl.cpp>
|