Freestyle: new stroke modifiers
[blender.git] / source / blender / freestyle / intern / stroke / BasicStrokeShaders.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/freestyle/intern/stroke/BasicStrokeShaders.cpp
22  *  \ingroup freestyle
23  *  \brief Class gathering basic stroke shaders
24  *  \author Stephane Grabli
25  *  \date 17/12/2002
26  */
27
28 #include <fstream>
29
30 #include "AdvancedFunctions0D.h"
31 #include "AdvancedFunctions1D.h"
32 #include "BasicStrokeShaders.h"
33 #include "StrokeIO.h"
34 #include "StrokeIterators.h"
35 #include "StrokeRenderer.h"
36
37 #include "../system/PseudoNoise.h"
38 #include "../system/RandGen.h"
39 #include "../system/StringUtils.h"
40
41 #include "../view_map/Functions0D.h"
42 #include "../view_map/Functions1D.h"
43
44 #include "BKE_global.h"
45
46 extern "C" {
47 #  include "IMB_imbuf.h"
48 #  include "IMB_imbuf_types.h"
49 }
50
51 namespace Freestyle {
52
53 namespace StrokeShaders {
54
55 //
56 //  Thickness modifiers
57 //
58 //////////////////////////////////////////////////////////
59
60 int ConstantThicknessShader::shade(Stroke& stroke) const
61 {
62         StrokeInternal::StrokeVertexIterator v, vend;
63         int i = 0;
64         int size = stroke.strokeVerticesSize();
65         for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
66                 // XXX What's the use of i here? And is not the thickness always overriden by the last line of the loop?
67                 if ((1 == i) || (size - 2 == i))
68                         v->attribute().setThickness(_thickness / 4.0, _thickness / 4.0);
69                 if ((0 == i) || (size - 1 == i))
70                         v->attribute().setThickness(0, 0);
71
72                 v->attribute().setThickness(_thickness / 2.0, _thickness / 2.0);
73         }
74         return 0;
75 }
76
77 int ConstantExternThicknessShader::shade(Stroke& stroke) const
78 {
79         StrokeInternal::StrokeVertexIterator v, vend;
80         int i = 0;
81         int size = stroke.strokeVerticesSize();
82         for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
83                 // XXX What's the use of i here? And is not the thickness always overriden by the last line of the loop?
84                 if ((1 == i) || (size - 2 == i))
85                         v->attribute().setThickness(_thickness / 2.0, 0);
86                 if ((0 == i) || (size - 1 == i))
87                         v->attribute().setThickness(0, 0);
88
89                 v->attribute().setThickness(_thickness, 0);
90         }
91         return 0;
92 }
93
94 int IncreasingThicknessShader::shade(Stroke& stroke) const
95 {
96         int n = stroke.strokeVerticesSize() - 1, i;
97         StrokeInternal::StrokeVertexIterator v, vend;
98         for (i = 0, v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd();
99              v != vend;
100              ++v, ++i)
101         {
102                 float t;
103                 if (i < (float)n / 2.0f)
104                         t = (1.0 - (float)i / (float)n) * _ThicknessMin + (float)i / (float)n * _ThicknessMax;
105                 else
106                         t = (1.0 - (float)i / (float)n) * _ThicknessMax + (float)i / (float)n * _ThicknessMin;
107                 v->attribute().setThickness(t / 2.0, t / 2.0);
108         }
109         return 0;
110 }
111
112 int ConstrainedIncreasingThicknessShader::shade(Stroke& stroke) const
113 {
114         float slength = stroke.getLength2D();
115         float maxT = min(_ratio * slength, _ThicknessMax);
116         int n = stroke.strokeVerticesSize() - 1, i;
117         StrokeInternal::StrokeVertexIterator v, vend;
118         for (i = 0, v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd();
119              v != vend;
120              ++v, ++i)
121         {
122                 // XXX Why not using an if/else here? Else, if last condition is true, everything else is computed for nothing!
123                 float t;
124                 if (i < (float)n / 2.0f)
125                         t = (1.0 - (float)i / (float)n) * _ThicknessMin + (float)i / (float)n * maxT;
126                 else
127                         t = (1.0 - (float)i / (float)n) * maxT + (float)i / (float)n * _ThicknessMin;
128                 v->attribute().setThickness(t / 2.0, t / 2.0);
129                 if (i == n - 1)
130                         v->attribute().setThickness(_ThicknessMin / 2.0, _ThicknessMin / 2.0);
131         }
132         return 0;
133 }
134
135
136 int LengthDependingThicknessShader::shade(Stroke& stroke) const
137 {
138         float step = (_maxThickness - _minThickness) / 3.0f;
139         float l = stroke.getLength2D();
140         float thickness = 0.0f;
141         if (l > 300.0f)
142                 thickness = _minThickness + 3.0f * step;
143         else if ((l < 300.0f) && (l > 100.0f))
144                 thickness = _minThickness + 2.0f * step;
145         else if ((l < 100.0f) && (l > 50.0f))
146                 thickness = _minThickness + 1.0f * step;
147         else // else if (l <  50.0f), tsst...
148                 thickness = _minThickness;
149
150         StrokeInternal::StrokeVertexIterator v, vend;
151         int i = 0;
152         int size = stroke.strokeVerticesSize();
153         for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
154                 // XXX What's the use of i here? And is not the thickness always overriden by the last line of the loop?
155                 if ((1 == i) || (size - 2 == i))
156                         v->attribute().setThickness(thickness / 4.0, thickness / 4.0);
157                 if ((0 == i) || (size - 1 == i))
158                         v->attribute().setThickness(0, 0);
159
160                 v->attribute().setThickness(thickness / 2.0, thickness / 2.0);
161         }
162         return 0;
163 }
164
165 static const unsigned NB_VALUE_NOISE = 512;
166
167 ThicknessNoiseShader::ThicknessNoiseShader() : StrokeShader()
168 {
169         _amplitude = 1.0f;
170         _scale = 1.0f / 2.0f / (float)NB_VALUE_NOISE;
171 }
172
173 ThicknessNoiseShader::ThicknessNoiseShader(float iAmplitude, float iPeriod) : StrokeShader()
174 {
175         _amplitude = iAmplitude;
176         _scale = 1.0f / iPeriod / (float)NB_VALUE_NOISE;
177 }
178
179 int ThicknessNoiseShader::shade(Stroke& stroke) const
180 {
181         StrokeInternal::StrokeVertexIterator v = stroke.strokeVerticesBegin(), vend;
182         real initU1 = v->strokeLength() * real(NB_VALUE_NOISE) + RandGen::drand48() * real(NB_VALUE_NOISE);
183         real initU2 = v->strokeLength() * real(NB_VALUE_NOISE) + RandGen::drand48() * real(NB_VALUE_NOISE);
184
185         real bruit, bruit2;
186         PseudoNoise mynoise, mynoise2;
187         for (vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
188                 bruit =   mynoise.turbulenceSmooth(_scale * v->curvilinearAbscissa() + initU1, 2); // 2 : nbOctaves
189                 bruit2 = mynoise2.turbulenceSmooth(_scale * v->curvilinearAbscissa() + initU2, 2); // 2 : nbOctaves
190                 const float *originalThickness = v->attribute().getThickness();
191                 float r = bruit * _amplitude + originalThickness[0];
192                 float l = bruit2 * _amplitude + originalThickness[1];
193                 v->attribute().setThickness(r, l);
194         }
195
196         return 0;
197 }
198
199 //
200 //  Color shaders
201 //
202 ///////////////////////////////////////////////////////////////////////////////
203
204 int ConstantColorShader::shade(Stroke& stroke) const
205 {
206         StrokeInternal::StrokeVertexIterator v, vend;
207         for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
208                 v->attribute().setColor(_color[0], _color[1], _color[2]);
209                 v->attribute().setAlpha(_color[3]);
210         }
211         return 0;
212 }
213
214 int IncreasingColorShader::shade(Stroke& stroke) const
215 {
216         StrokeInternal::StrokeVertexIterator v, vend;
217         int n = stroke.strokeVerticesSize() - 1, yo;
218         float newcolor[4];
219         for (yo = 0, v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd();
220              v != vend;
221              ++v, ++yo)
222         {
223                 for (int i = 0; i < 4; ++i) {
224                         newcolor[i] = (1.0 - (float) yo / (float)n) * _colorMin[i] + (float)yo / (float)n * _colorMax[i];
225                 }
226                 v->attribute().setColor(newcolor[0], newcolor[1], newcolor[2]);
227                 v->attribute().setAlpha(newcolor[3]);
228         }
229         return 0;
230 }
231
232 int MaterialColorShader::shade(Stroke& stroke) const
233 {
234         Interface0DIterator v, vend;
235         Functions0D::MaterialF0D fun;
236         StrokeVertex *sv;
237         for (v = stroke.verticesBegin(), vend = stroke.verticesEnd(); v != vend; ++v) {
238                 if (fun(v) < 0)
239                         return -1;
240                 const float *diffuse = fun.result.diffuse();
241                 sv = dynamic_cast<StrokeVertex*>(&(*v));
242                 sv->attribute().setColor(diffuse[0] * _coefficient, diffuse[1] * _coefficient, diffuse[2] * _coefficient);
243                 sv->attribute().setAlpha(diffuse[3]);
244         }
245         return 0;
246 }
247
248 ColorNoiseShader::ColorNoiseShader() : StrokeShader()
249 {
250         _amplitude = 1.0f;
251         _scale = 1.0f / 2.0f / (float)NB_VALUE_NOISE;
252 }
253
254 ColorNoiseShader::ColorNoiseShader(float iAmplitude, float iPeriod) : StrokeShader()
255 {
256         _amplitude = iAmplitude;
257         _scale = 1.0f / iPeriod / (float)NB_VALUE_NOISE;
258 }
259
260 int ColorNoiseShader::shade(Stroke& stroke) const
261 {
262         StrokeInternal::StrokeVertexIterator v = stroke.strokeVerticesBegin(), vend;
263         real initU = v->strokeLength() * real(NB_VALUE_NOISE) + RandGen::drand48() * real(NB_VALUE_NOISE);
264
265         real bruit;
266         PseudoNoise mynoise;
267         for (vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
268                 bruit = mynoise.turbulenceSmooth(_scale * v->curvilinearAbscissa() + initU, 2); // 2 : nbOctaves
269                 const float *originalColor = v->attribute().getColor();
270                 float r = bruit * _amplitude + originalColor[0];
271                 float g = bruit * _amplitude + originalColor[1];
272                 float b = bruit * _amplitude + originalColor[2];
273                 v->attribute().setColor(r, g, b);
274         }
275         return 0;
276 }
277
278 //
279 //  Texture Shaders
280 //
281 ///////////////////////////////////////////////////////////////////////////////
282
283 int BlenderTextureShader::shade(Stroke& stroke) const
284 {
285         if (_mtex)
286                 return stroke.setMTex(_mtex);
287         if (_nodeTree) {
288                 stroke.setNodeTree(_nodeTree);
289                 return 0;
290         }
291         return -1;
292 }
293
294 int StrokeTextureStepShader::shade(Stroke& stroke) const
295 {
296         stroke.setTextureStep(_step);
297         return 0;
298 }
299
300 //
301 //  Geometry Shaders
302 //
303 ///////////////////////////////////////////////////////////////////////////////
304
305 int BackboneStretcherShader::shade(Stroke& stroke) const
306 {
307         float l = stroke.getLength2D();
308         if (l <= 1.0e-6)
309                 return 0;
310
311         StrokeInternal::StrokeVertexIterator v0 = stroke.strokeVerticesBegin();
312         StrokeInternal::StrokeVertexIterator v1 = v0;
313         ++v1;
314         StrokeInternal::StrokeVertexIterator vn = stroke.strokeVerticesEnd();
315         --vn;
316         StrokeInternal::StrokeVertexIterator vn_1 = vn;
317         --vn_1;
318
319
320         Vec2d first((v0)->x(), (v0)->y());
321         Vec2d last((vn)->x(), (vn)->y());
322
323         Vec2d d1(first - Vec2d((v1)->x(), (v1)->y()));
324         d1.normalize();
325         Vec2d dn(last - Vec2d((vn_1)->x(), (vn_1)->y()));
326         dn.normalize();
327
328         Vec2d newFirst(first + _amount * d1);
329         (v0)->setPoint(newFirst[0], newFirst[1]);
330         Vec2d newLast(last + _amount * dn);
331         (vn)->setPoint(newLast[0], newLast[1]);
332
333         stroke.UpdateLength();
334         return 0;
335 }
336
337 int SamplingShader::shade(Stroke& stroke) const
338 {
339         stroke.Resample(_sampling);
340         stroke.UpdateLength();
341         return 0;
342 }
343
344 int ExternalContourStretcherShader::shade(Stroke& stroke) const
345 {
346         //float l = stroke.getLength2D();
347         Interface0DIterator it;
348         Functions0D::Normal2DF0D fun;
349         StrokeVertex *sv;
350         for (it = stroke.verticesBegin(); !it.isEnd(); ++it) {
351                 if (fun(it) < 0)
352                         return -1;
353                 Vec2f n(fun.result);
354                 sv = dynamic_cast<StrokeVertex*>(&(*it));
355                 Vec2d newPoint(sv->x() + _amount * n.x(), sv->y() + _amount * n.y());
356                 sv->setPoint(newPoint[0], newPoint[1]);
357         }
358         stroke.UpdateLength();
359         return 0;
360 }
361
362
363 //!! Bezier curve stroke shader
364 int BezierCurveShader::shade(Stroke& stroke) const
365 {
366         if (stroke.strokeVerticesSize() < 4)
367                 return 0;
368
369         // Build the Bezier curve from this set of data points:
370         vector<Vec2d> data;
371         StrokeInternal::StrokeVertexIterator v = stroke.strokeVerticesBegin(), vend;
372         data.push_back(Vec2d(v->x(), v->y())); //first one
373         StrokeInternal::StrokeVertexIterator previous = v;
374         ++v;
375         for (vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
376                 if (!((fabs(v->x() - (previous)->x()) < M_EPSILON) && ((fabs(v->y() - (previous)->y()) < M_EPSILON))))
377                         data.push_back(Vec2d(v->x(), v->y()));
378                 previous = v;
379         }
380
381         // here we build the bezier curve
382         BezierCurve bcurve(data, _error);
383
384         // bad performances are here !!! // FIXME
385         vector<Vec2d> CurveVertices;
386         vector<BezierCurveSegment*>& bsegments = bcurve.segments();
387         vector<BezierCurveSegment*>::iterator s = bsegments.begin(), send;
388         vector<Vec2d>& segmentsVertices = (*s)->vertices();
389         vector<Vec2d>::iterator p, pend;
390         // first point
391         CurveVertices.push_back(segmentsVertices[0]);
392         for (send = bsegments.end(); s != send; ++s) {
393                 segmentsVertices = (*s)->vertices();
394                 p = segmentsVertices.begin();
395                 ++p;
396                 for (pend = segmentsVertices.end(); p != pend; ++p) {
397                         CurveVertices.push_back((*p));
398                 }
399         }
400
401         // Resample the Stroke depending on the number of vertices of the bezier curve:
402         int originalSize = CurveVertices.size();
403 #if 0
404         float sampling = stroke.ComputeSampling(originalSize);
405         stroke.Resample(sampling);
406 #endif
407         stroke.Resample(originalSize);
408         int newsize = stroke.strokeVerticesSize();
409         int nExtraVertex = 0;
410         if (newsize < originalSize) {
411                 cerr << "Warning: unsufficient resampling" << endl;
412         }
413         else {
414                 nExtraVertex = newsize - originalSize;
415                 if (nExtraVertex != 0) {
416                         if (G.debug & G_DEBUG_FREESTYLE) {
417                                 cout << "Bezier Shader : Stroke " << stroke.getId() << " have not been resampled" << endl;
418                         }
419                 }
420         }
421
422         // assigns the new coordinates:
423         p = CurveVertices.begin();
424         vector<Vec2d>::iterator last = p;
425         int n;
426         StrokeInternal::StrokeVertexIterator it, itend;
427         for (n = 0, it = stroke.strokeVerticesBegin(), itend = stroke.strokeVerticesEnd(), pend = CurveVertices.end();
428              (it != itend) && (p != pend);
429              ++it, ++p, ++n)
430         {
431                 it->setX(p->x());
432                 it->setY(p->y());
433                 last = p;
434         }
435         stroke.UpdateLength();
436
437         // Deal with extra vertices:
438         if (nExtraVertex == 0)
439                 return 0;
440
441         // nExtraVertex should stay unassigned
442         vector<StrokeAttribute> attributes;
443         vector<StrokeVertex*> verticesToRemove;
444         for (int i = 0; i < nExtraVertex; ++i, ++it, ++n) {
445                 verticesToRemove.push_back(&(*it));
446                 if (it.isEnd()) {
447                         // XXX Shocking! :P Shouldn't we break in this case???
448                         if (G.debug & G_DEBUG_FREESTYLE) {
449                                 cout << "messed up!" << endl;
450                         }
451                 }
452         }
453         for (it = stroke.strokeVerticesBegin(); it != itend; ++it) {
454                 attributes.push_back(it->attribute());
455         }
456
457         for (vector<StrokeVertex*>::iterator vr = verticesToRemove.begin(), vrend = verticesToRemove.end();
458              vr != vrend;
459              ++vr)
460         {
461                 stroke.RemoveVertex(*vr);
462         }
463
464         vector<StrokeAttribute>::iterator a = attributes.begin(), aend = attributes.end();
465         int index = 0;
466         int index1 = (int)floor((float)originalSize / 2.0);
467         int index2 = index1 + nExtraVertex;
468         for (it = stroke.strokeVerticesBegin(), itend = stroke.strokeVerticesEnd();
469              (it != itend) && (a != aend);
470              ++it)
471         {
472                 (it)->setAttribute(*a);
473                 if ((index <= index1) || (index > index2)) {
474                         ++a;
475                 }
476                 ++index;
477         }
478         return 0;
479 }
480
481
482 class CurvePiece
483 {
484 public:
485         StrokeInternal::StrokeVertexIterator _begin;
486         StrokeInternal::StrokeVertexIterator _last;
487         Vec2d A;
488         Vec2d B;
489         int size;
490         float _error;
491
492         CurvePiece(StrokeInternal::StrokeVertexIterator b, StrokeInternal::StrokeVertexIterator l, int iSize)
493         {
494                 _error = 0.0f;
495                 _begin = b;
496                 _last = l;
497                 A = Vec2d((_begin)->x(), (_begin)->y());
498                 B = Vec2d((_last)->x(), (_last)->y());
499                 size = iSize;
500         }
501
502         float error()
503         {
504                 float maxE = 0.0f;
505                 for (StrokeInternal::StrokeVertexIterator it = _begin; it != _last; ++it) {
506                         Vec2d P(it->x(), it->y());
507                         float d = GeomUtils::distPointSegment(P, A, B);
508                         if (d > maxE)
509                                 maxE = d;
510                 }
511                 _error = maxE;
512                 return maxE;
513         }
514
515         //! Subdivides the curve into two pieces.
516         //  The first piece is this same object (modified)
517         //  The second piece is returned by the method
518         CurvePiece *subdivide()
519         {
520                 StrokeInternal::StrokeVertexIterator it = _begin;
521                 int ns = size - 1; // number of segments (ns > 1)
522                 int ns1 = ns / 2;
523                 int ns2 = ns - ns1;
524                 for (int i = 0; i < ns1; ++it, ++i);
525
526                 CurvePiece *second = new CurvePiece(it, _last, ns2 + 1);
527                 size = ns1 + 1;
528                 _last = it;
529                 B = Vec2d((_last)->x(), (_last)->y());
530                 return second;
531         }
532 };
533
534 int PolygonalizationShader::shade(Stroke& stroke) const
535 {
536         vector<CurvePiece*> _pieces;
537         vector<CurvePiece*> _results;
538         vector<CurvePiece*>::iterator cp, cpend;
539
540         // Compute first approx:
541         StrokeInternal::StrokeVertexIterator a = stroke.strokeVerticesBegin();
542         StrokeInternal::StrokeVertexIterator b = stroke.strokeVerticesEnd();
543         --b;
544         int size = stroke.strokeVerticesSize();
545
546         CurvePiece *piece = new CurvePiece(a, b, size);
547         _pieces.push_back(piece);
548
549         while (!_pieces.empty()) {
550                 piece = _pieces.back();
551                 _pieces.pop_back();
552                 if (piece->size > 2 && piece->error() > _error) {
553                         CurvePiece *second = piece->subdivide();
554                         _pieces.push_back(second);
555                         _pieces.push_back(piece);
556                 }
557                 else {
558                         _results.push_back(piece);
559                 }
560         }
561
562         // actually modify the geometry for each piece:
563         for (cp = _results.begin(), cpend = _results.end(); cp != cpend; ++cp) {
564                 a = (*cp)->_begin;
565                 b = (*cp)->_last;
566                 Vec2d u = (*cp)->B - (*cp)->A;
567                 Vec2d n(u[1], -u[0]);
568                 n.normalize();
569                 //Vec2d n(0, 0);
570                 float offset = ((*cp)->_error);
571                 StrokeInternal::StrokeVertexIterator v;
572                 for (v = a; v != b; ++v) {
573                         v->setPoint((*cp)->A.x() + v->u() * u.x() + n.x() * offset,
574                                     (*cp)->A.y() + v->u() * u.y() + n.y() * offset);
575                 }
576 #if 0
577                 u.normalize();
578                 (*a)->setPoint((*a)->x() - u.x() * 10, (*a)->y() - u.y() * 10);
579 #endif
580         }
581         stroke.UpdateLength();
582
583         // delete stuff
584         for (cp = _results.begin(), cpend = _results.end(); cp != cpend; ++cp) {
585                 delete (*cp);
586         }
587         _results.clear();
588         return 0;
589 }
590
591 int GuidingLinesShader::shade(Stroke& stroke) const
592 {
593         Functions1D::Normal2DF1D norm_fun;
594         StrokeInternal::StrokeVertexIterator a = stroke.strokeVerticesBegin();
595         StrokeInternal::StrokeVertexIterator b = stroke.strokeVerticesEnd();
596         --b;
597         int size = stroke.strokeVerticesSize();
598         CurvePiece piece(a, b, size);
599
600         Vec2d u = piece.B - piece.A;
601         Vec2f n(u[1], -u[0]);
602         n.normalize();
603         if (norm_fun(stroke) < 0)
604                 return -1;
605         Vec2f strokeN(norm_fun.result);
606         if (n * strokeN < 0) {
607                 n[0] = -n[0];
608                 n[1] = -n[1];
609         }
610         float offset = (piece.error()) / 2.0f * _offset;
611         StrokeInternal::StrokeVertexIterator v, vend;
612         for (v = a, vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
613                 v->setPoint(piece.A.x() + v->u() * u.x() + n.x() * offset,
614                             piece.A.y() + v->u() * u.y() + n.y() * offset);
615         }
616         stroke.UpdateLength();
617         return 0;
618 }
619
620 /////////////////////////////////////////
621 //
622 //  Tip Remover
623 //
624 /////////////////////////////////////////
625
626
627 TipRemoverShader::TipRemoverShader(real tipLength) : StrokeShader()
628 {
629         _tipLength = tipLength;
630 }
631
632 int TipRemoverShader::shade(Stroke& stroke) const
633 {
634         int originalSize = stroke.strokeVerticesSize();
635
636         if (originalSize < 4)
637                 return 0;
638
639         StrokeInternal::StrokeVertexIterator v, vend;
640         vector<StrokeVertex*> verticesToRemove;
641         vector<StrokeAttribute> oldAttributes;
642         for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
643                 if ((v->curvilinearAbscissa() < _tipLength) || (v->strokeLength() - v->curvilinearAbscissa() < _tipLength)) {
644                         verticesToRemove.push_back(&(*v));
645                 }
646                 oldAttributes.push_back(v->attribute());
647         }
648
649         if (originalSize - verticesToRemove.size() < 2)
650                 return 0;
651
652         vector<StrokeVertex*>::iterator sv, svend;
653         for (sv = verticesToRemove.begin(), svend = verticesToRemove.end(); sv != svend; ++sv) {
654                 stroke.RemoveVertex((*sv));
655         }
656
657         // Resample so that our new stroke have the same number of vertices than before
658         stroke.Resample(originalSize);
659
660         if ((int)stroke.strokeVerticesSize() != originalSize) //soc
661                 cerr << "Warning: resampling problem" << endl;
662
663         // assign old attributes to new stroke vertices:
664         vector<StrokeAttribute>::iterator a = oldAttributes.begin(), aend = oldAttributes.end();
665         for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd();
666              (v != vend) && (a != aend);
667              ++v, ++a)
668         {
669                 v->setAttribute(*a);
670         }
671         // we're done!
672         return 0;
673 }
674
675 } // end of namespace StrokeShaders
676
677 } /* namespace Freestyle */