From 2d59a6962253bda08dca71f08328010c4b77cae8 Mon Sep 17 00:00:00 2001 From: manongjohn Date: Tue, 1 Jun 2021 21:36:13 -0400 Subject: [PATCH 1/4] Add paint brush pressure sensitivity --- toonz/sources/tnztools/paintbrushtool.cpp | 133 +++++++++++++--------- 1 file changed, 80 insertions(+), 53 deletions(-) diff --git a/toonz/sources/tnztools/paintbrushtool.cpp b/toonz/sources/tnztools/paintbrushtool.cpp index 4799e6e8..cb25fe4f 100644 --- a/toonz/sources/tnztools/paintbrushtool.cpp +++ b/toonz/sources/tnztools/paintbrushtool.cpp @@ -46,7 +46,10 @@ using namespace ToolUtils; TEnv::StringVar PaintBrushColorType("InknpaintPaintBrushColorType", "Areas"); TEnv::IntVar PaintBrushSelective("InknpaintPaintBrushSelective", 0); -TEnv::DoubleVar PaintBrushSize("InknpaintPaintBrushSize", 10); +TEnv::DoubleVar PaintBrushSizeMax("InknpaintPaintBrushSizeMax", 10); +TEnv::DoubleVar PaintBrushSizeMin("InknpaintPaintBrushSizeMin", 10); +TEnv::IntVar PaintBrushPressureSensitivity("InknpaintBrushPressureSensitivity", + 1); //----------------------------------------------------------------------------- @@ -237,6 +240,16 @@ void drawEmptyCircle(int thick, const TPointD &mousePos, bool isPencil, } } +//------------------------------------------------------------------------------------------------------- + +double computeThickness(double pressure, const TDoublePairProperty &property) { + double t = pressure * pressure * pressure; + double thick0 = property.getValue().first; + double thick1 = property.getValue().second; + if (thick1 < 0.0001) thick0 = thick1 = 0.0; + return (thick0 + (thick1 - thick0) * t) * 0.5; +} + } // namespace //----------------------------------------------------------------------------- @@ -248,14 +261,13 @@ class PaintBrushTool final : public TTool { bool m_firstTime; - double m_pointSize, m_distance2; - bool m_selecting; TTileSaverCM32 *m_tileSaver; TPointD m_mousePos; - TIntProperty m_toolSize; + TDoublePairProperty m_rasThickness; + TBoolProperty m_pressure; TBoolProperty m_onlyEmptyAreas; TEnumProperty m_colorType; TPropertyGroup m_prop; @@ -266,6 +278,8 @@ class PaintBrushTool final : public TTool { 移動している場合に備える --*/ TFrameId m_workingFrameId; + double m_minThick, m_maxThick; + public: PaintBrushTool(); @@ -297,7 +311,7 @@ public: /*--- * 描画中にツールが切り替わった場合に備え、onDeactivateにもMouseReleaseと同じ終了処理を行う * ---*/ - void finishBrush(); + void finishBrush(double pressureValue); /*--- Brush、PaintBrush、EraserToolがPencilModeのときにTrueを返す。    PaintBrushはピクセルのStyleIndexを入れ替えるツールのため、   アンチエイリアスは存在しない、いわば常にPencilMode ---*/ @@ -315,17 +329,17 @@ PaintBrushTool paintBrushTool; PaintBrushTool::PaintBrushTool() : TTool("T_PaintBrush") , m_rasterTrack(0) - , m_pointSize(-1) , m_selecting(false) , m_tileSaver(0) , m_cursor(ToolCursor::EraserCursor) // sostituire i nomi con quelli del current, tipo W_ToolOptions... - , m_toolSize("Size:", 1, 1000, 10, false) // W_ToolOptions_BrushToolSize + , m_rasThickness("Size:", 1, 1000, 10, 5) , m_colorType("Mode:") // W_ToolOptions_InkOrPaint , m_onlyEmptyAreas("Selective", false) // W_ToolOptions_Selective , m_firstTime(true) + , m_pressure("Pressure", true) , m_workingFrameId(TFrameId()) { - m_toolSize.setNonLinearSlider(); + m_rasThickness.setNonLinearSlider(); m_colorType.addValue(LINES); m_colorType.addValue(AREAS); @@ -333,18 +347,20 @@ PaintBrushTool::PaintBrushTool() bind(TTool::ToonzImage); - m_prop.bind(m_toolSize); + m_prop.bind(m_rasThickness); m_prop.bind(m_colorType); m_prop.bind(m_onlyEmptyAreas); + m_prop.bind(m_pressure); m_onlyEmptyAreas.setId("Selective"); m_colorType.setId("Mode"); + m_pressure.setId("PressureSensitivity"); } //----------------------------------------------------------------------------- void PaintBrushTool::updateTranslation() { - m_toolSize.setQStringName(tr("Size:")); + m_rasThickness.setQStringName(tr("Size:")); m_colorType.setQStringName(tr("Mode:")); m_colorType.setItemUIName(LINES, tr("Lines")); @@ -352,6 +368,8 @@ void PaintBrushTool::updateTranslation() { m_colorType.setItemUIName(ALL, tr("Lines & Areas")); m_onlyEmptyAreas.setQStringName(tr("Selective", NULL)); + + m_pressure.setQStringName(tr("Pressure")); } //------------------------------------------------------------------------------------------------------- @@ -386,9 +404,6 @@ void PaintBrushTool::fixMousePos(TPointD pos, bool precise) { //----------------------------------------------------------------------------- void PaintBrushTool::draw() { - /*-- MouseLeave時にBrushTipが描かれるのを防止する --*/ - if (m_pointSize == -1) return; - // If toggled off, don't draw brush outline if (!Preferences::instance()->isCursorOutlineEnabled()) return; @@ -405,8 +420,10 @@ void PaintBrushTool::draw() { else glColor3d(1.0, 0.0, 0.0); - drawEmptyCircle(m_toolSize.getValue(), m_mousePos, true, lx % 2 == 0, - ly % 2 == 0); + drawEmptyCircle(tround(m_rasThickness.getValue().second), m_mousePos, true, + lx % 2 == 0, ly % 2 == 0); + drawEmptyCircle(tround(m_rasThickness.getValue().first), m_mousePos, true, + lx % 2 == 0, ly % 2 == 0); } //----------------------------------------------------------------------------- @@ -416,30 +433,19 @@ const UINT pointCount = 20; //----------------------------------------------------------------------------- bool PaintBrushTool::onPropertyChanged(std::string propertyName) { + PaintBrushColorType = ::to_string(m_colorType.getValue()); + PaintBrushSelective = (int)(m_onlyEmptyAreas.getValue()); + PaintBrushSizeMin = m_rasThickness.getValue().first; + PaintBrushSizeMax = m_rasThickness.getValue().second; + PaintBrushPressureSensitivity = m_pressure.getValue(); + /*-- Size ---*/ - if (propertyName == m_toolSize.getName()) { - PaintBrushSize = 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(); + if (propertyName == m_rasThickness.getName()) { + m_minThick = m_rasThickness.getValue().first; + m_maxThick = m_rasThickness.getValue().second; } - - // Selective - else if (propertyName == m_onlyEmptyAreas.getName()) { - PaintBrushSelective = (int)(m_onlyEmptyAreas.getValue()); - } - // Areas, Lines etc. else if (propertyName == m_colorType.getName()) { - PaintBrushColorType = ::to_string(m_colorType.getValue()); /*--- ColorModelのCursor更新のためにSIGNALを出す ---*/ TTool::getApplication()->getCurrentTool()->notifyToolChanged(); } @@ -460,7 +466,15 @@ void PaintBrushTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) { if (TToonzImageP ti = image) { TRasterCM32P ras = ti->getRaster(); if (ras) { - int thickness = m_toolSize.getValue(); + double maxThick = m_rasThickness.getValue().second; + double thickness = + (m_pressure.getValue()) + ? computeThickness(e.m_pressure, m_rasThickness) * 2 + : maxThick; + + if (m_pressure.getValue() && e.m_pressure == 1.0) + thickness = m_rasThickness.getValue().first; + int styleId = TTool::getApplication()->getCurrentLevelStyleIndex(); TTileSetCM32 *tileSet = new TTileSetCM32(ras->getSize()); m_tileSaver = new TTileSaverCM32(ras, tileSet); @@ -488,7 +502,12 @@ void PaintBrushTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { いきなりleftButtonDragから呼ばれることがあり、m_rasterTrackが無い可能性がある  ---*/ if (m_rasterTrack) { - int thickness = m_toolSize.getValue(); + double maxThick = m_rasThickness.getValue().second; + double thickness = + (m_pressure.getValue()) + ? computeThickness(e.m_pressure, m_rasThickness) * 2 + : maxThick; + m_rasterTrack->add(TThickPoint( m_mousePos + convert(ri->getRaster()->getCenter()), thickness)); m_tileSaver->save(m_rasterTrack->getLastRect()); @@ -500,12 +519,14 @@ void PaintBrushTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { //----------------------------------------------------------------------------- -void PaintBrushTool::leftButtonUp(const TPointD &pos, const TMouseEvent &) { +void PaintBrushTool::leftButtonUp(const TPointD &pos, const TMouseEvent &e) { if (!m_selecting) return; fixMousePos(pos); - finishBrush(); + double pressure = m_pressure.getValue() && e.isTablet() ? e.m_pressure : 0.5; + + finishBrush(pressure); } //----------------------------------------------------------------------------- @@ -521,19 +542,14 @@ void PaintBrushTool::onEnter() { if (m_firstTime) { m_onlyEmptyAreas.setValue(PaintBrushSelective ? 1 : 0); m_colorType.setValue(::to_wstring(PaintBrushColorType.getValue())); - m_toolSize.setValue(PaintBrushSize); + m_rasThickness.setValue( + TDoublePairProperty::Value(PaintBrushSizeMin, PaintBrushSizeMax)); + m_pressure.setValue(PaintBrushPressureSensitivity ? 1 : 0); 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; + m_minThick = m_rasThickness.getValue().first; + m_maxThick = m_rasThickness.getValue().second; if ((TToonzImageP)getImage(false)) m_cursor = ToolCursor::PenCursor; @@ -543,7 +559,10 @@ void PaintBrushTool::onEnter() { //----------------------------------------------------------------------------- -void PaintBrushTool::onLeave() { m_pointSize = -1; } +void PaintBrushTool::onLeave() { + m_minThick = 0; + m_maxThick = 0; +} //----------------------------------------------------------------------------- @@ -553,17 +572,25 @@ void PaintBrushTool::onActivate() { onEnter(); } void PaintBrushTool::onDeactivate() { /*--マウスドラッグ中(m_selecting=true)にツールが切り替わったときに描画の終了処理を行う---*/ - if (m_selecting) finishBrush(); + if (m_selecting) finishBrush(1); } //----------------------------------------------------------------------------- /*! * 描画中にツールが切り替わった場合に備え、onDeactivateでもMouseReleaseと同じ終了処理を行う */ -void PaintBrushTool::finishBrush() { +void PaintBrushTool::finishBrush(double pressureValue) { if (TToonzImageP ti = (TToonzImageP)getImage(true)) { if (m_rasterTrack) { - int thickness = m_toolSize.getValue(); + double maxThick = m_rasThickness.getValue().second; + double thickness = + (m_pressure.getValue()) + ? computeThickness(pressureValue, m_rasThickness) * 2 + : maxThick; + + if (m_pressure.getValue() && pressureValue == 1.0) + thickness = m_rasThickness.getValue().first; + m_rasterTrack->add(TThickPoint( m_mousePos + convert(ti->getRaster()->getCenter()), thickness)); m_tileSaver->save(m_rasterTrack->getLastRect()); From 80ebe0defa54619a11b1f67fc83a089ee24401a2 Mon Sep 17 00:00:00 2001 From: manongjohn Date: Wed, 2 Jun 2021 15:42:54 -0400 Subject: [PATCH 2/4] Replicate Finger Tool using Ctrl + Paint Brush Tool --- toonz/sources/tnztools/paintbrushtool.cpp | 54 ++++++++++++++++++++++- toonz/sources/toonz/statusbar.cpp | 6 ++- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/toonz/sources/tnztools/paintbrushtool.cpp b/toonz/sources/tnztools/paintbrushtool.cpp index cb25fe4f..d596a1e8 100644 --- a/toonz/sources/tnztools/paintbrushtool.cpp +++ b/toonz/sources/tnztools/paintbrushtool.cpp @@ -38,6 +38,9 @@ // For Qt translation support #include +#include "tools/stylepicker.h" +#include "toonzqt/styleselection.h" + using namespace ToolUtils; #define LINES L"Lines" @@ -280,6 +283,10 @@ class PaintBrushTool final : public TTool { double m_minThick, m_maxThick; + Tasks m_task; + + int getStyleUnderCursor(const TPointD &pos); + public: PaintBrushTool(); @@ -338,6 +345,7 @@ PaintBrushTool::PaintBrushTool() , m_onlyEmptyAreas("Selective", false) // W_ToolOptions_Selective , m_firstTime(true) , m_pressure("Pressure", true) + , m_task(PAINTBRUSH) , m_workingFrameId(TFrameId()) { m_rasThickness.setNonLinearSlider(); @@ -457,6 +465,7 @@ bool PaintBrushTool::onPropertyChanged(std::string propertyName) { void PaintBrushTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) { fixMousePos(pos); + m_task = PAINTBRUSH; m_selecting = true; TImageP image(getImage(true)); if (m_colorType.getValue() == LINES) m_colorTypeBrush = INK; @@ -475,11 +484,18 @@ void PaintBrushTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) { if (m_pressure.getValue() && e.m_pressure == 1.0) thickness = m_rasThickness.getValue().first; - int styleId = TTool::getApplication()->getCurrentLevelStyleIndex(); + int styleId = TTool::getApplication()->getCurrentLevelStyleIndex(); + + if (e.isCtrlPressed()) { + int styleIdUnderCursor = getStyleUnderCursor(m_mousePos); + if (styleIdUnderCursor > 0) styleId = styleIdUnderCursor; + m_task = FINGER; + } + TTileSetCM32 *tileSet = new TTileSetCM32(ras->getSize()); m_tileSaver = new TTileSaverCM32(ras, tileSet); m_rasterTrack = new RasterStrokeGenerator( - ras, PAINTBRUSH, m_colorTypeBrush, styleId, + ras, m_task, m_colorTypeBrush, styleId, TThickPoint(m_mousePos + convert(ras->getCenter()), thickness), m_onlyEmptyAreas.getValue(), 0, false); /*-- 現在のFidを記憶 --*/ @@ -508,6 +524,13 @@ void PaintBrushTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { ? computeThickness(e.m_pressure, m_rasThickness) * 2 : maxThick; + // If we were using FINGER mode before, but stopped mid drag, end previous + // stroke and switch + if (m_task == FINGER && !e.isCtrlPressed()) { + finishBrush(thickness); + leftButtonDown(pos, e); + } + m_rasterTrack->add(TThickPoint( m_mousePos + convert(ri->getRaster()->getCenter()), thickness)); m_tileSaver->save(m_rasterTrack->getLastRect()); @@ -521,6 +544,7 @@ void PaintBrushTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) { void PaintBrushTool::leftButtonUp(const TPointD &pos, const TMouseEvent &e) { if (!m_selecting) return; + m_task = PAINTBRUSH; fixMousePos(pos); @@ -550,6 +574,7 @@ void PaintBrushTool::onEnter() { m_minThick = m_rasThickness.getValue().first; m_maxThick = m_rasThickness.getValue().second; + m_task = PAINTBRUSH; if ((TToonzImageP)getImage(false)) m_cursor = ToolCursor::PenCursor; @@ -562,6 +587,7 @@ void PaintBrushTool::onEnter() { void PaintBrushTool::onLeave() { m_minThick = 0; m_maxThick = 0; + m_task = PAINTBRUSH; } //----------------------------------------------------------------------------- @@ -629,3 +655,27 @@ void PaintBrushTool::finishBrush(double pressureValue) { m_selecting = false; } + +int PaintBrushTool::getStyleUnderCursor(const TPointD &pos) { + int modeValue = 2; // Stylepicker modes: 0=AREAS, 1=LINES, 2=ALL + + TImageP image = getImage(false); + TToonzImageP ti = image; + TXshSimpleLevel *level = + getApplication()->getCurrentLevel()->getSimpleLevel(); + if (!ti || !level) return -1; + + if (!m_viewer->getGeometry().contains(pos)) return -1; + + int subsampling = level->getImageSubsampling(getCurrentFid()); + + StylePicker picker(image); + + int styleId = + picker.pickStyleId(TScale(1.0 / subsampling) * pos, + getPixelSize() * getPixelSize(), 1.0, modeValue); + + if (styleId <= 0) return -1; + + return styleId; +} diff --git a/toonz/sources/toonz/statusbar.cpp b/toonz/sources/toonz/statusbar.cpp index c12fc752..aed3faa4 100644 --- a/toonz/sources/toonz/statusbar.cpp +++ b/toonz/sources/toonz/statusbar.cpp @@ -327,7 +327,11 @@ std::unordered_map StatusBar::makeMap( .arg(cmdTextSeparator)}); lMap.insert({"T_Type", tr("Type Tool: Adds text")}); lMap.insert({"T_PaintBrush", - tr("Smart Raster Painter: Paints areas in Smart Raster leves")}); + tr("Smart Raster Painter: Paints areas in Smart Raster leves") + + spacer + + tr("%1%2Fix small fill gaps with click+dragged style") + .arg(trModKey("Ctrl")) + .arg(cmdTextSeparator)}); lMap.insert( {"T_Fill", tr("Fill Tool: Fills drawing areas with the current style")}); lMap.insert({"T_Eraser", tr("Eraser: Erases lines and areas")}); From eacc4d412e2b4e67d346a5fdae88731187467106 Mon Sep 17 00:00:00 2001 From: manongjohn Date: Wed, 2 Jun 2021 16:23:14 -0400 Subject: [PATCH 3/4] Select style using Shift key --- toonz/sources/tnztools/paintbrushtool.cpp | 6 ++++++ toonz/sources/toonz/statusbar.cpp | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/toonz/sources/tnztools/paintbrushtool.cpp b/toonz/sources/tnztools/paintbrushtool.cpp index d596a1e8..513bb3bb 100644 --- a/toonz/sources/tnztools/paintbrushtool.cpp +++ b/toonz/sources/tnztools/paintbrushtool.cpp @@ -490,6 +490,12 @@ void PaintBrushTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) { int styleIdUnderCursor = getStyleUnderCursor(m_mousePos); if (styleIdUnderCursor > 0) styleId = styleIdUnderCursor; m_task = FINGER; + } else if (e.isShiftPressed()) { + int styleIdUnderCursor = getStyleUnderCursor(m_mousePos); + if (styleIdUnderCursor > 0) { + styleId = styleIdUnderCursor; + getApplication()->setCurrentLevelStyleIndex(styleId); + } } TTileSetCM32 *tileSet = new TTileSetCM32(ras->getSize()); diff --git a/toonz/sources/toonz/statusbar.cpp b/toonz/sources/toonz/statusbar.cpp index aed3faa4..dab7b845 100644 --- a/toonz/sources/toonz/statusbar.cpp +++ b/toonz/sources/toonz/statusbar.cpp @@ -331,6 +331,10 @@ std::unordered_map StatusBar::makeMap( spacer + tr("%1%2Fix small fill gaps with click+dragged style") .arg(trModKey("Ctrl")) + .arg(cmdTextSeparator) + + spacer + + tr("%1%2Selects style on current drawing") + .arg(trModKey("Shift")) .arg(cmdTextSeparator)}); lMap.insert( {"T_Fill", tr("Fill Tool: Fills drawing areas with the current style")}); From 5b86aa264fa00d829e3965e4a4e3da3bc0930ff9 Mon Sep 17 00:00:00 2001 From: manongjohn Date: Tue, 8 Jun 2021 11:12:34 -0400 Subject: [PATCH 4/4] Fix Paint Brush statusbar text typo --- toonz/sources/toonz/statusbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toonz/sources/toonz/statusbar.cpp b/toonz/sources/toonz/statusbar.cpp index dab7b845..6dbb0b1f 100644 --- a/toonz/sources/toonz/statusbar.cpp +++ b/toonz/sources/toonz/statusbar.cpp @@ -327,7 +327,7 @@ std::unordered_map StatusBar::makeMap( .arg(cmdTextSeparator)}); lMap.insert({"T_Type", tr("Type Tool: Adds text")}); lMap.insert({"T_PaintBrush", - tr("Smart Raster Painter: Paints areas in Smart Raster leves") + + tr("Smart Raster Painter: Paints areas in Smart Raster levels") + spacer + tr("%1%2Fix small fill gaps with click+dragged style") .arg(trModKey("Ctrl"))