Skip to content

Commit

Permalink
Graph: initial support for easing (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
rodlie committed Aug 20, 2024
1 parent fe5297b commit b0b49bd
Show file tree
Hide file tree
Showing 12 changed files with 216 additions and 94 deletions.
77 changes: 4 additions & 73 deletions src/app/GUI/Expressions/expressiondialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ ExpressionDialog::ExpressionDialog(QrealAnimator* const target,
tr("No preset selected."));
return;
}
const auto preset = readEasingPreset(filename);
const auto preset = AppSupport::readEasingPreset(filename);
if (!preset.valid) { return; }
QString script = preset.script;
script.replace("__START_VALUE__",
Expand All @@ -437,7 +437,8 @@ ExpressionDialog::ExpressionDialog(QrealAnimator* const target,
if (valid) {
if (easingPresetConvertOnApply->isChecked()) {
mTarget->applyExpression(FrameRange{mEasingPresetStartFrameSpin->value(),
mEasingPresetEndFrameSpin->value()}, 10, true);
mEasingPresetEndFrameSpin->value()},
10, true, true);
}
accept();
}
Expand Down Expand Up @@ -646,57 +647,9 @@ void ExpressionDialog::setCurrentTabId(const int id) {
mDefinitionsError->setVisible(!first);
}

const QStringList ExpressionDialog::generateEasingPresets()
{
/*QDir userDir(QString::fromUtf8("%1/easing").arg(AppSupport::getAppUserExPresetsPath()));
QDir appDir(QString::fromUtf8("%1/easing").arg(AppSupport::getAppExPresetsPath()));
const auto userPresets = userDir.entryInfoList(QStringList() << "*.js", QDir::Files);
const auto appPresets = appDir.entryInfoList(QStringList() << "*.js", QDir::Files);
QFileInfoList presets;
QStringList usable;
presets << userPresets << appPresets;
for (int i = 0; i < presets.size(); ++i) {
qDebug() << "Checking expression preset" << presets.at(i).absoluteFilePath();
if (!readEasingPreset(presets.at(i).absoluteFilePath()).valid) { continue; }
usable << presets.at(i).absoluteFilePath();
}*/
QStringList presets;
presets << ":/easing/presets/easeInBack.js"
<< ":/easing/presets/easeInBounce.js"
<< ":/easing/presets/easeInCirc.js"
<< ":/easing/presets/easeInCubic.js"
<< ":/easing/presets/easeInElastic.js"
<< ":/easing/presets/easeInExpo.js"
<< ":/easing/presets/easeInOutBack.js"
<< ":/easing/presets/easeInOutBounce.js"
<< ":/easing/presets/easeInOutCirc.js"
<< ":/easing/presets/easeInOutCubic.js"
<< ":/easing/presets/easeInOutElastic.js"
<< ":/easing/presets/easeInOutExpo.js"
<< ":/easing/presets/easeInOutQuad.js"
<< ":/easing/presets/easeInOutQuart.js"
<< ":/easing/presets/easeInOutQuint.js"
<< ":/easing/presets/easeInOutSine.js"
<< ":/easing/presets/easeInQuad.js"
<< ":/easing/presets/easeInQuart.js"
<< ":/easing/presets/easeInQuint.js"
<< ":/easing/presets/easeInSine.js"
<< ":/easing/presets/easeOutBack.js"
<< ":/easing/presets/easeOutBounce.js"
<< ":/easing/presets/easeOutCirc.js"
<< ":/easing/presets/easeOutCubic.js"
<< ":/easing/presets/easeOutElastic.js"
<< ":/easing/presets/easeOutExpo.js"
<< ":/easing/presets/easeOutQuad.js"
<< ":/easing/presets/easeOutQuart.js"
<< ":/easing/presets/easeOutQuint.js"
<< ":/easing/presets/easeOutSine.js";
return presets;
}

bool ExpressionDialog::populateEasingPresets()
{
const auto presets = generateEasingPresets();
const auto presets = AppSupport::getEasingPresets();
if (presets.size() < 1) { return false; }
else {
mEasingPresetsBox->clear();
Expand Down Expand Up @@ -867,25 +820,3 @@ bool ExpressionDialog::apply(const bool action) {
Document::sInstance->actionFinished();
return true;
}

const ExpressionDialog::ExPreset ExpressionDialog::readEasingPreset(const QString &filename)
{
ExPreset preset;
preset.valid = false;
if (!QFile::exists(filename)) { return preset; }
QFile file(filename);
QString js;
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
js = file.readAll();
file.close();
}
if (js.isEmpty()) { return preset; }
QStringList parts = js.split("/*_FRICTION_EXPRESSION_PRESET_*/",
QT_SKIP_EMPTY);
if (parts.size() != 3) { return preset; }
preset.valid = true;
preset.definitions = parts.at(0).trimmed();
preset.bindings = parts.at(1).trimmed();
preset.script = parts.at(2).trimmed();
return preset;
}
9 changes: 0 additions & 9 deletions src/app/GUI/Expressions/expressiondialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,14 @@ class ExpressionDialog : public QDialog
QWidget * const parent = nullptr);

private:
struct ExPreset
{
bool valid = false;
QString definitions;
QString bindings;
QString script;
};
using PropertyBindingMap = std::map<QString, QSharedPointer<PropertyBindingBase>>;
bool getBindings(PropertyBindingMap& bindings);
void updateScriptBindings();
void updateScriptDefinitions();
void updateAllScript();
void setCurrentTabId(const int id);
const QStringList generateEasingPresets();
bool populateEasingPresets();
bool apply(const bool action);
const ExPreset readEasingPreset(const QString &filename);

QrealAnimator* const mTarget;

Expand Down
33 changes: 28 additions & 5 deletions src/app/GUI/animationdockwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@
// Fork of enve - Copyright (C) 2016-2020 Maurycy Liebner

#include "animationdockwidget.h"
#include "GUI/keysview.h"
#include "appsupport.h"

#include "GUI/global.h"
#include "keysview.h"
#include "widgets/actionbutton.h"
#include "Private/esettings.h"
#include <QMenu>

AnimationDockWidget::AnimationDockWidget(QWidget *parent,
KeysView *keysView)
Expand All @@ -38,7 +37,10 @@ AnimationDockWidget::AnimationDockWidget(QWidget *parent,
setSizePolicy(QSizePolicy::Maximum,
QSizePolicy::Maximum);

const QString iconsDir = eSettings::sIconsDir() + "/toolbarButtons";
const auto easingButton = new QPushButton(QIcon::fromTheme("easing"),
QString(), this);
easingButton->setFocusPolicy(Qt::NoFocus);
generateEasingActions(easingButton, keysView);

QAction *mLineButton = new QAction(QIcon::fromTheme("segmentLine"),
tr("Make Segment Line"), this);
Expand Down Expand Up @@ -94,6 +96,7 @@ AnimationDockWidget::AnimationDockWidget(QWidget *parent,
connect(onlySelectedAct, &QAction::triggered,
keysView, &KeysView::graphSetOnlySelectedVisible);

addWidget(easingButton);
addAction(mLineButton);
addAction(mCurveButton);
addAction(mSymmetricButton);
Expand All @@ -103,3 +106,23 @@ AnimationDockWidget::AnimationDockWidget(QWidget *parent,
//addWidget(valueLines);
addAction(onlySelectedAct);
}

void AnimationDockWidget::generateEasingActions(QPushButton *button,
KeysView *keysView)
{
const auto presets = AppSupport::getEasingPresets();
const auto menu = new QMenu(this);
for (const auto &preset : presets) {
QString title = preset;
const auto presetAct = new QAction(QIcon::fromTheme("easing"),
title.split("/").takeLast().remove(".js"),
this);
presetAct->setData(preset);
menu->addAction(presetAct);
connect(presetAct, &QAction::triggered,
this, [presetAct, keysView]() {
keysView->graphEasingAction(presetAct->data().toString());
});
}
if (!menu->isEmpty()) { button->setMenu(menu); }
}
6 changes: 6 additions & 0 deletions src/app/GUI/animationdockwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@
#ifndef ANIMATIONDOCKWIDGET_H
#define ANIMATIONDOCKWIDGET_H

#include "GUI/global.h"

#include <QToolBar>
#include <QPushButton>

class KeysView;

Expand All @@ -37,6 +40,9 @@ class AnimationDockWidget : public QToolBar
public:
explicit AnimationDockWidget(QWidget *parent,
KeysView *keysView);
private:
void generateEasingActions(QPushButton *button,
KeysView *keysView);
};

#endif // ANIMATIONDOCKWIDGET_H
78 changes: 78 additions & 0 deletions src/app/GUI/graphboxeslist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@

#include <QPainter>
#include <QMouseEvent>
#include "Animators/transformanimator.h"
#include "Expressions/expression.h"
#include "Expressions/propertybindingparser.h"
#include "mainwindow.h"
#include "dialogs/qrealpointvaluedialog.h"
#include "keysview.h"
Expand All @@ -47,6 +50,81 @@ bool KeysView::graphIsSelected(GraphAnimator * const anim) {
return false;
}

void KeysView::graphEasingAction(const QString &easing)
{
if (mSelectedKeysAnimators.isEmpty()) { return; }
for (const auto& anim : mGraphAnimators) {
QList<QList<GraphKey*>> segments;
anim->graph_getSelectedSegments(segments);
for (const auto& segment : segments) {
Q_ASSERT(segment.length() > 1);
auto firstKey = segment.first();
auto lastKey = segment.last();
graphEasingApply(static_cast<QrealAnimator*>(anim),
{firstKey->getRelFrame(),
lastKey->getRelFrame()},
easing);
}
}

}

void KeysView::graphEasingApply(QrealAnimator *anim,
const FrameRange &range,
const QString &easing)
{
if (!graphEasingApplyExpression(anim, range, easing)) {
// add warning or something
}
}

bool KeysView::graphEasingApplyExpression(QrealAnimator *anim,
const FrameRange &range,
const QString &easing)
{
if (!anim || easing.isEmpty() || !QFile::exists(easing)) { return false; }
qDebug() << "graphEasingApplyExpression" << anim->prp_getName() << range.fMin << range.fMax << easing;

const auto preset = AppSupport::readEasingPreset(easing);
if (!preset.valid) { return false; }
QString script = preset.script;
script.replace("__START_VALUE__",
QString::number(anim->getBaseValue(range.fMin)));
script.replace("__END_VALUE__",
QString::number(anim->getBaseValue(range.fMax)));
script.replace("__START_FRAME__",
QString::number(range.fMin));
script.replace("__END_FRAME__",
QString::number(range.fMax));

PropertyBindingMap bindings;
try { bindings = PropertyBindingParser::parseBindings(preset.bindings, nullptr, anim); }
catch (const std::exception& e) { return false; }

auto engine = std::make_unique<QJSEngine>();
try { Expression::sAddDefinitionsTo(preset.definitions, *engine); }
catch (const std::exception& e) { return false; }

QJSValue eEvaluate;
try {
Expression::sAddScriptTo(script, bindings, *engine, eEvaluate,
Expression::sQrealAnimatorTester);
} catch(const std::exception& e) { return false; }

try {
auto expr = Expression::sCreate(preset.definitions,
script, std::move(bindings),
std::move(engine),
std::move(eEvaluate));
if (expr && !expr->isValid()) { expr = nullptr; }
anim->setExpression(expr);
anim->applyExpression(range, 10, true, true);
Document::sInstance->actionFinished();
} catch (const std::exception& e) { return false; }

return true;
}

int KeysView::graphGetAnimatorId(GraphAnimator * const anim) {
return mGraphAnimators.indexOf(anim);
}
Expand Down
7 changes: 7 additions & 0 deletions src/app/GUI/keysview.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ class KeysView : public QWidget, public KeyFocusTarget {
void graphUpdateVisbile();
void graphSetOnlySelectedVisible(const bool selectedOnly);
bool graphIsSelected(GraphAnimator * const anim);
void graphEasingAction(const QString &easing);
void graphEasingApply(QrealAnimator *anim,
const FrameRange &range,
const QString &easing);
bool graphEasingApplyExpression(QrealAnimator *anim,
const FrameRange &range,
const QString &easing);

TimelineHighlightWidget *requestHighlighter();
private:
Expand Down
3 changes: 2 additions & 1 deletion src/app/friction.qss
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,8 @@ QToolBar#animationDockWidget {
background: transparent;
}

QToolBar#animationDockWidget QToolButton {
QToolBar#animationDockWidget QToolButton,
QToolBar#animationDockWidget QPushButton {
background-color: %3;
}

Expand Down
24 changes: 20 additions & 4 deletions src/core/Animators/qrealanimator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,18 @@ void QrealAnimator::setExpressionAction(const qsptr<Expression> &expression) {
setExpression(expression);
}

void QrealAnimator::setExpressionEasingAction(const qsptr<Expression> &expression)
{
{
prp_pushUndoRedoName("Change Easing");
UndoRedo ur;
ur.fUndo = [this]() { setExpression(nullptr); };
ur.fRedo = [this, expression]() { setExpression(expression); };
prp_addUndoRedo(ur);
}
setExpression(expression);
}

void QrealAnimator::applyExpressionSub(const FrameRange& relRange,
const int sampleInc,
const bool action,
Expand Down Expand Up @@ -363,14 +375,17 @@ void QrealAnimator::applyExpressionSub(const FrameRange& relRange,

void QrealAnimator::applyExpression(const FrameRange& relRange,
const qreal accuracy,
const bool action) {
const bool action,
const bool easing)
{
qDebug() << "applyExpression" << prp_getName() << relRange.fMin << relRange.fMax << accuracy << action << easing;
if(!hasValidExpression()) {}
else if(!relRange.isValid()) {}
else if(isZero4Dec(accuracy) || accuracy < 0) {}
else {
const int sampleInc = qMax(1, qRound(1/accuracy));

prp_pushUndoRedoName("Apply Expression");
prp_pushUndoRedoName(easing ? "Apply Easing" : "Apply Expression");

const bool isStatic = mExpression->isStatic();
if(isStatic) {
Expand All @@ -394,8 +409,9 @@ void QrealAnimator::applyExpression(const FrameRange& relRange,
}
}

if(action) setExpressionAction(nullptr);
else setExpression(nullptr);
if (easing) { setExpressionEasingAction(nullptr); }
else if (action) { setExpressionAction(nullptr); }
else { setExpression(nullptr); }
}

void QrealAnimator::setExpression(const qsptr<Expression>& expression) {
Expand Down
4 changes: 3 additions & 1 deletion src/core/Animators/qrealanimator.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,11 @@ class CORE_EXPORT QrealAnimator : public GraphAnimator {

void setExpression(const qsptr<Expression>& expression);
void setExpressionAction(const qsptr<Expression>& expression);
void setExpressionEasingAction(const qsptr<Expression>& expression);
void applyExpression(const FrameRange& relRange,
const qreal accuracy,
const bool action);
const bool action,
const bool easing = false);

void saveQrealSVG(SvgExporter& exp,
QDomElement& parent,
Expand Down
Loading

0 comments on commit b0b49bd

Please sign in to comment.