Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp')
-rwxr-xr-xsource/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp1168
1 files changed, 1168 insertions, 0 deletions
diff --git a/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp b/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp
new file mode 100755
index 00000000000..a342fed27e3
--- /dev/null
+++ b/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp
@@ -0,0 +1,1168 @@
+
+//
+// Copyright (C) : Please refer to the COPYRIGHT file distributed
+// with this source distribution.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "StrokeRenderer.h"
+#include <fstream>
+#include "BasicStrokeShaders.h"
+#include "../system/PseudoNoise.h"
+#include "../system/RandGen.h"
+#include "../view_map/Functions0D.h"
+#include "../view_map/Functions1D.h"
+#include "AdvancedFunctions0D.h"
+#include "AdvancedFunctions1D.h"
+#include "StrokeIterators.h"
+#include "../system/StringUtils.h"
+#include "StrokeIO.h"
+
+//soc #include <qimage.h>
+//soc #include <QString>
+extern "C" {
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+}
+
+// Internal function
+
+// soc
+// void convert(const QImage& iImage, float **oArray, unsigned &oSize) {
+// oSize = iImage.width();
+// *oArray = new float[oSize];
+// for(unsigned i=0; i<oSize; ++i) {
+// QRgb rgb = iImage.pixel(i,0);
+// (*oArray)[i] = ((float)qBlue(rgb))/255.f;
+// }
+// }
+void convert(ImBuf *imBuf, float **oArray, unsigned &oSize) {
+ oSize = imBuf->x;
+ *oArray = new float[oSize];
+
+ char *pix;
+ for(unsigned i=0; i < oSize; ++i) {
+ pix = (char*) imBuf->rect + i*4;
+ (*oArray)[i] = ((float) pix[2] )/255.f;
+ }
+}
+
+namespace StrokeShaders {
+
+ //
+ // Thickness modifiers
+ //
+ //////////////////////////////////////////////////////////
+
+ int ConstantThicknessShader::shade(Stroke& stroke) const
+ {
+ StrokeInternal::StrokeVertexIterator v, vend;
+ int i=0;
+ int size = stroke.strokeVerticesSize();
+ for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ v!=vend;
+ ++v)
+ {
+ if((1 == i) || (size-2 == i))
+ v->attribute().setThickness(_thickness/4.0,_thickness/4.0);
+ if((0 == i) || (size-1 == i))
+ v->attribute().setThickness(0,0);
+
+ v->attribute().setThickness(_thickness/2.0, _thickness/2.0);
+ }
+ return 0;
+ }
+
+ int ConstantExternThicknessShader::shade(Stroke& stroke) const
+ {
+ StrokeInternal::StrokeVertexIterator v, vend;
+ int i=0;
+ int size = stroke.strokeVerticesSize();
+ for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ v!=vend;
+ ++v)
+ {
+ if((1 == i) || (size-2 == i))
+ v->attribute().setThickness(_thickness/2.0,0);
+ if((0 == i) || (size-1 == i))
+ v->attribute().setThickness(0,0);
+
+ v->attribute().setThickness(_thickness, 0);
+ }
+ return 0;
+ }
+
+ int IncreasingThicknessShader::shade(Stroke& stroke) const
+ {
+
+ int n=stroke.strokeVerticesSize()-1;
+ StrokeInternal::StrokeVertexIterator v, vend;
+ int i=0;
+ for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ v!=vend;
+
+ ++v)
+ {
+ float t;
+ if(i < (float)n/2.f)
+ t = (1.0-(float)i/(float)n)*_ThicknessMin + (float)i/(float)n*_ThicknessMax;
+ else
+ t = (1.0-(float)i/(float)n)*_ThicknessMax + (float)i/(float)n*_ThicknessMin;
+ v->attribute().setThickness(t/2.0, t/2.0);
+ ++i;
+ }
+ return 0;
+ }
+
+ int ConstrainedIncreasingThicknessShader::shade(Stroke& stroke) const
+ {
+ float slength = stroke.getLength2D();
+ float maxT = min(_ratio*slength,_ThicknessMax);
+ int n=stroke.strokeVerticesSize()-1;
+ StrokeInternal::StrokeVertexIterator v, vend;
+ int i=0;
+ for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ v!=vend;
+
+ ++v)
+ {
+ float t;
+ if(i < (float)n/2.f)
+ t = (1.0-(float)i/(float)n)*_ThicknessMin + (float)i/(float)n*maxT;
+ else
+ t = (1.0-(float)i/(float)n)*maxT + (float)i/(float)n*_ThicknessMin;
+ v->attribute().setThickness(t/2.0, t/2.0);
+ if(i == n-1)
+ v->attribute().setThickness(_ThicknessMin/2.0, _ThicknessMin/2.0);
+ ++i;
+ }
+ return 0;
+ }
+
+
+ int LengthDependingThicknessShader::shade(Stroke& stroke) const
+ {
+ float step = (_maxThickness-_minThickness)/3.f;
+ float l = stroke.getLength2D();
+ float thickness = 0.0;
+ if(l>300.f)
+ thickness = _minThickness+3.f*step;
+ else if((l< 300.f) && (l>100.f))
+ thickness = _minThickness+2.f*step;
+ else if((l<100.f) && (l>50.f))
+ thickness = _minThickness+1.f*step;
+ else if(l< 50.f)
+ thickness = _minThickness;
+
+ StrokeInternal::StrokeVertexIterator v, vend;
+ int i=0;
+ int size = stroke.strokeVerticesSize();
+ for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ v!=vend;
+ ++v)
+ {
+ if((1 == i) || (size-2 == i))
+ v->attribute().setThickness(thickness/4.0,thickness/4.0);
+ if((0 == i) || (size-1 == i))
+ v->attribute().setThickness(0,0);
+
+ v->attribute().setThickness(thickness/2.0, thickness/2.0);
+ }
+ return 0;
+ }
+
+
+ ThicknessVariationPatternShader::ThicknessVariationPatternShader(const string pattern_name,
+ float iMinThickness,
+ float iMaxThickness,
+ bool stretch)
+ : StrokeShader() {
+ _stretch = stretch;
+ _minThickness = iMinThickness;
+ _maxThickness = iMaxThickness;
+ ImBuf *image = 0; //soc
+ vector<string> pathnames;
+ StringUtils::getPathName(TextureManager::Options::getPatternsPath(),
+ pattern_name,
+ pathnames);
+ for (vector<string>::const_iterator j = pathnames.begin(); j != pathnames.end(); j++) {
+ ifstream ifs(j->c_str());
+ if (ifs.is_open()) {
+ //soc image.load(j->c_str());
+ image = IMB_loadiffname(j->c_str(), 0);
+ break;
+ }
+ }
+ if (image == 0) //soc
+ cerr << "Error: cannot find pattern \"" << pattern_name
+ << "\" - check the path in the Options" << endl;
+ else
+ convert(image, &_aThickness, _size);
+ }
+
+ int ThicknessVariationPatternShader::shade(Stroke& stroke) const
+ {
+ StrokeInternal::StrokeVertexIterator v, vend;
+ float *array = 0;
+ int size;
+ array = _aThickness;
+ size = _size;
+ // }
+ int vert_size = stroke.strokeVerticesSize();
+ int sig = 0;
+ unsigned index;
+ const float* originalThickness;
+ for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ v!=vend;
+ ++v)
+ {
+ originalThickness = v->attribute().getThickness();
+ if (_stretch) {
+ float tmp = v->u()*(_size-1);
+ index = (unsigned)floor(tmp);
+ if((tmp-index) > (index+1-tmp))
+ ++index;
+ }
+ else
+ index = (unsigned)floor(v->curvilinearAbscissa());
+ index %= _size;
+ float thicknessR = array[index]*originalThickness[0];
+ float thicknessL = array[index]*originalThickness[1];
+ if(thicknessR+thicknessL < _minThickness)
+ {
+ thicknessL = _minThickness/2.f;
+ thicknessR = _minThickness/2.f;
+ }
+ if(thicknessR+thicknessL > _maxThickness)
+ {
+ thicknessL = _maxThickness/2.f;
+ thicknessR = _maxThickness/2.f;
+ }
+ if((sig==0) || (sig==vert_size-1))
+ v->attribute().setThickness(1, 1);
+ else
+ v->attribute().setThickness(thicknessR, thicknessL);
+ ++sig;
+ }
+ return 0;
+ }
+
+
+ static const unsigned NB_VALUE_NOISE = 512;
+ ThicknessNoiseShader::ThicknessNoiseShader()
+ :StrokeShader()
+ {_amplitude=1.f;_scale=1.f/2.f/(float)NB_VALUE_NOISE;}
+
+ ThicknessNoiseShader::ThicknessNoiseShader(float iAmplitude, float iPeriod)
+ :StrokeShader()
+ {_amplitude=iAmplitude;_scale=1.f/iPeriod/(float)NB_VALUE_NOISE;}
+
+
+ int ThicknessNoiseShader::shade(Stroke& stroke) const
+ {
+ StrokeInternal::StrokeVertexIterator v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ real initU1=v->strokeLength()*real(NB_VALUE_NOISE)+RandGen::drand48()*real(NB_VALUE_NOISE);
+ real initU2=v->strokeLength()*real(NB_VALUE_NOISE)+RandGen::drand48()*real(NB_VALUE_NOISE);
+
+ real bruit, bruit2;
+ PseudoNoise mynoise, mynoise2;
+ for(;
+ v!=vend;
+ ++v)
+ {
+
+ bruit=mynoise.turbulenceSmooth(_scale*v->curvilinearAbscissa()+initU1,
+ 2); //2 : nbOctaves
+ bruit2=mynoise2.turbulenceSmooth(_scale*v->curvilinearAbscissa()+initU2,
+ 2); //2 : nbOctaves
+ const float *originalThickness = v->attribute().getThickness();
+ float r = bruit*_amplitude+originalThickness[0];
+ float l = bruit2*_amplitude+originalThickness[1];
+ v->attribute().setThickness(r,l);
+ }
+
+ return 0;
+ }
+
+ //
+ // Color shaders
+ //
+ ///////////////////////////////////////////////////////////////////////////////
+
+ int ConstantColorShader::shade(Stroke& stroke) const
+ {
+ StrokeInternal::StrokeVertexIterator v, vend;
+ for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ v!=vend;
+ ++v)
+ {
+ v->attribute().setColor(_color[0], _color[1], _color[2]);
+ v->attribute().setAlpha(_color[3]);
+ }
+ return 0;
+ }
+
+ int IncreasingColorShader::shade(Stroke& stroke) const
+ {
+ StrokeInternal::StrokeVertexIterator v, vend;
+ int n=stroke.strokeVerticesSize()-1;
+ int yo=0;
+ float newcolor[4];
+ for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ v!=vend;
+
+ ++v)
+ {
+ for(int i=0; i<4; ++i)
+ {
+ newcolor[i] = (1.0-(float)yo/(float)n)*_colorMin[i] + (float)yo/(float)n*_colorMax[i];
+ }
+ v->attribute().setColor(newcolor[0], newcolor[1], newcolor[2]);
+ v->attribute().setAlpha(newcolor[3]);
+ ++yo;
+ }
+ return 0;
+ }
+
+ ColorVariationPatternShader::ColorVariationPatternShader(const string pattern_name,
+ bool stretch)
+ : StrokeShader() {
+ _stretch = stretch;
+ ImBuf *image = 0; //soc
+ vector<string> pathnames;
+ StringUtils::getPathName(TextureManager::Options::getPatternsPath(),
+ pattern_name,
+ pathnames);
+ for (vector<string>::const_iterator j = pathnames.begin(); j != pathnames.end(); j++) {
+ ifstream ifs(j->c_str());
+ if (ifs.is_open()) {
+ image = IMB_loadiffname(j->c_str(), 0); //soc
+ break;
+ }
+ }
+ if (image == 0) //soc
+ cerr << "Error: cannot find pattern \"" << pattern_name
+ << "\" - check the path in the Options" << endl;
+ else
+ convert(image, &_aVariation, _size);
+ }
+
+ int ColorVariationPatternShader::shade(Stroke& stroke) const
+ {
+ StrokeInternal::StrokeVertexIterator v, vend;
+ unsigned index;
+ for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ v!=vend;
+ ++v)
+ {
+ const float *originalColor = v->attribute().getColor();
+ if (_stretch) {
+ float tmp = v->u()*(_size-1);
+ index = (unsigned)floor(tmp);
+ if((tmp-index) > (index+1-tmp))
+ ++index;
+ }
+ else
+ index = (unsigned)floor(v->curvilinearAbscissa());
+ index %= _size;
+ float r = _aVariation[index]*originalColor[0];
+ float g = _aVariation[index]*originalColor[1];
+ float b = _aVariation[index]*originalColor[2];
+ v->attribute().setColor(r,g,b);
+ }
+ return 0;
+ }
+
+ int MaterialColorShader::shade(Stroke& stroke) const
+ {
+ Interface0DIterator v, vend;
+ Functions0D::MaterialF0D fun;
+ StrokeVertex *sv;
+ for(v=stroke.verticesBegin(), vend=stroke.verticesEnd();
+ v!=vend;
+ ++v)
+ {
+ if (fun(v) < 0)
+ return -1;
+ const float *diffuse = fun.result.diffuse();
+ sv = dynamic_cast<StrokeVertex*>(&(*v));
+ sv->attribute().setColor(diffuse[0]*_coefficient, diffuse[1]*_coefficient, diffuse[2]*_coefficient);
+ sv->attribute().setAlpha(diffuse[3]);
+ }
+ return 0;
+ }
+
+
+ int CalligraphicColorShader::shade(Stroke& stroke) const
+ {
+ Interface0DIterator v;
+ Functions0D::VertexOrientation2DF0D fun;
+ StrokeVertex* sv;
+ for(v=stroke.verticesBegin();
+ !v.isEnd();
+ ++v)
+ {
+ if (fun(v) < 0)
+ return -1;
+ Vec2f vertexOri(fun.result);
+ Vec2d ori2d(-vertexOri[1], vertexOri[0]);
+ ori2d.normalizeSafe();
+ real scal = ori2d * _orientation;
+ sv = dynamic_cast<StrokeVertex*>(&(*v));
+ if ((scal<0))
+ sv->attribute().setColor(0,0,0);
+ else
+ sv->attribute().setColor(1,1,1);
+ }
+ return 0;
+ }
+
+
+ ColorNoiseShader::ColorNoiseShader()
+ :StrokeShader()
+ {_amplitude=1.f;_scale=1.f/2.f/(float)NB_VALUE_NOISE;}
+
+ ColorNoiseShader::ColorNoiseShader(float iAmplitude, float iPeriod)
+ :StrokeShader()
+ {_amplitude=iAmplitude;_scale=1.f/iPeriod/(float)NB_VALUE_NOISE;}
+
+
+ int ColorNoiseShader::shade(Stroke& stroke) const
+ {
+ StrokeInternal::StrokeVertexIterator v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ real initU=v->strokeLength()*real(NB_VALUE_NOISE)+RandGen::drand48()*real(NB_VALUE_NOISE);
+
+ real bruit;
+ PseudoNoise mynoise;
+ for(;
+ v!=vend;
+ ++v)
+ {
+
+ bruit=mynoise.turbulenceSmooth(_scale*v->curvilinearAbscissa()+initU,
+ 2); //2 : nbOctaves
+ const float *originalColor = v->attribute().getColor();
+ float r = bruit*_amplitude+originalColor[0];
+ float g = bruit*_amplitude+originalColor[1];
+ float b = bruit*_amplitude+originalColor[2];
+ v->attribute().setColor(r,g,b);
+ }
+
+ return 0;
+ }
+
+
+ //
+ // Texture Shaders
+ //
+ ///////////////////////////////////////////////////////////////////////////////
+
+ int TextureAssignerShader::shade(Stroke& stroke) const
+ {
+ // getBrushTextureIndex(TEXTURES_DIR "/brushes/charcoalAlpha.bmp", Stroke::HUMID_MEDIUM);
+ // getBrushTextureIndex(TEXTURES_DIR "/brushes/washbrushAlpha.bmp", Stroke::HUMID_MEDIUM);
+ // getBrushTextureIndex(TEXTURES_DIR "/brushes/oil.bmp", Stroke::HUMID_MEDIUM);
+ // getBrushTextureIndex(TEXTURES_DIR "/brushes/oilnoblend.bmp", Stroke::HUMID_MEDIUM);
+ // getBrushTextureIndex(TEXTURES_DIR "/brushes/charcoalAlpha.bmp", Stroke::DRY_MEDIUM);
+ // getBrushTextureIndex(TEXTURES_DIR "/brushes/washbrushAlpha.bmp", Stroke::DRY_MEDIUM);
+ // getBrushTextureIndex(TEXTURES_DIR "/brushes/opaqueDryBrushAlpha.bmp", Stroke::OPAQUE_MEDIUM);
+ // getBrushTextureIndex(TEXTURES_DIR "/brushes/opaqueBrushAlpha.bmp", Stroke::OPAQUE_MEDIUM);
+
+ TextureManager * instance = TextureManager::getInstance();
+ if(!instance)
+ return 0;
+ string pathname;
+ Stroke::MediumType mediumType;
+ bool hasTips = false;
+ switch(_textureId)
+ {
+ case 0:
+ //pathname = TextureManager::Options::getBrushesPath() + "/charcoalAlpha.bmp";
+ pathname = "/charcoalAlpha.bmp";
+ mediumType = Stroke::HUMID_MEDIUM;
+ hasTips = false;
+ break;
+ case 1:
+ pathname = "/washbrushAlpha.bmp";
+ mediumType = Stroke::HUMID_MEDIUM;
+ hasTips = true;
+ break;
+ case 2:
+ pathname = "/oil.bmp";
+ mediumType = Stroke::HUMID_MEDIUM;
+ hasTips = true;
+ break;
+ case 3:
+ pathname = "/oilnoblend.bmp";
+ mediumType = Stroke::HUMID_MEDIUM;
+ hasTips = true;
+ break;
+ case 4:
+ pathname = "/charcoalAlpha.bmp";
+ mediumType = Stroke::DRY_MEDIUM;
+ hasTips = false;
+ break;
+ case 5:
+ mediumType = Stroke::DRY_MEDIUM;
+ hasTips = true;
+ break;
+ case 6:
+ pathname = "/opaqueDryBrushAlpha.bmp";
+ mediumType = Stroke::OPAQUE_MEDIUM;
+ hasTips = true;
+ break;
+ case 7:
+ pathname = "/opaqueBrushAlpha.bmp";
+ mediumType = Stroke::OPAQUE_MEDIUM;
+ hasTips = true;
+ break;
+ default:
+ pathname = "/smoothAlpha.bmp";
+ mediumType = Stroke::OPAQUE_MEDIUM;
+ hasTips = false;
+ break;
+ }
+ unsigned int texId = instance->getBrushTextureIndex(pathname, mediumType);
+ stroke.setMediumType(mediumType);
+ stroke.setTips(hasTips);
+ stroke.setTextureId(texId);
+ return 0;
+ }
+
+ // FIXME
+ int StrokeTextureShader::shade(Stroke& stroke) const
+ {
+ TextureManager * instance = TextureManager::getInstance();
+ if(!instance)
+ return 0;
+ string pathname = TextureManager::Options::getBrushesPath() + "/" + _texturePath;
+ unsigned int texId = instance->getBrushTextureIndex(pathname, _mediumType);
+ stroke.setMediumType(_mediumType);
+ stroke.setTips(_tips);
+ stroke.setTextureId(texId);
+ return 0;
+ }
+
+ //
+ // Geometry Shaders
+ //
+ ///////////////////////////////////////////////////////////////////////////////
+
+ int BackboneStretcherShader::shade(Stroke& stroke) const
+ {
+ float l=stroke.getLength2D();
+ if(l <= 1e-6)
+ return 0;
+
+ StrokeInternal::StrokeVertexIterator v0=stroke.strokeVerticesBegin();
+ StrokeInternal::StrokeVertexIterator v1=v0;++v1;
+ StrokeInternal::StrokeVertexIterator vn=stroke.strokeVerticesEnd();--vn;
+ StrokeInternal::StrokeVertexIterator vn_1=vn;--vn_1;
+
+
+ Vec2d first((v0)->x(), (v0)->y());
+ Vec2d last((vn)->x(), (vn)->y());
+
+ Vec2d d1(first-Vec2d((v1)->x(), (v1)->y()));
+ d1.normalize();
+ Vec2d dn(last-Vec2d((vn_1)->x(), (vn_1)->y()));
+ dn.normalize();
+
+ Vec2d newFirst(first+_amount*d1);
+ (v0)->setPoint(newFirst[0], newFirst[1]);
+ Vec2d newLast(last+_amount*dn);
+ (vn)->setPoint(newLast[0], newLast[1]);
+
+ stroke.UpdateLength();
+ return 0;
+ }
+
+ int SamplingShader::shade(Stroke& stroke) const
+ {
+ stroke.Resample(_sampling);
+ return 0;
+ }
+
+ int ExternalContourStretcherShader::shade(Stroke& stroke) const
+ {
+ //float l=stroke.getLength2D();
+ Interface0DIterator it=stroke.verticesBegin();
+ Functions0D::Normal2DF0D fun;
+ StrokeVertex* sv;
+ while (!it.isEnd())
+ {
+ if (fun(it) < 0)
+ return -1;
+ Vec2f n(fun.result);
+ sv = dynamic_cast<StrokeVertex*>(&(*it));
+ Vec2d newPoint(sv->x()+_amount*n.x(), sv->y()+_amount*n.y());
+ sv->setPoint(newPoint[0], newPoint[1]);
+ ++it;
+ }
+ stroke.UpdateLength();
+ return 0;
+ }
+
+ int BSplineShader::shade(Stroke& stroke) const
+ {
+ if(stroke.strokeVerticesSize() < 4)
+ return 0;
+
+ // Find the new vertices
+ vector<Vec2d> newVertices;
+ double t=0.f;
+ float _sampling = 5.f;
+
+ StrokeInternal::StrokeVertexIterator p0,p1,p2,p3, end;
+ p0 = stroke.strokeVerticesBegin();
+ p1 = p0;
+ p2 = p1;
+ p3 = p2;
+ end = stroke.strokeVerticesEnd();
+ double a[4],b[4];
+ int n=0;
+ while(p1 != end)
+ {
+ // if(p1 == end)
+ // p1 = p0;
+ if(p2 == end)
+ p2 = p1;
+ if(p3 == end)
+ p3 = p2;
+ // compute new matrix
+ a[0] = (-(p0)->x()+3*(p1)->x()-3*(p2)->x()+(p3)->x())/6.0;
+ a[1] = (3*(p0)->x()-6*(p1)->x()+3*(p2)->x())/6.0;
+ a[2] = (-3*(p0)->x()+3*(p2)->x())/6.0;
+ a[3] = ((p0)->x()+4*(p1)->x()+(p2)->x())/6.0;
+
+ b[0] = (-(p0)->y()+3*(p1)->y()-3*(p2)->y()+(p3)->y())/6.0;
+ b[1] = (3*(p0)->y()-6*(p1)->y()+3*(p2)->y())/6.0;
+ b[2] = (-3*(p0)->y()+3*(p2)->y())/6.0;
+ b[3] = ((p0)->y()+4*(p1)->y()+(p2)->y())/6.0;
+
+
+ // draw the spline depending on resolution:
+ Vec2d p1p2((p2)->x()-(p1)->x(), (p2)->y()-(p1)->y());
+ double norm = p1p2.norm();
+ //t = _sampling/norm;
+ t=0;
+ while(t<1)
+ {
+ newVertices.push_back(Vec2d((a[3] + t*(a[2] + t*(a[1] + t*a[0]))),
+ (b[3] + t*(b[2] + t*(b[1] + t*b[0])))));
+ t = t + _sampling/norm;
+ }
+ if(n > 2)
+ {
+ ++p0;
+ ++p1;
+ ++p2;
+ ++p3;
+ }
+ else
+ {
+ if(n==0)
+ ++p3;
+ if(n==1)
+ {++p2;++p3;}
+ if(n==2)
+ {++p1;++p2;++p3;}
+ ++n;
+ }
+ }
+ //last point:
+ newVertices.push_back(Vec2d((p0)->x(), (p0)->y()));
+
+ int originalSize = newVertices.size();
+ _sampling = stroke.ComputeSampling(originalSize);
+
+ // Resample and set x,y coordinates
+ stroke.Resample(_sampling);
+ int newsize = stroke.strokeVerticesSize();
+
+ int nExtraVertex=0;
+ if(newsize < originalSize)
+ cerr << "Warning: unsufficient resampling" << endl;
+ else
+ {
+ nExtraVertex = newsize - originalSize;
+ }
+
+ // assigns the new coordinates:
+ vector<Vec2d>::iterator p,pend;
+ p=newVertices.begin();pend=newVertices.end();
+ vector<Vec2d>::iterator last = p;
+ n=0;
+ StrokeInternal::StrokeVertexIterator it=stroke.strokeVerticesBegin(), itend=stroke.strokeVerticesEnd();
+ it=stroke.strokeVerticesBegin();
+ for(;
+ ((it!=itend) && (p!=pend));
+ ++it)
+ {
+ it->setX(p->x());
+ it->setY(p->y());
+ last = p;
+ ++p;
+ ++n;
+ }
+ // nExtraVertex should stay unassigned
+ for(int i=0; i< nExtraVertex; ++i)
+ {
+ it->setX(last->x());
+ it->setY(last->y());
+ if(it.isEnd())
+ cerr << "Warning: Problem encountered while creating B-spline" << endl;
+ ++it;
+ ++n;
+ }
+ stroke.UpdateLength();
+ return 0;
+ }
+
+ //!! Bezier curve stroke shader
+ int BezierCurveShader::shade(Stroke& stroke) const
+ {
+ if(stroke.strokeVerticesSize() < 4)
+ return 0;
+
+ // Build the Bezier curve from this set of data points:
+ vector<Vec2d> data;
+ StrokeInternal::StrokeVertexIterator v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd();
+ data.push_back(Vec2d(v->x(), v->y()));//first one
+ StrokeInternal::StrokeVertexIterator previous = v;++v;
+ for(;
+ v!=vend;
+ ++v)
+ {
+ if(!((fabs(v->x() -(previous)->x())<M_EPSILON) && ((fabs(v->y() - (previous)->y())<M_EPSILON))))
+ data.push_back(Vec2d(v->x(), v->y()));
+ previous = v;
+ }
+
+ // Vec2d tmp;bool equal=false;
+ // if(data.front() == data.back())
+ // {
+ // tmp = data.back();
+ // data.pop_back();
+ // equal=true;
+ // }
+ // here we build the bezier curve
+ BezierCurve bcurve(data, _error);
+
+ // bad performances are here !!! // FIXME
+ vector<Vec2d> CurveVertices;
+ vector<BezierCurveSegment*>& bsegments = bcurve.segments();
+ vector<BezierCurveSegment*>::iterator s=bsegments.begin(),send=bsegments.end();
+ vector<Vec2d>& segmentsVertices = (*s)->vertices();
+ vector<Vec2d>::iterator p,pend;
+ // first point
+ CurveVertices.push_back(segmentsVertices[0]);
+ for(;
+ s!=send;
+ ++s)
+ {
+ segmentsVertices = (*s)->vertices();
+ p=segmentsVertices.begin(); pend=segmentsVertices.end();
+ ++p;
+ for(;
+ p!=pend;
+ ++p)
+ {
+ CurveVertices.push_back((*p));
+ }
+ }
+
+ //if(equal)
+ // if(data.back() == data.front())
+ // {
+ // vector<Vec2d>::iterator d=data.begin(), dend=data.end();
+ // cout << "ending point = starting point" << endl;
+ // cout << "---------------DATA----------" << endl;
+ // for(;
+ // d!=dend;
+ // ++d)
+ // {
+ // cout << d->x() << "-" << d->y() << endl;
+ // }
+ // cout << "--------------BEZIER RESULT----------" << endl;
+ // d=CurveVertices.begin(), dend=CurveVertices.end();
+ // for(;d!=dend;++d)
+ // {
+ // cout << d->x() << "-" << d->y() << endl;
+ // }
+ // }
+
+ // Resample the Stroke depending on the number of
+ // vertices of the bezier curve:
+ int originalSize = CurveVertices.size();
+ //float sampling = stroke.ComputeSampling(originalSize);
+ //stroke.Resample(sampling);
+ stroke.Resample(originalSize);
+ int newsize = stroke.strokeVerticesSize();
+ int nExtraVertex=0;
+ if(newsize < originalSize)
+ cerr << "Warning: unsufficient resampling" << endl;
+ else
+ {
+ //cout << "Oversampling" << endl;
+ nExtraVertex = newsize - originalSize;
+ if(nExtraVertex != 0)
+ cout << "Bezier Shader : Stroke " << stroke.getId() << " have not been resampled" << endl;
+ }
+
+ // assigns the new coordinates:
+ p=CurveVertices.begin();pend=CurveVertices.end();
+ vector<Vec2d>::iterator last = p;
+ int n=0;
+ StrokeInternal::StrokeVertexIterator it=stroke.strokeVerticesBegin(), itend=stroke.strokeVerticesEnd();
+ // while(p!=pend)
+ // {
+ // ++n;
+ // ++p;
+ // }
+ it=stroke.strokeVerticesBegin();
+ for(;
+ ((it!=itend) && (p!=pend));
+ ++it)
+ {
+ it->setX(p->x());
+ it->setY(p->y());
+ // double x = p->x();
+ // double y = p->y();
+ // cout << "x = " << x << "-" << "y = " << y << endl;
+ last = p;
+ ++p;
+ ++n;
+ }
+ stroke.UpdateLength();
+
+ // Deal with extra vertices:
+ if(nExtraVertex == 0)
+ return 0;
+
+ // nExtraVertex should stay unassigned
+ vector<StrokeAttribute> attributes;
+ vector<StrokeVertex*> verticesToRemove;
+ for(int i=0; i< nExtraVertex; ++i)
+ {
+ verticesToRemove.push_back(&(*it));
+ if(it.isEnd())
+ cout << "fucked up" << endl;
+ ++it;
+ ++n;
+ }
+ it=stroke.strokeVerticesBegin();
+ for(;
+ it!=itend;
+ ++it)
+ {
+ attributes.push_back(it->attribute());
+ }
+
+ for(vector<StrokeVertex*>::iterator vr=verticesToRemove.begin(), vrend=verticesToRemove.end();
+ vr!=vrend;
+ ++vr)
+ {
+ stroke.RemoveVertex(*vr);
+ }
+ it=stroke.strokeVerticesBegin();
+ itend=stroke.strokeVerticesEnd();
+ vector<StrokeAttribute>::iterator a=attributes.begin(), aend=attributes.end();
+ int index = 0;
+ int index1 = (int)floor((float)originalSize/2.0);
+ int index2 = index1+nExtraVertex;
+ for(;
+ (it!=itend) && (a!=aend);
+ ++it)
+ {
+ (it)->setAttribute(*a);
+ if((index <= index1)||(index>index2))
+ ++a;
+ ++index;
+ }
+ return 0;
+ }
+
+ int InflateShader::shade(Stroke& stroke) const
+ {
+ // we're computing the curvature variance of the stroke.(Combo 5)
+ // If it's too high, forget about it
+ Functions1D::Curvature2DAngleF1D fun;
+ if (fun(stroke) < 0)
+ return -1;
+ if (fun.result > _curvatureThreshold)
+ return 0;
+
+ Functions0D::VertexOrientation2DF0D ori_fun;
+ Functions0D::Curvature2DAngleF0D curv_fun;
+ Functions1D::Normal2DF1D norm_fun;
+ Interface0DIterator it=stroke.verticesBegin();
+ StrokeVertex* sv;
+ while (!it.isEnd())
+ {
+ if (ori_fun(it) < 0)
+ return -1;
+ Vec2f ntmp(ori_fun.result);
+ Vec2f n(ntmp.y(), -ntmp.x());
+ if (norm_fun(stroke) < 0)
+ return -1;
+ Vec2f strokeN(norm_fun.result);
+ if(n*strokeN < 0)
+ {
+ n[0] = -n[0];
+ n[1] = -n[1];
+ }
+ sv = dynamic_cast<StrokeVertex*>(&(*it));
+ float u=sv->u();
+ float t = 4.f*(0.25f - (u-0.5)*(u-0.5));
+ if (curv_fun(it) < 0)
+ return -1;
+ float curvature_coeff = (M_PI-curv_fun.result)/M_PI;
+ Vec2d newPoint(sv->x()+curvature_coeff*t*_amount*n.x(), sv->y()+curvature_coeff*t*_amount*n.y());
+ sv->setPoint(newPoint[0], newPoint[1]);
+ ++it;
+ }
+ stroke.UpdateLength();
+ return 0;
+ }
+
+ class CurvePiece
+ {
+ public:
+ StrokeInternal::StrokeVertexIterator _begin;
+ StrokeInternal::StrokeVertexIterator _last;
+ Vec2d A;
+ Vec2d B;
+ int size;
+ float _error;
+
+ CurvePiece(StrokeInternal::StrokeVertexIterator b, StrokeInternal::StrokeVertexIterator l, int iSize)
+ {
+ _begin = b;
+ _last = l;
+ A = Vec2d((_begin)->x(),(_begin)->y());
+ B = Vec2d((_last)->x(),(_last)->y());
+ size = iSize;
+ }
+
+ float error()
+ {
+ float maxE = 0.f;
+ for(StrokeInternal::StrokeVertexIterator it=_begin;
+ it!=_last;
+ ++it)
+ {
+ Vec2d P(it->x(), it->y());
+ float d = GeomUtils::distPointSegment(P,A,B);
+ if(d > maxE)
+ maxE = d;
+ }
+ _error = maxE;
+ return maxE;
+ }
+ //! Subdivides the curve into two pieces.
+ // The first piece is this same object (modified)
+ // the second piece is returned by the method
+ CurvePiece * subdivide()
+ {
+ StrokeInternal::StrokeVertexIterator it=_begin;
+ int actualSize = 1;
+ for(int i=0; i<size/2; ++i)
+ {
+ ++it;
+ ++actualSize;
+ }
+ CurvePiece * second = new CurvePiece(it, _last, size-actualSize+1);
+ size = actualSize;
+ _last = it;
+ B = Vec2d((_last)->x(), (_last)->y());
+ return second;
+ }
+ };
+
+ int PolygonalizationShader::shade(Stroke& stroke) const
+ {
+ vector<CurvePiece*> _pieces;
+ vector<CurvePiece*> _results;
+ vector<CurvePiece*>::iterator cp,cpend;
+
+ // Compute first approx:
+ StrokeInternal::StrokeVertexIterator a=stroke.strokeVerticesBegin();
+ StrokeInternal::StrokeVertexIterator b=stroke.strokeVerticesEnd();--b;
+ int size = stroke.strokeVerticesSize();
+
+ CurvePiece * piece = new CurvePiece(a,b,size);
+ _pieces.push_back(piece);
+
+ while(!_pieces.empty())
+ {
+ piece = _pieces.back();_pieces.pop_back();
+ if(piece->error() > _error)
+ {
+ CurvePiece * second = piece->subdivide();
+ _pieces.push_back(second);
+ _pieces.push_back(piece);
+ }
+ else
+ {
+ _results.push_back(piece);
+ }
+ }
+
+ // actually modify the geometry for each piece:
+ for(cp=_results.begin(), cpend=_results.end();
+ cp!=cpend;
+ ++cp)
+ {
+ a = (*cp)->_begin;
+ b = (*cp)->_last;
+ Vec2d u = (*cp)->B-(*cp)->A;
+ Vec2d n(u[1], -u[0]);n.normalize();
+ //Vec2d n(0,0);
+ float offset = ((*cp)->_error);
+ StrokeInternal::StrokeVertexIterator v,vlast;
+ for(v=a;
+ v!=b;
+ ++v)
+ {
+ v->setPoint((*cp)->A.x()+v->u()*u.x()+n.x()*offset, (*cp)->A.y()+v->u()*u.y()+n.y()*offset);
+ }
+ // u.normalize();
+ // (*a)->setPoint((*a)->x()-u.x()*10, (*a)->y()-u.y()*10);
+ }
+ stroke.UpdateLength();
+
+ // delete stuff
+ for(cp=_results.begin(), cpend=_results.end();
+ cp!=cpend;
+ ++cp)
+ {
+ delete (*cp);
+ }
+ _results.clear();
+ return 0;
+ }
+
+ int GuidingLinesShader::shade(Stroke& stroke) const
+ {
+ Functions1D::Normal2DF1D norm_fun;
+ StrokeInternal::StrokeVertexIterator a=stroke.strokeVerticesBegin();
+ StrokeInternal::StrokeVertexIterator b=stroke.strokeVerticesEnd();--b;
+ int size = stroke.strokeVerticesSize();
+ CurvePiece piece(a,b,size);
+
+ Vec2d u = piece.B-piece.A;
+ Vec2f n(u[1], -u[0]);n.normalize();
+ if (norm_fun(stroke) < 0)
+ return -1;
+ Vec2f strokeN(norm_fun.result);
+ if(n*strokeN < 0)
+ {
+ n[0] = -n[0];
+ n[1] = -n[1];
+ }
+ float offset = (piece.error())/2.f*_offset;
+ StrokeInternal::StrokeVertexIterator v=a,vend=stroke.strokeVerticesEnd();
+ for(;
+ v!=vend;
+ ++v)
+ {
+ v->setPoint(piece.A.x()+v->u()*u.x()+n.x()*offset, piece.A.y()+v->u()*u.y()+n.y()*offset);
+ }
+ stroke.UpdateLength();
+ return 0;
+ }
+
+ /////////////////////////////////////////
+ //
+ // Tip Remover
+ //
+ /////////////////////////////////////////
+
+
+ TipRemoverShader::TipRemoverShader(real tipLength)
+ : StrokeShader()
+ {
+ _tipLength = tipLength;
+ }
+
+ int
+ TipRemoverShader::shade(Stroke& stroke) const
+ {
+ int originalSize = stroke.strokeVerticesSize();
+
+ if(originalSize<4)
+ return 0;
+
+ StrokeInternal::StrokeVertexIterator v, vend;
+ vector<StrokeVertex*> verticesToRemove;
+ vector<StrokeAttribute> oldAttributes;
+ v=stroke.strokeVerticesBegin(); vend=stroke.strokeVerticesEnd();
+ for(;
+ v!=vend;
+ ++v)
+ {
+ if ((v->curvilinearAbscissa()<_tipLength) ||
+ (v->strokeLength()-v->curvilinearAbscissa()<_tipLength))
+ {
+ verticesToRemove.push_back(&(*v));
+ }
+ oldAttributes.push_back(v->attribute());
+ }
+
+ if(originalSize-verticesToRemove.size() < 2)
+ return 0;
+
+ vector<StrokeVertex*>::iterator sv=verticesToRemove.begin(), svend=verticesToRemove.end();
+ for(;
+ sv!=svend;
+ ++sv)
+ {
+ stroke.RemoveVertex((*sv));
+ }
+
+ // Resample so that our new stroke have the same
+ // number of vertices than before
+ stroke.Resample(originalSize);
+
+ if((int)stroke.strokeVerticesSize() != originalSize) //soc
+ cerr << "Warning: resampling problem" << endl;
+
+ // assign old attributes to new stroke vertices:
+ v=stroke.strokeVerticesBegin(); vend=stroke.strokeVerticesEnd();
+ vector<StrokeAttribute>::iterator a=oldAttributes.begin(), aend=oldAttributes.end();
+ //cout << "-----------------------------------------------" << endl;
+ for(;(v!=vend)&&(a!=aend);++v,++a)
+ {
+ v->setAttribute(*a);
+ //cout << "thickness = " << (*a).getThickness()[0] << "-" << (*a).getThickness()[1] << endl;
+ }
+ // we're done!
+ return 0;
+ }
+
+ int streamShader::shade(Stroke& stroke) const{
+ cout << stroke << endl;
+ return 0;
+ }
+ int fstreamShader::shade(Stroke& stroke) const{
+ _stream << stroke << endl;
+ return 0;
+ }
+
+} // end of namespace StrokeShaders
+