/* SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup freestyle * \brief Fredo's stroke shaders */ #include "AdvancedStrokeShaders.h" #include "StrokeIterators.h" #include "../system/PseudoNoise.h" #include "../system/RandGen.h" #include "BLI_sys_types.h" namespace Freestyle { ///////////////////////////////////////// // // CALLIGRAPHICS SHADER // ///////////////////////////////////////// CalligraphicShader::CalligraphicShader(real iMinThickness, real iMaxThickness, const Vec2f &iOrientation, bool clamp) { _minThickness = iMinThickness; _maxThickness = iMaxThickness; _orientation = iOrientation; _orientation.normalize(); _clamp = clamp; } int CalligraphicShader::shade(Stroke &ioStroke) const { Interface0DIterator v; Functions0D::VertexOrientation2DF0D fun; StrokeVertex *sv; for (v = ioStroke.verticesBegin(); !v.isEnd(); ++v) { real thickness; if (fun(v) < 0) { return -1; } Vec2f vertexOri(fun.result); Vec2r ori2d(-vertexOri[1], vertexOri[0]); ori2d.normalizeSafe(); real scal = ori2d * _orientation; sv = dynamic_cast(&(*v)); if (_clamp && (scal < 0)) { scal = 0.0; sv->attribute().setColor(1, 1, 1); } else { scal = fabs(scal); sv->attribute().setColor(0, 0, 0); } thickness = _minThickness + scal * (_maxThickness - _minThickness); if (thickness < 0.0) { thickness = 0.0; } sv->attribute().setThickness(thickness / 2.0, thickness / 2.0); } return 0; } ///////////////////////////////////////// // // SPATIAL NOISE SHADER // ///////////////////////////////////////// static const uint NB_VALUE_NOISE = 512; SpatialNoiseShader::SpatialNoiseShader( float iAmount, float ixScale, int nbOctave, bool smooth, bool pureRandom) { _amount = iAmount; if (ixScale == 0) { _xScale = 0; } else { _xScale = 1.0 / ixScale / real(NB_VALUE_NOISE); } _nbOctave = nbOctave; _smooth = smooth; _pureRandom = pureRandom; } int SpatialNoiseShader::shade(Stroke &ioStroke) const { Interface0DIterator v, v2; v = ioStroke.verticesBegin(); Vec2r p(v->getProjectedX(), v->getProjectedY()); v2 = v; ++v2; Vec2r p0(v2->getProjectedX(), v2->getProjectedY()); p0 = p + 2 * (p - p0); StrokeVertex *sv; sv = dynamic_cast(&(*v)); real initU = sv->strokeLength() * real(NB_VALUE_NOISE); if (_pureRandom) { initU += RandGen::drand48() * real(NB_VALUE_NOISE); } Functions0D::VertexOrientation2DF0D fun; while (!v.isEnd()) { sv = dynamic_cast(&(*v)); Vec2r p(sv->getPoint()); if (fun(v) < 0) { return -1; } Vec2r vertexOri(fun.result); Vec2r ori2d(vertexOri[0], vertexOri[1]); ori2d = Vec2r(p - p0); ori2d.normalizeSafe(); PseudoNoise mynoise; real bruit; if (_smooth) { bruit = mynoise.turbulenceSmooth(_xScale * sv->curvilinearAbscissa() + initU, _nbOctave); } else { bruit = mynoise.turbulenceLinear(_xScale * sv->curvilinearAbscissa() + initU, _nbOctave); } Vec2r noise(-ori2d[1] * _amount * bruit, ori2d[0] * _amount * bruit); sv->setPoint(p[0] + noise[0], p[1] + noise[1]); p0 = p; ++v; } ioStroke.UpdateLength(); return 0; } ///////////////////////////////////////// // // SMOOTHING SHADER // ///////////////////////////////////////// SmoothingShader::SmoothingShader(int iNbIteration, real iFactorPoint, real ifactorCurvature, real iFactorCurvatureDifference, real iAnisoPoint, real iAnisoNormal, real iAnisoCurvature, real iCarricatureFactor) { _nbIterations = iNbIteration; _factorCurvature = ifactorCurvature; _factorCurvatureDifference = iFactorCurvatureDifference; _anisoNormal = iAnisoNormal; _anisoCurvature = iAnisoCurvature; _carricatureFactor = iCarricatureFactor; _factorPoint = iFactorPoint; _anisoPoint = iAnisoPoint; } int SmoothingShader::shade(Stroke &ioStroke) const { // cerr << " Smoothing a stroke " << endl; Smoother smoother(ioStroke); smoother.smooth(_nbIterations, _factorPoint, _factorCurvature, _factorCurvatureDifference, _anisoPoint, _anisoNormal, _anisoCurvature, _carricatureFactor); return 0; } // SMOOTHER //////////////////////////// Smoother::Smoother(Stroke &ioStroke) { _stroke = &ioStroke; _nbVertices = ioStroke.vertices_size(); _vertex = new Vec2r[_nbVertices]; _curvature = new real[_nbVertices]; _normal = new Vec2r[_nbVertices]; StrokeInternal::StrokeVertexIterator v, vend; int i = 0; for (v = ioStroke.strokeVerticesBegin(), vend = ioStroke.strokeVerticesEnd(); v != vend; ++v, ++i) { _vertex[i] = (v)->getPoint(); } Vec2r vec_tmp(_vertex[0] - _vertex[_nbVertices - 1]); _isClosedCurve = (vec_tmp.norm() < M_EPSILON); _safeTest = (_nbVertices > 4); } Smoother::~Smoother() { delete[] _vertex; delete[] _curvature; delete[] _normal; } void Smoother::smooth(int nbIteration, real iFactorPoint, real ifactorCurvature, real iFactorCurvatureDifference, real iAnisoPoint, real iAnisoNormal, real iAnisoCurvature, real iCarricatureFactor) { _factorCurvature = ifactorCurvature; _factorCurvatureDifference = iFactorCurvatureDifference; _anisoNormal = iAnisoNormal; _anisoCurvature = iAnisoCurvature; _carricatureFactor = iCarricatureFactor; _factorPoint = iFactorPoint; _anisoPoint = iAnisoPoint; for (int i = 0; i < nbIteration; ++i) { iteration(); } copyVertices(); } static real edgeStopping(real x, real sigma) { if (sigma == 0.0) { return 1.0; } return exp(-x * x / (sigma * sigma)); } void Smoother::iteration() { computeCurvature(); for (int i = 1; i < (_nbVertices - 1); ++i) { real motionNormal = _factorCurvature * _curvature[i] * edgeStopping(_curvature[i], _anisoNormal); real diffC1 = _curvature[i] - _curvature[i - 1]; real diffC2 = _curvature[i] - _curvature[i + 1]; real motionCurvature = edgeStopping(diffC1, _anisoCurvature) * diffC1 + edgeStopping(diffC2, _anisoCurvature) * diffC2; //_factorCurvatureDifference; motionCurvature *= _factorCurvatureDifference; // motionCurvature = _factorCurvatureDifference * (diffC1 + diffC2); if (_safeTest) { _vertex[i] = Vec2r(_vertex[i] + (motionNormal + motionCurvature) * _normal[i]); } Vec2r v1(_vertex[i - 1] - _vertex[i]); Vec2r v2(_vertex[i + 1] - _vertex[i]); real d1 = v1.norm(); real d2 = v2.norm(); _vertex[i] = Vec2r( _vertex[i] + _factorPoint * edgeStopping(d2, _anisoPoint) * (_vertex[i - 1] - _vertex[i]) + _factorPoint * edgeStopping(d1, _anisoPoint) * (_vertex[i + 1] - _vertex[i])); } if (_isClosedCurve) { real motionNormal = _factorCurvature * _curvature[0] * edgeStopping(_curvature[0], _anisoNormal); real diffC1 = _curvature[0] - _curvature[_nbVertices - 2]; real diffC2 = _curvature[0] - _curvature[1]; real motionCurvature = edgeStopping(diffC1, _anisoCurvature) * diffC1 + edgeStopping(diffC2, _anisoCurvature) * diffC2; //_factorCurvatureDifference; motionCurvature *= _factorCurvatureDifference; // motionCurvature = _factorCurvatureDifference * (diffC1 + diffC2); _vertex[0] = Vec2r(_vertex[0] + (motionNormal + motionCurvature) * _normal[0]); _vertex[_nbVertices - 1] = _vertex[0]; } } void Smoother::computeCurvature() { int i; Vec2r BA, BC, normalCurvature; for (i = 1; i < (_nbVertices - 1); ++i) { BA = _vertex[i - 1] - _vertex[i]; BC = _vertex[i + 1] - _vertex[i]; real lba = BA.norm(), lbc = BC.norm(); BA.normalizeSafe(); BC.normalizeSafe(); normalCurvature = BA + BC; _normal[i] = Vec2r(-(BC - BA)[1], (BC - BA)[0]); _normal[i].normalizeSafe(); _curvature[i] = normalCurvature * _normal[i]; if (lba + lbc > M_EPSILON) { _curvature[i] /= (0.5 * lba + lbc); } } _curvature[0] = _curvature[1]; _curvature[_nbVertices - 1] = _curvature[_nbVertices - 2]; Vec2r di(_vertex[1] - _vertex[0]); _normal[0] = Vec2r(-di[1], di[0]); _normal[0].normalizeSafe(); di = _vertex[_nbVertices - 1] - _vertex[_nbVertices - 2]; _normal[_nbVertices - 1] = Vec2r(-di[1], di[0]); _normal[_nbVertices - 1].normalizeSafe(); if (_isClosedCurve) { BA = _vertex[_nbVertices - 2] - _vertex[0]; BC = _vertex[1] - _vertex[0]; real lba = BA.norm(), lbc = BC.norm(); BA.normalizeSafe(); BC.normalizeSafe(); normalCurvature = BA + BC; _normal[i] = Vec2r(-(BC - BA)[1], (BC - BA)[0]); _normal[i].normalizeSafe(); _curvature[i] = normalCurvature * _normal[i]; if (lba + lbc > M_EPSILON) { _curvature[i] /= (0.5 * lba + lbc); } _normal[_nbVertices - 1] = _normal[0]; _curvature[_nbVertices - 1] = _curvature[0]; } } void Smoother::copyVertices() { int i = 0; StrokeInternal::StrokeVertexIterator v, vend; for (v = _stroke->strokeVerticesBegin(), vend = _stroke->strokeVerticesEnd(); v != vend; ++v) { const Vec2r p0((v)->getPoint()); const Vec2r p1(_vertex[i]); Vec2r p(p0 + _carricatureFactor * (p1 - p0)); (v)->setPoint(p[0], p[1]); ++i; } _stroke->UpdateLength(); } } /* namespace Freestyle */