Update motion interpolation editor
parent
4c6803ad8e
commit
6b3fe2b04b
|
@ -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>
|
||||||
|
|
|
@ -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 {
|
||||||
|
if (hasAccelerating && hasDecelerating) {
|
||||||
return InterpolationType::EaseInOutCubic;
|
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 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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue