Update motion interpolation editor
parent
4c6803ad8e
commit
6b3fe2b04b
|
@ -581,14 +581,6 @@ Tips:
|
|||
</context>
|
||||
<context>
|
||||
<name>MotionTimelineWidget</name>
|
||||
<message>
|
||||
<source>Bouncing Begin:</source>
|
||||
<translation>在开始时回弹</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Bouncing End:</source>
|
||||
<translation>在结束时回弹</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Duration:</source>
|
||||
<translation>持续时间:</translation>
|
||||
|
@ -605,6 +597,26 @@ Tips:
|
|||
<source>Delete</source>
|
||||
<translation>删除</translation>
|
||||
</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>
|
||||
<name>PartTreeWidget</name>
|
||||
|
|
|
@ -14,32 +14,65 @@ float calculateInterpolation(InterpolationType type, float 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)
|
||||
{
|
||||
QString name = InterpolationTypeToString(type);
|
||||
if (-1 == name.indexOf("InBack") && -1 == name.indexOf("InOutBack"))
|
||||
return false;
|
||||
return true;
|
||||
if (-1 != name.indexOf("InBack") || -1 != name.indexOf("InOutBack"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InterpolationIsBouncingEnd(InterpolationType type)
|
||||
{
|
||||
QString name = InterpolationTypeToString(type);
|
||||
if (-1 == name.indexOf("OutBack") && -1 == name.indexOf("InOutBack"))
|
||||
return false;
|
||||
return true;
|
||||
if (-1 != name.indexOf("OutBack") || -1 != name.indexOf("InOutBack"))
|
||||
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 (boucingBegin && bouncingEnd)
|
||||
if (isLinear)
|
||||
return InterpolationType::Linear;
|
||||
if (boucingBegin && bouncingEnd) {
|
||||
return InterpolationType::EaseInOutBack;
|
||||
else if (boucingBegin)
|
||||
} else if (boucingBegin) {
|
||||
return InterpolationType::EaseInBack;
|
||||
else if (bouncingEnd)
|
||||
} else if (bouncingEnd) {
|
||||
return InterpolationType::EaseOutBack;
|
||||
else
|
||||
return InterpolationType::EaseInOutCubic;
|
||||
} else {
|
||||
if (hasAccelerating && hasDecelerating) {
|
||||
return InterpolationType::EaseInOutCubic;
|
||||
} else if (hasAccelerating) {
|
||||
return InterpolationType::EaseInCubic;
|
||||
} else if (hasDecelerating) {
|
||||
return InterpolationType::EaseOutCubic;
|
||||
} else {
|
||||
return InterpolationType::EaseInOutCubic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -215,9 +215,14 @@ QEasingCurve::Type InterpolationTypeToEasingCurveType(InterpolationType type)\
|
|||
return s_types[index]; \
|
||||
return QEasingCurve::Linear; \
|
||||
}
|
||||
bool InterpolationIsLinear(InterpolationType type);
|
||||
bool InterpolationHasAccelerating(InterpolationType type);
|
||||
bool InterpolationHasDecelerating(InterpolationType type);
|
||||
bool InterpolationIsBouncingBegin(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);
|
||||
|
||||
|
|
|
@ -5,11 +5,15 @@
|
|||
#include <QCheckBox>
|
||||
#include <QFormLayout>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QStackedWidget>
|
||||
#include "motiontimelinewidget.h"
|
||||
#include "motionclipwidget.h"
|
||||
#include "theme.h"
|
||||
#include "posewidget.h"
|
||||
#include "motionwidget.h"
|
||||
#include "tabwidget.h"
|
||||
|
||||
MotionTimelineWidget::MotionTimelineWidget(const Document *document, QWidget *parent) :
|
||||
QListWidget(parent),
|
||||
|
@ -132,6 +136,9 @@ void MotionTimelineWidget::setClipInterpolationType(int index, InterpolationType
|
|||
if (m_clips[index].clipType != MotionClipType::Interpolation)
|
||||
return;
|
||||
|
||||
if (m_clips[index].interpolationType == type)
|
||||
return;
|
||||
|
||||
m_clips[index].interpolationType = type;
|
||||
emit clipsChanged();
|
||||
}
|
||||
|
@ -182,26 +189,72 @@ void MotionTimelineWidget::showInterpolationSettingPopup(int clipIndex, const QP
|
|||
|
||||
QWidget *popup = new QWidget;
|
||||
|
||||
QWidget *linearWidget = new QWidget;
|
||||
QWidget *cubicWidget = new QWidget;
|
||||
|
||||
QCheckBox *hasAcceleratingBox = new QCheckBox();
|
||||
QCheckBox *hasDeceleratingBox = new QCheckBox();
|
||||
QCheckBox *bouncingBeginBox = new QCheckBox();
|
||||
bouncingBeginBox->setChecked(InterpolationIsBouncingBegin(clips()[clipIndex].interpolationType));
|
||||
connect(bouncingBeginBox, &QCheckBox::stateChanged, this, [=]() {
|
||||
QCheckBox *bouncingEndBox = new QCheckBox();
|
||||
|
||||
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();
|
||||
const auto &oldType = clips()[clipIndex].interpolationType;
|
||||
bool bouncingEnd = InterpolationIsBouncingEnd(oldType);
|
||||
InterpolationType newType = InterpolationMakeBouncingType(oldType, bouncingBegin, bouncingEnd);
|
||||
bool bouncingEnd = bouncingEndBox->isChecked();
|
||||
bool hasAccelerating = bouncingBegin || hasAcceleratingBox->isChecked();
|
||||
bool hasDecelerating = bouncingEnd || hasDeceleratingBox->isChecked();
|
||||
InterpolationType newType = InterpolationMakeFromOptions(isLinear,
|
||||
hasAccelerating, hasDecelerating,
|
||||
bouncingBegin, bouncingEnd);
|
||||
setClipInterpolationType(clipIndex, newType);
|
||||
updateBoxes(newType);
|
||||
};
|
||||
|
||||
connect(tabWidget, &TabWidget::currentIndexChanged, this, [=](int index) {
|
||||
stackedWidget->setCurrentIndex(index);
|
||||
updateInterpolation();
|
||||
});
|
||||
|
||||
QCheckBox *bouncingEndBox = new QCheckBox();
|
||||
bouncingEndBox->setChecked(InterpolationIsBouncingEnd(clips()[clipIndex].interpolationType));
|
||||
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(hasAcceleratingBox, &QCheckBox::stateChanged, this, [=]() {
|
||||
updateInterpolation();
|
||||
});
|
||||
|
||||
connect(hasDeceleratingBox, &QCheckBox::stateChanged, this, [=]() {
|
||||
updateInterpolation();
|
||||
});
|
||||
|
||||
connect(bouncingBeginBox, &QCheckBox::stateChanged, this, [=]() {
|
||||
updateInterpolation();
|
||||
});
|
||||
|
||||
connect(bouncingEndBox, &QCheckBox::stateChanged, this, [=]() {
|
||||
updateInterpolation();
|
||||
});
|
||||
|
||||
updateInterpolation();
|
||||
|
||||
QDoubleSpinBox *durationEdit = new QDoubleSpinBox();
|
||||
durationEdit->setDecimals(2);
|
||||
durationEdit->setMaximum(60);
|
||||
|
@ -213,12 +266,48 @@ void MotionTimelineWidget::showInterpolationSettingPopup(int clipIndex, const QP
|
|||
setClipDuration(clipIndex, (float)value);
|
||||
});
|
||||
|
||||
QFormLayout *formLayout = new QFormLayout;
|
||||
formLayout->addRow(tr("Bouncing Begin:"), bouncingBeginBox);
|
||||
formLayout->addRow(tr("Bouncing End:"), bouncingEndBox);
|
||||
formLayout->addRow(tr("Duration:"), durationEdit);
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
|
||||
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);
|
||||
action->setDefaultWidget(popup);
|
||||
|
|
Loading…
Reference in New Issue