Enhancement of the Shift and Trace Feature (#2212)
* shift and trace enhancement * override shortcuts
This commit is contained in:
parent
a18e658d3e
commit
2b24e83965
17 changed files with 909 additions and 182 deletions
|
@ -118,7 +118,9 @@ enum class PredefinedRect {
|
|||
ZOOM_OUT_AREA,
|
||||
ZOOM_OUT,
|
||||
LAYER_FOOTER_PANEL,
|
||||
PREVIEW_FRAME_AREA
|
||||
PREVIEW_FRAME_AREA,
|
||||
SHIFTTRACE_DOT,
|
||||
SHIFTTRACE_DOT_AREA
|
||||
};
|
||||
enum class PredefinedLine {
|
||||
LOCKED, //! dotted vertical line when cell is locked
|
||||
|
|
|
@ -669,6 +669,26 @@ protected slots:
|
|||
void updateRealTimePickLabel(const int, const int, const int);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// ShiftTraceToolOptionBox
|
||||
// shown only when "Edit Shift" mode is active
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
class ShiftTraceToolOptionBox final : public ToolOptionsBox {
|
||||
Q_OBJECT
|
||||
QPushButton *m_resetPrevGhostBtn;
|
||||
QPushButton *m_resetAfterGhostBtn;
|
||||
void resetGhost(int index);
|
||||
|
||||
public:
|
||||
ShiftTraceToolOptionBox(QWidget *parent = 0);
|
||||
protected slots:
|
||||
void onResetPrevGhostBtnPressed();
|
||||
void onResetAfterGhostBtnPressed();
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class DVAPI ToolOptions final : public QFrame {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include "tcommon.h"
|
||||
#include "tgeometry.h"
|
||||
|
||||
#include <QList>
|
||||
|
||||
#undef DVAPI
|
||||
#undef DVVAR
|
||||
#ifdef TOONZLIB_EXPORTS
|
||||
|
@ -114,6 +116,23 @@ since underlying onion-skinned drawings must be visible.
|
|||
}
|
||||
void setShiftTraceGhostCenter(int index, const TPointD ¢er);
|
||||
|
||||
const int getShiftTraceGhostFrameOffset(int index) {
|
||||
return m_ghostFrame[index];
|
||||
}
|
||||
void setShiftTraceGhostFrameOffset(int index, int offset) {
|
||||
m_ghostFrame[index] = offset;
|
||||
}
|
||||
|
||||
const int getGhostFlipKey() {
|
||||
return (m_ghostFlipKeys.isEmpty()) ? 0 : m_ghostFlipKeys.last();
|
||||
}
|
||||
void appendGhostFlipKey(int key) {
|
||||
m_ghostFlipKeys.removeAll(key);
|
||||
m_ghostFlipKeys.append(key);
|
||||
}
|
||||
void removeGhostFlipKey(int key) { m_ghostFlipKeys.removeAll(key); }
|
||||
void clearGhostFlipKey() { m_ghostFlipKeys.clear(); }
|
||||
|
||||
private:
|
||||
std::vector<int> m_fos, m_mos; //!< Fixed and Mobile Onion Skin indices
|
||||
bool m_enabled; //!< Whether onion skin is enabled
|
||||
|
@ -122,6 +141,9 @@ private:
|
|||
ShiftTraceStatus m_shiftTraceStatus;
|
||||
TAffine m_ghostAff[2];
|
||||
TPointD m_ghostCenter[2];
|
||||
int m_ghostFrame[2]; // relative frame position of the ghosts
|
||||
QList<int> m_ghostFlipKeys; // If F1, F2 or F3 key is pressed, then only
|
||||
// display the corresponding ghost
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "toonz/preferences.h"
|
||||
#include "toonz/tstageobjecttree.h"
|
||||
#include "toonz/mypaintbrushstyle.h"
|
||||
#include "toonz/tonionskinmaskhandle.h"
|
||||
|
||||
// TnzCore includes
|
||||
#include "tproperty.h"
|
||||
|
@ -2496,6 +2497,45 @@ void StylePickerToolOptionsBox::updateRealTimePickLabel(const int ink,
|
|||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// ShiftTraceToolOptionBox
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ShiftTraceToolOptionBox::ShiftTraceToolOptionBox(QWidget *parent)
|
||||
: ToolOptionsBox(parent) {
|
||||
setFrameStyle(QFrame::StyledPanel);
|
||||
setFixedHeight(26);
|
||||
|
||||
m_resetPrevGhostBtn =
|
||||
new QPushButton(tr("Reset Shift of Previous Drawing"), this);
|
||||
m_resetAfterGhostBtn =
|
||||
new QPushButton(tr("Reset Shift of Forward Drawing"), this);
|
||||
|
||||
m_layout->addWidget(m_resetPrevGhostBtn, 0);
|
||||
m_layout->addWidget(m_resetAfterGhostBtn, 0);
|
||||
m_layout->addStretch(1);
|
||||
|
||||
connect(m_resetPrevGhostBtn, SIGNAL(clicked()), this,
|
||||
SLOT(onResetPrevGhostBtnPressed()));
|
||||
connect(m_resetAfterGhostBtn, SIGNAL(clicked()), this,
|
||||
SLOT(onResetAfterGhostBtnPressed()));
|
||||
}
|
||||
|
||||
void ShiftTraceToolOptionBox::resetGhost(int index) {
|
||||
TTool::Application *app = TTool::getApplication();
|
||||
OnionSkinMask osm = app->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
osm.setShiftTraceGhostCenter(index, TPointD());
|
||||
osm.setShiftTraceGhostAff(index, TAffine());
|
||||
app->getCurrentOnionSkin()->setOnionSkinMask(osm);
|
||||
app->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
|
||||
TTool *tool = app->getCurrentTool()->getTool();
|
||||
if (tool) tool->reset();
|
||||
}
|
||||
|
||||
void ShiftTraceToolOptionBox::onResetPrevGhostBtnPressed() { resetGhost(0); }
|
||||
|
||||
void ShiftTraceToolOptionBox::onResetAfterGhostBtnPressed() { resetGhost(1); }
|
||||
|
||||
//=============================================================================
|
||||
// ToolOptions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -2604,6 +2644,8 @@ void ToolOptions::onToolSwitched() {
|
|||
} else if (tool->getName() == T_StylePicker)
|
||||
panel = new StylePickerToolOptionsBox(0, tool, currPalette, currTool,
|
||||
app->getPaletteController());
|
||||
else if (tool->getName() == "T_ShiftTrace")
|
||||
panel = new ShiftTraceToolOptionBox(this);
|
||||
else
|
||||
panel = tool->createOptionsBox(); // Only this line should remain out
|
||||
// of that if/else monstrosity
|
||||
|
|
|
@ -54,7 +54,9 @@ FrameHeadGadget::~FrameHeadGadget() {}
|
|||
void FrameHeadGadget::draw(QPainter &p, const QColor &lightColor,
|
||||
const QColor &darkColor) {
|
||||
// drawPlayingHead(p, lightColor, darkColor);
|
||||
if (!Preferences::instance()->isOnionSkinEnabled()) return;
|
||||
if (CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked())
|
||||
drawShiftTraceMarker(p);
|
||||
else if (Preferences::instance()->isOnionSkinEnabled())
|
||||
drawOnionSkinSelection(p, lightColor, darkColor);
|
||||
}
|
||||
|
||||
|
@ -294,7 +296,8 @@ void FrameHeadGadget::setMos(int frame, bool on) {
|
|||
FilmstripFrameHeadGadget::FilmstripFrameHeadGadget(FilmstripFrames *filmstrip)
|
||||
: m_filmstrip(filmstrip)
|
||||
, m_dy(m_filmstrip->getIconSize().height() + fs_frameSpacing +
|
||||
fs_iconMarginTop + fs_iconMarginBottom) {}
|
||||
fs_iconMarginTop + fs_iconMarginBottom)
|
||||
, m_highlightedghostFrame(-1) {}
|
||||
|
||||
int FilmstripFrameHeadGadget::getY() const { return 50; }
|
||||
|
||||
|
@ -442,7 +445,83 @@ void FilmstripFrameHeadGadget::drawOnionSkinSelection(QPainter &p,
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void FilmstripFrameHeadGadget::drawShiftTraceMarker(QPainter &p) {
|
||||
int currentRow = getCurrentFrame();
|
||||
|
||||
TPixel frontPixel, backPixel;
|
||||
bool inksOnly;
|
||||
Preferences::instance()->getOnionData(frontPixel, backPixel, inksOnly);
|
||||
QColor frontColor((int)frontPixel.r, (int)frontPixel.g, (int)frontPixel.b);
|
||||
QColor backColor((int)backPixel.r, (int)backPixel.g, (int)backPixel.b);
|
||||
|
||||
OnionSkinMask osMask =
|
||||
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
|
||||
// draw lines to ghost frames
|
||||
int prevOffset = osMask.getShiftTraceGhostFrameOffset(0);
|
||||
int forwardOffset = osMask.getShiftTraceGhostFrameOffset(1);
|
||||
|
||||
const int shiftTraceDotSize = 12;
|
||||
const int shiftTraceDotXOffset = 3;
|
||||
int dotYPos = (m_dy - shiftTraceDotSize) / 2;
|
||||
|
||||
if (currentRow > 0 && prevOffset < 0) // previous ghost
|
||||
{
|
||||
p.setPen(backColor);
|
||||
int y0 = index2y(currentRow + prevOffset) + dotYPos + shiftTraceDotSize;
|
||||
int y1 = index2y(currentRow);
|
||||
p.drawLine(shiftTraceDotXOffset + shiftTraceDotSize / 2, y0,
|
||||
shiftTraceDotXOffset + shiftTraceDotSize / 2, y1);
|
||||
}
|
||||
if (forwardOffset > 0) // forward ghost
|
||||
{
|
||||
p.setPen(frontColor);
|
||||
int y0 = index2y(currentRow + 1);
|
||||
int y1 = index2y(currentRow + forwardOffset) + dotYPos + shiftTraceDotSize;
|
||||
p.drawLine(shiftTraceDotXOffset + shiftTraceDotSize / 2, y0,
|
||||
shiftTraceDotXOffset + shiftTraceDotSize / 2, y1);
|
||||
}
|
||||
// draw dots
|
||||
std::vector<int> offsVec = {prevOffset, 0, forwardOffset};
|
||||
std::vector<QColor> colorsVec = {backColor, QColor(0, 162, 232), frontColor};
|
||||
QFont currentFont = p.font();
|
||||
QFont tmpFont = p.font();
|
||||
tmpFont.setPointSize(7);
|
||||
p.setFont(tmpFont);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (i != 1 && offsVec[i] == 0) continue;
|
||||
p.setPen(colorsVec[i]);
|
||||
p.setBrush(Qt::gray);
|
||||
int topPos = index2y(currentRow + offsVec[i]) + dotYPos;
|
||||
QRect dotRect(shiftTraceDotXOffset, topPos, shiftTraceDotSize,
|
||||
shiftTraceDotSize);
|
||||
p.drawRect(dotRect);
|
||||
|
||||
// draw shortcut numbers
|
||||
p.setPen(Qt::black);
|
||||
p.drawText(dotRect, Qt::AlignCenter, QString::number(i + 1));
|
||||
}
|
||||
p.setFont(currentFont);
|
||||
|
||||
// highlight on mouse over
|
||||
if (m_highlightedghostFrame >= 0) {
|
||||
p.setPen(QColor(255, 255, 0));
|
||||
p.setBrush(QColor(255, 255, 0, 180));
|
||||
int topPos = index2y(m_highlightedghostFrame) + dotYPos;
|
||||
QRect dotRect(shiftTraceDotXOffset, topPos, shiftTraceDotSize,
|
||||
shiftTraceDotSize);
|
||||
p.drawRect(dotRect);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool FilmstripFrameHeadGadget::eventFilter(QObject *obj, QEvent *e) {
|
||||
// shift & trace case
|
||||
if (CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked())
|
||||
return shiftTraceEventFilter(obj, e);
|
||||
|
||||
if (!Preferences::instance()->isOnionSkinEnabled()) return false;
|
||||
|
||||
QWidget *viewer = dynamic_cast<QWidget *>(obj);
|
||||
|
@ -650,6 +729,97 @@ bool FilmstripFrameHeadGadget::eventFilter(QObject *obj, QEvent *e) {
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool FilmstripFrameHeadGadget::shiftTraceEventFilter(QObject *obj, QEvent *e) {
|
||||
QWidget *viewer = dynamic_cast<QWidget *>(obj);
|
||||
|
||||
if (e->type() != QEvent::MouseButtonPress && e->type() != QEvent::MouseMove)
|
||||
return false;
|
||||
|
||||
const int shiftTraceDotSize = 12;
|
||||
const int shiftTraceDotXOffset = 3;
|
||||
int dotYPos = (m_dy - shiftTraceDotSize) / 2;
|
||||
QRect dotRect(shiftTraceDotXOffset, dotYPos, shiftTraceDotSize,
|
||||
shiftTraceDotSize);
|
||||
|
||||
// reset highlight
|
||||
if (m_highlightedghostFrame >= 0) {
|
||||
m_highlightedghostFrame = -1;
|
||||
viewer->update();
|
||||
}
|
||||
|
||||
QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent *>(e);
|
||||
int frame = y2index(mouseEvent->pos().y());
|
||||
// position from top-left of the frame
|
||||
QPoint mousePos = mouseEvent->pos() + QPoint(0, -index2y(frame));
|
||||
int currentFrame = getCurrentFrame();
|
||||
|
||||
if (mousePos.x() < dotRect.left() || mousePos.x() > dotRect.right())
|
||||
return false;
|
||||
|
||||
if (e->type() == QEvent::MouseButtonPress) {
|
||||
if (frame == currentFrame) {
|
||||
if (dotRect.contains(mousePos))
|
||||
OnioniSkinMaskGUI::resetShiftTraceFrameOffset();
|
||||
else
|
||||
return false;
|
||||
} else {
|
||||
OnionSkinMask osMask =
|
||||
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
int prevOffset = osMask.getShiftTraceGhostFrameOffset(0);
|
||||
int forwardOffset = osMask.getShiftTraceGhostFrameOffset(1);
|
||||
// Hide previous ghost
|
||||
if (frame == currentFrame + prevOffset)
|
||||
osMask.setShiftTraceGhostFrameOffset(0, 0);
|
||||
// Hide forward ghost
|
||||
else if (frame == currentFrame + forwardOffset)
|
||||
osMask.setShiftTraceGhostFrameOffset(1, 0);
|
||||
// Move previous ghost
|
||||
else if (frame < currentFrame)
|
||||
osMask.setShiftTraceGhostFrameOffset(0, frame - currentFrame);
|
||||
// Move forward ghost
|
||||
else
|
||||
osMask.setShiftTraceGhostFrameOffset(1, frame - currentFrame);
|
||||
TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osMask);
|
||||
}
|
||||
TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
|
||||
return true;
|
||||
}
|
||||
//----
|
||||
else if (e->type() == QEvent::MouseMove) {
|
||||
if (frame == currentFrame) {
|
||||
if (dotRect.contains(mousePos)) {
|
||||
m_highlightedghostFrame = frame;
|
||||
viewer->setToolTip(
|
||||
tr("Click to Reset Shift & Trace Markers to Neighbor Frames\nHold "
|
||||
"F2 Key on the Viewer to Show This Frame Only"));
|
||||
} else {
|
||||
viewer->setToolTip(tr(""));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
m_highlightedghostFrame = frame;
|
||||
OnionSkinMask osMask =
|
||||
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
int prevOffset = osMask.getShiftTraceGhostFrameOffset(0);
|
||||
int forwardOffset = osMask.getShiftTraceGhostFrameOffset(1);
|
||||
// Hide ghost
|
||||
if (frame == currentFrame + prevOffset)
|
||||
viewer->setToolTip(
|
||||
tr("Click to Hide This Frame from Shift & Trace\nHold F1 Key on "
|
||||
"the Viewer to Show This Frame Only"));
|
||||
else if (frame == currentFrame + forwardOffset)
|
||||
viewer->setToolTip(
|
||||
tr("Click to Hide This Frame from Shift & Trace\nHold F3 Key on "
|
||||
"the Viewer to Show This Frame Only"));
|
||||
// Move ghost
|
||||
else
|
||||
viewer->setToolTip(tr("Click to Move Shift & Trace Marker"));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class OnionSkinToggle final : public MenuItemHandler {
|
||||
public:
|
||||
OnionSkinToggle() : MenuItemHandler(MI_OnionSkin) {}
|
||||
|
|
|
@ -46,6 +46,8 @@ public:
|
|||
virtual void drawOnionSkinSelection(QPainter &p, const QColor &lightColor,
|
||||
const QColor &darkColor);
|
||||
|
||||
virtual void drawShiftTraceMarker(QPainter &p) {}
|
||||
|
||||
virtual int getY() const = 0;
|
||||
|
||||
virtual int index2y(int index) const = 0;
|
||||
|
@ -70,6 +72,7 @@ public:
|
|||
class FilmstripFrameHeadGadget final : public FrameHeadGadget {
|
||||
FilmstripFrames *m_filmstrip;
|
||||
int m_dy;
|
||||
int m_highlightedghostFrame;
|
||||
|
||||
public:
|
||||
FilmstripFrameHeadGadget(FilmstripFrames *filmstrip);
|
||||
|
@ -81,10 +84,13 @@ public:
|
|||
void drawOnionSkinSelection(QPainter &p, const QColor &lightColor,
|
||||
const QColor &darkColor) override;
|
||||
|
||||
void drawShiftTraceMarker(QPainter &p) override;
|
||||
|
||||
void setCurrentFrame(int index) const override;
|
||||
int getCurrentFrame() const override;
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
bool shiftTraceEventFilter(QObject *obj, QEvent *event);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1960,6 +1960,8 @@ void MainWindow::defineActions() {
|
|||
MenuViewCommandType);
|
||||
createToggle(MI_EditShift, tr("Edit Shift"), "", false, MenuViewCommandType);
|
||||
createToggle(MI_NoShift, tr("No Shift"), "", false, MenuViewCommandType);
|
||||
CommandManager::instance()->enable(MI_EditShift, false);
|
||||
CommandManager::instance()->enable(MI_NoShift, false);
|
||||
createAction(MI_ResetShift, tr("Reset Shift"), "", MenuViewCommandType);
|
||||
|
||||
if (QGLPixelBuffer::hasOpenGLPbuffers())
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
#include "onionskinmaskgui.h"
|
||||
#include "tapp.h"
|
||||
#include "toonz/tonionskinmaskhandle.h"
|
||||
#include "toonz/tframehandle.h"
|
||||
#include "toonz/txshlevelhandle.h"
|
||||
#include "toonz/txsheethandle.h"
|
||||
#include "toonz/tcolumnhandle.h"
|
||||
#include "toonz/txshsimplelevel.h"
|
||||
|
||||
#include "toonz/onionskinmask.h"
|
||||
|
||||
|
@ -105,3 +110,66 @@ void OnioniSkinMaskGUI::addOnionSkinCommand(QMenu *menu, bool isFilmStrip) {
|
|||
SLOT(activate()));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void OnioniSkinMaskGUI::resetShiftTraceFrameOffset() {
|
||||
auto setGhostOffset = [](int firstOffset, int secondOffset) {
|
||||
OnionSkinMask osm =
|
||||
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
osm.setShiftTraceGhostFrameOffset(0, firstOffset);
|
||||
osm.setShiftTraceGhostFrameOffset(1, secondOffset);
|
||||
TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osm);
|
||||
};
|
||||
|
||||
TApp *app = TApp::instance();
|
||||
if (app->getCurrentFrame()->isEditingLevel()) {
|
||||
TXshSimpleLevel *level = app->getCurrentLevel()->getSimpleLevel();
|
||||
if (!level) {
|
||||
setGhostOffset(0, 0);
|
||||
return;
|
||||
}
|
||||
TFrameId fid = app->getCurrentFrame()->getFid();
|
||||
int firstOffset = (fid > level->getFirstFid()) ? -1 : 0;
|
||||
int secondOffset = (fid < level->getLastFid()) ? 1 : 0;
|
||||
|
||||
setGhostOffset(firstOffset, secondOffset);
|
||||
} else { // when scene frame is selected
|
||||
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
|
||||
int col = app->getCurrentColumn()->getColumnIndex();
|
||||
TXshColumn *column = xsh->getColumn(col);
|
||||
if (!column || column->isEmpty()) {
|
||||
setGhostOffset(0, 0);
|
||||
return;
|
||||
}
|
||||
int r0, r1;
|
||||
column->getRange(r0, r1);
|
||||
int row = app->getCurrentFrame()->getFrame();
|
||||
TXshCell cell = xsh->getCell(row, col);
|
||||
int firstOffset = -1;
|
||||
while (1) {
|
||||
int r = row + firstOffset;
|
||||
if (r < r0) {
|
||||
firstOffset = 0;
|
||||
break;
|
||||
}
|
||||
if (!xsh->getCell(r, col).isEmpty() && xsh->getCell(r, col) != cell) {
|
||||
break;
|
||||
}
|
||||
firstOffset--;
|
||||
}
|
||||
int secondOffset = 1;
|
||||
while (1) {
|
||||
int r = row + secondOffset;
|
||||
if (r > r1) {
|
||||
secondOffset = 0;
|
||||
break;
|
||||
}
|
||||
if (!xsh->getCell(r, col).isEmpty() && xsh->getCell(r, col) != cell) {
|
||||
break;
|
||||
}
|
||||
secondOffset++;
|
||||
}
|
||||
setGhostOffset(firstOffset, secondOffset);
|
||||
}
|
||||
}
|
|
@ -15,6 +15,8 @@ namespace OnioniSkinMaskGUI {
|
|||
// Da fare per la filmstrip!!
|
||||
void addOnionSkinCommand(QMenu *, bool isFilmStrip = false);
|
||||
|
||||
void resetShiftTraceFrameOffset();
|
||||
|
||||
//=============================================================================
|
||||
// OnionSkinSwitcher
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -391,10 +391,9 @@ public:
|
|||
if (std::string(m_cmdId) == MI_ShiftTrace) {
|
||||
cm->enable(MI_EditShift, checked);
|
||||
cm->enable(MI_NoShift, checked);
|
||||
if (!checked) {
|
||||
cm->setChecked(MI_EditShift, false);
|
||||
}
|
||||
if (checked) OnioniSkinMaskGUI::resetShiftTraceFrameOffset();
|
||||
// cm->getAction(MI_NoShift)->setChecked(false);
|
||||
TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
|
||||
} else if (std::string(m_cmdId) == MI_EditShift) {
|
||||
if (checked) {
|
||||
QAction *noShiftAction =
|
||||
|
@ -427,6 +426,7 @@ public:
|
|||
OnionSkinMask osm =
|
||||
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
osm.setShiftTraceStatus(status);
|
||||
osm.clearGhostFlipKey();
|
||||
TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
|
||||
TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osm);
|
||||
}
|
||||
|
@ -2379,16 +2379,22 @@ int SceneViewer::posToRow(const TPointD &p, double distance,
|
|||
bool includeInvisible) const {
|
||||
int oldRasterizePli = TXshSimpleLevel::m_rasterizePli;
|
||||
TApp *app = TApp::instance();
|
||||
ToonzScene *scene = app->getCurrentScene()->getScene();
|
||||
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
|
||||
int frame = app->getCurrentFrame()->getFrame();
|
||||
int currentColumnIndex = app->getCurrentColumn()->getColumnIndex();
|
||||
OnionSkinMask osm = app->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
|
||||
TPointD pos = TPointD(p.x - width() / 2, p.y - height() / 2);
|
||||
Stage::Picker picker(getViewMatrix(), pos, m_visualSettings);
|
||||
picker.setDistance(distance);
|
||||
|
||||
if (app->getCurrentFrame()->isEditingLevel()) {
|
||||
Stage::visit(picker, app->getCurrentLevel()->getLevel(),
|
||||
app->getCurrentFrame()->getFid(), osm,
|
||||
app->getCurrentFrame()->isPlaying(), false);
|
||||
} else {
|
||||
ToonzScene *scene = app->getCurrentScene()->getScene();
|
||||
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
|
||||
int frame = app->getCurrentFrame()->getFrame();
|
||||
int currentColumnIndex = app->getCurrentColumn()->getColumnIndex();
|
||||
|
||||
TXshSimpleLevel::m_rasterizePli = 0;
|
||||
|
||||
Stage::VisitArgs args;
|
||||
|
@ -2400,7 +2406,7 @@ int SceneViewer::posToRow(const TPointD &p, double distance,
|
|||
args.m_onlyVisible = includeInvisible;
|
||||
|
||||
Stage::visit(picker, args);
|
||||
|
||||
}
|
||||
TXshSimpleLevel::m_rasterizePli = oldRasterizePli;
|
||||
return picker.getRow();
|
||||
}
|
||||
|
|
|
@ -1039,6 +1039,16 @@ bool SceneViewer::event(QEvent *e) {
|
|||
else if (tool && tool->isEventAcceptable(e)) {
|
||||
e->accept();
|
||||
}
|
||||
// if the Shift & Trace mode is active, then override F1, F2 and F3 key
|
||||
// actions by flipping feature
|
||||
else if (CommandManager::instance()
|
||||
->getAction(MI_ShiftTrace)
|
||||
->isChecked() &&
|
||||
TTool::getTool("T_ShiftTrace", TTool::ToonzImage)
|
||||
->isEventAcceptable(e)) {
|
||||
e->accept();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (e->type() == QEvent::KeyRelease) {
|
||||
|
@ -1211,6 +1221,18 @@ void SceneViewer::keyPressEvent(QKeyEvent *event) {
|
|||
event->ignore();
|
||||
return true;
|
||||
}
|
||||
// pressing F1, F2 or F3 key will flip between corresponding ghost
|
||||
if (CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked() &&
|
||||
(Qt::Key_F1 <= key && key <= Qt::Key_F3)) {
|
||||
OnionSkinMask osm =
|
||||
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
if (osm.getGhostFlipKey() != key) {
|
||||
osm.appendGhostFlipKey(key);
|
||||
TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osm);
|
||||
TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tool->isEnabled()) return false;
|
||||
|
@ -1309,6 +1331,15 @@ void SceneViewer::keyReleaseEvent(QKeyEvent *event) {
|
|||
setToolCursor(this, tool->getCursorId());
|
||||
}
|
||||
|
||||
if (CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked() &&
|
||||
(Qt::Key_F1 <= key && key <= Qt::Key_F3)) {
|
||||
OnionSkinMask osm =
|
||||
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
osm.removeGhostFlipKey(key);
|
||||
TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osm);
|
||||
TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
|
||||
}
|
||||
|
||||
if (tool->getName() == T_Type)
|
||||
event->accept();
|
||||
else
|
||||
|
|
|
@ -14,13 +14,20 @@
|
|||
#include "toonz/txsheethandle.h"
|
||||
#include "toonz/tframehandle.h"
|
||||
#include "toonz/tcolumnhandle.h"
|
||||
#include "toonz/txshlevelhandle.h"
|
||||
#include "toonz/txshsimplelevel.h"
|
||||
#include "toonz/dpiscale.h"
|
||||
#include "toonz/stage.h"
|
||||
#include "tapp.h"
|
||||
#include "tpixel.h"
|
||||
#include "toonzqt/menubarcommand.h"
|
||||
|
||||
#include "toonz/preferences.h"
|
||||
#include "toonzqt/gutil.h"
|
||||
|
||||
#include "tgl.h"
|
||||
#include <math.h>
|
||||
#include <QKeyEvent>
|
||||
|
||||
//=============================================================================
|
||||
|
||||
|
@ -54,12 +61,14 @@ public:
|
|||
|
||||
enum GadgetId {
|
||||
NoGadget,
|
||||
NoGadget_InBox,
|
||||
CurveP0Gadget,
|
||||
CurveP1Gadget,
|
||||
CurvePmGadget,
|
||||
MoveCenterGadget,
|
||||
RotateGadget,
|
||||
TranslateGadget
|
||||
TranslateGadget,
|
||||
ScaleGadget
|
||||
};
|
||||
inline bool isCurveGadget(GadgetId id) const {
|
||||
return CurveP0Gadget <= id && id <= CurvePmGadget;
|
||||
|
@ -80,6 +89,8 @@ private:
|
|||
TAffine m_aff[2];
|
||||
TPointD m_center[2];
|
||||
|
||||
TAffine m_oldAff;
|
||||
|
||||
public:
|
||||
ShiftTraceTool();
|
||||
|
||||
|
@ -92,8 +103,10 @@ public:
|
|||
void updateGhost();
|
||||
|
||||
void reset() override {
|
||||
int ghostIndex = m_ghostIndex;
|
||||
onActivate();
|
||||
invalidate();
|
||||
m_ghostIndex = ghostIndex;
|
||||
}
|
||||
|
||||
void mouseMove(const TPointD &, const TMouseEvent &e) override;
|
||||
|
@ -124,6 +137,7 @@ public:
|
|||
QAction *action = CommandManager::instance()->getAction("MI_EditShift");
|
||||
action->setChecked(false);
|
||||
}
|
||||
bool isEventAcceptable(QEvent *e) override;
|
||||
|
||||
int getCursorId() const override;
|
||||
};
|
||||
|
@ -152,16 +166,35 @@ void ShiftTraceTool::clearData() {
|
|||
}
|
||||
|
||||
void ShiftTraceTool::updateBox() {
|
||||
if (0 <= m_ghostIndex && m_ghostIndex < 2 && m_row[m_ghostIndex] >= 0) {
|
||||
int col = TApp::instance()->getCurrentColumn()->getColumnIndex();
|
||||
if (m_ghostIndex < 0 || 2 <= m_ghostIndex || m_row[m_ghostIndex] < 0) return;
|
||||
|
||||
TImageP img;
|
||||
|
||||
TApp *app = TApp::instance();
|
||||
if (app->getCurrentFrame()->isEditingScene()) {
|
||||
int col = app->getCurrentColumn()->getColumnIndex();
|
||||
int row = m_row[m_ghostIndex];
|
||||
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
|
||||
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
|
||||
|
||||
TXshCell cell = xsh->getCell(row, col);
|
||||
TXshSimpleLevel *sl = cell.getSimpleLevel();
|
||||
if (sl) {
|
||||
m_dpiAff = getDpiAffine(sl, cell.m_frameId);
|
||||
TImageP img = cell.getImage(false);
|
||||
img = cell.getImage(false);
|
||||
}
|
||||
}
|
||||
// on editing level
|
||||
else {
|
||||
TXshLevel *level = app->getCurrentLevel()->getLevel();
|
||||
if (!level) return;
|
||||
TXshSimpleLevel *sl = level->getSimpleLevel();
|
||||
if (!sl) return;
|
||||
|
||||
const TFrameId &ghostFid = sl->index2fid(m_row[m_ghostIndex]);
|
||||
m_dpiAff = getDpiAffine(sl, ghostFid);
|
||||
img = sl->getFrame(ghostFid, false);
|
||||
}
|
||||
|
||||
if (img) {
|
||||
if (TRasterImageP ri = img) {
|
||||
TRasterP ras = ri->getRaster();
|
||||
|
@ -176,47 +209,52 @@ void ShiftTraceTool::updateBox() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShiftTraceTool::updateData() {
|
||||
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
|
||||
int row = TApp::instance()->getCurrentFrame()->getFrame();
|
||||
int col = TApp::instance()->getCurrentColumn()->getColumnIndex();
|
||||
TXshCell cell = xsh->getCell(row, col);
|
||||
m_box = TRectD();
|
||||
for (int i = 0; i < 2; i++) m_row[i] = -1;
|
||||
m_dpiAff = TAffine();
|
||||
TApp *app = TApp::instance();
|
||||
|
||||
OnionSkinMask osm =
|
||||
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
int previousOffset = osm.getShiftTraceGhostFrameOffset(0);
|
||||
int forwardOffset = osm.getShiftTraceGhostFrameOffset(1);
|
||||
// we must find the prev (m_row[0]) and next (m_row[1]) reference images
|
||||
// (either might not exist)
|
||||
// see also stage.cpp, StageBuilder::addCellWithOnionSkin
|
||||
|
||||
if (cell.isEmpty()) {
|
||||
// current cell is empty. search for the prev ref img
|
||||
int r = row - 1;
|
||||
while (r >= 0 && xsh->getCell(r, col).getSimpleLevel() == 0) r--;
|
||||
if (r >= 0) m_row[0] = r;
|
||||
// else prev drawing doesn't exist : nothing to do
|
||||
} else {
|
||||
// current cell is not empty
|
||||
// search for prev ref img
|
||||
TXshSimpleLevel *sl = cell.getSimpleLevel();
|
||||
int r = row - 1;
|
||||
if (r >= 0) {
|
||||
TXshCell otherCell = xsh->getCell(r, col);
|
||||
if (otherCell.getSimpleLevel() == sl) {
|
||||
// find the span start
|
||||
while (r - 1 >= 0 && xsh->getCell(r - 1, col) == otherCell) r--;
|
||||
if (app->getCurrentFrame()->isEditingScene()) {
|
||||
TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
|
||||
int row = app->getCurrentFrame()->getFrame();
|
||||
int col = app->getCurrentColumn()->getColumnIndex();
|
||||
TXshCell cell = xsh->getCell(row, col);
|
||||
int r;
|
||||
r = row + previousOffset;
|
||||
if (r >= 0 && xsh->getCell(r, col) != cell &&
|
||||
(cell.getSimpleLevel() == 0 ||
|
||||
xsh->getCell(r, col).getSimpleLevel() == cell.getSimpleLevel())) {
|
||||
m_row[0] = r;
|
||||
}
|
||||
}
|
||||
|
||||
// search for next ref img
|
||||
r = row + 1;
|
||||
while (xsh->getCell(r, col) == cell) r++;
|
||||
// first cell after the current span has the same level
|
||||
if (xsh->getCell(r, col).getSimpleLevel() == sl) m_row[1] = r;
|
||||
r = row + forwardOffset;
|
||||
if (r >= 0 && xsh->getCell(r, col) != cell &&
|
||||
(cell.getSimpleLevel() == 0 ||
|
||||
xsh->getCell(r, col).getSimpleLevel() == cell.getSimpleLevel())) {
|
||||
m_row[1] = r;
|
||||
}
|
||||
}
|
||||
// on editing level
|
||||
else {
|
||||
TXshLevel *level = app->getCurrentLevel()->getLevel();
|
||||
if (level) {
|
||||
TXshSimpleLevel *sl = level->getSimpleLevel();
|
||||
if (sl) {
|
||||
TFrameId fid = app->getCurrentFrame()->getFid();
|
||||
int row = sl->guessIndex(fid);
|
||||
m_row[0] = row + previousOffset;
|
||||
m_row[1] = row + forwardOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateBox();
|
||||
}
|
||||
|
@ -269,34 +307,41 @@ void ShiftTraceTool::drawDot(const TPointD ¢er, double r,
|
|||
tglDrawCircle(center, r);
|
||||
}
|
||||
|
||||
void ShiftTraceTool::drawControlRect() {
|
||||
void ShiftTraceTool::drawControlRect() { // TODO
|
||||
if (m_ghostIndex < 0 || m_ghostIndex > 1) return;
|
||||
int row = m_row[m_ghostIndex];
|
||||
if (row < 0) return;
|
||||
int col = TApp::instance()->getCurrentColumn()->getColumnIndex();
|
||||
TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
|
||||
TXshCell cell = xsh->getCell(row, col);
|
||||
if (cell.isEmpty()) return;
|
||||
TImageP img = cell.getImage(false);
|
||||
if (!img) return;
|
||||
TRectD box;
|
||||
if (TRasterImageP ri = img) {
|
||||
TRasterP ras = ri->getRaster();
|
||||
box =
|
||||
(convert(ras->getBounds()) - ras->getCenterD()) * ri->getSubsampling();
|
||||
} else if (TToonzImageP ti = img) {
|
||||
TRasterP ras = ti->getRaster();
|
||||
box =
|
||||
(convert(ras->getBounds()) - ras->getCenterD()) * ti->getSubsampling();
|
||||
} else if (TVectorImageP vi = img) {
|
||||
box = vi->getBBox();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
TRectD box = m_box;
|
||||
if (box.isEmpty()) return;
|
||||
glPushMatrix();
|
||||
tglMultMatrix(getGhostAff());
|
||||
|
||||
TPixel32 color;
|
||||
color = m_highlightedGadget == TranslateGadget ? TPixel32(200, 100, 100)
|
||||
|
||||
// draw onion-colored rectangle to indicate which ghost is grabbed
|
||||
{
|
||||
TPixel32 frontOniColor, backOniColor;
|
||||
bool inksOnly;
|
||||
Preferences::instance()->getOnionData(frontOniColor, backOniColor,
|
||||
inksOnly);
|
||||
color = (m_ghostIndex == 0) ? backOniColor : frontOniColor;
|
||||
double unit = sqrt(tglGetPixelSize2());
|
||||
unit *= getDevPixRatio();
|
||||
TRectD coloredBox = box.enlarge(3.0 * unit);
|
||||
tglColor(color);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
glVertex2d(coloredBox.x0, coloredBox.y0);
|
||||
glVertex2d(coloredBox.x1, coloredBox.y0);
|
||||
glVertex2d(coloredBox.x1, coloredBox.y1);
|
||||
glVertex2d(coloredBox.x0, coloredBox.y1);
|
||||
glVertex2d(coloredBox.x0, coloredBox.y0);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
color = m_highlightedGadget == TranslateGadget
|
||||
? TPixel32(200, 100, 100)
|
||||
: m_highlightedGadget == RotateGadget ? TPixel32(100, 200, 100)
|
||||
: TPixel32(120, 120, 120);
|
||||
tglColor(color);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
|
@ -306,16 +351,16 @@ void ShiftTraceTool::drawControlRect() {
|
|||
glVertex2d(box.x0, box.y1);
|
||||
glVertex2d(box.x0, box.y0);
|
||||
glEnd();
|
||||
color =
|
||||
m_highlightedGadget == 2000 ? TPixel32(200, 100, 100) : TPixel32::White;
|
||||
color = m_highlightedGadget == ScaleGadget ? TPixel32(200, 100, 100)
|
||||
: TPixel32::White;
|
||||
double r = 4 * sqrt(tglGetPixelSize2());
|
||||
drawDot(box.getP00(), r, color);
|
||||
drawDot(box.getP01(), r, color);
|
||||
drawDot(box.getP10(), r, color);
|
||||
drawDot(box.getP11(), r, color);
|
||||
if (m_curveStatus == NoCurve) {
|
||||
color =
|
||||
m_highlightedGadget == 2001 ? TPixel32(200, 100, 100) : TPixel32::White;
|
||||
color = m_highlightedGadget == MoveCenterGadget ? TPixel32(200, 100, 100)
|
||||
: TPixel32::White;
|
||||
TPointD c = m_center[m_ghostIndex];
|
||||
drawDot(c, r, color);
|
||||
}
|
||||
|
@ -327,18 +372,20 @@ void ShiftTraceTool::drawCurve() {
|
|||
double r = 4 * sqrt(tglGetPixelSize2());
|
||||
double u = getPixelSize();
|
||||
if (m_curveStatus == TwoPointsCurve) {
|
||||
TPixel32 color =
|
||||
m_highlightedGadget == 1000 ? TPixel32(200, 100, 100) : TPixel32::White;
|
||||
TPixel32 color = m_highlightedGadget == CurveP0Gadget
|
||||
? TPixel32(200, 100, 100)
|
||||
: TPixel32::White;
|
||||
drawDot(m_p0, r, color);
|
||||
glColor3d(0.2, 0.2, 0.2);
|
||||
tglDrawSegment(m_p0, m_p1);
|
||||
drawDot(m_p1, r, TPixel32::Red);
|
||||
} else if (m_curveStatus == ThreePointsCurve) {
|
||||
TPixel32 color =
|
||||
m_highlightedGadget == 1000 ? TPixel32(200, 100, 100) : TPixel32::White;
|
||||
TPixel32 color = m_highlightedGadget == CurveP0Gadget
|
||||
? TPixel32(200, 100, 100)
|
||||
: TPixel32::White;
|
||||
drawDot(m_p0, r, color);
|
||||
color =
|
||||
m_highlightedGadget == 1001 ? TPixel32(200, 100, 100) : TPixel32::White;
|
||||
color = m_highlightedGadget == CurveP1Gadget ? TPixel32(200, 100, 100)
|
||||
: TPixel32::White;
|
||||
drawDot(m_p1, r, color);
|
||||
|
||||
glColor3d(0.2, 0.2, 0.2);
|
||||
|
@ -364,8 +411,8 @@ void ShiftTraceTool::drawCurve() {
|
|||
} else {
|
||||
tglDrawSegment(m_p0, m_p1);
|
||||
}
|
||||
color =
|
||||
m_highlightedGadget == 1002 ? TPixel32(200, 100, 100) : TPixel32::White;
|
||||
color = m_highlightedGadget == CurvePmGadget ? TPixel32(200, 100, 100)
|
||||
: TPixel32::White;
|
||||
drawDot(m_p2, r, color);
|
||||
}
|
||||
}
|
||||
|
@ -376,16 +423,18 @@ ShiftTraceTool::GadgetId ShiftTraceTool::getGadget(const TPointD &p) {
|
|||
gadgets.push_back(std::make_pair(m_p1, CurveP1Gadget));
|
||||
gadgets.push_back(std::make_pair(m_p2, CurvePmGadget));
|
||||
TAffine aff = getGhostAff();
|
||||
double pixelSize = getPixelSize();
|
||||
double d = 15 * pixelSize; // offset for rotation handle
|
||||
if (0 <= m_ghostIndex && m_ghostIndex < 2) {
|
||||
gadgets.push_back(std::make_pair(aff * m_box.getP00(), RotateGadget));
|
||||
gadgets.push_back(std::make_pair(aff * m_box.getP01(), RotateGadget));
|
||||
gadgets.push_back(std::make_pair(aff * m_box.getP10(), RotateGadget));
|
||||
gadgets.push_back(std::make_pair(aff * m_box.getP11(), RotateGadget));
|
||||
gadgets.push_back(std::make_pair(aff * m_box.getP00(), ScaleGadget));
|
||||
gadgets.push_back(std::make_pair(aff * m_box.getP01(), ScaleGadget));
|
||||
gadgets.push_back(std::make_pair(aff * m_box.getP10(), ScaleGadget));
|
||||
gadgets.push_back(std::make_pair(aff * m_box.getP11(), ScaleGadget));
|
||||
gadgets.push_back(
|
||||
std::make_pair(aff * m_center[m_ghostIndex], MoveCenterGadget));
|
||||
}
|
||||
int k = -1;
|
||||
double minDist2 = pow(10 * getPixelSize(), 2);
|
||||
double minDist2 = pow(10 * pixelSize, 2);
|
||||
for (int i = 0; i < (int)gadgets.size(); i++) {
|
||||
double d2 = norm2(gadgets[i].first - p);
|
||||
if (d2 < minDist2) {
|
||||
|
@ -398,7 +447,6 @@ ShiftTraceTool::GadgetId ShiftTraceTool::getGadget(const TPointD &p) {
|
|||
// rect-point
|
||||
if (0 <= m_ghostIndex && m_ghostIndex < 2) {
|
||||
TPointD q = aff.inv() * p;
|
||||
|
||||
double big = 1.0e6;
|
||||
double d = big, x = 0, y = 0;
|
||||
if (m_box.x0 < q.x && q.x < m_box.x1) {
|
||||
|
@ -414,8 +462,8 @@ ShiftTraceTool::GadgetId ShiftTraceTool::getGadget(const TPointD &p) {
|
|||
}
|
||||
}
|
||||
if (m_box.y0 < q.y && q.y < m_box.y1) {
|
||||
double d0 = fabs(m_box.x0 - q.y);
|
||||
double d1 = fabs(m_box.x1 - q.y);
|
||||
double d0 = fabs(m_box.x0 - q.x);
|
||||
double d1 = fabs(m_box.x1 - q.x);
|
||||
if (d0 < d) {
|
||||
d = d0;
|
||||
y = q.y;
|
||||
|
@ -431,9 +479,16 @@ ShiftTraceTool::GadgetId ShiftTraceTool::getGadget(const TPointD &p) {
|
|||
TPointD pp = aff * TPointD(x, y);
|
||||
double d = norm(p - pp);
|
||||
if (d < 10 * getPixelSize()) {
|
||||
if (m_box.contains(q))
|
||||
return TranslateGadget;
|
||||
else
|
||||
return RotateGadget;
|
||||
}
|
||||
}
|
||||
if (m_box.contains(q))
|
||||
return NoGadget_InBox;
|
||||
else
|
||||
return NoGadget;
|
||||
}
|
||||
return NoGadget;
|
||||
}
|
||||
|
@ -450,31 +505,46 @@ void ShiftTraceTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) {
|
|||
m_gadget = m_highlightedGadget;
|
||||
m_oldPos = m_startPos = pos;
|
||||
|
||||
if (m_gadget == NoGadget) {
|
||||
if (m_gadget == NoGadget || m_gadget == NoGadget_InBox) {
|
||||
if (!e.isCtrlPressed()) {
|
||||
if (m_gadget == NoGadget_InBox)
|
||||
m_gadget = TranslateGadget;
|
||||
else
|
||||
m_gadget = RotateGadget;
|
||||
// m_curveStatus = NoCurve;
|
||||
}
|
||||
int row = getViewer()->posToRow(e.m_pos, 5 * getPixelSize(), false);
|
||||
if (row >= 0) {
|
||||
int currentRow = getFrame();
|
||||
int index = -1;
|
||||
TApp *app = TApp::instance();
|
||||
if (app->getCurrentFrame()->isEditingScene()) {
|
||||
int currentRow = getFrame();
|
||||
if (m_row[0] >= 0 && row <= currentRow)
|
||||
index = 0;
|
||||
else if (m_row[1] >= 0 && row > currentRow)
|
||||
index = 1;
|
||||
} else {
|
||||
if (m_row[0] == row)
|
||||
index = 0;
|
||||
else if (m_row[1] == row)
|
||||
index = 1;
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
m_ghostIndex = index;
|
||||
updateBox();
|
||||
m_gadget = TranslateGadget;
|
||||
m_highlightedGadget = TranslateGadget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!e.isCtrlPressed()) {
|
||||
m_gadget = TranslateGadget;
|
||||
// m_curveStatus = NoCurve;
|
||||
}
|
||||
}
|
||||
m_oldAff = m_aff[m_ghostIndex];
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void ShiftTraceTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &) {
|
||||
if (m_gadget == NoGadget) {
|
||||
void ShiftTraceTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
|
||||
if (m_gadget == NoGadget || m_gadget == NoGadget_InBox) {
|
||||
if (norm(pos - m_oldPos) > 10 * getPixelSize()) {
|
||||
m_curveStatus = TwoPointsCurve;
|
||||
m_p0 = m_oldPos;
|
||||
|
@ -512,6 +582,17 @@ void ShiftTraceTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &) {
|
|||
TPointD delta = pos - m_oldPos;
|
||||
m_oldPos = pos;
|
||||
m_aff[m_ghostIndex] = TTranslation(delta) * m_aff[m_ghostIndex];
|
||||
} else if (m_gadget == ScaleGadget) {
|
||||
TAffine aff = getGhostAff();
|
||||
TPointD c = aff * m_center[m_ghostIndex];
|
||||
TPointD a = m_oldPos - c;
|
||||
TPointD b = pos - c;
|
||||
if (e.isShiftPressed())
|
||||
m_aff[m_ghostIndex] = m_oldAff * TScale(b.x / a.x, b.y / a.y);
|
||||
else {
|
||||
double scale = std::max(b.x / a.x, b.y / a.y);
|
||||
m_aff[m_ghostIndex] = m_oldAff * TScale(scale, scale);
|
||||
}
|
||||
}
|
||||
|
||||
updateGhost();
|
||||
|
@ -541,12 +622,21 @@ void ShiftTraceTool::draw() {
|
|||
}
|
||||
|
||||
int ShiftTraceTool::getCursorId() const {
|
||||
if (m_highlightedGadget == RotateGadget)
|
||||
if (m_highlightedGadget == RotateGadget || m_highlightedGadget == NoGadget)
|
||||
return ToolCursor::RotateCursor;
|
||||
else if (m_highlightedGadget == ScaleGadget)
|
||||
return ToolCursor::ScaleCursor;
|
||||
else if (isCurveGadget(m_highlightedGadget))
|
||||
return ToolCursor::PinchCursor;
|
||||
else
|
||||
else // Curve Points, TranslateGadget, NoGadget_InBox
|
||||
return ToolCursor::MoveCursor;
|
||||
}
|
||||
|
||||
bool ShiftTraceTool::isEventAcceptable(QEvent *e) {
|
||||
// F1, F2 and F3 keys are used for flipping
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(e);
|
||||
int key = keyEvent->key();
|
||||
return (Qt::Key_F1 <= key && key <= Qt::Key_F3);
|
||||
}
|
||||
|
||||
ShiftTraceTool shiftTraceTool;
|
||||
|
|
|
@ -561,6 +561,105 @@ void RowArea::drawCurrentTimeLine(QPainter &p) {
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void RowArea::drawShiftTraceMarker(QPainter &p) {
|
||||
TApp *app = TApp::instance();
|
||||
OnionSkinMask osMask = app->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
|
||||
TXsheet *xsh = app->getCurrentScene()->getScene()->getXsheet();
|
||||
assert(xsh);
|
||||
int currentRow = m_viewer->getCurrentRow();
|
||||
|
||||
int frameAdj = m_viewer->getFrameZoomAdjustment();
|
||||
|
||||
// get onion colors
|
||||
TPixel frontPixel, backPixel;
|
||||
bool inksOnly;
|
||||
Preferences::instance()->getOnionData(frontPixel, backPixel, inksOnly);
|
||||
QColor frontColor((int)frontPixel.r, (int)frontPixel.g, (int)frontPixel.b);
|
||||
QColor backColor((int)backPixel.r, (int)backPixel.g, (int)backPixel.b);
|
||||
|
||||
// draw lines to ghost frames
|
||||
int prevOffset = osMask.getShiftTraceGhostFrameOffset(0);
|
||||
int forwardOffset = osMask.getShiftTraceGhostFrameOffset(1);
|
||||
|
||||
QRect onionRect =
|
||||
m_viewer->orientation()->rect(PredefinedRect::SHIFTTRACE_DOT);
|
||||
int onionCenter_frame =
|
||||
m_viewer->orientation()->frameSide(onionRect).middle();
|
||||
int onionCenter_layer =
|
||||
m_viewer->orientation()->layerSide(onionRect).middle();
|
||||
|
||||
if (currentRow > 0 && prevOffset < 0) // previous ghost
|
||||
{
|
||||
int layerAxis = onionCenter_layer;
|
||||
int fromFrameAxis = m_viewer->rowToFrameAxis(currentRow + prevOffset) +
|
||||
onionCenter_frame - (frameAdj / 2);
|
||||
int toFrameAxis = m_viewer->rowToFrameAxis(currentRow) + onionCenter_frame -
|
||||
(frameAdj / 2);
|
||||
QLine verticalLine = m_viewer->orientation()->verticalLine(
|
||||
layerAxis, NumberRange(fromFrameAxis, toFrameAxis));
|
||||
p.setPen(backColor);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawLine(verticalLine);
|
||||
}
|
||||
if (forwardOffset > 0) // forward ghost
|
||||
{
|
||||
int layerAxis = onionCenter_layer;
|
||||
int fromFrameAxis = m_viewer->rowToFrameAxis(currentRow) +
|
||||
onionCenter_frame - (frameAdj / 2);
|
||||
int toFrameAxis = m_viewer->rowToFrameAxis(currentRow + forwardOffset) +
|
||||
onionCenter_frame - (frameAdj / 2);
|
||||
QLine verticalLine = m_viewer->orientation()->verticalLine(
|
||||
layerAxis, NumberRange(fromFrameAxis, toFrameAxis));
|
||||
p.setPen(frontColor);
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.drawLine(verticalLine);
|
||||
}
|
||||
|
||||
if (!m_viewer->orientation()->isVerticalTimeline())
|
||||
drawCurrentTimeIndicator(p);
|
||||
|
||||
// draw dots
|
||||
std::vector<int> offsVec = {prevOffset, 0, forwardOffset};
|
||||
std::vector<QColor> colorsVec = {backColor, QColor(0, 162, 232), frontColor};
|
||||
QFont currentFont = p.font();
|
||||
QFont tmpFont = p.font();
|
||||
tmpFont.setPointSize(7);
|
||||
p.setFont(tmpFont);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (i != 1 && offsVec[i] == 0) continue;
|
||||
p.setPen(colorsVec[i]);
|
||||
p.setBrush(Qt::gray);
|
||||
QPoint topLeft =
|
||||
m_viewer->positionToXY(CellPosition(currentRow + offsVec[i], 0));
|
||||
if (!m_viewer->orientation()->isVerticalTimeline()) topLeft.setY(0);
|
||||
QRect dotRect = m_viewer->orientation()
|
||||
->rect(PredefinedRect::SHIFTTRACE_DOT)
|
||||
.translated(topLeft);
|
||||
dotRect.adjust(-frameAdj / 2, 0, -frameAdj / 2, 0);
|
||||
p.drawRect(dotRect);
|
||||
// draw shortcut numbers
|
||||
p.setPen(Qt::black);
|
||||
p.drawText(dotRect, Qt::AlignCenter, QString::number(i + 1));
|
||||
}
|
||||
p.setFont(currentFont);
|
||||
|
||||
//-- onion placement hint under mouse
|
||||
if (m_showOnionToSet == ShiftTraceGhost) {
|
||||
p.setPen(QColor(255, 255, 0));
|
||||
p.setBrush(QColor(255, 255, 0, 180));
|
||||
QPoint topLeft = m_viewer->positionToXY(CellPosition(m_row, 0));
|
||||
if (!m_viewer->orientation()->isVerticalTimeline()) topLeft.setY(0);
|
||||
QRect dotRect = m_viewer->orientation()
|
||||
->rect(PredefinedRect::SHIFTTRACE_DOT)
|
||||
.translated(topLeft);
|
||||
dotRect.adjust(-frameAdj / 2, 0, -frameAdj / 2, 0);
|
||||
p.drawRect(dotRect);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
TStageObjectId getAncestor(TXsheet *xsh, TStageObjectId id) {
|
||||
|
@ -672,7 +771,9 @@ void RowArea::paintEvent(QPaintEvent *event) {
|
|||
drawRows(p, r0, r1);
|
||||
|
||||
if (TApp::instance()->getCurrentFrame()->isEditingScene()) {
|
||||
if (Preferences::instance()->isOnionSkinEnabled())
|
||||
if (CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked())
|
||||
drawShiftTraceMarker(p);
|
||||
else if (Preferences::instance()->isOnionSkinEnabled())
|
||||
drawOnionSkinSelection(p);
|
||||
else if (Preferences::instance()->isCurrentTimelineIndicatorEnabled() &&
|
||||
!m_viewer->orientation()->isVerticalTimeline())
|
||||
|
@ -711,7 +812,38 @@ void RowArea::mousePressEvent(QMouseEvent *event) {
|
|||
QPoint mouseInCell = event->pos() - topLeft;
|
||||
int frameAdj = m_viewer->getFrameZoomAdjustment();
|
||||
|
||||
if (Preferences::instance()->isOnionSkinEnabled() &&
|
||||
if (CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked() &&
|
||||
o->rect(PredefinedRect::SHIFTTRACE_DOT_AREA)
|
||||
.adjusted(0, 0, -frameAdj, 0)
|
||||
.contains(mouseInCell)) {
|
||||
// Reset ghosts to neighbor frames
|
||||
if (row == currentFrame)
|
||||
OnioniSkinMaskGUI::resetShiftTraceFrameOffset();
|
||||
else {
|
||||
OnionSkinMask osMask =
|
||||
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
int prevOffset = osMask.getShiftTraceGhostFrameOffset(0);
|
||||
int forwardOffset = osMask.getShiftTraceGhostFrameOffset(1);
|
||||
// Hide previous ghost
|
||||
if (row == currentFrame + prevOffset)
|
||||
osMask.setShiftTraceGhostFrameOffset(0, 0);
|
||||
// Hide forward ghost
|
||||
else if (row == currentFrame + forwardOffset)
|
||||
osMask.setShiftTraceGhostFrameOffset(1, 0);
|
||||
// Move previous ghost
|
||||
else if (row < currentFrame)
|
||||
osMask.setShiftTraceGhostFrameOffset(0, row - currentFrame);
|
||||
// Move forward ghost
|
||||
else
|
||||
osMask.setShiftTraceGhostFrameOffset(1, row - currentFrame);
|
||||
TApp::instance()->getCurrentOnionSkin()->setOnionSkinMask(osMask);
|
||||
}
|
||||
TApp::instance()->getCurrentOnionSkin()->notifyOnionSkinMaskChanged();
|
||||
return;
|
||||
} else if (!CommandManager::instance()
|
||||
->getAction(MI_ShiftTrace)
|
||||
->isChecked() &&
|
||||
Preferences::instance()->isOnionSkinEnabled() &&
|
||||
o->rect(PredefinedRect::ONION_AREA)
|
||||
.adjusted(0, 0, -frameAdj, 0)
|
||||
.contains(mouseInCell)) {
|
||||
|
@ -833,8 +965,38 @@ void RowArea::mouseMoveEvent(QMouseEvent *event) {
|
|||
if (!m_viewer->orientation()->isVerticalTimeline()) topLeft.setY(0);
|
||||
QPoint mouseInCell = event->pos() - topLeft;
|
||||
if (row < 0) return;
|
||||
|
||||
m_tooltip = tr("");
|
||||
|
||||
// whether to show ability to move the shift and trace ghost frame
|
||||
if (CommandManager::instance()->getAction(MI_ShiftTrace)->isChecked()) {
|
||||
if (o->rect(PredefinedRect::SHIFTTRACE_DOT_AREA)
|
||||
.adjusted(0, 0, -frameAdj, 0)
|
||||
.contains(mouseInCell)) {
|
||||
m_showOnionToSet = ShiftTraceGhost;
|
||||
|
||||
OnionSkinMask osMask =
|
||||
TApp::instance()->getCurrentOnionSkin()->getOnionSkinMask();
|
||||
int prevOffset = osMask.getShiftTraceGhostFrameOffset(0);
|
||||
int forwardOffset = osMask.getShiftTraceGhostFrameOffset(1);
|
||||
if (row == currentRow)
|
||||
m_tooltip =
|
||||
tr("Click to Reset Shift & Trace Markers to Neighbor Frames\nHold "
|
||||
"F2 Key on the Viewer to Show This Frame Only");
|
||||
else if (row == currentRow + prevOffset)
|
||||
m_tooltip =
|
||||
tr("Click to Hide This Frame from Shift & Trace\nHold F1 Key on "
|
||||
"the Viewer to Show This Frame Only");
|
||||
else if (row == currentRow + forwardOffset)
|
||||
m_tooltip =
|
||||
tr("Click to Hide This Frame from Shift & Trace\nHold F3 Key on "
|
||||
"the Viewer to Show This Frame Only");
|
||||
else
|
||||
m_tooltip = tr("Click to Move Shift & Trace Marker");
|
||||
}
|
||||
}
|
||||
// whether to show ability to set onion marks
|
||||
if (Preferences::instance()->isOnionSkinEnabled() && row != currentRow) {
|
||||
else if (Preferences::instance()->isOnionSkinEnabled() && row != currentRow) {
|
||||
if (o->rect(PredefinedRect::ONION_FIXED_DOT_AREA)
|
||||
.adjusted(0, 0, -frameAdj, 0)
|
||||
.contains(mouseInCell))
|
||||
|
@ -884,6 +1046,7 @@ void RowArea::mouseMoveEvent(QMouseEvent *event) {
|
|||
QPainterPath endArrow =
|
||||
o->path(PredefinedPath::END_PLAY_RANGE).translated(base1);
|
||||
|
||||
if (!m_tooltip.isEmpty()) return;
|
||||
if (startArrow.contains(m_pos))
|
||||
m_tooltip = tr("Playback Start Marker");
|
||||
else if (endArrow.contains(m_pos))
|
||||
|
@ -904,8 +1067,6 @@ void RowArea::mouseMoveEvent(QMouseEvent *event) {
|
|||
m_tooltip = tr("Fixed Onion Skin Toggle");
|
||||
else if (m_showOnionToSet == Mos)
|
||||
m_tooltip = tr("Relative Onion Skin Toggle");
|
||||
else
|
||||
m_tooltip = tr("");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -26,8 +26,9 @@ class RowArea final : public QWidget {
|
|||
enum ShowOnionToSetFlag {
|
||||
None = 0,
|
||||
Fos,
|
||||
Mos
|
||||
} m_showOnionToSet; // TODO:明日はこれをFos,Mosどちらをハイライトしているのか判定させる!!!!
|
||||
Mos,
|
||||
ShiftTraceGhost
|
||||
} m_showOnionToSet;
|
||||
|
||||
enum Direction { up = 0, down };
|
||||
|
||||
|
@ -51,6 +52,7 @@ class RowArea final : public QWidget {
|
|||
void drawPinnedCenterKeys(QPainter &p, int r0, int r1);
|
||||
void drawCurrentTimeIndicator(QPainter &p);
|
||||
void drawCurrentTimeLine(QPainter &p);
|
||||
void drawShiftTraceMarker(QPainter &p);
|
||||
|
||||
DragTool *getDragTool() const;
|
||||
void setDragTool(DragTool *dragTool);
|
||||
|
|
|
@ -64,6 +64,8 @@ void OnionSkinMask::clear() {
|
|||
m_ghostAff[1] = TAffine();
|
||||
m_ghostCenter[0] = TPointD();
|
||||
m_ghostCenter[1] = TPointD();
|
||||
m_ghostFrame[0] = 0;
|
||||
m_ghostFrame[1] = 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
|
|
@ -19,6 +19,7 @@ const int ONION_DOT_SIZE = 8;
|
|||
const int PINNED_SIZE = 10;
|
||||
const int FRAME_MARKER_SIZE = 4;
|
||||
const int FOLDED_CELL_SIZE = 9;
|
||||
const int SHIFTTRACE_DOT_SIZE = 12;
|
||||
}
|
||||
|
||||
class TopToBottomOrientation : public Orientation {
|
||||
|
@ -37,6 +38,7 @@ class TopToBottomOrientation : public Orientation {
|
|||
const int ICON_WIDTH = 18;
|
||||
const int ICON_HEIGHT = 18;
|
||||
const int TRACKLEN = 60;
|
||||
const int SHIFTTRACE_DOT_OFFSET = 3;
|
||||
|
||||
public:
|
||||
TopToBottomOrientation();
|
||||
|
@ -97,6 +99,7 @@ class LeftToRightOrientation : public Orientation {
|
|||
const int FOLDED_LAYER_HEADER_HEIGHT = 8;
|
||||
const int FOLDED_LAYER_HEADER_WIDTH = LAYER_HEADER_WIDTH;
|
||||
const int TRACKLEN = 60;
|
||||
const int SHIFTTRACE_DOT_OFFSET = 5;
|
||||
|
||||
public:
|
||||
LeftToRightOrientation();
|
||||
|
@ -347,6 +350,12 @@ TopToBottomOrientation::TopToBottomOrientation() {
|
|||
PredefinedRect::PREVIEW_FRAME_AREA,
|
||||
QRect(PLAY_RANGE_X, 0, (FRAME_HEADER_WIDTH - PLAY_RANGE_X), CELL_HEIGHT));
|
||||
|
||||
addRect(PredefinedRect::SHIFTTRACE_DOT,
|
||||
QRect(SHIFTTRACE_DOT_OFFSET, (CELL_HEIGHT - SHIFTTRACE_DOT_SIZE) / 2,
|
||||
SHIFTTRACE_DOT_SIZE, SHIFTTRACE_DOT_SIZE));
|
||||
addRect(PredefinedRect::SHIFTTRACE_DOT_AREA,
|
||||
QRect(SHIFTTRACE_DOT_OFFSET, 0, SHIFTTRACE_DOT_SIZE, CELL_HEIGHT));
|
||||
|
||||
// Column viewer
|
||||
addRect(PredefinedRect::LAYER_HEADER,
|
||||
QRect(0, 1, CELL_WIDTH, use_header_height - 3));
|
||||
|
@ -953,6 +962,13 @@ LeftToRightOrientation::LeftToRightOrientation() {
|
|||
PredefinedRect::PREVIEW_FRAME_AREA,
|
||||
QRect(0, PLAY_RANGE_Y, CELL_WIDTH, (FRAME_HEADER_HEIGHT - PLAY_RANGE_Y)));
|
||||
|
||||
addRect(PredefinedRect::SHIFTTRACE_DOT,
|
||||
QRect((CELL_WIDTH - SHIFTTRACE_DOT_SIZE) / 2, SHIFTTRACE_DOT_OFFSET,
|
||||
SHIFTTRACE_DOT_SIZE, SHIFTTRACE_DOT_SIZE)
|
||||
.adjusted(-1, 0, -1, 0));
|
||||
addRect(PredefinedRect::SHIFTTRACE_DOT_AREA,
|
||||
QRect(0, SHIFTTRACE_DOT_OFFSET, CELL_WIDTH, SHIFTTRACE_DOT_SIZE));
|
||||
|
||||
// Column viewer
|
||||
addRect(PredefinedRect::LAYER_HEADER,
|
||||
QRect(1, 0, LAYER_HEADER_WIDTH - 2, CELL_HEIGHT));
|
||||
|
|
|
@ -398,7 +398,29 @@ void StageBuilder::addCell(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
|
|||
}
|
||||
|
||||
if (m_shiftTraceGhostId != NO_GHOST) {
|
||||
if (m_shiftTraceGhostId != TRACED) player.m_opacity = 127;
|
||||
// if F1, F2 or F3 key is pressed, then draw only the corresponding ghost
|
||||
int flipKey = m_onionSkinMask.getGhostFlipKey();
|
||||
if (Qt::Key_F1 <= flipKey && flipKey <= Qt::Key_F3) {
|
||||
if (m_shiftTraceGhostId == TRACED && flipKey == Qt::Key_F2) {
|
||||
players.push_back(player);
|
||||
} else if (m_shiftTraceGhostId == FIRST_GHOST &&
|
||||
flipKey == Qt::Key_F1) {
|
||||
player.m_placement =
|
||||
m_onionSkinMask.getShiftTraceGhostAff(0) * player.m_placement;
|
||||
players.push_back(player);
|
||||
} else if (m_shiftTraceGhostId == SECOND_GHOST &&
|
||||
flipKey == Qt::Key_F3) {
|
||||
player.m_placement =
|
||||
m_onionSkinMask.getShiftTraceGhostAff(1) * player.m_placement;
|
||||
players.push_back(player);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
else {
|
||||
if (m_shiftTraceGhostId != TRACED)
|
||||
player.m_opacity =
|
||||
UCHAR(255.0 * (1.0 - OnionSkinMask::getOnionSkinFade(1)));
|
||||
int opacity = player.m_opacity;
|
||||
player.m_bingoOrder = 10;
|
||||
if (m_onionSkinMask.getShiftTraceStatus() !=
|
||||
|
@ -418,6 +440,7 @@ void StageBuilder::addCell(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
players.push_back(player);
|
||||
} else if (TXshChildLevel *cl = xl->getChildLevel()) {
|
||||
|
@ -511,31 +534,26 @@ void StageBuilder::addCellWithOnionSkin(PlayerSet &players, ToonzScene *scene,
|
|||
|
||||
if (m_onionSkinMask.isShiftTraceEnabled() && col == m_currentColumnIndex) {
|
||||
TXshCell cell = xsh->getCell(row, col);
|
||||
int r = row - 1;
|
||||
|
||||
// r,col can be a hold. find its starting point
|
||||
for (; r - 1 >= 0 && xsh->getCell(r - 1, col) == cell; r--)
|
||||
;
|
||||
if (cell.isEmpty()) r--;
|
||||
|
||||
if (r >= 0 &&
|
||||
// First Ghost
|
||||
int r;
|
||||
r = row + m_onionSkinMask.getShiftTraceGhostFrameOffset(0);
|
||||
if (r >= 0 && xsh->getCell(r, col) != cell &&
|
||||
(cell.getSimpleLevel() == 0 ||
|
||||
xsh->getCell(r, col).getSimpleLevel() == cell.getSimpleLevel())) {
|
||||
m_shiftTraceGhostId = FIRST_GHOST;
|
||||
addCell(players, scene, xsh, r, col, level);
|
||||
}
|
||||
|
||||
TXshCell otherCell;
|
||||
if (cell.getSimpleLevel() != 0) {
|
||||
for (r = row + 1; (otherCell = xsh->getCell(r, col)) == cell; r++)
|
||||
;
|
||||
|
||||
if (cell.getSimpleLevel() == 0 ||
|
||||
otherCell.getSimpleLevel() == cell.getSimpleLevel()) {
|
||||
r = row + m_onionSkinMask.getShiftTraceGhostFrameOffset(1);
|
||||
if (r >= 0 && xsh->getCell(r, col) != cell &&
|
||||
(cell.getSimpleLevel() == 0 ||
|
||||
xsh->getCell(r, col).getSimpleLevel() == cell.getSimpleLevel())) {
|
||||
m_shiftTraceGhostId = SECOND_GHOST;
|
||||
addCell(players, scene, xsh, r, col, level);
|
||||
}
|
||||
}
|
||||
|
||||
// draw current working frame
|
||||
if (!cell.isEmpty()) {
|
||||
m_shiftTraceGhostId = TRACED;
|
||||
addCell(players, scene, xsh, row, col, level);
|
||||
|
@ -632,10 +650,77 @@ void StageBuilder::addFrame(PlayerSet &players, ToonzScene *scene, TXsheet *xsh,
|
|||
void StageBuilder::addSimpleLevelFrame(PlayerSet &players,
|
||||
TXshSimpleLevel *level,
|
||||
const TFrameId &fid) {
|
||||
auto addGhost = [&](int ghostIndex, int ghostRow, bool fullOpac = false) {
|
||||
const TFrameId &ghostFid = level->index2fid(ghostRow);
|
||||
|
||||
Player player;
|
||||
player.m_sl = level;
|
||||
player.m_frame = level->guessIndex(ghostFid);
|
||||
player.m_fid = ghostFid;
|
||||
player.m_isCurrentColumn = true;
|
||||
player.m_isCurrentXsheetLevel = true;
|
||||
player.m_isEditingLevel = true;
|
||||
player.m_currentFrameId = m_currentFrameId;
|
||||
player.m_isGuidedDrawingEnabled = m_isGuidedDrawingEnabled;
|
||||
player.m_isVisibleinOSM = ghostRow >= 0;
|
||||
player.m_onionSkinDistance = m_onionSkinDistance;
|
||||
player.m_dpiAff = getDpiAffine(level, ghostFid);
|
||||
player.m_ancestorColumnIndex = -1;
|
||||
|
||||
if (fullOpac) {
|
||||
player.m_placement = m_onionSkinMask.getShiftTraceGhostAff(ghostIndex) *
|
||||
player.m_placement;
|
||||
players.push_back(player);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_shiftTraceGhostId != TRACED)
|
||||
player.m_opacity =
|
||||
UCHAR(255.0 * (1.0 - OnionSkinMask::getOnionSkinFade(1)));
|
||||
;
|
||||
int opacity = player.m_opacity;
|
||||
player.m_bingoOrder = 10;
|
||||
if (m_onionSkinMask.getShiftTraceStatus() !=
|
||||
OnionSkinMask::ENABLED_WITHOUT_GHOST_MOVEMENTS) {
|
||||
player.m_opacity = 30;
|
||||
players.push_back(player);
|
||||
player.m_opacity = opacity;
|
||||
player.m_placement = m_onionSkinMask.getShiftTraceGhostAff(ghostIndex) *
|
||||
player.m_placement;
|
||||
}
|
||||
players.push_back(player);
|
||||
};
|
||||
|
||||
int index = -1;
|
||||
|
||||
int row = level->guessIndex(fid);
|
||||
if (!m_onionSkinMask.isEmpty() && m_onionSkinMask.isEnabled()) {
|
||||
|
||||
// Shift & Trace
|
||||
if (m_onionSkinMask.isShiftTraceEnabled()) {
|
||||
int previousOffset = m_onionSkinMask.getShiftTraceGhostFrameOffset(0);
|
||||
int forwardOffset = m_onionSkinMask.getShiftTraceGhostFrameOffset(1);
|
||||
|
||||
// If F1, F2 or F3 key is pressed, then only
|
||||
// display the corresponding ghost
|
||||
int flipKey = m_onionSkinMask.getGhostFlipKey();
|
||||
if (Qt::Key_F1 <= flipKey && flipKey <= Qt::Key_F3) {
|
||||
if (flipKey == Qt::Key_F1 && previousOffset != 0) {
|
||||
addGhost(0, row + previousOffset, true);
|
||||
return;
|
||||
} else if (flipKey == Qt::Key_F3 && forwardOffset != 0) {
|
||||
addGhost(1, row + forwardOffset, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
// draw the first ghost
|
||||
if (previousOffset != 0) addGhost(0, row + previousOffset);
|
||||
if (forwardOffset != 0) addGhost(1, row + forwardOffset);
|
||||
}
|
||||
}
|
||||
// Onion Skin
|
||||
else if (!m_onionSkinMask.isEmpty() && m_onionSkinMask.isEnabled()) {
|
||||
std::vector<int> rows;
|
||||
m_onionSkinMask.getAll(row, rows);
|
||||
|
||||
|
|
Loading…
Reference in a new issue