From 1428753c7bc8e4443e915646f38a40c16a9200b2 Mon Sep 17 00:00:00 2001 From: Rodney Date: Apr 19 2021 15:51:23 +0000 Subject: Merge pull request #3895 from shun-iwasawa/fix_brush_smooth Fix Brush Smoothing --- diff --git a/toonz/sources/tnztools/toonzrasterbrushtool.cpp b/toonz/sources/tnztools/toonzrasterbrushtool.cpp index 028f140..36a77b6 100644 --- a/toonz/sources/tnztools/toonzrasterbrushtool.cpp +++ b/toonz/sources/tnztools/toonzrasterbrushtool.cpp @@ -655,7 +655,8 @@ static void CatmullRomInterpolate(const TThickPoint &P0, const TThickPoint &P1, //-------------------------------------------------------------------------------------------------- -static void Smooth(std::vector &points, int radius) { +static void Smooth(std::vector &points, const int radius, + const int readIndex, const int level) { int n = (int)points.size(); if (radius < 1 || n < 3) { return; @@ -665,7 +666,10 @@ static void Smooth(std::vector &points, int radius) { float d = 1.0f / (radius * 2 + 1); - for (int i = 1; i < n - 1; ++i) { + int endSamples = 10; + int startId = std::max(readIndex - endSamples * 3 - radius * level, 1); + + for (int i = startId; i < n - 1; ++i) { int lower = i - radius; int upper = i + radius; @@ -692,21 +696,23 @@ static void Smooth(std::vector &points, int radius) { result.push_back(total); } - for (int i = 1; i < n - 1; ++i) { - points[i].x = result[i - 1].x; - points[i].y = result[i - 1].y; - points[i].thick = result[i - 1].thick; + auto result_itr = result.begin(); + for (int i = startId; i < n - 1; ++i, ++result_itr) { + points[i].x = (*result_itr).x; + points[i].y = (*result_itr).y; + points[i].thick = (*result_itr).thick; } if (points.size() >= 3) { std::vector pts; - CatmullRomInterpolate(points[0], points[0], points[1], points[2], 10, pts); + CatmullRomInterpolate(points[0], points[0], points[1], points[2], + endSamples, pts); std::vector::iterator it = points.begin() + 1; points.insert(it, pts.begin(), pts.end()); pts.clear(); CatmullRomInterpolate(points[n - 3], points[n - 2], points[n - 1], - points[n - 1], 10, pts); + points[n - 1], endSamples, pts); it = points.begin(); it += n - 1; points.insert(it, pts.begin(), pts.end()); @@ -721,6 +727,8 @@ void SmoothStroke::beginStroke(int smooth) { m_readIndex = -1; m_rawPoints.clear(); m_outputPoints.clear(); + m_resampledIndex = 0; + m_resampledPoints.clear(); } //-------------------------------------------------------------------------------------------------- @@ -749,6 +757,8 @@ void SmoothStroke::clearPoints() { m_readIndex = -1; m_outputPoints.clear(); m_rawPoints.clear(); + m_resampledIndex = 0; + m_resampledPoints.clear(); } //-------------------------------------------------------------------------------------------------- @@ -781,26 +791,40 @@ void SmoothStroke::generatePoints() { return; } - std::vector smoothedPoints; + std::vector smoothedPoints = m_resampledPoints; // Add more stroke samples before applying the smoothing // This is because the raw inputs points are too few to support smooth result, // especially on stroke ends - smoothedPoints.push_back(m_rawPoints.front()); - for (int i = 1; i < n; ++i) { - const TThickPoint &p1 = m_rawPoints[i - 1]; - const TThickPoint &p2 = m_rawPoints[i]; - const TThickPoint &p0 = i - 2 >= 0 ? m_rawPoints[i - 2] : p1; - const TThickPoint &p3 = i + 1 < n ? m_rawPoints[i + 1] : p2; - - int samples = 8; - CatmullRomInterpolate(p0, p1, p2, p3, samples, smoothedPoints); - smoothedPoints.push_back(p2); + + int resampleStartId = m_resampledIndex; + for (int i = resampleStartId; i < n - 1; ++i) { + const TThickPoint &p1 = m_rawPoints[i]; + const TThickPoint &p2 = m_rawPoints[i + 1]; + const TThickPoint &p0 = i - 1 >= 0 ? m_rawPoints[i - 1] : p1; + const TThickPoint &p3 = i + 2 < n ? m_rawPoints[i + 2] : p2; + + std::vector tmpResampled; + tmpResampled.push_back(p1); + // define subsample amount according to distance between points + int samples = std::min((int)tdistance(p1, p2), 8); + if (samples >= 1) + CatmullRomInterpolate(p0, p1, p2, p3, samples, tmpResampled); + + if (i + 2 < n) { + m_resampledIndex = i + 1; + std::copy(tmpResampled.begin(), tmpResampled.end(), + std::back_inserter(m_resampledPoints)); + } + std::copy(tmpResampled.begin(), tmpResampled.end(), + std::back_inserter(smoothedPoints)); } + smoothedPoints.push_back(m_rawPoints.back()); // Apply the 1D box filter // Multiple passes result in better quality and fix the stroke ends break // issue - for (int i = 0; i < 3; ++i) { - Smooth(smoothedPoints, m_smooth); + // level is passed to define range where the points are smoothed + for (int level = 2; level >= 0; --level) { + Smooth(smoothedPoints, m_smooth, m_readIndex, level); } // Compare the new smoothed stroke with old one // Enable the output for unchanged parts diff --git a/toonz/sources/tnztools/toonzrasterbrushtool.h b/toonz/sources/tnztools/toonzrasterbrushtool.h index 0e8eaef..2dfab81 100644 --- a/toonz/sources/tnztools/toonzrasterbrushtool.h +++ b/toonz/sources/tnztools/toonzrasterbrushtool.h @@ -109,6 +109,9 @@ private: int m_readIndex; std::vector m_rawPoints; std::vector m_outputPoints; + + int m_resampledIndex; + std::vector m_resampledPoints; }; //************************************************************************ // Toonz Raster Brush Tool declaration