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