From ebedef48b1ad8e21b9c88c4594803146ecd81580 Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Aug 26 2023 14:51:24 +0000 Subject: #assistants: tv: via imput manager --- diff --git a/toonz/sources/include/tools/modifiers/modifiersegmentation.h b/toonz/sources/include/tools/modifiers/modifiersegmentation.h index 48d8872..e706d5c 100644 --- a/toonz/sources/include/tools/modifiers/modifiersegmentation.h +++ b/toonz/sources/include/tools/modifiers/modifiersegmentation.h @@ -32,7 +32,7 @@ private: void addSegments(TTrack &track, const TTrackPoint &p0, const TTrackPoint &p1, int maxLevel); public: - TModifierSegmentation(const TPointD &step = TPointD(1.0, 1.0), int level = 10); + explicit TModifierSegmentation(const TPointD &step = TPointD(1.0, 1.0), int level = 10); void setStep(const TPointD &step); inline const TPointD& getStep() const { return m_step; } diff --git a/toonz/sources/include/tools/modifiers/modifiersimplify.h b/toonz/sources/include/tools/modifiers/modifiersimplify.h new file mode 100644 index 0000000..6f353b8 --- /dev/null +++ b/toonz/sources/include/tools/modifiers/modifiersimplify.h @@ -0,0 +1,43 @@ +#pragma once + +#ifndef MODIFIERSIMPLIFY_INCLUDED +#define MODIFIERSIMPLIFY_INCLUDED + +// TnzTools includes +#include + + +#undef DVAPI +#undef DVVAR +#ifdef TNZTOOLS_EXPORTS +#define DVAPI DV_EXPORT_API +#define DVVAR DV_EXPORT_VAR +#else +#define DVAPI DV_IMPORT_API +#define DVVAR DV_IMPORT_VAR +#endif + + +//=================================================================== + +//***************************************************************************************** +// TModifierSimplify definition +//***************************************************************************************** + +class DVAPI TModifierSimplify: public TInputModifier { +private: + double step; + +public: + explicit TModifierSimplify(double step = 1.0); + + void setStep(double step); + inline double getStep() const { return step; } + + void modifyTrack( + const TTrack &track, + TTrackList &outTracks ) override; +}; + + +#endif diff --git a/toonz/sources/include/tools/modifiers/modifiersmooth.h b/toonz/sources/include/tools/modifiers/modifiersmooth.h index 6968d38..193db53 100644 --- a/toonz/sources/include/tools/modifiers/modifiersmooth.h +++ b/toonz/sources/include/tools/modifiers/modifiersmooth.h @@ -39,7 +39,7 @@ private: int m_radius; public: - TModifierSmooth(int radius = 10); + explicit TModifierSmooth(int radius = 10); void setRadius(int radius); int getRadius() const { return m_radius; } diff --git a/toonz/sources/include/toonz/strokegenerator.h b/toonz/sources/include/toonz/strokegenerator.h index bc96dc7..1d253f0 100644 --- a/toonz/sources/include/toonz/strokegenerator.h +++ b/toonz/sources/include/toonz/strokegenerator.h @@ -39,6 +39,9 @@ class DVAPI StrokeGenerator { //! Ultimo punto del frammento visualizzato TPointD m_p0, /*! Ultimo punto del frammento visualizzato*/ m_p1; + + //! mark that stroke must be looped + bool m_loop; //! Visualizza i frammenti /*! @@ -66,10 +69,19 @@ public: di 4*pixelSize2 \param point TThickPoint da aggiungere al vettore - \param pixelSize2 Dimensione pixel + \param pixelSize2 Size of pixel, use 0 to guarantee that new point will be added + \returns true if point was actually added */ - void add(const TThickPoint &point, double pixelSize2); - + bool add(const TThickPoint &point, double pixelSize2); + + //! Remove last point (keep in mind that not each 'add' call produces new point) + void pop(); + + //! Mark/unmark track as looped + void setLoop(bool loop = true); + + inline bool getLoop() const { return m_loop; } + TPointD getFirstPoint(); // returns the first point //! Filtra i punti di m_points @@ -107,7 +119,7 @@ onlyLastPoint elementi di m_points \param onlyLastPoints Numero elementi sulla base dei quali creare la stroke */ - TStroke *makeStroke(double error, UINT onlyLastPoints = 0) const; + TStroke *makeStroke(double error, UINT onlyLastPoints = 0, bool useLoop = false) const; }; //=============================================================== diff --git a/toonz/sources/tnztools/CMakeLists.txt b/toonz/sources/tnztools/CMakeLists.txt index 2675853..2a3d441 100644 --- a/toonz/sources/tnztools/CMakeLists.txt +++ b/toonz/sources/tnztools/CMakeLists.txt @@ -54,6 +54,7 @@ set(HEADERS ../include/tools/modifiers/modifiertangents.h ../include/tools/modifiers/modifiertest.h ../include/tools/modifiers/modifiersegmentation.h + ../include/tools/modifiers/modifiersimplify.h ../include/tools/modifiers/modifiersmooth.h ../include/tools/assistants/guidelineline.h ../include/tools/assistants/guidelineellipse.h @@ -130,6 +131,7 @@ set(SOURCES modifiers/modifiertangents.cpp modifiers/modifiertest.cpp modifiers/modifiersegmentation.cpp + modifiers/modifiersimplify.cpp modifiers/modifiersmooth.cpp assistants/guidelineline.cpp assistants/guidelineellipse.cpp diff --git a/toonz/sources/tnztools/modifiers/modifiersimplify.cpp b/toonz/sources/tnztools/modifiers/modifiersimplify.cpp new file mode 100644 index 0000000..85b66b3 --- /dev/null +++ b/toonz/sources/tnztools/modifiers/modifiersimplify.cpp @@ -0,0 +1,78 @@ + + +#include +#include + + +//***************************************************************************************** +// TModifierSimplify implementation +//***************************************************************************************** + + +TModifierSimplify::TModifierSimplify(double step): + step() { setStep(step); } + + +void +TModifierSimplify::setStep(double step) + { this->step = std::max(0.0, step); } + + +void +TModifierSimplify::modifyTrack( + const TTrack &track, + TTrackList &outTracks ) +{ + if (!track.handler) { + track.handler = new TTrackHandler(track); + track.handler->tracks.push_back( + new TTrack( + new TTrackModifier(*track.handler) )); + } + + if (track.handler->tracks.empty()) + return; + + TTrack &subTrack = *track.handler->tracks.front(); + outTracks.push_back(track.handler->tracks.front()); + + if (!track.changed()) + return; + + // remove points + int start = track.size() - track.pointsAdded; + if (start < 0) start = 0; + int subStart = subTrack.floorIndex(subTrack.indexByOriginalIndex(start)); + if (subStart < 0) subStart = 0; + start = track.floorIndex(subTrack[subStart].originalIndex); + if (start < 0) start = 0; + subTrack.truncate(subStart); + + // add points + double step2 = step*step; + TTrackPoint p0 = subTrack.back(); + for(int i = start; i < track.size(); ++i) { + const TTrackPoint &p1 = track[i]; + if (!subTrack.empty() && tdistance2(p1.position, p0.position) < step2) { + if (p0.pressure < p1.pressure) p0.pressure = p1.pressure; + p0.tilt = p1.tilt; + p0.time = p1.time; + p0.final = p1.final; + subTrack.pop_back(); + subTrack.push_back(p0, false); + } else { + p0 = p1; + p0.originalIndex = i; + subTrack.push_back(p0, false); + } + } + + // fix points + if (track.fixedFinished()) + subTrack.fix_all(); + else + subTrack.fix_to( + subTrack.floorIndex( subTrack.indexByOriginalIndex(track.fixedSize()) )); + + track.resetChanges(); +} diff --git a/toonz/sources/tnztools/toonzvectorbrushtool.cpp b/toonz/sources/tnztools/toonzvectorbrushtool.cpp index 6198b6b..bb58efc 100644 --- a/toonz/sources/tnztools/toonzvectorbrushtool.cpp +++ b/toonz/sources/tnztools/toonzvectorbrushtool.cpp @@ -567,6 +567,22 @@ ToonzVectorBrushTool::ToonzVectorBrushTool(std::string name, int targetType) m_capStyle.setId("Cap"); m_joinStyle.setId("Join"); m_miterJoinLimit.setId("Miter"); + + m_inputmanager.setHandler(this); + m_modifierLine = new TModifierLine(); + m_modifierTangents = new TModifierTangents(); + m_modifierAssistants = new TModifierAssistants(); + m_modifierSegmentation = new TModifierSegmentation(); + m_modifierSmoothSegmentation = new TModifierSegmentation(TPointD(1, 1), 3); + for(int i = 0; i < 3; ++i) + m_modifierSmooth[i] = new TModifierSmooth(); + m_modifierSimplify = new TModifierSimplify(); +#ifndef NDEBUG + m_modifierTest = new TModifierTest(5, 40); +#endif + + m_inputmanager.addModifier( + TInputModifierP(m_modifierAssistants.getPointer())); } //------------------------------------------------------------------------------------------------------- @@ -649,46 +665,93 @@ void ToonzVectorBrushTool::onDeactivate() { //-------------------------------------------------------------------------------------------------- -bool ToonzVectorBrushTool::preLeftButtonDown() { - if (getViewer() && getViewer()->getGuidedStrokePickerMode()) return false; +void ToonzVectorBrushTool::inputMouseMove( + const TPointD &position, const TInputState &state ) +{ + struct Locals { + ToonzVectorBrushTool *m_this; - touchImage(); - if (m_isFrameCreated) { - // When the xsheet frame is selected, whole viewer will be updated from - // SceneViewer::onXsheetChanged() on adding a new frame. - // We need to take care of a case when the level frame is selected. - if (m_application->getCurrentFrame()->isEditingLevel()) invalidate(); - } - return true; -} + void setValue(TDoublePairProperty &prop, + const TDoublePairProperty::Value &value) { + prop.setValue(value); -//-------------------------------------------------------------------------------------------------- + m_this->onPropertyChanged(prop.getName()); + TTool::getApplication()->getCurrentTool()->notifyToolChanged(); + } -void ToonzVectorBrushTool::leftButtonDown(const TPointD &pos, - const TMouseEvent &e) -{ - m_active = false; + void addMinMax(TDoublePairProperty &prop, double min, double max) { + if (min == 0.0 && max == 0.0) return; + const TDoublePairProperty::Range &range = prop.getRange(); + + TDoublePairProperty::Value value = prop.getValue(); + value.first += min; + value.second += max; + if (value.first > value.second) value.first = value.second; + value.first = tcrop(value.first, range.first, range.second); + value.second = tcrop(value.second, range.first, range.second); + + setValue(prop, value); + } + + } locals = {this}; + + TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5); + TRectD invalidateRect(m_brushPos - halfThick, m_brushPos + halfThick); + + bool alt = state.isKeyPressed(TInputState::Key::alt); + bool shift = state.isKeyPressed(TInputState::Key::shift); + bool control = state.isKeyPressed(TInputState::Key::control); - TTool::Application *app = TTool::getApplication(); - if (!app) - return; + if ( alt && control && !shift + && Preferences::instance()->useCtrlAltToResizeBrushEnabled() ) + { + // Resize the brush if CTRL+ALT is pressed and the preference is enabled. + const TPointD &diff = position - m_mousePos; + double max = diff.x / 2; + double min = diff.y / 2; - if (getViewer() && getViewer()->getGuidedStrokePickerMode()) { - getViewer()->doPickGuideStroke(pos); - return; - } + locals.addMinMax(m_thickness, min, max); - m_isPath = app->getCurrentObject()->isSpline(); + double radius = m_thickness.getValue().second * 0.5; + halfThick = TPointD(radius, radius); + } else { + m_brushPos = m_mousePos = position; + } - // todo: gestire autoenable - if ( !m_isPath - && app->getCurrentColumn()->getColumnIndex() < 0 - && !app->getCurrentFrame()->isEditingLevel() ) - return; + invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick); + + if (m_minThick == 0 && m_maxThick == 0) { + m_minThick = m_thickness.getValue().first; + m_maxThick = m_thickness.getValue().second; + } + + invalidate(invalidateRect.enlarge(2)); +} + +//-------------------------------------------------------------------------------------------------- + +void ToonzVectorBrushTool::inputSetBusy(bool busy) { + if (m_active == busy) return; - if (m_isPath) { - m_currentColor = TPixel32::Red; - } else { + if (busy) { + + // begin painting ////////////////////////// + + TTool::Application *app = TTool::getApplication(); + if (!app) + return; + + m_isPath = app->getCurrentObject()->isSpline(); + if (m_isPath) { + m_currentColor = TPixel32::Red; + m_active = true; + return; + } + + // todo: gestire autoenable + if ( app->getCurrentColumn()->getColumnIndex() < 0 + && !app->getCurrentFrame()->isEditingLevel() ) + return; if (!getImage(true) || !touchImage()) return; if (!app->getCurrentLevel()->getLevel()) @@ -707,159 +770,32 @@ void ToonzVectorBrushTool::leftButtonDown(const TPointD &pos, m_styleId = 1; m_currentColor = TPixel32::Black; } + + m_active = true; + + return; // painting has begun } - m_active = true; - - // assert(0<=m_styleId && m_styleId<2); - m_track.clear(); - double thickness = computeThickness(e.m_pressure, m_thickness, m_pressure.getValue(), m_isPath); - - /*--- ストロークの最初にMaxサイズの円が描かれてしまう不具合を防止する ---*/ - if (m_pressure.getValue() && e.m_pressure == 1.0) - thickness = m_thickness.getValue().first * 0.5; - m_currThickness = thickness; - m_smoothStroke.beginStroke(m_smooth.getValue()); - - TPointD p = m_foundFirstSnap ? m_firstSnapPoint : pos; - addTrackPoint( TThickPoint(p, thickness), m_pixelSize*m_pixelSize ); - TRectD invalidateRect = m_track.getLastModifiedRegion(); - invalidate(invalidateRect.enlarge(2)); - - // updating m_brushPos is needed to refresh viewer properly - m_brushPos = m_mousePos = pos; -} - -//------------------------------------------------------------------------------------------------------------- - -void ToonzVectorBrushTool::leftButtonDrag(const TPointD &pos, - const TMouseEvent &e) { - if (!m_active) { - m_brushPos = m_mousePos = pos; - return; - } - - m_lastDragPos = pos; - m_lastDragEvent = e; - - double thickness = computeThickness(e.m_pressure, m_thickness, m_pressure.getValue(), m_isPath); - - TRectD invalidateRect; - TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5); - TPointD snapThick(6.0 * m_pixelSize, 6.0 * m_pixelSize); - - // In order to clear the previous brush tip - invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick); - - // In order to clear the previous snap indicator - if (m_foundLastSnap) - invalidateRect += - TRectD(m_lastSnapPoint - snapThick, m_lastSnapPoint + snapThick); - - m_currThickness = thickness; - - m_mousePos = pos; - m_lastSnapPoint = pos; - m_foundLastSnap = false; - m_foundFirstSnap = false; - m_snapSelf = false; - m_altPressed = e.isAltPressed() && !e.isCtrlPressed(); - - checkStrokeSnapping(false, m_altPressed); - checkGuideSnapping(false, m_altPressed); - m_brushPos = m_lastSnapPoint; - - if (m_foundLastSnap) - invalidateRect += - TRectD(m_lastSnapPoint - snapThick, m_lastSnapPoint + snapThick); - - if (e.isShiftPressed() && e.isCtrlPressed()) { - TPointD m_firstPoint = m_track.getFirstPoint(); - - double denominator = m_lastSnapPoint.x - m_firstPoint.x; - if (denominator == 0) denominator = 0.001; - double slope = ((m_brushPos.y - m_firstPoint.y) / denominator); - double angle = std::atan(slope) * (180 / 3.14159); - if (abs(angle) > 67.5) - m_lastSnapPoint.x = m_firstPoint.x; - else if (abs(angle) < 22.5) - m_lastSnapPoint.y = m_firstPoint.y; - else { - double xDistance = m_lastSnapPoint.x - m_firstPoint.x; - double yDistance = m_lastSnapPoint.y - m_firstPoint.y; - if (abs(xDistance) > abs(yDistance)) { - if (abs(yDistance) == yDistance) - m_lastSnapPoint.y = m_firstPoint.y + abs(xDistance); - else - m_lastSnapPoint.y = m_firstPoint.y - abs(xDistance); - } else { - if (abs(xDistance) == xDistance) - m_lastSnapPoint.x = m_firstPoint.x + abs(yDistance); - else - m_lastSnapPoint.x = m_firstPoint.x - abs(yDistance); - } - } - - m_smoothStroke.clearPoints(); - m_track.add(TThickPoint(m_lastSnapPoint, thickness), - getPixelSize() * getPixelSize()); - m_track.removeMiddlePoints(); - invalidateRect += m_track.getModifiedRegion(); - } else if (e.isShiftPressed()) { - m_smoothStroke.clearPoints(); - m_track.add(TThickPoint(m_brushPos, thickness), - getPixelSize() * getPixelSize()); - m_track.removeMiddlePoints(); - invalidateRect += m_track.getModifiedRegion(); - } else if (m_dragDraw) { - addTrackPoint(TThickPoint(pos, thickness), getPixelSize() * getPixelSize()); - invalidateRect += m_track.getLastModifiedRegion(); - } - - // In order to draw the current brush tip - invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick); - - if (!invalidateRect.isEmpty()) { - // for motion path, call the invalidate function directly to ignore dpi of - // the current level - if (m_isPath) - m_viewer->GLInvalidateRect(invalidateRect.enlarge(2)); - else - invalidate(invalidateRect.enlarge(2)); - } -} - -//--------------------------------------------------------------------------------------------------------------- - -void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos, - const TMouseEvent &e) { - if (!m_active) { - // in case the current frame is moved to empty cell while dragging - if (!m_track.isEmpty()) invalidate(); - m_track.clear(); - return; - } + // end painting ////////////////////////// + m_active = false; - + // clear tracks automatically when return from this function + struct Cleanup { + ToonzVectorBrushTool &owner; + inline ~Cleanup() { owner.m_track.clear(); owner.invalidate(); } + } cleanup = {*this}; + + + // make motion path (if need) + if (m_isPath) { double error = 20.0 * m_pixelSize; - TStroke *stroke; - if (e.isShiftPressed()) { - m_track.removeMiddlePoints(); - stroke = m_track.makeStroke(0); - } else { - flushTrackPoint(); - stroke = m_track.makeStroke(error); - } + TStroke *stroke = m_track.makeStroke(error); TVectorImageP vi = getImage(true); - struct Cleanup { - ToonzVectorBrushTool *m_this; - ~Cleanup() { m_this->m_track.clear(), m_this->invalidate(); } - } cleanup = {this}; if (!isJustCreatedSpline(vi.getPointer())) { m_isPrompting = true; @@ -873,8 +809,8 @@ void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos, QMutexLocker lock(vi->getMutex()); - TUndo *undo = - new UndoPath(getXsheet()->getStageObject(getObjectId())->getSpline()); + TUndo *undo = new UndoPath( + getXsheet()->getStageObject(getObjectId())->getSpline() ); while(vi->getStrokeCount() > 0) vi->deleteStroke(0); vi->addStroke(stroke, false); @@ -882,35 +818,21 @@ void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos, notifyImageChanged(); TUndoManager::manager()->add(undo); - return; + return; // done with motion path } + + + // paint regular strokes - TVectorImageP vi = getImage(true); - if (m_track.isEmpty()) { - m_styleId = 0; - m_track.clear(); + if (m_track.isEmpty()) return; - } - - if (vi && (m_snap.getValue() != m_altPressed) && m_foundLastSnap) { - addTrackPoint( TThickPoint(m_lastSnapPoint, m_currThickness), - m_pixelSize*m_pixelSize ); - } - m_foundFirstSnap = false; - m_foundLastSnap = false; + + // prepare stroke + m_track.filterPoints(); - double error = 30.0 / (1 + 0.5 * m_accuracy.getValue()); - error *= getPixelSize(); - - TStroke *stroke; - if (e.isShiftPressed()) { - m_track.removeMiddlePoints(); - stroke = m_track.makeStroke(0); - } else { - flushTrackPoint(); - stroke = m_track.makeStroke(error); - } + double error = 30.0/(1 + 0.5 * m_accuracy.getValue())*m_pixelSize; + TStroke *stroke = m_track.makeStroke(error); stroke->setStyle(m_styleId); m_styleId = 0; @@ -920,8 +842,6 @@ void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos, options.m_joinStyle = m_joinStyle.getIndex(); options.m_miterUpper = m_miterJoinLimit.getValue(); - QMutexLocker lock(vi->getMutex()); - if ( stroke->getControlPointCount() == 3 && stroke->getControlPoint(0) != stroke->getControlPoint(2) ) // gli stroke con solo 1 chunk vengono @@ -930,9 +850,17 @@ void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos, // autoclose proprio dal fatto che // hanno 1 solo chunk. stroke->insertControlPoints(0.5); + + // lock image + + TVectorImageP vi = getImage(true); + QMutexLocker lock(vi->getMutex()); TTool::Application *app = TTool::getApplication(); + + // add stroke to image + if (m_frameRange.getIndex()) { if (m_firstFrameId == -1) { m_firstStroke = new TStroke(*stroke); @@ -964,7 +892,7 @@ void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos, m_frameRange.getIndex(), m_breakAngles.getValue(), false, false, m_firstFrameRange ); - if (e.isCtrlPressed()) { + if (m_inputmanager.state.isKeyPressed(TInputState::Key::control)) { if (app && m_firstFrameId > currentId) { if (app->getCurrentFrame()->isEditingScene()) { app->getCurrentColumn()->setColumnIndex(curCol); @@ -991,48 +919,165 @@ void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos, resetFrameRange(); } } - - invalidate(); } else { - if (m_snapSelf) { + if (m_track.getLoop()) stroke->setSelfLoop(true); - m_snapSelf = false; - } addStrokeToImage(app, vi, stroke, m_breakAngles.getValue(), false, false, m_isFrameCreated, m_isLevelCreated); - TRectD bbox = stroke->getBBox().enlarge(2) + m_track.getModifiedRegion(); - - invalidate(); // should use bbox? if ((Preferences::instance()->getGuidedDrawingType() == 1 || Preferences::instance()->getGuidedDrawingType() == 2) && Preferences::instance()->getGuidedAutoInbetween()) { - int fidx = app->getCurrentFrame()->getFrameIndex(); TFrameId fId = getFrameId(); - doGuidedAutoInbetween(fId, vi, stroke, m_breakAngles.getValue(), false, false, false); - if (app->getCurrentFrame()->isEditingScene()) - app->getCurrentFrame()->setFrame(fidx); + app->getCurrentFrame()->setFrame( app->getCurrentFrame()->getFrameIndex() ); else app->getCurrentFrame()->setFid(fId); } } - assert(stroke); - m_track.clear(); - m_altPressed = false; + delete stroke; +} + +//-------------------------------------------------------------------------------------------------- + +void ToonzVectorBrushTool::inputPaintTracks(const TTrackList &tracks) { + if (tracks.size() != 1 || !tracks.front()) return; + + TRectD invalidateRect; + + const TTrack &track = *tracks.front(); + while(track.pointsRemoved) { + m_track.pop(); + --track.pointsRemoved; + } + + while(track.pointsAdded) { + const TTrackPoint &p = track.current(); + double t = computeThickness(p.pressure, m_thickness, m_pressure.getValue(), m_isPath); + m_track.add(TThickPoint(p.position, t), 0); + --track.pointsAdded; + } + + invalidateRect += m_track.getLastModifiedRegion(); + + TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5); + invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick); + m_brushPos = m_mousePos = track.current().position; + invalidateRect += TRectD(m_brushPos - halfThick, m_brushPos + halfThick); + + if (!invalidateRect.isEmpty()) + invalidate(invalidateRect.enlarge(2)); +} + +//-------------------------------------------------------------------------------------------------- + +bool ToonzVectorBrushTool::preLeftButtonDown() { + if (getViewer() && getViewer()->getGuidedStrokePickerMode()) return false; + + int smoothRadius = (int)round(m_smooth.getValue()); + m_modifierAssistants->drawOnly = true; //bwtodo: !RasterBrushAssistants; + m_modifierSegmentation->setStep(TPointD(m_pixelSize, m_pixelSize)); + m_modifierSmoothSegmentation->setStep(TPointD(2*m_pixelSize, 2*m_pixelSize)); + m_modifierSimplify->setStep(2*m_pixelSize); + m_inputmanager.drawPreview = false; + + m_inputmanager.clearModifiers(); + m_inputmanager.addModifier(TInputModifierP(m_modifierTangents.getPointer())); + if (smoothRadius > 0) { + m_inputmanager.addModifier(TInputModifierP(m_modifierSmoothSegmentation.getPointer())); + for(int i = 0; i < 3; ++i) { + m_modifierSmooth[i]->setRadius(smoothRadius); + m_inputmanager.addModifier(TInputModifierP(m_modifierSmooth[i].getPointer())); + } + } + m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer())); +#ifndef NDEBUG + m_inputmanager.addModifier(TInputModifierP(m_modifierTest.getPointer())); +#endif + m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer())); + m_inputmanager.addModifier(TInputModifierP(m_modifierSimplify.getPointer())); + + touchImage(); + if (m_isFrameCreated) { + // When the xsheet frame is selected, whole viewer will be updated from + // SceneViewer::onXsheetChanged() on adding a new frame. + // We need to take care of a case when the level frame is selected. + if (m_application->getCurrentFrame()->isEditingLevel()) invalidate(); + } + return true; +} + +//-------------------------------------------------------------------------------------------------- + +void ToonzVectorBrushTool::handleMouseEvent(MouseEventType type, + const TPointD &pos, + const TMouseEvent &e) +{ + TTimerTicks t = TToolTimer::ticks(); + bool alt = e.getModifiersMask() & TMouseEvent::ALT_KEY; + bool shift = e.getModifiersMask() & TMouseEvent::SHIFT_KEY; + bool control = e.getModifiersMask() & TMouseEvent::CTRL_KEY; + + if (shift && type == ME_DOWN && e.button() == Qt::LeftButton && !m_active) { + m_modifierAssistants->drawOnly = true; + m_inputmanager.clearModifiers(); + m_inputmanager.addModifier(TInputModifierP(m_modifierLine.getPointer())); + m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer())); + m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer())); + m_inputmanager.addModifier(TInputModifierP(m_modifierSimplify.getPointer())); + m_inputmanager.drawPreview = true; + } + + if (alt != m_inputmanager.state.isKeyPressed(TKey::alt)) + m_inputmanager.keyEvent(alt, TKey::alt, t, nullptr); + if (shift != m_inputmanager.state.isKeyPressed(TKey::shift)) + m_inputmanager.keyEvent(shift, TKey::shift, t, nullptr); + if (control != m_inputmanager.state.isKeyPressed(TKey::control)) + m_inputmanager.keyEvent(control, TKey::control, t, nullptr); + + if (type == ME_MOVE) { + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + THoverList hovers(1, pos); + m_inputmanager.hoverEvent(hovers); + } else + if (getViewer() && getViewer()->getGuidedStrokePickerMode()) { + if (type == ME_DOWN) getViewer()->doPickGuideStroke(pos); + } else { + m_inputmanager.trackEvent(e.isTablet(), 0, pos, + &e.m_pressure, nullptr, + type == ME_UP, t); + m_inputmanager.processTracks(); + } +} + +//-------------------------------------------------------------------------------------------------- + +void ToonzVectorBrushTool::leftButtonDown(const TPointD &pos, + const TMouseEvent &e) { + handleMouseEvent(ME_DOWN, pos, e); +} +void ToonzVectorBrushTool::leftButtonDrag(const TPointD &pos, + const TMouseEvent &e) { + handleMouseEvent(ME_DRAG, pos, e); +} +void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos, + const TMouseEvent &e) { + handleMouseEvent(ME_UP, pos, e); +} +void ToonzVectorBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { + handleMouseEvent(ME_MOVE, pos, e); } //-------------------------------------------------------------------------------------------------- bool ToonzVectorBrushTool::keyDown(QKeyEvent *event) { - if (event->key() == Qt::Key_Escape) { + if (event->key() == Qt::Key_Escape) resetFrameRange(); - } return false; } @@ -1050,14 +1095,14 @@ bool ToonzVectorBrushTool::doFrameRangeStrokes( TVectorImageP firstImage = new TVectorImage(); TVectorImageP lastImage = new TVectorImage(); - *first = *firstStroke; - *last = *lastStroke; - bool swapped = false; - if (firstFrameId > lastFrameId) { + bool swapped = firstFrameId > lastFrameId; + if (swapped) { std::swap(firstFrameId, lastFrameId); - *first = *lastStroke; - *last = *firstStroke; - swapped = true; + *first = *lastStroke; + *last = *firstStroke; + } else { + *first = *firstStroke; + *last = *lastStroke; } firstImage->addStroke(first, false); @@ -1258,97 +1303,6 @@ void ToonzVectorBrushTool::flushTrackPoint() { } } -//--------------------------------------------------------------------------------------------------------------- - -void ToonzVectorBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { - qApp->processEvents(QEventLoop::ExcludeUserInputEvents); - - struct Locals { - ToonzVectorBrushTool *m_this; - - void setValue(TDoublePairProperty &prop, - const TDoublePairProperty::Value &value) { - prop.setValue(value); - - m_this->onPropertyChanged(prop.getName()); - TTool::getApplication()->getCurrentTool()->notifyToolChanged(); - } - - void addMinMax(TDoublePairProperty &prop, double add) { - if (add == 0.0) return; - const TDoublePairProperty::Range &range = prop.getRange(); - - TDoublePairProperty::Value value = prop.getValue(); - value.first = tcrop(value.first + add, range.first, range.second); - value.second = tcrop(value.second + add, range.first, range.second); - - setValue(prop, value); - } - - void addMinMaxSeparate(TDoublePairProperty &prop, double min, double max) { - if (min == 0.0 && max == 0.0) return; - const TDoublePairProperty::Range &range = prop.getRange(); - - TDoublePairProperty::Value value = prop.getValue(); - value.first += min; - value.second += max; - if (value.first > value.second) value.first = value.second; - value.first = tcrop(value.first, range.first, range.second); - value.second = tcrop(value.second, range.first, range.second); - - setValue(prop, value); - } - - } locals = {this}; - - TPointD halfThick(m_maxThick * 0.5, m_maxThick * 0.5); - TRectD invalidateRect(m_brushPos - halfThick, m_brushPos + halfThick); - - if (e.isCtrlPressed() && e.isAltPressed() && !e.isShiftPressed() && - Preferences::instance()->useCtrlAltToResizeBrushEnabled()) { - // Resize the brush if CTRL+ALT is pressed and the preference is enabled. - const TPointD &diff = pos - m_mousePos; - double max = diff.x / 2; - double min = diff.y / 2; - - locals.addMinMaxSeparate(m_thickness, min, max); - - double radius = m_thickness.getValue().second * 0.5; - invalidateRect += TRectD(m_brushPos - TPointD(radius, radius), - m_brushPos + TPointD(radius, radius)); - - } else { - m_mousePos = pos; - m_brushPos = pos; - - TPointD snapThick(6.0 * m_pixelSize, 6.0 * m_pixelSize); - // In order to clear the previous snap indicator - if (m_foundFirstSnap) - invalidateRect += - TRectD(m_firstSnapPoint - snapThick, m_firstSnapPoint + snapThick); - - m_firstSnapPoint = pos; - m_foundFirstSnap = false; - m_altPressed = e.isAltPressed() && !e.isCtrlPressed(); - checkStrokeSnapping(true, m_altPressed); - checkGuideSnapping(true, m_altPressed); - m_brushPos = m_firstSnapPoint; - // In order to draw the snap indicator - if (m_foundFirstSnap) - invalidateRect += - TRectD(m_firstSnapPoint - snapThick, m_firstSnapPoint + snapThick); - - invalidateRect += TRectD(pos - halfThick, pos + halfThick); - } - - invalidate(invalidateRect.enlarge(2)); - - if (m_minThick == 0 && m_maxThick == 0) { - m_minThick = m_thickness.getValue().first; - m_maxThick = m_thickness.getValue().second; - } -} - //------------------------------------------------------------------------------------------------------------- void ToonzVectorBrushTool::checkStrokeSnapping(bool beforeMousePress, @@ -1462,6 +1416,8 @@ void ToonzVectorBrushTool::checkGuideSnapping(bool beforeMousePress, //------------------------------------------------------------------------------------------------------------- void ToonzVectorBrushTool::draw() { + m_inputmanager.draw(); + /*--ショートカットでのツール切り替え時に赤点が描かれるのを防止する--*/ if (m_minThick == 0 && m_maxThick == 0 && !Preferences::instance()->getShow0ThickLines()) diff --git a/toonz/sources/tnztools/toonzvectorbrushtool.h b/toonz/sources/tnztools/toonzvectorbrushtool.h index 7b8bd63..b43d5b1 100644 --- a/toonz/sources/tnztools/toonzvectorbrushtool.h +++ b/toonz/sources/tnztools/toonzvectorbrushtool.h @@ -13,6 +13,17 @@ #include "tools/tool.h" #include "tools/cursors.h" +#include +#include +#include +#include +#include +#include +#include +#ifndef NDEBUG +#include +#endif + #include "toonzrasterbrushtool.h" #include @@ -80,7 +91,9 @@ public: // Brush Tool declaration //************************************************************************ -class ToonzVectorBrushTool final : public TTool { +class ToonzVectorBrushTool final : public TTool, + public TInputHandler +{ Q_DECLARE_TR_FUNCTIONS(ToonzVectorBrushTool) public: @@ -102,6 +115,13 @@ public: void mouseMove(const TPointD &pos, const TMouseEvent &e) override; bool keyDown(QKeyEvent *event) override; + void inputMouseMove(const TPointD &position, + const TInputState &state) override; + void inputSetBusy(bool busy) override; + void inputPaintTracks(const TTrackList &tracks) override; + void inputInvalidateRect(const TRectD &bounds) override { invalidate(bounds); } + TTool *inputGetTool() override { return this; }; + void draw() override; void onEnter() override; @@ -143,6 +163,11 @@ public: bool autoGroup = false, bool autoFill = false, bool drawStroke = true); +private: + enum MouseEventType { ME_DOWN, ME_DRAG, ME_UP, ME_MOVE }; + void handleMouseEvent(MouseEventType type, const TPointD &pos, + const TMouseEvent &e); + protected: TPropertyGroup m_prop[2]; @@ -159,6 +184,18 @@ protected: TEnumProperty m_joinStyle; TIntProperty m_miterJoinLimit; + TInputManager m_inputmanager; + TSmartPointerT m_modifierLine; + TSmartPointerT m_modifierTangents; + TSmartPointerT m_modifierAssistants; + TSmartPointerT m_modifierSegmentation; + TSmartPointerT m_modifierSmoothSegmentation; + TSmartPointerT m_modifierSmooth[3]; + TSmartPointerT m_modifierSimplify; +#ifndef NDEBUG + TSmartPointerT m_modifierTest; +#endif + StrokeGenerator m_track; StrokeGenerator m_rangeTrack; RasterStrokeGenerator *m_rasterTrack; @@ -167,7 +204,7 @@ protected: TTileSaverCM32 *m_tileSaver; TFrameId m_firstFrameId, m_veryFirstFrameId; TPixel32 m_currentColor; - int m_styleId; + int m_styleId; // bwtodo: remove double m_minThick, m_maxThick; // for snapping and framerange diff --git a/toonz/sources/toonzlib/strokegenerator.cpp b/toonz/sources/toonzlib/strokegenerator.cpp index 9a5c9c7..755a928 100644 --- a/toonz/sources/toonzlib/strokegenerator.cpp +++ b/toonz/sources/toonzlib/strokegenerator.cpp @@ -26,32 +26,61 @@ bool StrokeGenerator::isEmpty() const { return m_points.empty(); } //------------------------------------------------------------------- -void StrokeGenerator::add(const TThickPoint &point, double pixelSize2) { +bool StrokeGenerator::add(const TThickPoint &point, double pixelSize2) { if (m_points.empty()) { double x = point.x, y = point.y, d = point.thick + 3; m_points.push_back(point); TRectD rect(x - d, y - d, x + d, y + d); - m_modifiedRegion = rect; - m_lastPointRect = rect; - m_lastModifiedRegion = rect; + m_modifiedRegion += rect; + m_lastPointRect += rect; + m_lastModifiedRegion += rect; m_p0 = m_p1 = point; - } else { - TThickPoint lastPoint = m_points.back(); - if (tdistance2(lastPoint, point) >= 4 * pixelSize2) { - m_points.push_back(point); + return true; + } + + TThickPoint lastPoint = m_points.back(); + if (tdistance2(lastPoint, point) >= 4 * pixelSize2) { + m_points.push_back(point); + double d = std::max(point.thick, lastPoint.thick) + 3; + TRectD rect(TRectD(lastPoint, point).enlarge(d)); + m_modifiedRegion += rect; + m_lastModifiedRegion += rect; + m_lastPointRect = rect; + return true; + } + + m_points.back().thick = std::max(m_points.back().thick, point.thick); + return false; +} + +//------------------------------------------------------------------- + +void StrokeGenerator::pop() { + if (!m_points.empty()) { + TRectD rect; + TThickPoint point = m_points.back(); + m_points.pop_back(); + if (!m_points.empty()) { + const TThickPoint &lastPoint = m_points.back(); double d = std::max(point.thick, lastPoint.thick) + 3; - TRectD rect(TRectD(lastPoint, point).enlarge(d)); - m_modifiedRegion += rect; - m_lastModifiedRegion += rect; - m_lastPointRect = rect; + rect = TRectD(lastPoint, point).enlarge(d); } else { - m_points.back().thick = std::max(m_points.back().thick, point.thick); + double x = point.x, y = point.y, d = point.thick + 3; + rect = TRectD(x - d, y - d, x + d, y + d); } + m_modifiedRegion += rect; + m_lastModifiedRegion += rect; + m_lastPointRect = rect; } } //------------------------------------------------------------------- +void StrokeGenerator::setLoop(bool loop) + { m_loop = loop; } + +//------------------------------------------------------------------- + void StrokeGenerator::filterPoints() { if (m_points.size() < 10) return; @@ -274,7 +303,7 @@ TPointD StrokeGenerator::getFirstPoint() { return m_points[0]; } //------------------------------------------------------------------- -TStroke *StrokeGenerator::makeStroke(double error, UINT onlyLastPoints) const { +TStroke *StrokeGenerator::makeStroke(double error, UINT onlyLastPoints, bool useLoop) const { if (onlyLastPoints == 0 || onlyLastPoints > m_points.size()) return TStroke::interpolate(m_points, error); @@ -283,7 +312,9 @@ TStroke *StrokeGenerator::makeStroke(double error, UINT onlyLastPoints) const { m_points.begin() + (m_points.size() - onlyLastPoints); copy(first, m_points.end(), lastPoints.begin()); - return TStroke::interpolate(lastPoints, error); + TStroke *stroke = TStroke::interpolate(lastPoints, error); + if (useLoop) stroke->setSelfLoop(m_loop); + return stroke; } //-------------------------------------------------------------------