diff --git a/toonz/sources/common/timage_io/timage_io.cpp b/toonz/sources/common/timage_io/timage_io.cpp index 0c0973d..c9fb84c 100644 --- a/toonz/sources/common/timage_io/timage_io.cpp +++ b/toonz/sources/common/timage_io/timage_io.cpp @@ -813,9 +813,9 @@ TString TImageException::getMessage() const { TImageVersionException::TImageVersionException(const TFilePath &fp, int major, int minor) - : TException( - L"The file " + fp.getWideString() + - L" was generated by a newer version of Toonz and cannot be loaded.") + : TException(L"The file " + fp.getWideString() + + L" was generated by a newer version of OpenToonz and cannot " + L"be loaded.") , m_fp(fp) , m_major(major) , m_minor(minor) {} diff --git a/toonz/sources/image/pli/pli_io.cpp b/toonz/sources/image/pli/pli_io.cpp index 371aa8b..6a10954 100644 --- a/toonz/sources/image/pli/pli_io.cpp +++ b/toonz/sources/image/pli/pli_io.cpp @@ -29,7 +29,7 @@ typedef TVectorImage::IntersectionBranch IntersectionBranch; TNZ_LITTLE_ENDIAN undefined !! #endif - static const int c_majorVersionNumber = 71; + static const int c_majorVersionNumber = 120; static const int c_minorVersionNumber = 0; /*=====================================================================*/ @@ -317,6 +317,7 @@ class ParsedPliImp { public: UCHAR m_majorVersionNumber; UCHAR m_minorVersionNumber; + bool m_versionLocked = false; USHORT m_framesNumber; double m_thickRatio; double m_maxThickness; @@ -345,6 +346,7 @@ public: PliTag *readIntersectionDataTag(); PliTag *readOutlineOptionsTag(); PliTag *readPrecisionScaleTag(); + PliTag *readAutoCloseToleranceTag(); inline void readDinamicData(TUINT32 &val, TUINT32 &bufOffs); inline bool readDinamicData(TINT32 &val, TUINT32 &bufOffs); @@ -370,6 +372,7 @@ public: TUINT32 writeIntersectionDataTag(IntersectionDataTag *tag); TUINT32 writeOutlineOptionsTag(StrokeOutlineOptionsTag *tag); TUINT32 writePrecisionScaleTag(PrecisionScaleTag *tag); + TUINT32 writeAutoCloseToleranceTag(AutoCloseToleranceTag *tag); inline void writeDinamicData(TUINT32 val); inline void writeDinamicData(TINT32 val, bool isNegative); @@ -956,6 +959,9 @@ TagElem *ParsedPliImp::readTag() { case PliTag::PRECISION_SCALE_GOBJ: newTag = readPrecisionScaleTag(); break; + case PliTag::AUTOCLOSE_TOLERANCE_GOBJ: + newTag = readAutoCloseToleranceTag(); + break; case PliTag::END_CNTRL: return 0; } @@ -1410,6 +1416,17 @@ PliTag *ParsedPliImp::readPrecisionScaleTag() { /*=====================================================================*/ +PliTag *ParsedPliImp::readAutoCloseToleranceTag() { + TUINT32 bufOffs = 0; + + TINT32 d; + readDinamicData(d, bufOffs); + + return new AutoCloseToleranceTag(d); +} + +/*=====================================================================*/ + void ParsedPliImp::readFloatData(double &val, TUINT32 &bufOffs) { // UCHAR currDinamicTypeBytesNumSaved = m_currDinamicTypeBytesNum; // m_currDinamicTypeBytesNum = 2; @@ -1808,6 +1825,10 @@ void ParsedPliImp::writeTag(TagElem *elem) { case PliTag::PRECISION_SCALE_GOBJ: elem->m_offset = writePrecisionScaleTag((PrecisionScaleTag *)elem->m_tag); break; + case PliTag::AUTOCLOSE_TOLERANCE_GOBJ: + elem->m_offset = + writeAutoCloseToleranceTag((AutoCloseToleranceTag *)elem->m_tag); + break; default: assert(false); // m_error = UNKNOWN_TAG; @@ -2357,6 +2378,20 @@ TUINT32 ParsedPliImp::writePrecisionScaleTag(PrecisionScaleTag *tag) { /*=====================================================================*/ +TUINT32 ParsedPliImp::writeAutoCloseToleranceTag(AutoCloseToleranceTag *tag) { + assert(m_oChan); + setDinamicTypeBytesNum(0, 10000); + + int tagLength = m_currDinamicTypeBytesNum; + int offset = + (int)writeTagHeader((UCHAR)PliTag::AUTOCLOSE_TOLERANCE_GOBJ, tagLength); + + writeDinamicData((TINT32)tag->m_autoCloseTolerance); + return offset; +} + +/*=====================================================================*/ + TUINT32 ParsedPliImp::writeGeometricTransformationTag( GeometricTransformationTag *tag) { assert(m_oChan); @@ -2599,6 +2634,15 @@ void ParsedPli::getVersion(UINT &majorVersionNumber, /*=====================================================================*/ +void ParsedPli::setVersion(UINT majorVersionNumber, UINT minorVersionNumber) { + if (imp->m_versionLocked) return; + if (majorVersionNumber >= 120) imp->m_versionLocked = true; + imp->m_majorVersionNumber = majorVersionNumber; + imp->m_minorVersionNumber = minorVersionNumber; +} + +/*=====================================================================*/ + bool ParsedPli::writePli(const TFilePath &filename) { return imp->writePli(filename); } @@ -2667,4 +2711,12 @@ double ParsedPli::getAutocloseTolerance() const { /*=====================================================================*/ +/*=====================================================================*/ + +void ParsedPli::setAutocloseTolerance(int tolerance) { + imp->m_autocloseTolerance = tolerance; +} + +/*=====================================================================*/ + int &ParsedPli::precisionScale() { return imp->m_precisionScale; } diff --git a/toonz/sources/image/pli/pli_io.h b/toonz/sources/image/pli/pli_io.h index d57702b..48c96bd 100644 --- a/toonz/sources/image/pli/pli_io.h +++ b/toonz/sources/image/pli/pli_io.h @@ -79,6 +79,7 @@ public: THICK_QUADRATIC_LOOP_GOBJ, OUTLINE_OPTIONS_GOBJ, PRECISION_SCALE_GOBJ, + AUTOCLOSE_TOLERANCE_GOBJ, // ... HOW_MANY_TAG_TYPES }; @@ -429,6 +430,16 @@ public: //===================================================================== +class AutoCloseToleranceTag final : public PliObjectTag { +public: + int m_autoCloseTolerance; + + AutoCloseToleranceTag(); + AutoCloseToleranceTag(int tolerance); +}; + +//===================================================================== + /*! The class which will store the parsed info in reading. (reading is realized by means of the constructor) @@ -458,7 +469,7 @@ public: void setCreator(const QString &creator); void getVersion(UINT &majorVersionNumber, UINT &minorVersionNumber) const; - + void setVersion(UINT majorVersionNumber, UINT minorVersionNumber); bool addTag(PliTag *tag, bool addFront = false); void loadInfo(bool readPalette, TPalette *&palette, @@ -471,6 +482,7 @@ public: double getMaxThickness() const; void setMaxThickness(double maxThickness); double getAutocloseTolerance() const; + void setAutocloseTolerance(int tolerance); int &precisionScale(); // aggiuti questi 2 membri per salvare la paletta globale diff --git a/toonz/sources/image/pli/tags.cpp b/toonz/sources/image/pli/tags.cpp index 58fa3c6..b36a0e7 100644 --- a/toonz/sources/image/pli/tags.cpp +++ b/toonz/sources/image/pli/tags.cpp @@ -322,6 +322,16 @@ PrecisionScaleTag::PrecisionScaleTag(int precisionScale) : PliObjectTag(PRECISION_SCALE_GOBJ), m_precisionScale(precisionScale) {} /*=====================================================================*/ + +AutoCloseToleranceTag::AutoCloseToleranceTag() + : PliObjectTag(AUTOCLOSE_TOLERANCE_GOBJ) {} + +/*=====================================================================*/ + +AutoCloseToleranceTag::AutoCloseToleranceTag(int tolerance) + : PliObjectTag(AUTOCLOSE_TOLERANCE_GOBJ), m_autoCloseTolerance(tolerance) {} + +/*=====================================================================*/ /*=====================================================================*/ /*=====================================================================*/ diff --git a/toonz/sources/image/pli/tiio_pli.cpp b/toonz/sources/image/pli/tiio_pli.cpp index e2abe83..df0dcb5 100644 --- a/toonz/sources/image/pli/tiio_pli.cpp +++ b/toonz/sources/image/pli/tiio_pli.cpp @@ -400,6 +400,14 @@ TImageP TImageReaderPli::doLoad() { strokeData.m_options = ((StrokeOutlineOptionsTag *)imageTag->m_object[i])->m_options; break; + case PliTag::AUTOCLOSE_TOLERANCE_GOBJ: + // aggiunge curve quadratiche con spessore costante + AutoCloseToleranceTag *toleranceTag = + (AutoCloseToleranceTag *)imageTag->m_object[i]; + assert(toleranceTag->m_autoCloseTolerance >= 0); + outVectImage->setAutocloseTolerance( + ((double)toleranceTag->m_autoCloseTolerance) / 1000); + break; } // switch(groupTag->m_object[j]->m_type) } // for (i=0; im_numObjects; i++) @@ -500,13 +508,13 @@ GroupTag *makeGroup(TVectorImageP &vi, int &currStyleId, int &index, int currDepth); void TImageWriterPli::save(const TImageP &img) { - // alloco un'immagine + // Allocate an image TVectorImageP tempVecImg = img; int currStyleId = -1; if (!tempVecImg) throw TImageException(m_path, "No data to save"); - // controllo che il frame che sto per inserire non sia gia' presente - // in modo da non incrementare il numero di frame correnti + // Check that the frame about to insert is not already present + // So you do not increase the number of current frames ++m_lwp->m_frameNumber; std::unique_ptr v; @@ -519,6 +527,7 @@ void TImageWriterPli::save(const TImageP &img) { tempVecImg->getAutocloseTolerance())); m_lwp->m_pli->setCreator(m_lwp->m_creator); } + buildPalette(m_lwp->m_pli.get(), img); ParsedPli *pli = m_lwp->m_pli.get(); @@ -541,7 +550,21 @@ solo nel costruttore) PliTag *tag = new PrecisionScaleTag(precisionScale); tags.push_back((PliObjectTag *)tag); } - + // Store the auto close tolerance + double pliTolerance = m_lwp->m_pli->getAutocloseTolerance(); + // write the tag if the frame's tolerance has been changed or + // if the first frame's tolerance (and therefore the level's tolerance) + // has been changed. + if (!areAlmostEqual(tempVecImg->getAutocloseTolerance(), 1.15, 0.001) || + !areAlmostEqual(pliTolerance, 1.15, 0.001)) { + int tolerance = + (int)((roundf(tempVecImg->getAutocloseTolerance() * 100) / 100) * 1000); + PliTag *tag = new AutoCloseToleranceTag(tolerance); + tags.push_back((PliObjectTag *)tag); + pli->setVersion(120, 0); + } else { + pli->setVersion(71, 0); + } // recupero il numero di stroke dall'immagine int numStrokes = tempVecImg->getStrokeCount(); diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt index 75ca52f..496d4c1 100644 --- a/toonz/sources/tnztools/CMakeLists.txt +++ b/toonz/sources/tnztools/CMakeLists.txt @@ -3,6 +3,7 @@ set(MOC_HEADERS controlpointselection.h ../include/tools/imagegrouping.h edittoolgadgets.h + filltool.h skeletonsubtools.h tooloptionscontrols.h plastictool.h diff --git a/toonz/sources/tnztools/filltool.cpp b/toonz/sources/tnztools/filltool.cpp index 7f05403..d3347d8 100644 --- a/toonz/sources/tnztools/filltool.cpp +++ b/toonz/sources/tnztools/filltool.cpp @@ -1,14 +1,13 @@ +#include "filltool.h" -#include "tools/tool.h" -#include "tools/toolutils.h" -#include "toonz/txshlevelhandle.h" #include "toonz/tframehandle.h" #include "toonz/tcolumnhandle.h" #include "toonz/tpalettehandle.h" #include "toonz/preferences.h" #include "toonz/txsheethandle.h" #include "toonz/tobjecthandle.h" +#include "toonz/tscenehandle.h" #include "tools/toolhandle.h" #include "tools/toolutils.h" #include "toonz/tonionskinmaskhandle.h" @@ -22,7 +21,7 @@ #include "tproperty.h" #include "tenv.h" #include "tools/stylepicker.h" -#include "toonz/fill.h" + #include "toonz/stage2.h" #include "tstroke.h" #include "drawutil.h" @@ -31,13 +30,13 @@ #include "tregion.h" #include "tgl.h" #include "trop.h" -#include "toonz/txshsimplelevel.h" + #include "toonz/onionskinmask.h" #include "toonz/ttileset.h" #include "toonz/ttilesaver.h" #include "toonz/toonzimageutils.h" #include "toonz/levelproperties.h" -#include "toonz/strokegenerator.h" + #include "toonz/txshcell.h" #include "toonzqt/imageutils.h" #include "autofill.h" @@ -51,9 +50,9 @@ using namespace ToolUtils; -#define LINES L"Lines" -#define AREAS L"Areas" -#define ALL L"Lines & Areas" +//#define LINES L"Lines" +//#define AREAS L"Areas" +//#define ALL L"Lines & Areas" #define NORMALFILL L"Normal" #define RECTFILL L"Rectangular" @@ -254,6 +253,96 @@ public: }; //============================================================================= +// VectorGapSizeChangeUndo +//----------------------------------------------------------------------------- + +class VectorGapSizeChangeUndo final : public TToolUndo { + double m_oldGapSize; + double m_newGapSize; + int m_row; + int m_column; + TVectorImageP m_vi; + std::vector m_oldFillInformation; + +public: + VectorGapSizeChangeUndo(double oldGapSize, double newGapSize, + TXshSimpleLevel *sl, const TFrameId &fid, + TVectorImageP vi, + std::vector oldFillInformation) + : TToolUndo(sl, fid) + , m_oldGapSize(oldGapSize) + , m_newGapSize(newGapSize) + , m_oldFillInformation(oldFillInformation) + , m_vi(vi) { + TTool::Application *app = TTool::getApplication(); + if (app) { + m_row = app->getCurrentFrame()->getFrame(); + m_column = app->getCurrentColumn()->getColumnIndex(); + } + } + + void undo() const override { + TTool::Application *app = TTool::getApplication(); + if (!app || !m_level) return; + app->getCurrentLevel()->setLevel(m_level.getPointer()); + if (app->getCurrentFrame()->isEditingScene()) { + app->getCurrentFrame()->setFrame(m_row); + app->getCurrentColumn()->setColumnIndex(m_column); + } else + app->getCurrentFrame()->setFid(m_frameId); + + m_vi->setAutocloseTolerance(m_oldGapSize); + int count = m_vi->getStrokeCount(); + std::vector v(count); + int i; + for (i = 0; i < (int)count; i++) v[i] = i; + m_vi->notifyChangedStrokes(v, std::vector(), false); + if (m_vi->isComputedRegionAlmostOnce()) m_vi->findRegions(); + if (m_oldFillInformation.size()) { + for (UINT j = 0; j < m_oldFillInformation.size(); j++) { + TRegion *reg = m_vi->getRegion(m_oldFillInformation[j].m_regionId); + if (reg) reg->setStyle(m_oldFillInformation[j].m_styleId); + } + } + app->getCurrentXsheet()->notifyXsheetChanged(); + app->getCurrentTool()->notifyToolChanged(); + notifyImageChanged(); + } + + void redo() const override { + TTool::Application *app = TTool::getApplication(); + if (!app || !m_level) return; + + app->getCurrentLevel()->setLevel(m_level.getPointer()); + TVectorImageP img = m_level->getFrame(m_frameId, true); + if (app->getCurrentFrame()->isEditingScene()) { + app->getCurrentFrame()->setFrame(m_row); + app->getCurrentColumn()->setColumnIndex(m_column); + } else + app->getCurrentFrame()->setFid(m_frameId); + + m_vi->setAutocloseTolerance(m_newGapSize); + int count = m_vi->getStrokeCount(); + std::vector v(count); + int i; + for (i = 0; i < (int)count; i++) v[i] = i; + m_vi->notifyChangedStrokes(v, std::vector(), false); + app->getCurrentXsheet()->notifyXsheetChanged(); + app->getCurrentTool()->notifyToolChanged(); + notifyImageChanged(); + } + + void onAdd() override {} + + int getSize() const override { return sizeof(*this); } + + QString getToolName() override { + return QString("Fill Tool: Set Gap Size ") + QString::number(m_newGapSize); + } + int getHistoryType() override { return HistoryType::FillTool; } +}; + +//============================================================================= // RasterFillUndo //----------------------------------------------------------------------------- @@ -1113,190 +1202,238 @@ void drawPolyline(const std::vector &points) { //----------------------------------------------------------------------------- -class AreaFillTool { -public: - enum Type { RECT, FREEHAND, POLYLINE }; +AreaFillTool::AreaFillTool(TTool *parent) + : m_frameRange(false) + , m_onlyUnfilled(false) + , m_selecting(false) + , m_selectingRect(TRectD()) + , m_firstRect(TRectD()) + , m_firstFrameSelected(false) + , m_level(0) + , m_parent(parent) + , m_colorType(AREAS) + , m_currCell(-1, -1) + , m_type(RECT) + , m_isPath(false) + , m_enabled(false) + , m_active(false) + , m_firstStroke(0) + , m_thick(0.5) + , m_mousePosition() + , m_onion(false) + , m_isLeftButtonPressed(false) + , m_autopaintLines(true) {} + +void AreaFillTool::draw() { + m_thick = m_parent->getPixelSize() / 2.0; + + TPixel color = TPixel32::Red; + if (m_type == RECT) { + if (m_frameRange && m_firstFrameSelected) + drawRect(m_firstRect, color, 0x3F33, true); + if (m_selecting || (m_frameRange && !m_firstFrameSelected)) + drawRect(m_selectingRect, color, 0xFFFF, true); + } else if ((m_type == FREEHAND || m_type == POLYLINE) && m_frameRange) { + tglColor(color); + if (m_firstStroke) drawStrokeCenterline(*m_firstStroke, 1); + } + + if (m_type == POLYLINE && !m_polyline.empty()) { + glPushMatrix(); + tglColor(TPixel::Red); + tglDrawCircle(m_polyline[0], 2); + glBegin(GL_LINE_STRIP); + for (UINT i = 0; i < m_polyline.size(); i++) tglVertex(m_polyline[i]); + tglVertex(m_mousePosition); + glEnd(); + glPopMatrix(); + } else if (m_type == FREEHAND && !m_track.isEmpty()) { + tglColor(TPixel::Red); + glPushMatrix(); + m_track.drawAllFragments(); + glPopMatrix(); + } +} -private: - bool m_frameRange; - bool m_onlyUnfilled; - Type m_type; +void AreaFillTool::resetMulti() { + m_firstFrameSelected = false; + m_firstRect.empty(); + m_selectingRect.empty(); + TTool::Application *app = TTool::getApplication(); + TXshLevel *xl = app->getCurrentLevel()->getLevel(); + m_level = xl ? xl->getSimpleLevel() : 0; + m_firstFrameId = m_veryFirstFrameId = m_parent->getCurrentFid(); + if (m_firstStroke) { + delete m_firstStroke; + m_firstStroke = 0; + } +} - bool m_selecting; - TRectD m_selectingRect; +void AreaFillTool::leftButtonDown(const TPointD &pos, const TMouseEvent &, + TImage *img) { + TVectorImageP vi = TImageP(img); + TToonzImageP ti = TToonzImageP(img); - TRectD m_firstRect; - bool m_firstFrameSelected; - TXshSimpleLevelP m_level; - TFrameId m_firstFrameId, m_veryFirstFrameId; - TTool *m_parent; - std::wstring m_colorType; - std::pair m_currCell; - StrokeGenerator m_track; - std::vector m_polyline; - bool m_isPath; - bool m_active; - bool m_enabled; - double m_thick; - TPointD m_firstPos; - TStroke *m_firstStroke; - TPointD m_mousePosition; - bool m_onion; - bool m_isLeftButtonPressed; - bool m_autopaintLines; + if (!vi && !ti) { + m_selecting = false; + return; + } -public: - AreaFillTool(TTool *parent) - : m_frameRange(false) - , m_onlyUnfilled(false) - , m_selecting(false) - , m_selectingRect(TRectD()) - , m_firstRect(TRectD()) - , m_firstFrameSelected(false) - , m_level(0) - , m_parent(parent) - , m_colorType(AREAS) - , m_currCell(-1, -1) - , m_type(RECT) - , m_isPath(false) - , m_enabled(false) - , m_active(false) - , m_firstStroke(0) - , m_thick(0.5) - , m_mousePosition() - , m_onion(false) - , m_isLeftButtonPressed(false) - , m_autopaintLines(true) {} + m_selecting = true; + if (m_type == RECT) { + m_selectingRect.x0 = pos.x; + m_selectingRect.y0 = pos.y; + m_selectingRect.x1 = pos.x + 1; + m_selectingRect.y1 = pos.y + 1; + } else if (m_type == FREEHAND || m_type == POLYLINE) { + int col = TTool::getApplication()->getCurrentColumn()->getColumnIndex(); + m_isPath = TTool::getApplication() + ->getCurrentObject() + ->isSpline(); // getApplication()->isEditingSpline(); + m_enabled = col >= 0 || m_isPath; + + if (!m_enabled) return; + m_active = true; + + m_track.clear(); + m_firstPos = pos; + double pixelSize2 = m_parent->getPixelSize() * m_parent->getPixelSize(); + m_track.add(TThickPoint(pos, m_thick), pixelSize2); + if (m_type == POLYLINE) { + if (m_polyline.empty() || m_polyline.back() != pos) + m_polyline.push_back(pos); + m_mousePosition = pos; + } else + m_track.add(TThickPoint(pos, m_thick), pixelSize2); - void draw() { - m_thick = m_parent->getPixelSize() / 2.0; - - TPixel color = TPixel32::Red; - if (m_type == RECT) { - if (m_frameRange && m_firstFrameSelected) - drawRect(m_firstRect, color, 0x3F33, true); - if (m_selecting || (m_frameRange && !m_firstFrameSelected)) - drawRect(m_selectingRect, color, 0xFFFF, true); - } else if ((m_type == FREEHAND || m_type == POLYLINE) && m_frameRange) { - tglColor(color); - if (m_firstStroke) drawStrokeCenterline(*m_firstStroke, 1); + if (m_type == POLYLINE) { + if (m_polyline.empty() || m_polyline.back() != pos) + m_polyline.push_back(pos); } + } + m_isLeftButtonPressed = true; +} - if (m_type == POLYLINE && !m_polyline.empty()) { - glPushMatrix(); - tglColor(TPixel::Red); - tglDrawCircle(m_polyline[0], 2); - glBegin(GL_LINE_STRIP); - for (UINT i = 0; i < m_polyline.size(); i++) tglVertex(m_polyline[i]); - tglVertex(m_mousePosition); - glEnd(); - glPopMatrix(); - } else if (m_type == FREEHAND && !m_track.isEmpty()) { - tglColor(TPixel::Red); - glPushMatrix(); - m_track.drawAllFragments(); - glPopMatrix(); - } +/*-- PolyLineFillを閉じる時に呼ばれる --*/ +void AreaFillTool::leftButtonDoubleClick(const TPointD &pos, + const TMouseEvent &e) { + TStroke *stroke; + + TTool::Application *app = TTool::getApplication(); + if (!app) return; + + if (m_polyline.size() <= 1) { + resetMulti(); + return; } - void resetMulti() { - m_firstFrameSelected = false; - m_firstRect.empty(); - m_selectingRect.empty(); - TTool::Application *app = TTool::getApplication(); - TXshLevel *xl = app->getCurrentLevel()->getLevel(); - m_level = xl ? xl->getSimpleLevel() : 0; - m_firstFrameId = m_veryFirstFrameId = m_parent->getCurrentFid(); - if (m_firstStroke) { - delete m_firstStroke; - m_firstStroke = 0; + if (m_polyline.back() != pos) m_polyline.push_back(pos); + if (m_polyline.back() != m_polyline.front()) + m_polyline.push_back(m_polyline.front()); + std::vector strokePoints; + for (UINT i = 0; i < m_polyline.size() - 1; i++) { + strokePoints.push_back(TThickPoint(m_polyline[i], 1)); + strokePoints.push_back( + TThickPoint(0.5 * (m_polyline[i] + m_polyline[i + 1]), 1)); + } + strokePoints.push_back(TThickPoint(m_polyline.back(), 1)); + m_polyline.clear(); + stroke = new TStroke(strokePoints); + assert(stroke->getPoint(0) == stroke->getPoint(1)); + + // if (m_type==POLYLINE) + // m_polyline.push_back(pos); + // drawPolyline(m_polyline); + int styleIndex = app->getCurrentLevelStyleIndex(); + if (m_frameRange) // stroke multi + { + if (m_firstFrameSelected) { + MultiAreaFiller filler(m_firstStroke, stroke, m_onlyUnfilled, m_colorType, + styleIndex, m_autopaintLines); + filler.processSequence(m_level.getPointer(), m_firstFrameId, + m_parent->getCurrentFid()); + m_parent->invalidate(m_selectingRect.enlarge(2)); + if (e.isShiftPressed()) { + m_firstStroke = stroke; + m_firstFrameId = m_parent->getCurrentFid(); + } else { + if (app->getCurrentFrame()->isEditingScene()) { + app->getCurrentColumn()->setColumnIndex(m_currCell.first); + app->getCurrentFrame()->setFrame(m_currCell.second); + } else + app->getCurrentFrame()->setFid(m_veryFirstFrameId); + resetMulti(); + } + } else // primo frame + { + m_firstStroke = stroke; + // if (app->getCurrentFrame()->isEditingScene()) + m_currCell = + std::pair(app->getCurrentColumn()->getColumnIndex(), + app->getCurrentFrame()->getFrame()); } + } else { + if (m_onion) { + OnionSkinMask osMask = app->getCurrentOnionSkin()->getOnionSkinMask(); + doStrokeAutofill(m_parent->getImage(true), stroke, m_onlyUnfilled, osMask, + m_level.getPointer(), m_parent->getCurrentFid()); + } else + fillAreaWithUndo(m_parent->getImage(true), TRectD(), stroke, + m_onlyUnfilled, m_colorType, m_level.getPointer(), + m_parent->getCurrentFid(), styleIndex, m_autopaintLines); + TTool *t = app->getCurrentTool()->getTool(); + if (t) t->notifyImageChanged(); } +} - void leftButtonDown(const TPointD &pos, const TMouseEvent &, TImage *img) { - TVectorImageP vi = TImageP(img); - TToonzImageP ti = TToonzImageP(img); +void AreaFillTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { + if (m_type == RECT) { + m_selectingRect.x1 = pos.x; + m_selectingRect.y1 = pos.y; + m_parent->invalidate(); + } else if (m_type == FREEHAND) { + if (!m_enabled || !m_active) return; - if (!vi && !ti) { - m_selecting = false; - return; - } + double pixelSize2 = m_parent->getPixelSize() * m_parent->getPixelSize(); + m_track.add(TThickPoint(pos, m_thick), pixelSize2); + m_parent->invalidate(); + } +} - m_selecting = true; - if (m_type == RECT) { - m_selectingRect.x0 = pos.x; - m_selectingRect.y0 = pos.y; - m_selectingRect.x1 = pos.x + 1; - m_selectingRect.y1 = pos.y + 1; - } else if (m_type == FREEHAND || m_type == POLYLINE) { - int col = TTool::getApplication()->getCurrentColumn()->getColumnIndex(); - m_isPath = TTool::getApplication() - ->getCurrentObject() - ->isSpline(); // getApplication()->isEditingSpline(); - m_enabled = col >= 0 || m_isPath; - - if (!m_enabled) return; - m_active = true; - - m_track.clear(); - m_firstPos = pos; - double pixelSize2 = m_parent->getPixelSize() * m_parent->getPixelSize(); - m_track.add(TThickPoint(pos, m_thick), pixelSize2); - if (m_type == POLYLINE) { - if (m_polyline.empty() || m_polyline.back() != pos) - m_polyline.push_back(pos); - m_mousePosition = pos; - } else - m_track.add(TThickPoint(pos, m_thick), pixelSize2); +void AreaFillTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { + if (m_type != POLYLINE || m_polyline.empty()) return; + if (!m_enabled || !m_active) return; + m_mousePosition = pos; + m_parent->invalidate(); +} - if (m_type == POLYLINE) { - if (m_polyline.empty() || m_polyline.back() != pos) - m_polyline.push_back(pos); - } - } - m_isLeftButtonPressed = true; - } +void AreaFillTool::leftButtonUp(const TPointD &pos, const TMouseEvent &e) { + if (!m_isLeftButtonPressed) return; + m_isLeftButtonPressed = false; - /*-- PolyLineFillを閉じる時に呼ばれる --*/ - void leftButtonDoubleClick(const TPointD &pos, const TMouseEvent &e) { - TStroke *stroke; + TTool::Application *app = TTool::getApplication(); + if (!app) return; - TTool::Application *app = TTool::getApplication(); - if (!app) return; + TXshLevel *xl = app->getCurrentLevel()->getLevel(); + m_level = xl ? xl->getSimpleLevel() : 0; - if (m_polyline.size() <= 1) { - resetMulti(); - return; - } + int styleIndex = app->getCurrentLevelStyleIndex(); + m_selecting = false; + if (m_type == RECT) { + if (m_selectingRect.x0 > m_selectingRect.x1) + tswap(m_selectingRect.x0, m_selectingRect.x1); + if (m_selectingRect.y0 > m_selectingRect.y1) + tswap(m_selectingRect.y0, m_selectingRect.y1); - if (m_polyline.back() != pos) m_polyline.push_back(pos); - if (m_polyline.back() != m_polyline.front()) - m_polyline.push_back(m_polyline.front()); - std::vector strokePoints; - for (UINT i = 0; i < m_polyline.size() - 1; i++) { - strokePoints.push_back(TThickPoint(m_polyline[i], 1)); - strokePoints.push_back( - TThickPoint(0.5 * (m_polyline[i] + m_polyline[i + 1]), 1)); - } - strokePoints.push_back(TThickPoint(m_polyline.back(), 1)); - m_polyline.clear(); - stroke = new TStroke(strokePoints); - assert(stroke->getPoint(0) == stroke->getPoint(1)); - - // if (m_type==POLYLINE) - // m_polyline.push_back(pos); - // drawPolyline(m_polyline); - int styleIndex = app->getCurrentLevelStyleIndex(); - if (m_frameRange) // stroke multi - { + if (m_frameRange) { if (m_firstFrameSelected) { - MultiAreaFiller filler(m_firstStroke, stroke, m_onlyUnfilled, + MultiAreaFiller filler(m_firstRect, m_selectingRect, m_onlyUnfilled, m_colorType, styleIndex, m_autopaintLines); filler.processSequence(m_level.getPointer(), m_firstFrameId, m_parent->getCurrentFid()); m_parent->invalidate(m_selectingRect.enlarge(2)); if (e.isShiftPressed()) { - m_firstStroke = stroke; + m_firstRect = m_selectingRect; m_firstFrameId = m_parent->getCurrentFid(); } else { if (app->getCurrentFrame()->isEditingScene()) { @@ -1306,9 +1443,7 @@ public: app->getCurrentFrame()->setFid(m_veryFirstFrameId); resetMulti(); } - } else // primo frame - { - m_firstStroke = stroke; + } else { // if (app->getCurrentFrame()->isEditingScene()) m_currCell = std::pair(app->getCurrentColumn()->getColumnIndex(), @@ -1317,222 +1452,139 @@ public: } else { if (m_onion) { OnionSkinMask osMask = app->getCurrentOnionSkin()->getOnionSkinMask(); - doStrokeAutofill(m_parent->getImage(true), stroke, m_onlyUnfilled, - osMask, m_level.getPointer(), - m_parent->getCurrentFid()); + doRectAutofill(m_parent->getImage(true), m_selectingRect, + m_onlyUnfilled, osMask, m_level.getPointer(), + m_parent->getCurrentFid()); } else - fillAreaWithUndo(m_parent->getImage(true), TRectD(), stroke, + fillAreaWithUndo(m_parent->getImage(true), m_selectingRect, 0, m_onlyUnfilled, m_colorType, m_level.getPointer(), m_parent->getCurrentFid(), styleIndex, m_autopaintLines); + m_parent->invalidate(); + m_selectingRect.empty(); TTool *t = app->getCurrentTool()->getTool(); if (t) t->notifyImageChanged(); } - } - - void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { - if (m_type == RECT) { - m_selectingRect.x1 = pos.x; - m_selectingRect.y1 = pos.y; - m_parent->invalidate(); - } else if (m_type == FREEHAND) { - if (!m_enabled || !m_active) return; - -#if defined(MACOSX) -// m_parent->m_viewer->enableRedraw(false); -#endif - double pixelSize2 = m_parent->getPixelSize() * m_parent->getPixelSize(); - m_track.add(TThickPoint(pos, m_thick), pixelSize2); - m_parent->invalidate(); - } - } - - void mouseMove(const TPointD &pos, const TMouseEvent &e) { - if (m_type != POLYLINE || m_polyline.empty()) return; - if (!m_enabled || !m_active) return; - m_mousePosition = pos; - m_parent->invalidate(); - } - - void leftButtonUp(const TPointD &pos, const TMouseEvent &e) { - if (!m_isLeftButtonPressed) return; - m_isLeftButtonPressed = false; - - TTool::Application *app = TTool::getApplication(); - if (!app) return; - - TXshLevel *xl = app->getCurrentLevel()->getLevel(); - m_level = xl ? xl->getSimpleLevel() : 0; - - int styleIndex = app->getCurrentLevelStyleIndex(); - m_selecting = false; - if (m_type == RECT) { - if (m_selectingRect.x0 > m_selectingRect.x1) - tswap(m_selectingRect.x0, m_selectingRect.x1); - if (m_selectingRect.y0 > m_selectingRect.y1) - tswap(m_selectingRect.y0, m_selectingRect.y1); - - if (m_frameRange) { - if (m_firstFrameSelected) { - MultiAreaFiller filler(m_firstRect, m_selectingRect, m_onlyUnfilled, - m_colorType, styleIndex, m_autopaintLines); - filler.processSequence(m_level.getPointer(), m_firstFrameId, - m_parent->getCurrentFid()); - m_parent->invalidate(m_selectingRect.enlarge(2)); - if (e.isShiftPressed()) { - m_firstRect = m_selectingRect; - m_firstFrameId = m_parent->getCurrentFid(); - } else { - if (app->getCurrentFrame()->isEditingScene()) { - app->getCurrentColumn()->setColumnIndex(m_currCell.first); - app->getCurrentFrame()->setFrame(m_currCell.second); - } else - app->getCurrentFrame()->setFid(m_veryFirstFrameId); - resetMulti(); - } - } else { - // if (app->getCurrentFrame()->isEditingScene()) - m_currCell = - std::pair(app->getCurrentColumn()->getColumnIndex(), - app->getCurrentFrame()->getFrame()); - } - } else { - if (m_onion) { - OnionSkinMask osMask = app->getCurrentOnionSkin()->getOnionSkinMask(); - doRectAutofill(m_parent->getImage(true), m_selectingRect, - m_onlyUnfilled, osMask, m_level.getPointer(), - m_parent->getCurrentFid()); - } else - fillAreaWithUndo(m_parent->getImage(true), m_selectingRect, 0, - m_onlyUnfilled, m_colorType, m_level.getPointer(), - m_parent->getCurrentFid(), styleIndex, - m_autopaintLines); - m_parent->invalidate(); - m_selectingRect.empty(); - TTool *t = app->getCurrentTool()->getTool(); - if (t) t->notifyImageChanged(); - } - } else if (m_type == FREEHAND) { + } else if (m_type == FREEHAND) { #if defined(MACOSX) // m_parent->m_viewer->enableRedraw(true); #endif - bool isValid = m_enabled && m_active; - m_enabled = m_active = false; - if (!isValid || m_track.isEmpty()) return; - double pixelSize2 = m_parent->getPixelSize() * m_parent->getPixelSize(); - m_track.add(TThickPoint(m_firstPos, m_thick), pixelSize2); - m_track.filterPoints(); - double error = (m_isPath ? 20.0 : 30.0 / 11) * sqrt(pixelSize2); - TStroke *stroke = m_track.makeStroke(error); + bool isValid = m_enabled && m_active; + m_enabled = m_active = false; + if (!isValid || m_track.isEmpty()) return; + double pixelSize2 = m_parent->getPixelSize() * m_parent->getPixelSize(); + m_track.add(TThickPoint(m_firstPos, m_thick), pixelSize2); + m_track.filterPoints(); + double error = (m_isPath ? 20.0 : 30.0 / 11) * sqrt(pixelSize2); + TStroke *stroke = m_track.makeStroke(error); - stroke->setStyle(1); - m_track.clear(); + stroke->setStyle(1); + m_track.clear(); - if (m_frameRange) // stroke multi - { - if (m_firstFrameSelected) { - MultiAreaFiller filler(m_firstStroke, stroke, m_onlyUnfilled, - m_colorType, styleIndex, m_autopaintLines); - filler.processSequence(m_level.getPointer(), m_firstFrameId, - m_parent->getCurrentFid()); - m_parent->invalidate(m_selectingRect.enlarge(2)); - if (e.isShiftPressed()) { - m_firstStroke = stroke; - m_firstFrameId = m_parent->getCurrentFid(); - } else { - if (app->getCurrentFrame()->isEditingScene()) { - app->getCurrentColumn()->setColumnIndex(m_currCell.first); - app->getCurrentFrame()->setFrame(m_currCell.second); - } else - app->getCurrentFrame()->setFid(m_veryFirstFrameId); - resetMulti(); - } - } else // primo frame - { - m_firstStroke = stroke; - // if (app->getCurrentFrame()->isEditingScene()) - m_currCell = - std::pair(app->getCurrentColumn()->getColumnIndex(), - app->getCurrentFrame()->getFrame()); + if (m_frameRange) // stroke multi + { + if (m_firstFrameSelected) { + MultiAreaFiller filler(m_firstStroke, stroke, m_onlyUnfilled, + m_colorType, styleIndex, m_autopaintLines); + filler.processSequence(m_level.getPointer(), m_firstFrameId, + m_parent->getCurrentFid()); + m_parent->invalidate(m_selectingRect.enlarge(2)); + if (e.isShiftPressed()) { + m_firstStroke = stroke; + m_firstFrameId = m_parent->getCurrentFid(); + } else { + if (app->getCurrentFrame()->isEditingScene()) { + app->getCurrentColumn()->setColumnIndex(m_currCell.first); + app->getCurrentFrame()->setFrame(m_currCell.second); + } else + app->getCurrentFrame()->setFid(m_veryFirstFrameId); + resetMulti(); } - - } else // stroke non multi + } else // primo frame { - if (!m_parent->getImage(true)) return; - if (m_onion) { - OnionSkinMask osMask = app->getCurrentOnionSkin()->getOnionSkinMask(); - doStrokeAutofill(m_parent->getImage(true), stroke, m_onlyUnfilled, - osMask, m_level.getPointer(), - m_parent->getCurrentFid()); - } else - fillAreaWithUndo( - m_parent->getImage(true), TRectD(), stroke /*, imageLocation*/, - m_onlyUnfilled, m_colorType, m_level.getPointer(), - m_parent->getCurrentFid(), styleIndex, m_autopaintLines); - delete stroke; - TTool *t = app->getCurrentTool()->getTool(); - if (t) t->notifyImageChanged(); - m_parent->invalidate(); + m_firstStroke = stroke; + // if (app->getCurrentFrame()->isEditingScene()) + m_currCell = + std::pair(app->getCurrentColumn()->getColumnIndex(), + app->getCurrentFrame()->getFrame()); } + + } else // stroke non multi + { + if (!m_parent->getImage(true)) return; + if (m_onion) { + OnionSkinMask osMask = app->getCurrentOnionSkin()->getOnionSkinMask(); + doStrokeAutofill(m_parent->getImage(true), stroke, m_onlyUnfilled, + osMask, m_level.getPointer(), + m_parent->getCurrentFid()); + } else + fillAreaWithUndo( + m_parent->getImage(true), TRectD(), stroke /*, imageLocation*/, + m_onlyUnfilled, m_colorType, m_level.getPointer(), + m_parent->getCurrentFid(), styleIndex, m_autopaintLines); + delete stroke; + TTool *t = app->getCurrentTool()->getTool(); + if (t) t->notifyImageChanged(); + m_parent->invalidate(); } } +} - void onImageChanged() { - if (!m_frameRange) return; - TTool::Application *app = TTool::getApplication(); - if (!app) return; - TXshLevel *xshl = app->getCurrentLevel()->getLevel(); - - if (!xshl || m_level.getPointer() != xshl || - (m_selectingRect.isEmpty() && !m_firstStroke)) - resetMulti(); - else if (m_firstFrameId == m_parent->getCurrentFid()) - m_firstFrameSelected = false; // nel caso sono passato allo stato 1 e - // torno all'immagine iniziale, torno allo - // stato iniziale - else { // cambio stato. - m_firstFrameSelected = true; - if (m_type != FREEHAND && m_type != POLYLINE) { - assert(!m_selectingRect.isEmpty()); - m_firstRect = m_selectingRect; - } +void AreaFillTool::onImageChanged() { + if (!m_frameRange) return; + TTool::Application *app = TTool::getApplication(); + if (!app) return; + TXshLevel *xshl = app->getCurrentLevel()->getLevel(); + + if (!xshl || m_level.getPointer() != xshl || + (m_selectingRect.isEmpty() && !m_firstStroke)) + resetMulti(); + else if (m_firstFrameId == m_parent->getCurrentFid()) + m_firstFrameSelected = false; // nel caso sono passato allo stato 1 e + // torno all'immagine iniziale, torno allo + // stato iniziale + else { // cambio stato. + m_firstFrameSelected = true; + if (m_type != FREEHAND && m_type != POLYLINE) { + assert(!m_selectingRect.isEmpty()); + m_firstRect = m_selectingRect; } } +} - /*--Normal以外のTypeが選択された場合に呼ばれる--*/ - bool onPropertyChanged(bool multi, bool onlyUnfilled, bool onion, Type type, - std::wstring colorType, bool autopaintLines) { - m_frameRange = multi; - m_onlyUnfilled = onlyUnfilled; - m_colorType = colorType; - m_type = type; - m_onion = onion; - m_autopaintLines = autopaintLines; +/*--Normal以外のTypeが選択された場合に呼ばれる--*/ +bool AreaFillTool::onPropertyChanged(bool multi, bool onlyUnfilled, bool onion, + Type type, std::wstring colorType, + bool autopaintLines) { + m_frameRange = multi; + m_onlyUnfilled = onlyUnfilled; + m_colorType = colorType; + m_type = type; + m_onion = onion; + m_autopaintLines = autopaintLines; - if (m_frameRange) resetMulti(); + if (m_frameRange) resetMulti(); - /*--動作中にプロパティが変わったら、現在の動作を無効にする--*/ - if (m_isLeftButtonPressed) m_isLeftButtonPressed = false; + /*--動作中にプロパティが変わったら、現在の動作を無効にする--*/ + if (m_isLeftButtonPressed) m_isLeftButtonPressed = false; - if (m_type == POLYLINE && !m_polyline.empty()) m_polyline.clear(); + if (m_type == POLYLINE && !m_polyline.empty()) m_polyline.clear(); - return true; - } + return true; +} - void onActivate() { - // getApplication()->editImage(); +void AreaFillTool::onActivate() { + // getApplication()->editImage(); - if (m_frameRange) resetMulti(); + if (m_frameRange) resetMulti(); - if (TVectorImageP vi = TImageP(m_parent->getImage(false))) - vi->findRegions(); - } + if (TVectorImageP vi = TImageP(m_parent->getImage(false))) vi->findRegions(); +} - void onEnter() { - // getApplication()->editImage(); - } -}; +void AreaFillTool::onEnter() { + // getApplication()->editImage(); +} } // namespace @@ -1663,72 +1715,6 @@ public: // Fill Tool //----------------------------------------------------------------------------- -class FillTool final : public TTool { - Q_DECLARE_TR_FUNCTIONS(FillTool) - - bool m_firstTime; - TPointD m_firstPoint, m_clickPoint; - bool m_firstClick; - TXshSimpleLevelP m_level; - TFrameId m_firstFrameId, m_veryFirstFrameId; - int m_onionStyleId; - TEnumProperty m_colorType; // Line, Area - TEnumProperty m_fillType; // Rect, Polyline etc. - TBoolProperty m_onion; - TBoolProperty m_frameRange; - TBoolProperty m_selective; - TDoublePairProperty m_fillDepth; - TBoolProperty m_segment; - - AreaFillTool *m_rectFill; - NormalLineFillTool *m_normalLineFillTool; - - TPropertyGroup m_prop; - std::pair m_currCell; -#ifdef _DEBUG - std::vector m_rects; -#endif - - // For the raster fill tool, autopaint lines is optional and can be temporary - // disabled - TBoolProperty m_autopaintLines; - -public: - FillTool(int targetType); - - ToolType getToolType() const override { return TTool::LevelWriteTool; } - - void updateTranslation() override; - - TPropertyGroup *getProperties(int targetType) override { return &m_prop; } - - FillParameters getFillParameters() const; - - void leftButtonDown(const TPointD &pos, const TMouseEvent &e) override; - void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override; - void leftButtonUp(const TPointD &pos, const TMouseEvent &e) override; - void mouseMove(const TPointD &pos, const TMouseEvent &e) override; - void leftButtonDoubleClick(const TPointD &pos, const TMouseEvent &e) override; - void resetMulti(); - - bool onPropertyChanged(std::string propertyName) override; - void onImageChanged() override; - - void draw() override; - - int pick(const TImageP &image, const TPointD &pos); - int pickOnionColor(const TPointD &pos); - - void onEnter() override; - - void onActivate() override; - void onDeactivate() override; - - int getCursorId() const override; - - int getColorClass() const { return 2; } -}; - //----------------------------------------------------------------------------- FillTool::FillTool(int targetType) @@ -1742,6 +1728,7 @@ FillTool::FillTool(int targetType) , m_segment("Segment", false) , m_onionStyleId(0) , m_currCell(-1, -1) + , m_maxGapDistance("Maximum Gap", 0.01, 10.0, 1.15) , m_firstTime(true) , m_autopaintLines("Autopaint Lines", true) { m_rectFill = new AreaFillTool(this); @@ -1766,9 +1753,11 @@ FillTool::FillTool(int targetType) } m_prop.bind(m_onion); m_prop.bind(m_frameRange); - + if (targetType == TTool::VectorImage) { + m_prop.bind(m_maxGapDistance); + m_maxGapDistance.setId("MaxGapDistance"); + } if (targetType == TTool::ToonzImage) m_prop.bind(m_autopaintLines); - m_selective.setId("Selective"); m_onion.setId("OnionSkin"); m_frameRange.setId("FrameRange"); @@ -1811,6 +1800,7 @@ void FillTool::updateTranslation() { m_onion.setQStringName(tr("Onion Skin")); m_fillDepth.setQStringName(tr("Fill Depth")); m_segment.setQStringName(tr("Segment")); + m_maxGapDistance.setQStringName("Maximum Gap"); m_autopaintLines.setQStringName(tr("Autopaint Lines")); } @@ -2032,11 +2022,49 @@ bool FillTool::onPropertyChanged(std::string propertyName) { if (m_segment.getValue()) FillType = ::to_string(m_fillType.getValue()); FillSegment = (int)(m_segment.getValue()); } + // Autopaint else if (propertyName == m_autopaintLines.getName()) { rectPropChangedflag = true; } + else if (!m_frameSwitched && + (propertyName == m_maxGapDistance.getName() || + propertyName == m_maxGapDistance.getName() + "withUndo")) { + TXshLevel *xl = TTool::getApplication()->getCurrentLevel()->getLevel(); + m_level = xl ? xl->getSimpleLevel() : 0; + if (TVectorImageP vi = getImage(true)) { + if (m_changedGapOriginalValue == -1.0) { + ImageUtils::getFillingInformationInArea(vi, m_oldFillInformation, + vi->getBBox()); + m_changedGapOriginalValue = vi->getAutocloseTolerance(); + } + TFrameId fid = getCurrentFid(); + vi->setAutocloseTolerance(m_maxGapDistance.getValue()); + int count = vi->getStrokeCount(); + std::vector v(count); + int i; + for (i = 0; i < (int)count; i++) v[i] = i; + vi->notifyChangedStrokes(v, std::vector(), false); + + if (m_level) { + m_level->setDirtyFlag(true); + TTool::getApplication()->getCurrentLevel()->notifyLevelChange(); + if (propertyName == m_maxGapDistance.getName() + "withUndo" && + m_changedGapOriginalValue != -1.0) { + TUndoManager::manager()->add(new VectorGapSizeChangeUndo( + m_changedGapOriginalValue, m_maxGapDistance.getValue(), + m_level.getPointer(), fid, vi, m_oldFillInformation)); + m_changedGapOriginalValue = -1.0; + m_oldFillInformation.clear(); + TTool::Application *app = TTool::getApplication(); + app->getCurrentXsheet()->notifyXsheetChanged(); + notifyImageChanged(); + } + } + } + } + /*--- fillType, frameRange, selective, colorTypeが変わったとき ---*/ if (rectPropChangedflag && m_fillType.getValue() != NORMALFILL) { AreaFillTool::Type type; @@ -2070,11 +2098,33 @@ void FillTool::onImageChanged() { m_rectFill->onImageChanged(); return; } + if (TVectorImageP vi = getImage(true)) { + m_frameSwitched = true; + if (m_maxGapDistance.getValue() != vi->getAutocloseTolerance()) { + m_maxGapDistance.setValue(vi->getAutocloseTolerance()); + getApplication()->getCurrentTool()->notifyToolChanged(); + } + m_frameSwitched = false; + } if (!m_level) resetMulti(); } //----------------------------------------------------------------------------- +void FillTool::onFrameSwitched() { + m_frameSwitched = true; + if (TVectorImageP vi = getImage(true)) { + if (m_maxGapDistance.getValue() != vi->getAutocloseTolerance()) { + m_maxGapDistance.setValue(vi->getAutocloseTolerance()); + getApplication()->getCurrentTool()->notifyToolChanged(); + } + } + m_frameSwitched = false; + m_changedGapOriginalValue = -1.0; +} + +//----------------------------------------------------------------------------- + void FillTool::draw() { if (Preferences::instance()->getFillOnlySavebox()) { TToonzImageP ti = (TToonzImageP)getImage(false); @@ -2212,11 +2262,37 @@ void FillTool::onActivate() { TVectorImageP vi = TImageP(getImage(false)); if (!vi) return; vi->findRegions(); + if (m_targetType == TTool::VectorImage) { + if (m_level) { + TImageP img = getImage(true); + if (TVectorImageP vi = img) { + double tolerance = vi->getAutocloseTolerance(); + if (tolerance < 9.9) tolerance += 0.000001; + m_maxGapDistance.setValue(tolerance); + } + } + } + bool ret = true; + ret = ret && connect(TTool::m_application->getCurrentFrame(), + SIGNAL(frameSwitched()), this, SLOT(onFrameSwitched())); + ret = ret && connect(TTool::m_application->getCurrentScene(), + SIGNAL(sceneSwitched()), this, SLOT(onFrameSwitched())); + ret = ret && + connect(TTool::m_application->getCurrentColumn(), + SIGNAL(columnIndexSwitched()), this, SLOT(onFrameSwitched())); + assert(ret); } //----------------------------------------------------------------------------- -void FillTool::onDeactivate() {} +void FillTool::onDeactivate() { + disconnect(TTool::m_application->getCurrentFrame(), SIGNAL(frameSwitched()), + this, SLOT(onFrameSwitched())); + disconnect(TTool::m_application->getCurrentScene(), SIGNAL(sceneSwitched()), + this, SLOT(onFrameSwitched())); + disconnect(TTool::m_application->getCurrentColumn(), + SIGNAL(columnIndexSwitched()), this, SLOT(onColumnSwitched())); +} //----------------------------------------------------------------------------- diff --git a/toonz/sources/tnztools/filltool.h b/toonz/sources/tnztools/filltool.h new file mode 100644 index 0000000..30ec357 --- /dev/null +++ b/toonz/sources/tnztools/filltool.h @@ -0,0 +1,143 @@ +#pragma once + +#ifndef FILLTOOL_H +#define FILLTOOL_H + +// TnzCore includes +#include "tproperty.h" +#include "toonz/txshlevelhandle.h" +#include "toonz/txshsimplelevel.h" +#include "toonz/strokegenerator.h" +// TnzTools includes +#include "tools/tool.h" +#include "tools/toolutils.h" +#include "autofill.h" +#include "toonz/fill.h" + +#include + +#define LINES L"Lines" +#define AREAS L"Areas" +#define ALL L"Lines & Areas" + +class NormalLineFillTool; +namespace { +class AreaFillTool { +public: + enum Type { RECT, FREEHAND, POLYLINE }; + +private: + bool m_frameRange; + bool m_onlyUnfilled; + Type m_type; + + bool m_selecting; + TRectD m_selectingRect; + + TRectD m_firstRect; + bool m_firstFrameSelected; + TXshSimpleLevelP m_level; + TFrameId m_firstFrameId, m_veryFirstFrameId; + TTool *m_parent; + std::wstring m_colorType; + std::pair m_currCell; + StrokeGenerator m_track; + std::vector m_polyline; + bool m_isPath; + bool m_active; + bool m_enabled; + double m_thick; + TPointD m_firstPos; + TStroke *m_firstStroke; + TPointD m_mousePosition; + bool m_onion; + bool m_isLeftButtonPressed; + bool m_autopaintLines; + +public: + AreaFillTool(TTool *Parent); + void draw(); + void resetMulti(); + void leftButtonDown(const TPointD &pos, const TMouseEvent &, TImage *img); + void leftButtonDoubleClick(const TPointD &pos, const TMouseEvent &e); + void leftButtonDrag(const TPointD &pos, const TMouseEvent &e); + void mouseMove(const TPointD &pos, const TMouseEvent &e); + void leftButtonUp(const TPointD &pos, const TMouseEvent &e); + void onImageChanged(); + bool onPropertyChanged(bool multi, bool onlyUnfilled, bool onion, Type type, + std::wstring colorType, bool autopaintLines); + void onActivate(); + void onEnter(); +}; +} +class FillTool final : public QObject, public TTool { + // Q_DECLARE_TR_FUNCTIONS(FillTool) + Q_OBJECT + bool m_firstTime; + TPointD m_firstPoint, m_clickPoint; + bool m_firstClick; + bool m_frameSwitched = false; + double m_changedGapOriginalValue = -1.0; + TXshSimpleLevelP m_level; + TFrameId m_firstFrameId, m_veryFirstFrameId; + int m_onionStyleId; + TEnumProperty m_colorType; // Line, Area + TEnumProperty m_fillType; // Rect, Polyline etc. + TBoolProperty m_onion; + TBoolProperty m_frameRange; + TBoolProperty m_selective; + TDoublePairProperty m_fillDepth; + TBoolProperty m_segment; + TDoubleProperty m_maxGapDistance; + AreaFillTool *m_rectFill; + NormalLineFillTool *m_normalLineFillTool; + + TPropertyGroup m_prop; + std::pair m_currCell; + std::vector m_oldFillInformation; +#ifdef _DEBUG + std::vector m_rects; +#endif + + // For the raster fill tool, autopaint lines is optional and can be temporary + // disabled + TBoolProperty m_autopaintLines; + +public: + FillTool(int targetType); + + ToolType getToolType() const override { return TTool::LevelWriteTool; } + + void updateTranslation() override; + + TPropertyGroup *getProperties(int targetType) override { return &m_prop; } + + FillParameters getFillParameters() const; + + void leftButtonDown(const TPointD &pos, const TMouseEvent &e) override; + void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override; + void leftButtonUp(const TPointD &pos, const TMouseEvent &e) override; + void mouseMove(const TPointD &pos, const TMouseEvent &e) override; + void leftButtonDoubleClick(const TPointD &pos, const TMouseEvent &e) override; + void resetMulti(); + + bool onPropertyChanged(std::string propertyName) override; + void onImageChanged() override; + void draw() override; + + int pick(const TImageP &image, const TPointD &pos); + int pickOnionColor(const TPointD &pos); + + void onEnter() override; + + void onActivate() override; + void onDeactivate() override; + + int getCursorId() const override; + + int getColorClass() const { return 2; } +public slots: + void onFrameSwitched() override; +}; + +#endif // FILLTOOL_H diff --git a/toonz/sources/tnztools/tooloptionscontrols.cpp b/toonz/sources/tnztools/tooloptionscontrols.cpp index ea14e28..eab8b7e 100644 --- a/toonz/sources/tnztools/tooloptionscontrols.cpp +++ b/toonz/sources/tnztools/tooloptionscontrols.cpp @@ -52,8 +52,11 @@ ToolOptionControl::ToolOptionControl(TTool *tool, std::string propertyName, //----------------------------------------------------------------------------- -void ToolOptionControl::notifyTool() { - m_tool->onPropertyChanged(m_propertyName); +void ToolOptionControl::notifyTool(bool addToUndo) { + std::string tempPropertyName = m_propertyName; + if (addToUndo && m_propertyName == "Maximum Gap") + tempPropertyName = tempPropertyName + "withUndo"; + m_tool->onPropertyChanged(tempPropertyName); } //----------------------------------------------------------------------------- @@ -183,7 +186,7 @@ void ToolOptionSlider::updateStatus() { void ToolOptionSlider::onValueChanged(bool isDragging) { m_property->setValue(getValue()); - notifyTool(); + notifyTool(!isDragging); } //----------------------------------------------------------------------------- diff --git a/toonz/sources/tnztools/tooloptionscontrols.h b/toonz/sources/tnztools/tooloptionscontrols.h index f9595ac..657b294 100644 --- a/toonz/sources/tnztools/tooloptionscontrols.h +++ b/toonz/sources/tnztools/tooloptionscontrols.h @@ -72,7 +72,7 @@ public: const std::string &propertyName() const { return m_propertyName; } void onPropertyChanged() override { updateStatus(); } - void notifyTool(); + void notifyTool(bool addToUndo = false); // return true if the control is belonging to the visible viewer bool isInVisibleViewer(QWidget *widget);