From 6b3fe2b04bac5bd09f68ff1a402bc9d5690549d1 Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Wed, 6 Nov 2019 20:20:00 +0930 Subject: [PATCH] Update motion interpolation editor --- languages/dust3d_zh_CN.ts | 28 +++++--- src/interpolationtype.cpp | 59 +++++++++++++---- src/interpolationtype.h | 7 +- src/motiontimelinewidget.cpp | 125 ++++++++++++++++++++++++++++++----- 4 files changed, 179 insertions(+), 40 deletions(-) diff --git a/languages/dust3d_zh_CN.ts b/languages/dust3d_zh_CN.ts index 14d28069..78188e31 100644 --- a/languages/dust3d_zh_CN.ts +++ b/languages/dust3d_zh_CN.ts @@ -581,14 +581,6 @@ Tips: MotionTimelineWidget - - Bouncing Begin: - 在开始时回弹 - - - Bouncing End: - 在结束时回弹 - Duration: 持续时间: @@ -605,6 +597,26 @@ Tips: Delete 删除 + + Linear + 直线 + + + Cubic + 曲线 + + + Accelerating: + 加速: + + + Bouncing: + 回弹: + + + Decelerating: + 减速: + PartTreeWidget diff --git a/src/interpolationtype.cpp b/src/interpolationtype.cpp index 7b0cab00..568e2acc 100644 --- a/src/interpolationtype.cpp +++ b/src/interpolationtype.cpp @@ -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; + } + } } diff --git a/src/interpolationtype.h b/src/interpolationtype.h index 6afd6497..f20074f9 100644 --- a/src/interpolationtype.h +++ b/src/interpolationtype.h @@ -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); diff --git a/src/motiontimelinewidget.cpp b/src/motiontimelinewidget.cpp index a51d9076..08029495 100644 --- a/src/motiontimelinewidget.cpp +++ b/src/motiontimelinewidget.cpp @@ -5,11 +5,15 @@ #include #include #include +#include +#include +#include #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 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);