The 'polygon', 'draw', 'sketch', and 'width' tools are disabled by default. This...
[synfig.git] / synfig-studio / trunk / src / synfigapp / blineconvert.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file blineconvert.cpp
3 **      \brief Template File
4 **
5 **      $Id: blineconvert.cpp,v 1.1.1.1 2005/01/07 03:34:37 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include "blineconvert.h"
33 #include <vector>
34 #include <ETL/gaussian>
35 #include <ETL/hermite>
36 #include <ETL/clock>
37 #include <float.h>
38 #include <algorithm>
39 #include <synfig/general.h>
40 #include <cassert>
41
42
43
44 #endif
45
46 /* === U S I N G =========================================================== */
47
48 using namespace std;
49 using namespace etl;
50 using namespace synfig;
51
52 /* === M A C R O S ========================================================= */
53
54 #define EPSILON         (1e-10)
55
56 /* === G L O B A L S ======================================================= */
57
58 /* === P R O C E D U R E S ================================================= */
59
60 /* === M E T H O D S ======================================================= */
61
62
63 //Derivative Functions for numerical approximation
64
65 //bias == 0 will get F' at f3, bias < 0 will get F' at f1, and bias > 0 will get F' at f5
66 template < class T >
67 inline void FivePointdt(T &df, const T &f1, const T &f2, const T &f3, const T &f4, const T &f5, int bias)
68 {
69         if(bias == 0)
70         {
71                 //middle
72                 df = (f1 - f2*8 + f4*8 - f5)*(1/12.0f);
73         }else if(bias < 0)
74         {
75                 //left
76                 df = (-f1*25 + f2*48 - f3*36 + f4*16 - f5*3)*(1/12.0f);
77         }else
78         {
79                 //right
80                 df = (f1*3 - f2*16 + f3*36 - f4*48 + f5*25)*(1/12.0f);
81         }
82 }
83
84 template < class T >
85 inline void ThreePointdt(T &df, const T &f1, const T &f2, const T &f3, int bias)
86 {
87         if(bias == 0)
88         {
89                 //middle
90                 df = (-f1 + f3)*(1/2.0f);
91         }else if(bias < 0)
92         {
93                 //left
94                 df = (-f1*3 + f2*4 - f3)*(1/2.0f);
95         }else
96         {
97                 //right
98                 df = (f1 - f2*4 + f3*3)*(1/2.0f);
99         }
100 }
101
102 template < class T >
103 inline void ThreePointddt(T &df, const T &f1, const T &f2, const T &f3, int bias)
104 {
105         //a 3 point approximation pretends to have constant acceleration, so only one algorithm needed for left, middle, or right
106         df = (f1 -f2*2 + f3)*(1/2.0f);
107 }
108
109 // WARNING -- totaly broken
110 template < class T >
111 inline void FivePointddt(T &df, const T &f1, const T &f2, const T &f3, int bias)
112 {
113         if(bias == 0)
114         {
115                 assert(0); // !?
116                 //middle
117                 //df = (- f1 + f2*16 - f3*30 +  f4*16 - f5)*(1/12.0f);
118         }/*else if(bias < 0)
119         {
120                 //left
121                 df = (f1*7 - f2*26*4 + f3*19*6 - f4*14*4 + f5*11)*(1/12.0f);
122         }else
123         {
124                 //right
125                 df = (f1*3 - f2*16 + f3*36 - f4*48 + f5*25)*(1/12.0f);
126         }*/
127         //side ones don't work, use 3 point
128 }
129
130 //implement an arbitrary derivative
131 //dumb algorithm
132 template < class T >
133 void DerivativeApprox(T &df, const T f[], const Real t[], int npoints, int indexval)
134 {
135         /*
136         Lj(x) = PI_i!=j (x - xi) / PI_i!=j (xj - xi)
137         
138         so Lj'(x) = SUM_k PI_i!=j|k (x - xi) / PI_i!=j (xj - xi)
139         */
140         
141         unsigned int i,j,k,i0,i1;
142         
143         Real Lpj,mult,div,tj;
144         Real tval = t[indexval];
145                                 
146         //sum k 
147         for(j=0;j<npoints;++j)
148         {
149                 Lpj = 0;
150                 div = 1;
151                 tj = t[j];
152                 
153                 for(k=0;k<npoints;++k)
154                 {
155                         if(k != j) //because there is no summand for k == j, since that term is missing from the original equation
156                         {
157                                 //summation for k
158                                 for(i=0;i<npoints;++i)
159                                 {
160                                         if(i != k)
161                                         {
162                                                 mult *= tval - t[i];
163                                         }
164                                 }
165                                 
166                                 Lpj += mult; //add into the summation
167                                 
168                                 //since the ks follow the exact patern we need for the divisor (use that too)
169                                 div *= tj - t[k];
170                         }
171                 }
172                 
173                 //get the actual coefficient
174                 Lpj /= div;
175                 
176                 //add it in to the equation
177                 df += f[j]*Lpj;
178         }
179 }
180
181 //END numerical derivatives
182
183 template < class T >
184 inline int sign(T f, T tol)
185 {
186         if(f < -tol) return -1;
187         if(f > tol) return 1;
188         return 0;
189 }
190
191 void GetFirstDerivatives(const std::vector<synfig::Point> &f, unsigned int left, unsigned int right, char *out, unsigned int dfstride)
192 {
193         unsigned int current = left;
194
195         if(right - left < 2)
196                 return;
197         else if(right - left < 3)
198         {
199                 synfig::Vector v = f[left+1] - f[left];
200                 
201                 //set both to the one we want
202                 *(synfig::Vector*)out = v;
203                 out += dfstride;
204                 *(synfig::Vector*)out = v;
205                 out += dfstride;
206         }
207         else if(right - left < 6/*5*/) //should use 3 point
208         {
209                 //left then middle then right
210                 ThreePointdt(*(synfig::Vector*)out,f[left+0], f[left+1], f[left+2], -1);
211                 current += 1;
212                 out += dfstride;
213                 
214                 for(;current < right-1; current++, out += dfstride)
215                 {
216                         ThreePointdt(*(synfig::Vector*)out,f[current-1], f[current], f[current+1], 0);
217                 }
218
219                 ThreePointdt(*(synfig::Vector*)out,f[right-3], f[right-2], f[right-1], 1);
220                 current++;
221                 out += dfstride;
222                 
223         }else //can use 5 point
224         {
225                 //left 2 then middle bunch then right two
226                 //may want to use 3 point for inner edge ones
227                 
228                 FivePointdt(*(synfig::Vector*)out,f[left+0], f[left+1], f[left+2], f[left+3], f[left+4], -2);
229                 out += dfstride;
230                 FivePointdt(*(synfig::Vector*)out,f[left+1], f[left+2], f[left+3], f[left+4], f[left+5], -1);
231                 out += dfstride;
232                 current += 2;
233                 
234                 for(;current < right-2; current++, out += dfstride)
235                 {
236                         FivePointdt(*(synfig::Vector*)out,f[current-2], f[current-1], f[current], f[current+1], f[current+2], 0);
237                 }
238
239                 FivePointdt(*(synfig::Vector*)out,f[right-5], f[right-4], f[right-3], f[right-2], f[right-1], 1);
240                 out += dfstride;
241                 FivePointdt(*(synfig::Vector*)out,f[right-6], f[right-5], f[right-4], f[right-3], f[right-2], 2);               
242                 out += dfstride;
243                 current += 2;
244         }
245 }
246
247 void GetSimpleDerivatives(const std::vector<synfig::Point> &f, int left, int right, 
248                                                         std::vector<synfig::Point> &df, int outleft,
249                                                         const std::vector<synfig::Real> &di)
250 {
251         int i1,i2,i;
252         int offset = 2; //df = 1/2 (f[i+o]-f[i-o])
253         
254         assert((int)df.size() >= right-left+outleft); //must be big enough
255         
256         for(i = left; i < right; ++i)
257         {
258                 //right now indices (figure out distance later)
259                 i1 = std::max(left,i-offset);
260                 i2 = std::max(left,i+offset);
261                 
262                 df[outleft++] = (f[i2] - f[i1])*0.5f;
263         }
264 }
265
266 //get the curve error from the double sample list of work points (hopefully that's enough)
267 Real CurveError(const synfig::Point *pts, unsigned int n, std::vector<synfig::Point> &work, int left, int right)
268 {
269         if(right-left < 2) return -1;
270                 
271         int i,j;
272         
273         //get distances to each point
274         Real d,dtemp,dsum;
275         //synfig::Vector v,vt;
276         //synfig::Point p1,p2;
277         synfig::Point pi;
278         std::vector<synfig::Point>::const_iterator it;//,end = work.begin()+right;
279         
280         //unsigned int size = work.size();
281         
282         //for each line, get distance
283         d = 0; //starts at 0
284         for(i = 0; i < (int)n; ++i)
285         {               
286                 pi = pts[i];
287                 
288                 dsum = FLT_MAX;
289                 
290                 it = work.begin()+left;
291                 //p2 = *it++; //put it at left+1
292                 for(j = left/*+1*/; j < right; ++j,++it)
293                 {
294                         /*p1 = p2;
295                         p2 = *it;
296                         
297                         v = p2 - p1;                    
298                         vt = pi - p1;
299                         
300                         dtemp = v.mag_squared() > 1e-12 ? (vt*v)/v.mag_squared() : 0; //get the projected time value for the current line
301                         
302                         //get distance to line segment with the time value clamped 0-1                  
303                         if(dtemp >= 1)  //use p+v
304                         {
305                                 vt += v; //makes it pp - (p+v)  
306                         }else if(dtemp > 0)     //use vt-proj
307                         {
308                                 vt -= v*dtemp; // vt - proj_v(vt)       //must normalize the projection vector to work
309                         }
310                         
311                         //else use p
312                         dtemp = vt.mag_squared();*/
313                         
314                         dtemp = (pi - *it).mag_squared();                       
315                         if(dtemp < dsum)
316                                 dsum = dtemp;
317                 }
318                 
319                 //accumulate the points' min distance from the curve
320                 d += sqrt(dsum);
321         }
322         
323         return d;
324 }
325
326 typedef synfigapp::BLineConverter::cpindex cpindex;
327
328 //has the index data and the tangent scale data (relevant as it may be)
329 int tesselate_curves(const std::vector<cpindex> &inds, const std::vector<Point> &f, const std::vector<synfig::Vector> &df, std::vector<Point> &work)
330 {
331         if(inds.size() < 2)
332                 return 0;
333         
334         etl::hermite<Point>     curve;
335         int ntess = 0;
336         
337         std::vector<cpindex>::const_iterator j = inds.begin(),j2, end = inds.end();
338         
339         unsigned int ibase = inds[0].curind;
340                 
341         j2 = j++;
342         for(; j != end; j2 = j++)
343         {
344                 //if this curve has invalid error (in j) then retesselate it's work points (requires reparametrization, etc.)
345                 if(j->error < 0)
346                 {
347                         //get the stepsize etc. for the number of points in here
348                         unsigned int n = j->curind - j2->curind + 1; //thats the number of points in the span
349                         unsigned int k, kend, i0, i3;
350                         //so reset the right chunk
351                         
352                         Real t, dt = 1/(Real)(n*2-2); //assuming that they own only n points
353                         
354                         //start at first intermediate
355                         t = 0;
356
357                         i0 = j2->curind; i3 = j->curind;                        
358                         k = (i0-ibase)*2; //start on first intermediary point (2x+1)
359                         kend = (i3-ibase)*2; //last point to set (not intermediary)
360                         
361                         //build hermite curve, it's easier
362                         curve.p1() = f[i0];
363                         curve.p2() = f[i3];
364                         curve.t1() = df[i0]*(df[i0].mag_squared() > 1e-4 ? j2->tangentscale/df[i0].mag() : j2->tangentscale);
365                         curve.t2() = df[i3]*(df[i3].mag_squared() > 1e-4 ? j->tangentscale/df[i3].mag() : j->tangentscale);
366                         curve.sync();
367                                                 
368                         //MUST include the end point (since we are ignoring left one)
369                         for(; k < kend; ++k, t += dt)
370                         {
371                                 work[k] = curve(t);
372                         }
373                         
374                         work[k] = curve(1); //k == kend, t == 1 -> c(t) == p2
375                         ++ntess;
376                 }
377         }
378         
379         return ntess;
380 }
381
382 synfigapp::BLineConverter::BLineConverter()
383 {
384         pixelwidth = 1;
385         smoothness = 0.70f;
386         width = 0;
387 };
388
389 void 
390 synfigapp::BLineConverter::clear()
391 {
392         f.clear();
393         f_w.clear();
394         ftemp.clear();
395         df.clear();
396         cvt.clear();
397         brk.clear();
398         di.clear();
399         d_i.clear();
400         work.clear();
401         curind.clear();
402 }
403
404 void
405 synfigapp::BLineConverter::operator () (std::list<synfig::BLinePoint> &out, const std::list<synfig::Point> &in,const std::list<synfig::Real> &in_w)
406 {       
407         //Profiling information
408         /*etl::clock::value_type initialprocess=0, curveval=0, breakeval=0, disteval=0;
409         etl::clock::value_type preproceval=0, tesseval=0, erroreval=0, spliteval=0;
410         unsigned int                    numpre=0, numtess=0, numerror=0, numsplit=0;
411         etl::clock_realtime timer,total;*/
412
413         //total.reset();
414         if(in.size()<=1)
415                 return;
416
417         clear();
418         
419         //removing digitization error harder than expected
420         
421         //intended to fix little pen errors caused by the way people draw (tiny juts in opposite direction)
422         //Different solutions
423         //      Average at both end points (will probably eliminate many points at each end of the samples)
424         //      Average after the break points are found (weird points would still affect the curve)
425         //      Just always get rid of breaks at the beginning and end if they are a certain distance apart
426         //              This is will be current approach so all we do now is try to remove duplicate points
427         
428         //remove duplicate points - very bad for fitting
429         
430         //timer.reset();
431         
432         {
433                 std::list<synfig::Point>::const_iterator i = in.begin(), end = in.end();
434                 std::list<synfig::Real>::const_iterator iw = in_w.begin();
435                 synfig::Point   c;
436                 
437                 if(in.size() == in_w.size())
438                 {
439                         for(;i != end; ++i,++iw)
440                         {       
441                                 //eliminate duplicate points
442                                 if(*i != c)
443                                 {
444                                         f.push_back(c = *i);
445                                         f_w.push_back(*iw);
446                                 }
447                         }
448                 }else
449                 {
450                         for(;i != end; ++i)
451                         {       
452                                 //eliminate duplicate points
453                                 if(*i != c)
454                                 {
455                                         f.push_back(c = *i);
456                                 }
457                         }
458                 }
459         }
460         //initialprocess = timer();
461         
462         if(f.size()<=6)
463                 return;
464         
465         //get curvature information
466         //timer.reset();
467         
468         {
469                 int i,i0,i1;
470                 synfig::Vector v1,v2;
471                 
472                 cvt.resize(f.size());
473                 
474                 cvt.front() = 1;
475                 cvt.back() = 1;
476                 
477                 for(i = 1; i < (int)f.size()-1; ++i)
478                 {
479                         i0 = std::max(0,i - 2);
480                         i1 = std::min((int)(f.size()-1),i + 2);
481                         
482                         v1 = f[i] - f[i0];
483                         v2 = f[i1] - f[i];
484         
485                         cvt[i] = (v1*v2)/(v1.mag()*v2.mag());
486                 }
487         }
488         
489         //curveval = timer();
490         //synfig::info("calculated curvature");
491         
492         //find corner points and interpolate inside those
493         //timer.reset();
494         {               
495                 //break at sharp derivative points
496                 //TODO tolerance should be set based upon digitization resolution (length dependent index selection)
497                 Real    tol = 0;                //break tolerance, for the cosine of the change in angle (really high curvature or something)
498                 Real    fixdistsq = 4*width*width; //the distance to ignore breaks at the end points (for fixing stuff)
499                 unsigned int i = 0;
500                 
501                 int             maxi = -1, last=0;
502                 Real    minc = 1;
503                 
504                 brk.push_back(0);
505                 
506                 for(i = 1; i < cvt.size()-1; ++i)
507                 {                       
508                         //insert if too sharp (we need to break the tangents to insert onto the break list)
509                         
510                         if(cvt[i] < tol)
511                         {
512                                 if(cvt[i] < minc)
513                                 {
514                                         minc = cvt[i];
515                                         maxi = i;
516                                 }
517                         }else if(maxi >= 0)
518                         {
519                                 if(maxi >= last + 8)
520                                 {
521                                         //synfig::info("break: %d-%d",maxi+1,cvt.size());                                               
522                                         brk.push_back(maxi);
523                                         last = maxi;
524                                 }
525                                 maxi = -1;
526                                 minc = 1;
527                         }
528                 }
529                 
530                 brk.push_back(i);
531                 
532                 //postprocess for breaks too close to eachother
533                 Real d = 0;
534                 Point p = f[brk.front()];
535                 
536                 //first set
537                 for(i = 1; i < brk.size()-1; ++i) //do not want to include end point...
538                 {
539                         d = (f[brk[i]] - p).mag_squared();
540                         if(d > fixdistsq) break; //don't want to group breaks if we ever get over the dist... 
541                 }
542                 //want to erase all points before...
543                 if(i != 1)
544                         brk.erase(brk.begin(),brk.begin()+i-1); 
545                 
546                 //end set
547                 p = f[brk.back()];
548                 for(i = brk.size()-2; i > 0; --i) //start at one in from the end
549                 {
550                         d = (f[brk[i]] - p).mag_squared();
551                         if(d > fixdistsq) break; //don't want to group breaks if we ever get over the dist
552                 }
553                 if(i != brk.size()-2)
554                         brk.erase(brk.begin()+i+2,brk.end()); //erase all points that we found... found none if i has not advanced
555                 //must not include the one we ended up on
556         }
557         //breakeval = timer();
558         //synfig::info("found break points: %d",brk.size());
559         
560         //get the distance calculation of the entire curve (for tangent scaling)
561
562         //timer.reset();
563         {
564                 synfig::Point p1,p2;
565                 
566                 p1=p2=f[0];
567                 
568                 di.resize(f.size()); d_i.resize(f.size());
569                 Real d = 0;
570                 for(unsigned int i = 0; i < f.size();)
571                 {
572                         d += (d_i[i] = (p2-p1).mag());
573                         di[i] = d;
574                         
575                         p1=p2;
576                         p2=f[++i];
577                 }
578         }
579         //disteval = timer();
580         //synfig::info("calculated distance");
581                 
582         //now break at every point - calculate new derivatives each time
583         
584         //TODO
585         //must be sure that the break points are 3 or more apart
586         //then must also store the breaks which are not smooth, etc.
587         //and figure out tangents between there
588         
589         //for each pair of break points (as long as they are far enough apart) recursively subdivide stuff
590         //ignore the detected intermediate points
591         {
592                 unsigned int i0=0,i3=0,is=0;
593                 int i=0,j=0;
594                 
595                 bool done = false;
596                 
597                 Real errortol = smoothness*pixelwidth; //???? what the hell should this value be
598                 
599                 BLinePoint a;
600                 synfig::Vector v;
601                 
602                 //intemp = f; //don't want to smooth out the corners
603                 
604                 bool breaktan = false, setwidth;
605                 a.set_split_tangent_flag(false);
606                 //a.set_width(width);
607                 a.set_width(1.0f);
608                 
609                 setwidth = (f.size() == f_w.size());
610                 
611                 for(j = 0; j < (int)brk.size() - 1; ++j)
612                 {
613                         //for b[j] to b[j+1] subdivide and stuff
614                         i0 = brk[j];
615                         i3 = brk[j+1];
616                         
617                         unsigned int size = i3-i0+1; //must include the end points
618                         
619                         //new derivatives
620                         //timer.reset();
621                         ftemp.assign(f.begin()+i0, f.begin()+i3+1);
622                         for(i=0;i<20;++i)
623                                 gaussian_blur_3(ftemp.begin(),ftemp.end(),false);
624                         
625                         df.resize(size);
626                         GetFirstDerivatives(ftemp,0,size,(char*)&df[0],sizeof(df[0]));
627                         //GetSimpleDerivatives(ftemp,0,size,df,0,di); 
628                         //< don't have to worry about indexing stuff as it is all being taken car of right now
629                         //preproceval += timer();
630                         //numpre++;
631                         
632                         work.resize(size*2-1); //guarantee that all points will be tesselated correctly (one point inbetween every 2 adjacent points)
633                         
634                         //if size of work is size*2-1, the step size should be 1/(size*2 - 2)
635                         //Real step = 1/(Real)(size*2 - 1);
636                         
637                         //start off with break points as indices
638                         curind.clear();
639                         curind.push_back(cpindex(i0,di[i3]-di[i0],0)); //0 error because no curve on the left
640                         curind.push_back(cpindex(i3,di[i3]-di[i0],-1)); //error needs to be reevaluated
641                         done = false; //we want to loop
642                         
643                         unsigned int dcount = 0;
644                         
645                         //while there are still enough points between us, and the error is too high subdivide (and invalidate neighbors that share tangents)            
646                         while(!done)
647                         {                                       
648                                 //tesselate all curves with invalid error values
649                                 work[0] = f[i0];
650                                 
651                                 //timer.reset();
652                                 /*numtess += */tesselate_curves(curind,f,df,work);
653                                 //tesseval += timer();
654                                 
655                                 //now get all error values
656                                 //timer.reset();
657                                 for(i = 1; i < (int)curind.size(); ++i)
658                                 {
659                                         if(curind[i].error < 0) //must have been retesselated, so now recalculate error value
660                                         {
661                                                 //evaluate error from points (starting at current index)
662                                                 int size = curind[i].curind - curind[i-1].curind + 1;
663                                                 curind[i].error = CurveError(&f[curind[i-1].curind], size,
664                                                                                                          work,(curind[i-1].curind - i0)*2,(curind[i].curind - i0)*2+1);
665                                                 
666                                                 /*if(curind[i].error > 1.0e5)
667                                                 {
668                                                         synfig::info("Holy crap %d-%d error %f",curind[i-1].curind,curind[i].curind,curind[i].error);
669                                                         curind[i].error = -1;
670                                                         numtess += tesselate_curves(curind,f,df,work);
671                                                         curind[i].error = CurveError(&f[curind[i-1].curind], size,
672                                                                                                          work,0,work.size());//(curind[i-1].curind - i0)*2,(curind[i].curind - i0)*2+1);
673                                                 }*/
674                                                 //numerror++;
675                                         }
676                                 }
677                                 //erroreval += timer();
678                                 
679                                 //assume we're done
680                                 done = true;
681                                 
682                                 //check each error to see if it's too big, if so, then subdivide etc.
683                                 int indsize = (int)curind.size();
684                                 Real maxrelerror = 0;
685                                 int maxi = -1;//, numpoints;
686                                 
687                                 //timer.reset();
688                                 //get the maximum error and split there
689                                 for(i = 1; i < indsize; ++i)
690                                 {
691                                         //numpoints = curind[i].curind - curind[i-1].curind + 1;
692                                         
693                                         if(curind[i].error > maxrelerror && curind[i].curind - curind[i-1].curind > 2) //only accept if it's valid
694                                         {
695                                                 maxrelerror = curind[i].error;
696                                                 maxi = i;
697                                         }
698                                 }
699                                 
700                                 //split if error is too great
701                                 if(maxrelerror > errortol)
702                                 {
703                                         //add one to the left etc
704                                         unsigned int    ibase = curind[maxi-1].curind, itop = curind[maxi].curind,
705                                                                         ibreak = (ibase + itop)/2;
706                                         Real scale, scale2;
707                                         
708                                         assert(ibreak < f.size());
709                                         
710                                         //synfig::info("Split %d -%d- %d, error: %f", ibase,ibreak,itop,maxrelerror);
711                                         
712                                         if(ibase != itop)
713                                         {
714                                                 //invalidate current error of the changed tangents and add an extra segment
715                                                 //enforce minimum tangents property
716                                                 curind[maxi].error = -1;
717                                                 curind[maxi-1].error = -1;
718                                                 if(maxi+1 < indsize) curind[maxi+1].error = -1; //if there is a curve segment beyond this it will be effected as well
719                                                 
720                                                 scale = di[itop] - di[ibreak];
721                                                 scale2 = maxi+1 < indsize ? di[curind[maxi+1].curind] - di[itop] : scale; //to the right valid?
722                                                 curind[maxi].tangentscale = std::min(scale, scale2);
723                                                                                                 
724                                                 scale = di[ibreak] - di[ibase];
725                                                 scale2 = maxi >= 2 ? di[ibase] - di[curind[maxi-2].curind] : scale; // to the left valid -2 ?
726                                                 curind[maxi-1].tangentscale = std::min(scale, scale2);
727                                                 
728                                                 scale = std::min(di[ibreak] - di[ibase], di[itop] - di[ibreak]);
729                                                 
730                                                 curind.insert(curind.begin()+maxi,cpindex(ibreak, scale, -1));
731                                                 //curind.push_back(cpindex(ibreak, scale, -1));
732                                                 //std::sort(curind.begin(), curind.end());
733                                                 
734                                                 done = false;
735                                                 //numsplit++;
736                                         }
737                                 }
738                                 //spliteval += timer();
739                                 
740                                 dcount++;
741                         }
742         
743                         //insert the last point too (just set tangent for now                   
744                         is = curind[0].curind;
745                         
746                         //first point inherits current tangent status                   
747                         v = df[is - i0];
748                         if(v.mag_squared() > EPSILON)
749                                 v *= (curind[0].tangentscale/v.mag());
750                                                         
751                         if(!breaktan)
752                                 a.set_tangent(v);
753                         else a.set_tangent2(v);
754                         
755                         a.set_vertex(f[is]);
756                         if(setwidth)a.set_width(f_w[is]);
757                         
758                         out.push_back(a);
759                         a.set_split_tangent_flag(false); //won't need to break anymore
760                         breaktan = false;
761                         
762                         for(i = 1; i < (int)curind.size()-1; ++i)
763                         {
764                                 is = curind[i].curind;
765                                 
766                                 //first point inherits current tangent status
767                                 v = df[is-i0];
768                                 if(v.mag_squared() > EPSILON)
769                                         v *= (curind[i].tangentscale/v.mag());
770                                                                 
771                                 a.set_tangent(v); // always inside, so guaranteed to be smooth
772                                 a.set_vertex(f[is]);
773                                 if(setwidth)a.set_width(f_w[is]);
774                                 
775                                 out.push_back(a);
776                         }
777                         
778                         //set the last point's data
779                         is = curind.back().curind; //should already be this
780                         
781                         v = df[is-i0];
782                         if(v.mag_squared() > EPSILON)
783                                 v *= (curind.back().tangentscale/v.mag());
784                         
785                         a.set_tangent1(v);
786                         a.set_split_tangent_flag(true);
787                         breaktan = true;
788                         
789                         //will get the vertex and tangent 2 from next round
790                 }
791                 
792                 a.set_vertex(f[i3]);
793                 a.set_split_tangent_flag(false);
794                 if(setwidth)
795                         a.set_width(f_w[i3]);
796                 out.push_back(a);
797                 
798                 /*etl::clock::value_type totaltime = total(),
799                                                            misctime = totaltime - initialprocess - curveval - breakeval - disteval
800                                                                           - preproceval - tesseval - erroreval - spliteval;
801                 
802                 synfig::info(
803                         "Curve Convert Profile:\n"
804                         "\tInitial Preprocess:    %f\n"
805                         "\tCurvature Calculation: %f\n"
806                         "\tBreak Calculation:     %f\n"
807                         "\tDistance Calculation:  %f\n"
808                         "  Algorithm: (numtimes,totaltime)\n"
809                         "\tPreprocess step:      (%d,%f)\n"
810                         "\tTesselation step:     (%d,%f)\n"
811                         "\tError step:           (%d,%f)\n"
812                         "\tSplit step:           (%d,%f)\n"
813                         "  Num Input: %d, Num Output: %d\n"
814                         "  Total time: %f, Misc time: %f\n",
815                         initialprocess, curveval,breakeval,disteval,
816                         numpre,preproceval,numtess,tesseval,numerror,erroreval,numsplit,spliteval,
817                         in.size(),out.size(),
818                         totaltime,misctime);*/
819                 
820                 return;
821         }
822 }
823
824 void synfigapp::BLineConverter::EnforceMinWidth(std::list<synfig::BLinePoint> &bline, synfig::Real min_pressure)
825 {
826         std::list<synfig::BLinePoint>::iterator i = bline.begin(),
827                                                                                         end = bline.end();
828         
829         for(i = bline.begin(); i != end; ++i)
830         {
831                 if(i->get_width() < min_pressure)
832                 {
833                         i->set_width(min_pressure);
834                 }
835         }
836 }