| |
| |
| |
| #include "tstroke.h" |
| #include "tools/toolutils.h" |
| #include "tools/tool.h" |
| #include "tmathutil.h" |
| #include "tools/cursors.h" |
| #include "drawutil.h" |
| #include "tcolorstyles.h" |
| #include "tundo.h" |
| #include "tvectorimage.h" |
| #include "ttoonzimage.h" |
| #include "tproperty.h" |
| #include "toonz/strokegenerator.h" |
| #include "toonz/ttilesaver.h" |
| #include "toonz/txshsimplelevel.h" |
| #include "toonz/observer.h" |
| #include "toonz/toonzimageutils.h" |
| #include "toonz/levelproperties.h" |
| #include "toonz/stage2.h" |
| #include "toonz/ttileset.h" |
| #include "toonz/rasterstrokegenerator.h" |
| #include "tgl.h" |
| #include "tenv.h" |
| |
| #include "trop.h" |
| |
| #include "tinbetween.h" |
| #include "ttile.h" |
| |
| #include "toonz/tpalettehandle.h" |
| #include "toonz/txsheethandle.h" |
| #include "toonz/txshlevelhandle.h" |
| #include "toonz/tframehandle.h" |
| #include "tools/toolhandle.h" |
| #include "tools/toolutils.h" |
| |
| |
| #include <QCoreApplication> |
| |
| #include "tools/stylepicker.h" |
| #include "toonzqt/tselectionhandle.h" |
| #include "toonzqt/styleselection.h" |
| #include "historytypes.h" |
| |
| using namespace ToolUtils; |
| |
| TEnv::IntVar FingerInvert("InknpaintFingerInvert", 0); |
| TEnv::DoubleVar FingerSize("InknpaintFingerSize", 10); |
| |
| |
| |
| const int BackgroundStyle = 0; |
| |
| |
| |
| namespace { |
| |
| class FingerUndo : public TRasterUndo { |
| std::vector<TThickPoint> m_points; |
| int m_styleId; |
| bool m_invert; |
| |
| public: |
| FingerUndo(TTileSetCM32 *tileSet, const std::vector<TThickPoint> &points, |
| int styleId, bool invert, TXshSimpleLevel *level, |
| const TFrameId &frameId) |
| : TRasterUndo(tileSet, level, frameId, false, false, 0) |
| , m_points(points) |
| , m_styleId(styleId) |
| , m_invert(invert) {} |
| |
| void redo() const { |
| TToonzImageP image = m_level->getFrame(m_frameId, true); |
| TRasterCM32P ras = image->getRaster(); |
| RasterStrokeGenerator m_rasterTrack(ras, FINGER, INK, m_styleId, |
| m_points[0], m_invert, 0, false); |
| m_rasterTrack.setPointsSequence(m_points); |
| m_rasterTrack.generateStroke(true); |
| image->setSavebox(image->getSavebox() + |
| m_rasterTrack.getBBox(m_rasterTrack.getPointsSequence())); |
| |
| ToolUtils::updateSaveBox(); |
| |
| TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| int getSize() const { return sizeof(*this) + TRasterUndo::getSize(); } |
| |
| virtual QString getToolName() { return QString("Finger Tool"); } |
| int getHistoryType() { return HistoryType::FingerTool; } |
| }; |
| |
| |
| |
| void drawLine(const TPointD &point, const TPointD ¢re, bool horizontal, |
| bool isDecimal) { |
| if (!isDecimal) { |
| if (horizontal) { |
| tglDrawSegment(TPointD(point.x - 1.5, point.y + 0.5) + centre, |
| TPointD(point.x - 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y - 0.5, -point.x + 1.5) + centre, |
| TPointD(point.y - 0.5, -point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, -point.y + 0.5) + centre, |
| TPointD(-point.x - 0.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x + 0.5) + centre); |
| |
| tglDrawSegment(TPointD(point.y - 0.5, point.x + 0.5) + centre, |
| TPointD(point.y - 0.5, point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 0.5, -point.y + 0.5) + centre, |
| TPointD(point.x - 1.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, -point.x + 0.5) + centre, |
| TPointD(-point.y - 0.5, -point.x + 1.5) + centre); |
| tglDrawSegment(TPointD(-point.x - 0.5, point.y + 0.5) + centre, |
| TPointD(-point.x + 0.5, point.y + 0.5) + centre); |
| } else { |
| tglDrawSegment(TPointD(point.x - 1.5, point.y + 1.5) + centre, |
| TPointD(point.x - 1.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 1.5, point.y + 0.5) + centre, |
| TPointD(point.x - 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 0.5, -point.x + 1.5) + centre, |
| TPointD(point.y - 0.5, -point.x + 1.5) + centre); |
| tglDrawSegment(TPointD(point.y - 0.5, -point.x + 1.5) + centre, |
| TPointD(point.y - 0.5, -point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, -point.y - 0.5) + centre, |
| TPointD(-point.x + 0.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, -point.y + 0.5) + centre, |
| TPointD(-point.x - 0.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 1.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x + 0.5) + centre); |
| |
| tglDrawSegment(TPointD(point.y + 0.5, point.x - 0.5) + centre, |
| TPointD(point.y - 0.5, point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(point.y - 0.5, point.x - 0.5) + centre, |
| TPointD(point.y - 0.5, point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 1.5, -point.y - 0.5) + centre, |
| TPointD(point.x - 1.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 1.5, -point.y + 0.5) + centre, |
| TPointD(point.x - 0.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 1.5, -point.x + 1.5) + centre, |
| TPointD(-point.y - 0.5, -point.x + 1.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, -point.x + 1.5) + centre, |
| TPointD(-point.y - 0.5, -point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, point.y + 1.5) + centre, |
| TPointD(-point.x + 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, point.y + 0.5) + centre, |
| TPointD(-point.x - 0.5, point.y + 0.5) + centre); |
| } |
| } else { |
| if (horizontal) { |
| tglDrawSegment(TPointD(point.x - 0.5, point.y + 0.5) + centre, |
| TPointD(point.x + 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 0.5, point.x - 0.5) + centre, |
| TPointD(point.y + 0.5, point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 0.5, -point.x + 0.5) + centre, |
| TPointD(point.y + 0.5, -point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(point.x + 0.5, -point.y - 0.5) + centre, |
| TPointD(point.x - 0.5, -point.y - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x - 0.5, -point.y - 0.5) + centre, |
| TPointD(-point.x + 0.5, -point.y - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, -point.x + 0.5) + centre, |
| TPointD(-point.y - 0.5, -point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, point.y + 0.5) + centre, |
| TPointD(-point.x - 0.5, point.y + 0.5) + centre); |
| } else { |
| tglDrawSegment(TPointD(point.x - 0.5, point.y + 1.5) + centre, |
| TPointD(point.x - 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 0.5, point.y + 0.5) + centre, |
| TPointD(point.x + 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 1.5, point.x - 0.5) + centre, |
| TPointD(point.y + 0.5, point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 0.5, point.x - 0.5) + centre, |
| TPointD(point.y + 0.5, point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 1.5, -point.x + 0.5) + centre, |
| TPointD(point.y + 0.5, -point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 0.5, -point.x + 0.5) + centre, |
| TPointD(point.y + 0.5, -point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 0.5, -point.y - 1.5) + centre, |
| TPointD(point.x - 0.5, -point.y - 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 0.5, -point.y - 0.5) + centre, |
| TPointD(point.x + 0.5, -point.y - 0.5) + centre); |
| |
| tglDrawSegment(TPointD(-point.x + 0.5, -point.y - 1.5) + centre, |
| TPointD(-point.x + 0.5, -point.y - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, -point.y - 0.5) + centre, |
| TPointD(-point.x - 0.5, -point.y - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 1.5, -point.x + 0.5) + centre, |
| TPointD(-point.y - 0.5, -point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, -point.x + 0.5) + centre, |
| TPointD(-point.y - 0.5, -point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 1.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, point.y + 1.5) + centre, |
| TPointD(-point.x + 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, point.y + 0.5) + centre, |
| TPointD(-point.x - 0.5, point.y + 0.5) + centre); |
| } |
| } |
| } |
| |
| |
| |
| void drawEmptyCircle(int thick, const TPointD &mousePos, bool isPencil, |
| bool isLxEven, bool isLyEven) { |
| TPointD pos = mousePos; |
| if (isLxEven) pos.x += 0.5; |
| if (isLyEven) pos.y += 0.5; |
| if (!isPencil) |
| tglDrawCircle(pos, (thick + 1) * 0.5); |
| else { |
| int x = 0, y = tround((thick * 0.5) - 0.5); |
| int d = 3 - 2 * (int)(thick * 0.5); |
| bool horizontal = true, isDecimal = thick % 2 != 0; |
| drawLine(TPointD(x, y), pos, horizontal, isDecimal); |
| while (y > x) { |
| if (d < 0) { |
| d = d + 4 * x + 6; |
| horizontal = true; |
| } else { |
| d = d + 4 * (x - y) + 10; |
| horizontal = false; |
| y--; |
| } |
| x++; |
| drawLine(TPointD(x, y), pos, horizontal, isDecimal); |
| } |
| } |
| } |
| |
| } |
| |
| |
| |
| class FingerTool : public TTool { |
| Q_DECLARE_TR_FUNCTIONS(FingerTool) |
| |
| RasterStrokeGenerator *m_rasterTrack; |
| |
| bool m_firstTime; |
| |
| double m_pointSize, m_distance2; |
| |
| bool m_selecting; |
| TTileSaverCM32 *m_tileSaver; |
| |
| TPointD m_mousePos; |
| |
| TIntProperty m_toolSize; |
| TBoolProperty m_invert; |
| TPropertyGroup m_prop; |
| int m_cursor; |
| |
| |
| |
| TFrameId m_workingFrameId; |
| |
| |
| void pick(const TPointD &pos); |
| |
| public: |
| FingerTool(); |
| |
| void draw(); |
| void update(TToonzImageP ti, TRectD area); |
| |
| void updateTranslation(); |
| |
| void leftButtonDown(const TPointD &pos, const TMouseEvent &e); |
| void leftButtonDrag(const TPointD &pos, const TMouseEvent &e); |
| void leftButtonUp(const TPointD &pos, const TMouseEvent &); |
| void mouseMove(const TPointD &pos, const TMouseEvent &e); |
| void onEnter(); |
| void onLeave(); |
| void onActivate(); |
| void onDeactivate(); |
| bool onPropertyChanged(std::string propertyName); |
| |
| TPropertyGroup *getProperties(int targetType) { return &m_prop; } |
| ToolType getToolType() const { return TTool::LevelWriteTool; } |
| int getCursorId() const { return m_cursor; } |
| |
| int getColorClass() const { return 2; } |
| |
| |
| |
| |
| void finishBrush(); |
| }; |
| |
| FingerTool fingerTool; |
| |
| |
| |
| |
| |
| |
| |
| FingerTool::FingerTool() |
| : TTool("T_Finger") |
| , m_rasterTrack(0) |
| , m_pointSize(-1) |
| , m_selecting(false) |
| , m_tileSaver(0) |
| , m_cursor(ToolCursor::EraserCursor) |
| , m_toolSize("Size:", 1, 100, 10, false) |
| , m_invert("Invert", false) |
| , m_firstTime(true) |
| , m_workingFrameId(TFrameId()) { |
| bind(TTool::ToonzImage); |
| |
| m_prop.bind(m_toolSize); |
| m_prop.bind(m_invert); |
| |
| m_invert.setId("Invert"); |
| } |
| |
| |
| |
| void FingerTool::updateTranslation() { |
| m_toolSize.setQStringName(tr("Size:")); |
| m_invert.setQStringName(tr("Invert", NULL)); |
| } |
| |
| |
| |
| void FingerTool::draw() { |
| if (m_pointSize == -1) { |
| return; |
| } |
| |
| TToonzImageP ti = (TToonzImageP)getImage(false); |
| if (!ti) return; |
| TRasterP ras = ti->getRaster(); |
| int lx = ras->getLx(); |
| int ly = ras->getLy(); |
| |
| if ((ToonzCheck::instance()->getChecks() & ToonzCheck::eInk) || |
| (ToonzCheck::instance()->getChecks() & ToonzCheck::ePaint)) |
| glColor3d(0.5, 0.8, 0.8); |
| else |
| glColor3d(1.0, 0.0, 0.0); |
| |
| drawEmptyCircle(m_toolSize.getValue(), m_mousePos, true, lx % 2 == 0, |
| ly % 2 == 0); |
| } |
| |
| |
| |
| const UINT pointCount = 20; |
| |
| |
| |
| bool FingerTool::onPropertyChanged(std::string propertyName) { |
| |
| if (propertyName == m_toolSize.getName()) { |
| FingerSize = m_toolSize.getValue(); |
| double x = m_toolSize.getValue(); |
| |
| double minRange = 1; |
| double maxRange = 100; |
| |
| double minSize = 0.01; |
| double maxSize = 100; |
| |
| m_pointSize = |
| (x - minRange) / (maxRange - minRange) * (maxSize - minSize) + minSize; |
| invalidate(); |
| } |
| |
| |
| else if (propertyName == m_invert.getName()) { |
| FingerInvert = (int)(m_invert.getValue()); |
| } |
| |
| return true; |
| } |
| |
| |
| |
| void FingerTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) { |
| pick(pos); |
| |
| m_selecting = true; |
| TImageP image(getImage(true)); |
| |
| if (TToonzImageP ti = image) { |
| TRasterCM32P ras = ti->getRaster(); |
| if (ras) { |
| int thickness = m_toolSize.getValue(); |
| int styleId = TTool::getApplication()->getCurrentLevelStyleIndex(); |
| TTileSetCM32 *tileSet = new TTileSetCM32(ras->getSize()); |
| m_tileSaver = new TTileSaverCM32(ras, tileSet); |
| m_rasterTrack = new RasterStrokeGenerator( |
| ras, FINGER, INK, styleId, |
| TThickPoint(pos + convert(ras->getCenter()), thickness), |
| m_invert.getValue(), 0, false); |
| |
| |
| m_workingFrameId = getFrameId(); |
| |
| m_tileSaver->save(m_rasterTrack->getLastRect()); |
| TRect modifiedBbox = m_rasterTrack->generateLastPieceOfStroke(true); |
| invalidate(); |
| } |
| } |
| } |
| |
| |
| |
| void FingerTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { |
| if (!m_selecting) return; |
| |
| m_mousePos = pos; |
| if (TToonzImageP ri = TImageP(getImage(true))) { |
| |
| |
| |
| if (m_rasterTrack) { |
| int thickness = m_toolSize.getValue(); |
| bool isAdded = m_rasterTrack->add( |
| TThickPoint(pos + convert(ri->getRaster()->getCenter()), thickness)); |
| if (isAdded) { |
| m_tileSaver->save(m_rasterTrack->getLastRect()); |
| TRect modifiedBbox = m_rasterTrack->generateLastPieceOfStroke(true); |
| invalidate(); |
| } |
| } |
| } |
| } |
| |
| |
| |
| void FingerTool::leftButtonUp(const TPointD &pos, const TMouseEvent &) { |
| if (!m_selecting) return; |
| |
| m_mousePos = pos; |
| |
| finishBrush(); |
| } |
| |
| |
| |
| void FingerTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { |
| m_mousePos = pos; |
| TPointD pp(tround(pos.x), tround(pos.y)); |
| m_mousePos = pp; |
| invalidate(); |
| } |
| |
| |
| |
| void FingerTool::onEnter() { |
| if (m_firstTime) { |
| m_invert.setValue(FingerInvert ? 1 : 0); |
| m_toolSize.setValue(FingerSize); |
| m_firstTime = false; |
| } |
| double x = m_toolSize.getValue(); |
| |
| double minRange = 1; |
| double maxRange = 100; |
| |
| double minSize = 0.01; |
| double maxSize = 100; |
| |
| m_pointSize = |
| (x - minRange) / (maxRange - minRange) * (maxSize - minSize) + minSize; |
| |
| if ((TToonzImageP)getImage(false)) |
| m_cursor = ToolCursor::PenCursor; |
| else |
| m_cursor = ToolCursor::CURSOR_NO; |
| } |
| |
| |
| |
| void FingerTool::onLeave() { m_pointSize = -1; } |
| |
| |
| |
| void FingerTool::onActivate() { onEnter(); } |
| |
| |
| |
| void FingerTool::onDeactivate() { |
| |
| |
| |
| if (m_selecting) finishBrush(); |
| } |
| |
| |
| |
| |
| |
| void FingerTool::finishBrush() { |
| if (TToonzImageP ti = (TToonzImageP)getImage(true)) { |
| if (m_rasterTrack) { |
| int thickness = m_toolSize.getValue(); |
| bool isAdded = m_rasterTrack->add(TThickPoint( |
| m_mousePos + convert(ti->getRaster()->getCenter()), thickness)); |
| if (isAdded) { |
| m_tileSaver->save(m_rasterTrack->getLastRect()); |
| TRect modifiedBbox = |
| m_rasterTrack->generateLastPieceOfStroke(true, true); |
| } |
| |
| TTool::Application *app = TTool::getApplication(); |
| TXshLevel *level = app->getCurrentLevel()->getLevel(); |
| TXshSimpleLevelP simLevel = level->getSimpleLevel(); |
| |
| TFrameId frameId = |
| m_workingFrameId.isEmptyFrame() ? getCurrentFid() : m_workingFrameId; |
| |
| TUndoManager::manager()->add(new FingerUndo( |
| m_tileSaver->getTileSet(), m_rasterTrack->getPointsSequence(), |
| m_rasterTrack->getStyleId(), m_rasterTrack->isSelective(), |
| simLevel.getPointer(), frameId)); |
| ToolUtils::updateSaveBox(); |
| |
| |
| |
| |
| notifyImageChanged(frameId); |
| |
| invalidate(); |
| delete m_rasterTrack; |
| m_rasterTrack = 0; |
| delete m_tileSaver; |
| |
| |
| m_workingFrameId = TFrameId(); |
| } |
| } |
| m_selecting = false; |
| } |
| |
| void FingerTool::pick(const TPointD &pos) { |
| int modeValue = 2; |
| |
| TImageP image = getImage(false); |
| TToonzImageP ti = image; |
| TVectorImageP vi = image; |
| TXshSimpleLevel *level = |
| getApplication()->getCurrentLevel()->getSimpleLevel(); |
| if (!ti || !level) return; |
| |
| |
| if (!m_viewer->getGeometry().contains(pos)) return; |
| |
| int subsampling = level->getImageSubsampling(getCurrentFid()); |
| |
| StylePicker picker(image); |
| |
| int styleId = |
| picker.pickStyleId(TScale(1.0 / subsampling) * pos + TPointD(-0.5, -0.5), |
| getPixelSize() * getPixelSize(), modeValue); |
| |
| if (styleId < 0) return; |
| |
| if (modeValue == 2) |
| { |
| |
| |
| if (styleId == 0) return; |
| |
| |
| |
| |
| if (ti && |
| picker.pickTone(TScale(1.0 / subsampling) * pos + |
| TPointD(-0.5, -0.5)) == 255) |
| return; |
| } |
| |
| |
| TSelection *selection = |
| TTool::getApplication()->getCurrentSelection()->getSelection(); |
| if (selection) { |
| TStyleSelection *styleSelection = |
| dynamic_cast<TStyleSelection *>(selection); |
| if (styleSelection) styleSelection->selectNone(); |
| } |
| |
| getApplication()->setCurrentLevelStyleIndex(styleId); |
| } |