Skip to content

Commit

Permalink
Merge pull request #1428 from manongjohn/inbetween_from_timeline
Browse files Browse the repository at this point in the history
Inbetween from Timeline/Xsheet and Hook movement tweening
  • Loading branch information
manongjohn committed Mar 29, 2024
2 parents eb33965 + 0e1a728 commit ccd369c
Show file tree
Hide file tree
Showing 10 changed files with 312 additions and 5 deletions.
186 changes: 181 additions & 5 deletions toonz/sources/toonz/cellselection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1489,7 +1489,124 @@ void getLevelSetFromData(const TCellData *cellData,
levelSet.insert(tmpLevel.getPointer());
}
}
}
};

//=============================================================================
// inbetweenWithoutUndo
//-----------------------------------------------------------------------------

void inbetweenWithoutUndo(TXshSimpleLevel *sl, std::vector<TFrameId> fids,
TInbetween::TweenAlgorithm algorithm) {
if (!sl || fids.empty()) return;

int m = fids.size();
TFrameId fid0 = fids[0];
TFrameId fid1 = fids[m - 1];

TVectorImageP img0 = sl->getFrame(fid0, false);
TVectorImageP img1 = sl->getFrame(fid1, false);
if (!img0 || !img1) return;

HookSet *hooks = sl->getHookSet();

TInbetween inbetween(img0, img1);

for (int i = 1; i < m - 1; ++i) {
double t = m > 1 ? (double)i / (double)(m - 1) : 0.5;
t = TInbetween::interpolation(t, algorithm);

TVectorImageP vi = inbetween.tween(t);
sl->setFrame(fids[i], vi);
IconGenerator::instance()->invalidate(sl, fids[i]);

if (hooks) {
for (int j = 0; j < hooks->getHookCount(); j++) {
Hook *hook = hooks->getHook(j);
if (!hook || hook->isEmpty()) continue;
TPointD firstPos = hook->getAPos(fid0);
TPointD lastPos = hook->getAPos(fid1);
TPointD iPos = firstPos * (1 - t) + lastPos * t;
hook->setAPos(fids[i], iPos);

firstPos = hook->getBPos(fid0);
lastPos = hook->getBPos(fid1);
iPos = firstPos * (1 - t) + lastPos * t;
hook->setBPos(fids[i], iPos);
}
}
}
};

//=============================================================================
// UndoInbetween
//-----------------------------------------------------------------------------

class UndoInbetween final : public TUndo {
TXshSimpleLevelP m_level;
HookSet m_oldHooks;
std::vector<TFrameId> m_fids;
std::vector<TVectorImageP> m_images;
TInbetween::TweenAlgorithm m_interpolation;

public:
UndoInbetween(TXshSimpleLevel *xl, std::vector<TFrameId> fids,
TInbetween::TweenAlgorithm interpolation)
: m_level(xl), m_fids(fids), m_interpolation(interpolation) {
std::vector<TFrameId>::iterator it = fids.begin();
for (; it != fids.end(); ++it)
m_images.push_back(m_level->getFrame(*it, false));

HookSet *hookSet = m_level->getHookSet();
m_oldHooks = *hookSet;
}

void undo() const override {
UINT levelSize = m_fids.size() - 1;
for (UINT count = 1; count != levelSize; count++) {
TVectorImageP vImage = m_images[count];
m_level->setFrame(m_fids[count], vImage);
IconGenerator::instance()->invalidate(m_level.getPointer(),
m_fids[count]);
}

HookSet *hookSet = m_level->getHookSet();
if (hookSet) *hookSet = m_oldHooks;

TApp::instance()->getCurrentLevel()->notifyLevelChange();
}

void redo() const override {
TFrameId fid0 = *m_fids.begin();
TFrameId fid1 = *(--m_fids.end());
inbetweenWithoutUndo(m_level.getPointer(), m_fids, m_interpolation);
}

int getSize() const override {
assert(!m_images.empty());
return m_images.size() * m_images.front()->getStrokeCount() * 100;
}

QString getHistoryString() override {
QString str = QObject::tr("Inbetween : Level %1, ")
.arg(QString::fromStdWString(m_level->getName()));
switch (m_interpolation) {
case FilmstripCmd::II_Linear:
str += QString("Linear Interpolation");
break;
case FilmstripCmd::II_EaseIn:
str += QString("Ease In Interpolation");
break;
case FilmstripCmd::II_EaseOut:
str += QString("Ease Out Interpolation");
break;
case FilmstripCmd::II_EaseInOut:
str += QString("Ease In-Out Interpolation");
break;
}
return str;
}
int getHistoryType() override { return HistoryType::FilmStrip; }
};

} // namespace
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -1592,6 +1709,11 @@ void TCellSelection::enableCommands() {
enableCommand(this, MI_Duplicate, &TCellSelection::duplicateFrames);
enableCommand(this, MI_PasteDuplicate, &TCellSelection::pasteDuplicateCells);
enableCommand(this, MI_StopFrameHold, &TCellSelection::stopFrameHold);

enableCommand(this, MI_InbetweenLinear, &TCellSelection::inbetweenLinear);
enableCommand(this, MI_InbetweenEaseIn, &TCellSelection::inbetweenEaseIn);
enableCommand(this, MI_InbetweenEaseOut, &TCellSelection::inbetweenEaseOut);
enableCommand(this, MI_InbetweenEaseInOut, &TCellSelection::inbetweenEaseInOut);
}

//-----------------------------------------------------------------------------
Expand All @@ -1603,9 +1725,7 @@ void TCellSelection::setAlternativeCommandNames() {
{MI_PasteInto, QObject::tr("Overwrite Paste Cells", "TCellSelection")},
{MI_Cut, QObject::tr("Cut Cells", "TCellSelection")},
{MI_Clear, QObject::tr("Delete Cells", "TCellSelection")},
{MI_Insert, QObject::tr("Insert Cells", "TCellSelection")},
{MI_RemoveCells, QObject::tr("Remove Cells", "TCellSelection")},
{MI_ClearFrames, QObject::tr("Clear Frames", "TCellSelection")}};
{MI_Insert, QObject::tr("Insert Cells", "TCellSelection")}};
}

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -1653,7 +1773,11 @@ bool TCellSelection::isEnabledCommand(
MI_ConvertVectorToVector,
MI_CreateBlankDrawing,
MI_FillEmptyCell,
MI_StopFrameHold};
MI_StopFrameHold,
MI_InbetweenLinear,
MI_InbetweenEaseIn,
MI_InbetweenEaseOut,
MI_InbetweenEaseInOut};
return commands.contains(commandId);
}

Expand Down Expand Up @@ -4347,3 +4471,55 @@ void TCellSelection::fillEmptyCell() {

TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
}

//-----------------------------------------------------------------------------
// Vector interpolation

void TCellSelection::inbetween(TInbetween::TweenAlgorithm algorithm) {
if (isEmpty()) return;

// set up basics
bool initUndo = false;
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
int r, r0, c0, c, r1, c1;

getSelectedCells(r0, c0, r1, c1);

TUndoManager::manager()->beginBlock();
for (c = c0; c <= c1; c++) {
TXshColumn *column = xsh->getColumn(c);
if (!column || column->isEmpty() || column->isLocked() ||
!column->getLevelColumn())
continue;

int cr0, cr1;
column->getRange(cr0, cr1);
TXshCell cell = xsh->getCell(cr0, c);
if (cell.isEmpty()) continue;

TXshSimpleLevel *sl = cell.getSimpleLevel();
if (!sl || sl->getType() != PLI_XSHLEVEL) continue;

TFrameId lastFrameId;
std::vector<TFrameId> fids;
for (r = r0; r <= r1; r++) {
TXshCell cell = xsh->getCell(r, c);
if (cell.isEmpty()) continue;
TFrameId fid = cell.getFrameId();
if (lastFrameId == fid) continue; // Skip held cells
fids.push_back(fid);
lastFrameId = fid;
}

int m = fids.size();
if (m < 3) continue;

TUndoManager::manager()->add(new UndoInbetween(sl, fids, algorithm));

inbetweenWithoutUndo(sl, fids, algorithm);

sl->setDirtyFlag(true);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
}
TUndoManager::manager()->endBlock();
}
7 changes: 7 additions & 0 deletions toonz/sources/toonz/cellselection.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "toonzqt/selection.h"
#include "tgeometry.h"
#include "tinbetween.h"
#include <set>

class TimeStretchPopup;
Expand Down Expand Up @@ -133,6 +134,12 @@ class TCellSelection final : public TSelection {
void stopFrameHold(int row, int col, bool inRange);
void stopFrameHold();
void fillEmptyCell();

void inbetween(TInbetween::TweenAlgorithm algorithm);
void inbetweenLinear() { inbetween(TInbetween::LinearInterpolation); }
void inbetweenEaseIn() { inbetween(TInbetween::EaseInInterpolation); }
void inbetweenEaseOut() { inbetween(TInbetween::EaseOutInterpolation); }
void inbetweenEaseInOut() { inbetween(TInbetween::EaseInOutInterpolation); }
};

#endif // TCELLSELECTION_H
10 changes: 10 additions & 0 deletions toonz/sources/toonz/filmstrip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1289,6 +1289,16 @@ void FilmstripFrames::contextMenuEvent(QContextMenuEvent *event) {
menu->addAction(cm->getAction(MI_ExposeResource));
if (!isSubsequenceLevel && !isReadOnly) {
menu->addAction(cm->getAction(MI_AddFrames));
if (sl && sl->getType() == PLI_XSHLEVEL) {
QMenu *inbetweenMenu = new QMenu(tr("Inbetween"), this);
{
inbetweenMenu->addAction(cm->getAction(MI_InbetweenLinear));
inbetweenMenu->addAction(cm->getAction(MI_InbetweenEaseIn));
inbetweenMenu->addAction(cm->getAction(MI_InbetweenEaseOut));
inbetweenMenu->addAction(cm->getAction(MI_InbetweenEaseInOut));
}
menu->addMenu(inbetweenMenu);
}
menu->addAction(cm->getAction(MI_Renumber));
if (sl && sl->getType() == TZP_XSHLEVEL)
menu->addAction(cm->getAction(MI_RevertToCleanedUp));
Expand Down
25 changes: 25 additions & 0 deletions toonz/sources/toonz/filmstripcommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,7 @@ namespace {

class UndoInbetween final : public TUndo {
TXshSimpleLevelP m_level;
HookSet m_oldHooks;
std::vector<TFrameId> m_fids;
std::vector<TVectorImageP> m_images;
FilmstripCmd::InbetweenInterpolation m_interpolation;
Expand All @@ -2645,6 +2646,9 @@ class UndoInbetween final : public TUndo {
m_images.push_back(m_level->getFrame(
*it, false)); // non si fa clone perche' il livello subito dopo
// rilascia queste immagini a causa dell'inbetweener

HookSet *hookSet = m_level->getHookSet();
m_oldHooks = *hookSet;
}

void undo() const override {
Expand All @@ -2656,6 +2660,9 @@ class UndoInbetween final : public TUndo {
m_fids[count]);
}

HookSet *hookSet = m_level->getHookSet();
if (hookSet) *hookSet = m_oldHooks;

TApp::instance()->getCurrentLevel()->notifyLevelChange();
}

Expand Down Expand Up @@ -2734,6 +2741,8 @@ void FilmstripCmd::inbetweenWithoutUndo(
break;
}

HookSet *hooks = sl->getHookSet();

TInbetween inbetween(img0, img1);
int i;
for (i = ia + 1; i < ib; i++) {
Expand All @@ -2743,6 +2752,22 @@ void FilmstripCmd::inbetweenWithoutUndo(
TVectorImageP vi = inbetween.tween(s);
sl->setFrame(fids[i], vi);
IconGenerator::instance()->invalidate(sl, fids[i]);

if (hooks) {
for (int j = 0; j < hooks->getHookCount(); j++) {
Hook *hook = hooks->getHook(j);
if (!hook || hook->isEmpty()) continue;
TPointD firstPos = hook->getAPos(fid0);
TPointD lastPos = hook->getAPos(fid1);
TPointD iPos = firstPos * (1 - s) + lastPos * s;
hook->setAPos(fids[i], iPos);

firstPos = hook->getBPos(fid0);
lastPos = hook->getBPos(fid1);
iPos = firstPos * (1 - s) + lastPos * s;
hook->setBPos(fids[i], iPos);
}
}
}
sl->setDirtyFlag(true);
TApp::instance()->getCurrentLevel()->notifyLevelChange();
Expand Down
51 changes: 51 additions & 0 deletions toonz/sources/toonz/filmstripselection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ void TFilmstripSelection::enableCommands() {

if (doEnable && !isNotEditableFullColorLevel)
enableCommand(this, MI_Renumber, &TFilmstripSelection::renumberFrames);

if (type == PLI_XSHLEVEL) {
enableCommand(this, MI_InbetweenLinear, &TFilmstripSelection::inbetweenLinear);
enableCommand(this, MI_InbetweenEaseIn, &TFilmstripSelection::inbetweenEaseIn);
enableCommand(this, MI_InbetweenEaseOut, &TFilmstripSelection::inbetweenEaseOut);
enableCommand(this, MI_InbetweenEaseInOut, &TFilmstripSelection::inbetweenEaseInOut);
}
}

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -391,3 +398,47 @@ void TFilmstripSelection::renumberFrames() {
FilmstripCmd::renumber(sl, m_selectedFrames, startFrame, stepFrame);
updateInbetweenRange();
}

//-----------------------------------------------------------------------------

void TFilmstripSelection::inbetweenLinear() {
TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
if (!sl) return;
if (m_selectedFrames.size() < 3) return;
TFrameId fid1 = *m_selectedFrames.begin();
TFrameId fid2 = *m_selectedFrames.rbegin();
FilmstripCmd::inbetween(sl, fid1, fid2, FilmstripCmd::II_Linear);
}

//-----------------------------------------------------------------------------

void TFilmstripSelection::inbetweenEaseIn() {
TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
if (!sl) return;
if (m_selectedFrames.size() < 3) return;
TFrameId fid1 = *m_selectedFrames.begin();
TFrameId fid2 = *m_selectedFrames.rbegin();
FilmstripCmd::inbetween(sl, fid1, fid2, FilmstripCmd::II_EaseIn);
}

//-----------------------------------------------------------------------------

void TFilmstripSelection::inbetweenEaseOut() {
TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
if (!sl) return;
if (m_selectedFrames.size() < 3) return;
TFrameId fid1 = *m_selectedFrames.begin();
TFrameId fid2 = *m_selectedFrames.rbegin();
FilmstripCmd::inbetween(sl, fid1, fid2, FilmstripCmd::II_EaseOut);
}

//-----------------------------------------------------------------------------

void TFilmstripSelection::inbetweenEaseInOut() {
TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
if (!sl) return;
if (m_selectedFrames.size() < 3) return;
TFrameId fid1 = *m_selectedFrames.begin();
TFrameId fid2 = *m_selectedFrames.rbegin();
FilmstripCmd::inbetween(sl, fid1, fid2, FilmstripCmd::II_EaseInOut);
}
4 changes: 4 additions & 0 deletions toonz/sources/toonz/filmstripselection.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ class TFilmstripSelection final : public TSelection {
void duplicateFrames();
void exposeFrames();
void renumberFrames();
void inbetweenLinear();
void inbetweenEaseIn();
void inbetweenEaseOut();
void inbetweenEaseInOut();
};

#endif // TFILMSTRIPSELECTION_H

0 comments on commit ccd369c

Please sign in to comment.