Update motion interpolation editor

master
Jeremy Hu 2019-11-06 20:20:00 +09:30
parent 4c6803ad8e
commit 6b3fe2b04b
4 changed files with 179 additions and 40 deletions

View File

@ -581,14 +581,6 @@ Tips:
</context> </context>
<context> <context>
<name>MotionTimelineWidget</name> <name>MotionTimelineWidget</name>
<message>
<source>Bouncing Begin:</source>
<translation></translation>
</message>
<message>
<source>Bouncing End:</source>
<translation></translation>
</message>
<message> <message>
<source>Duration:</source> <source>Duration:</source>
<translation></translation> <translation></translation>
@ -605,6 +597,26 @@ Tips:
<source>Delete</source> <source>Delete</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>Linear</source>
<translation>线</translation>
</message>
<message>
<source>Cubic</source>
<translation>线</translation>
</message>
<message>
<source>Accelerating:</source>
<translation></translation>
</message>
<message>
<source>Bouncing:</source>
<translation></translation>
</message>
<message>
<source>Decelerating:</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>PartTreeWidget</name> <name>PartTreeWidget</name>

View File

@ -14,32 +14,65 @@ float calculateInterpolation(InterpolationType type, float knot)
return easing.valueForProgress(knot); return easing.valueForProgress(knot);
} }
bool InterpolationIsLinear(InterpolationType type)
{
return QEasingCurve::Linear == InterpolationTypeToEasingCurveType(type);
}
bool InterpolationHasAccelerating(InterpolationType type)
{
QString name = InterpolationTypeToString(type);
if (-1 != name.indexOf("In"))
return true;
return false;
}
bool InterpolationHasDecelerating(InterpolationType type)
{
QString name = InterpolationTypeToString(type);
if (-1 != name.indexOf("Out"))
return true;
return false;
}
bool InterpolationIsBouncingBegin(InterpolationType type) bool InterpolationIsBouncingBegin(InterpolationType type)
{ {
QString name = InterpolationTypeToString(type); QString name = InterpolationTypeToString(type);
if (-1 == name.indexOf("InBack") && -1 == name.indexOf("InOutBack")) if (-1 != name.indexOf("InBack") || -1 != name.indexOf("InOutBack"))
return false; return true;
return true; return false;
} }
bool InterpolationIsBouncingEnd(InterpolationType type) bool InterpolationIsBouncingEnd(InterpolationType type)
{ {
QString name = InterpolationTypeToString(type); QString name = InterpolationTypeToString(type);
if (-1 == name.indexOf("OutBack") && -1 == name.indexOf("InOutBack")) if (-1 != name.indexOf("OutBack") || -1 != name.indexOf("InOutBack"))
return false; return true;
return true; return false;
} }
InterpolationType InterpolationMakeBouncingType(InterpolationType type, bool boucingBegin, bool bouncingEnd) InterpolationType InterpolationMakeFromOptions(bool isLinear,
bool hasAccelerating, bool hasDecelerating,
bool boucingBegin, bool bouncingEnd)
{ {
Q_UNUSED(type); if (isLinear)
if (boucingBegin && bouncingEnd) return InterpolationType::Linear;
if (boucingBegin && bouncingEnd) {
return InterpolationType::EaseInOutBack; return InterpolationType::EaseInOutBack;
else if (boucingBegin) } else if (boucingBegin) {
return InterpolationType::EaseInBack; return InterpolationType::EaseInBack;
else if (bouncingEnd) } else if (bouncingEnd) {
return InterpolationType::EaseOutBack; return InterpolationType::EaseOutBack;
else } else {
return InterpolationType::EaseInOutCubic; if (hasAccelerating && hasDecelerating) {
return InterpolationType::EaseInOutCubic;
} else if (hasAccelerating) {
return InterpolationType::EaseInCubic;
} else if (hasDecelerating) {
return InterpolationType::EaseOutCubic;
} else {
return InterpolationType::EaseInOutCubic;
}
}
} }

View File

@ -215,9 +215,14 @@ QEasingCurve::Type InterpolationTypeToEasingCurveType(InterpolationType type)\
return s_types[index]; \ return s_types[index]; \
return QEasingCurve::Linear; \ return QEasingCurve::Linear; \
} }
bool InterpolationIsLinear(InterpolationType type);
bool InterpolationHasAccelerating(InterpolationType type);
bool InterpolationHasDecelerating(InterpolationType type);
bool InterpolationIsBouncingBegin(InterpolationType type); bool InterpolationIsBouncingBegin(InterpolationType type);
bool InterpolationIsBouncingEnd(InterpolationType type); bool InterpolationIsBouncingEnd(InterpolationType type);
InterpolationType InterpolationMakeBouncingType(InterpolationType type, bool boucingBegin, bool bouncingEnd); InterpolationType InterpolationMakeFromOptions(bool isLinear,
bool hasAccelerating, bool hasDecelerating,
bool boucingBegin, bool bouncingEnd);
float calculateInterpolation(InterpolationType type, float knot); float calculateInterpolation(InterpolationType type, float knot);

View File

@ -5,11 +5,15 @@
#include <QCheckBox> #include <QCheckBox>
#include <QFormLayout> #include <QFormLayout>
#include <QDoubleSpinBox> #include <QDoubleSpinBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QStackedWidget>
#include "motiontimelinewidget.h" #include "motiontimelinewidget.h"
#include "motionclipwidget.h" #include "motionclipwidget.h"
#include "theme.h" #include "theme.h"
#include "posewidget.h" #include "posewidget.h"
#include "motionwidget.h" #include "motionwidget.h"
#include "tabwidget.h"
MotionTimelineWidget::MotionTimelineWidget(const Document *document, QWidget *parent) : MotionTimelineWidget::MotionTimelineWidget(const Document *document, QWidget *parent) :
QListWidget(parent), QListWidget(parent),
@ -132,6 +136,9 @@ void MotionTimelineWidget::setClipInterpolationType(int index, InterpolationType
if (m_clips[index].clipType != MotionClipType::Interpolation) if (m_clips[index].clipType != MotionClipType::Interpolation)
return; return;
if (m_clips[index].interpolationType == type)
return;
m_clips[index].interpolationType = type; m_clips[index].interpolationType = type;
emit clipsChanged(); emit clipsChanged();
} }
@ -182,26 +189,72 @@ void MotionTimelineWidget::showInterpolationSettingPopup(int clipIndex, const QP
QWidget *popup = new QWidget; QWidget *popup = new QWidget;
QWidget *linearWidget = new QWidget;
QWidget *cubicWidget = new QWidget;
QCheckBox *hasAcceleratingBox = new QCheckBox();
QCheckBox *hasDeceleratingBox = new QCheckBox();
QCheckBox *bouncingBeginBox = new QCheckBox(); QCheckBox *bouncingBeginBox = new QCheckBox();
bouncingBeginBox->setChecked(InterpolationIsBouncingBegin(clips()[clipIndex].interpolationType)); QCheckBox *bouncingEndBox = new QCheckBox();
connect(bouncingBeginBox, &QCheckBox::stateChanged, this, [=]() {
QStackedWidget *stackedWidget = new QStackedWidget;
stackedWidget->addWidget(linearWidget);
stackedWidget->addWidget(cubicWidget);
bool currentIsLinear = InterpolationIsLinear(clips()[clipIndex].interpolationType);
std::vector<QString> tabs = {
tr("Linear"),
tr("Cubic")
};
TabWidget *tabWidget = new TabWidget(tabs);
tabWidget->setCurrentIndex(currentIsLinear ? 0 : 1);
stackedWidget->setCurrentIndex(currentIsLinear ? 0 : 1);
auto updateBoxes = [=](InterpolationType type) {
hasAcceleratingBox->setChecked(InterpolationHasAccelerating(type));
hasDeceleratingBox->setChecked(InterpolationHasDecelerating(type));
bouncingBeginBox->setChecked(InterpolationIsBouncingBegin(type));
bouncingEndBox->setChecked(InterpolationIsBouncingEnd(type));
};
updateBoxes(clips()[clipIndex].interpolationType);
auto updateInterpolation = [=]() {
bool isLinear = 0 == tabWidget->currentIndex();
bool bouncingBegin = bouncingBeginBox->isChecked(); bool bouncingBegin = bouncingBeginBox->isChecked();
const auto &oldType = clips()[clipIndex].interpolationType; bool bouncingEnd = bouncingEndBox->isChecked();
bool bouncingEnd = InterpolationIsBouncingEnd(oldType); bool hasAccelerating = bouncingBegin || hasAcceleratingBox->isChecked();
InterpolationType newType = InterpolationMakeBouncingType(oldType, bouncingBegin, bouncingEnd); bool hasDecelerating = bouncingEnd || hasDeceleratingBox->isChecked();
InterpolationType newType = InterpolationMakeFromOptions(isLinear,
hasAccelerating, hasDecelerating,
bouncingBegin, bouncingEnd);
setClipInterpolationType(clipIndex, newType); setClipInterpolationType(clipIndex, newType);
updateBoxes(newType);
};
connect(tabWidget, &TabWidget::currentIndexChanged, this, [=](int index) {
stackedWidget->setCurrentIndex(index);
updateInterpolation();
}); });
QCheckBox *bouncingEndBox = new QCheckBox(); connect(hasAcceleratingBox, &QCheckBox::stateChanged, this, [=]() {
bouncingEndBox->setChecked(InterpolationIsBouncingEnd(clips()[clipIndex].interpolationType)); updateInterpolation();
connect(bouncingEndBox, &QCheckBox::stateChanged, this, [=]() {
bool bouncingEnd = bouncingEndBox->isChecked();
const auto &oldType = clips()[clipIndex].interpolationType;
bool bouncingBegin = InterpolationIsBouncingBegin(oldType);
InterpolationType newType = InterpolationMakeBouncingType(oldType, bouncingBegin, bouncingEnd);
setClipInterpolationType(clipIndex, newType);
}); });
connect(hasDeceleratingBox, &QCheckBox::stateChanged, this, [=]() {
updateInterpolation();
});
connect(bouncingBeginBox, &QCheckBox::stateChanged, this, [=]() {
updateInterpolation();
});
connect(bouncingEndBox, &QCheckBox::stateChanged, this, [=]() {
updateInterpolation();
});
updateInterpolation();
QDoubleSpinBox *durationEdit = new QDoubleSpinBox(); QDoubleSpinBox *durationEdit = new QDoubleSpinBox();
durationEdit->setDecimals(2); durationEdit->setDecimals(2);
durationEdit->setMaximum(60); durationEdit->setMaximum(60);
@ -213,12 +266,48 @@ void MotionTimelineWidget::showInterpolationSettingPopup(int clipIndex, const QP
setClipDuration(clipIndex, (float)value); setClipDuration(clipIndex, (float)value);
}); });
QFormLayout *formLayout = new QFormLayout; QVBoxLayout *mainLayout = new QVBoxLayout;
formLayout->addRow(tr("Bouncing Begin:"), bouncingBeginBox);
formLayout->addRow(tr("Bouncing End:"), bouncingEndBox);
formLayout->addRow(tr("Duration:"), durationEdit);
popup->setLayout(formLayout); QVBoxLayout *cubicLayout = new QVBoxLayout;
QHBoxLayout *acceleratingLayout = new QHBoxLayout;
{
QFormLayout *formLayout = new QFormLayout;
formLayout->addRow(tr("Accelerating:"), hasAcceleratingBox);
acceleratingLayout->addLayout(formLayout);
}
{
QFormLayout *formLayout = new QFormLayout;
formLayout->addRow(tr("Bouncing:"), bouncingBeginBox);
acceleratingLayout->addLayout(formLayout);
}
cubicLayout->addLayout(acceleratingLayout);
QHBoxLayout *deceleratingLayout = new QHBoxLayout;
{
QFormLayout *formLayout = new QFormLayout;
formLayout->addRow(tr("Decelerating:"), hasDeceleratingBox);
deceleratingLayout->addLayout(formLayout);
}
{
QFormLayout *formLayout = new QFormLayout;
formLayout->addRow(tr("Bouncing:"), bouncingEndBox);
deceleratingLayout->addLayout(formLayout);
}
cubicLayout->addLayout(deceleratingLayout);
cubicWidget->setLayout(cubicLayout);
mainLayout->addWidget(tabWidget);
mainLayout->addWidget(stackedWidget);
{
QFormLayout *formLayout = new QFormLayout;
formLayout->addRow(tr("Duration:"), durationEdit);
mainLayout->addLayout(formLayout);
}
popup->setLayout(mainLayout);
QWidgetAction *action = new QWidgetAction(this); QWidgetAction *action = new QWidgetAction(this);
action->setDefaultWidget(popup); action->setDefaultWidget(popup);