Quote the "Working Draft, Standard for Programming Language C++" on a point I wasn...
[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-6], f[right-5], f[right-4], f[right-3], f[right-2], 2);
221                 out += dfstride;
222                 FivePointdt(*(synfig::Vector*)out,f[right-5], f[right-4], f[right-3], f[right-2], f[right-1], 1);
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                                 if(*i != c)             // eliminate duplicate points
426                                 {
427                                         f.push_back(c = *i);
428                                         f_w.push_back(*iw);
429                                 }
430                 }
431                 else
432                 {
433                         for(;i != end; ++i)
434                                 if(*i != c)             // eliminate duplicate points
435                                         f.push_back(c = *i);
436                 }
437         }
438         //initialprocess = timer();
439
440         if(f.size()<=6)
441                 return;
442
443         //get curvature information
444         //timer.reset();
445
446         {
447                 int i,i0,i1;
448                 synfig::Vector v1,v2;
449
450                 cvt.resize(f.size());
451
452                 cvt.front() = 1;
453                 cvt.back() = 1;
454
455                 for(i = 1; i < (int)f.size()-1; ++i)
456                 {
457                         i0 = std::max(0,i - 2);
458                         i1 = std::min((int)(f.size()-1),i + 2);
459
460                         v1 = f[i] - f[i0];
461                         v2 = f[i1] - f[i];
462
463                         cvt[i] = (v1*v2)/(v1.mag()*v2.mag());
464                 }
465         }
466
467         //curveval = timer();
468         //synfig::info("calculated curvature");
469
470         //find corner points and interpolate inside those
471         //timer.reset();
472         {
473                 //break at sharp derivative points
474                 //TODO tolerance should be set based upon digitization resolution (length dependent index selection)
475                 Real    tol = 0;                //break tolerance, for the cosine of the change in angle (really high curvature or something)
476                 Real    fixdistsq = 4*width*width; //the distance to ignore breaks at the end points (for fixing stuff)
477                 unsigned int i = 0;
478
479                 int             maxi = -1, last=0;
480                 Real    minc = 1;
481
482                 brk.push_back(0);
483
484                 for(i = 1; i < cvt.size()-1; ++i)
485                 {
486                         //insert if too sharp (we need to break the tangents to insert onto the break list)
487
488                         if(cvt[i] < tol)
489                         {
490                                 if(cvt[i] < minc)
491                                 {
492                                         minc = cvt[i];
493                                         maxi = i;
494                                 }
495                         }
496                         else if(maxi >= 0)
497                         {
498                                 if(maxi >= last + 8)
499                                 {
500                                         //synfig::info("break: %d-%d",maxi+1,cvt.size());
501                                         brk.push_back(maxi);
502                                         last = maxi;
503                                 }
504                                 maxi = -1;
505                                 minc = 1;
506                         }
507                 }
508
509                 brk.push_back(i);
510
511                 //postprocess for breaks too close to each other
512                 Real d = 0;
513                 Point p = f[brk.front()];
514
515                 //first set
516                 for(i = 1; i < brk.size()-1; ++i) //do not want to include end point...
517                 {
518                         d = (f[brk[i]] - p).mag_squared();
519                         if(d > fixdistsq) break; //don't want to group breaks if we ever get over the dist...
520                 }
521                 //want to erase all points before...
522                 if(i != 1)
523                         brk.erase(brk.begin(),brk.begin()+i-1);
524
525                 //end set
526                 p = f[brk.back()];
527                 for(i = brk.size()-2; i > 0; --i) //start at one in from the end
528                 {
529                         d = (f[brk[i]] - p).mag_squared();
530                         if(d > fixdistsq) break; //don't want to group breaks if we ever get over the dist
531                 }
532                 if(i != brk.size()-2)
533                         brk.erase(brk.begin()+i+2,brk.end()); //erase all points that we found... found none if i has not advanced
534                 //must not include the one we ended up on
535         }
536         //breakeval = timer();
537         //synfig::info("found break points: %d",brk.size());
538
539         //get the distance calculation of the entire curve (for tangent scaling)
540
541         //timer.reset();
542         {
543                 synfig::Point p1,p2;
544
545                 p1=p2=f[0];
546
547                 di.resize(f.size()); d_i.resize(f.size());
548                 Real d = 0;
549                 for(unsigned int i = 0; i < f.size();)
550                 {
551                         d += (d_i[i] = (p2-p1).mag());
552                         di[i] = d;
553
554                         p1=p2;
555                         p2=f[++i];
556                 }
557         }
558         //disteval = timer();
559         //synfig::info("calculated distance");
560
561         //now break at every point - calculate new derivatives each time
562
563         //TODO
564         //must be sure that the break points are 3 or more apart
565         //then must also store the breaks which are not smooth, etc.
566         //and figure out tangents between there
567
568         //for each pair of break points (as long as they are far enough apart) recursively subdivide stuff
569         //ignore the detected intermediate points
570         {
571                 unsigned int i0=0,i3=0,is=0;
572                 int i=0,j=0;
573
574                 bool done = false;
575
576                 Real errortol = smoothness*pixelwidth; //???? what the hell should this value be
577
578                 BLinePoint a;
579                 synfig::Vector v;
580
581                 //intemp = f; //don't want to smooth out the corners
582
583                 bool breaktan = false, setwidth;
584                 a.set_split_tangent_flag(false);
585                 //a.set_width(width);
586                 a.set_width(1.0f);
587
588                 setwidth = (f.size() == f_w.size());
589
590                 for(j = 0; j < (int)brk.size() - 1; ++j)
591                 {
592                         //for b[j] to b[j+1] subdivide and stuff
593                         i0 = brk[j];
594                         i3 = brk[j+1];
595
596                         unsigned int size = i3-i0+1; //must include the end points
597
598                         //new derivatives
599                         //timer.reset();
600                         ftemp.assign(f.begin()+i0, f.begin()+i3+1);
601                         for(i=0;i<20;++i)
602                                 gaussian_blur_3(ftemp.begin(),ftemp.end(),false);
603
604                         df.resize(size);
605
606                         // Wondering whether the modification of the df vector
607                         // using a char* pointer and pointer arithmetric was safe,
608                         // I looked it up...
609                         // 
610                         // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2369.pdf tells me:
611                         // 
612                         //      23.2.5  Class template vector [vector]
613                         // 
614                         //      [...] The elements of a vector are stored contiguously,
615                         //      meaning that if v is a vector<T,Allocator> where T is
616                         //      some type other than bool, then it obeys the identity
617                         //      &v[n] == &v[0] + n for all 0 <= n < v.size().
618                         // 
619                         GetFirstDerivatives(ftemp,0,size,(char*)&df[0],sizeof(df[0]));
620
621                         //GetSimpleDerivatives(ftemp,0,size,df,0,di);
622                         //< don't have to worry about indexing stuff as it is all being taken care of right now
623                         //preproceval += timer();
624                         //numpre++;
625
626                         work.resize(size*2-1); //guarantee that all points will be tessellated correctly (one point in between every 2 adjacent points)
627
628                         //if size of work is size*2-1, the step size should be 1/(size*2 - 2)
629                         //Real step = 1/(Real)(size*2 - 1);
630
631                         //start off with break points as indices
632                         curind.clear();
633                         curind.push_back(cpindex(i0,di[i3]-di[i0],0)); //0 error because no curve on the left
634                         curind.push_back(cpindex(i3,di[i3]-di[i0],-1)); //error needs to be reevaluated
635                         done = false; //we want to loop
636
637                         unsigned int dcount = 0;
638
639                         //while there are still enough points between us, and the error is too high subdivide (and invalidate neighbors that share tangents)
640                         while(!done)
641                         {
642                                 //tessellate all curves with invalid error values
643                                 work[0] = f[i0];
644
645                                 //timer.reset();
646                                 /*numtess += */tessellate_curves(curind,f,df,work);
647                                 //tesseval += timer();
648
649                                 //now get all error values
650                                 //timer.reset();
651                                 for(i = 1; i < (int)curind.size(); ++i)
652                                 {
653                                         if(curind[i].error < 0) //must have been retessellated, so now recalculate error value
654                                         {
655                                                 //evaluate error from points (starting at current index)
656                                                 int size = curind[i].curind - curind[i-1].curind + 1;
657                                                 curind[i].error = CurveError(&f[curind[i-1].curind], size,
658                                                                                                          work,(curind[i-1].curind - i0)*2,(curind[i].curind - i0)*2+1);
659
660                                                 /*if(curind[i].error > 1.0e5)
661                                                 {
662                                                         synfig::info("Holy crap %d-%d error %f",curind[i-1].curind,curind[i].curind,curind[i].error);
663                                                         curind[i].error = -1;
664                                                         numtess += tessellate_curves(curind,f,df,work);
665                                                         curind[i].error = CurveError(&f[curind[i-1].curind], size,
666                                                                                                          work,0,work.size());//(curind[i-1].curind - i0)*2,(curind[i].curind - i0)*2+1);
667                                                 }*/
668                                                 //numerror++;
669                                         }
670                                 }
671                                 //erroreval += timer();
672
673                                 //assume we're done
674                                 done = true;
675
676                                 //check each error to see if it's too big, if so, then subdivide etc.
677                                 int indsize = (int)curind.size();
678                                 Real maxrelerror = 0;
679                                 int maxi = -1;//, numpoints;
680
681                                 //timer.reset();
682                                 //get the maximum error and split there
683                                 for(i = 1; i < indsize; ++i)
684                                 {
685                                         //numpoints = curind[i].curind - curind[i-1].curind + 1;
686
687                                         if(curind[i].error > maxrelerror && curind[i].curind - curind[i-1].curind > 2) //only accept if it's valid
688                                         {
689                                                 maxrelerror = curind[i].error;
690                                                 maxi = i;
691                                         }
692                                 }
693
694                                 //split if error is too great
695                                 if(maxrelerror > errortol)
696                                 {
697                                         //add one to the left etc
698                                         unsigned int    ibase = curind[maxi-1].curind, itop = curind[maxi].curind,
699                                                                         ibreak = (ibase + itop)/2;
700                                         Real scale, scale2;
701
702                                         assert(ibreak < f.size());
703
704                                         //synfig::info("Split %d -%d- %d, error: %f", ibase,ibreak,itop,maxrelerror);
705
706                                         if(ibase != itop)
707                                         {
708                                                 //invalidate current error of the changed tangents and add an extra segment
709                                                 //enforce minimum tangents property
710                                                 curind[maxi].error = -1;
711                                                 curind[maxi-1].error = -1;
712                                                 if(maxi+1 < indsize) curind[maxi+1].error = -1; //if there is a curve segment beyond this it will be effected as well
713
714                                                 scale = di[itop] - di[ibreak];
715                                                 scale2 = maxi+1 < indsize ? di[curind[maxi+1].curind] - di[itop] : scale; //to the right valid?
716                                                 curind[maxi].tangentscale = std::min(scale, scale2);
717
718                                                 scale = di[ibreak] - di[ibase];
719                                                 scale2 = maxi >= 2 ? di[ibase] - di[curind[maxi-2].curind] : scale; // to the left valid -2 ?
720                                                 curind[maxi-1].tangentscale = std::min(scale, scale2);
721
722                                                 scale = std::min(di[ibreak] - di[ibase], di[itop] - di[ibreak]);
723
724                                                 curind.insert(curind.begin()+maxi,cpindex(ibreak, scale, -1));
725                                                 //curind.push_back(cpindex(ibreak, scale, -1));
726                                                 //std::sort(curind.begin(), curind.end());
727
728                                                 done = false;
729                                                 //numsplit++;
730                                         }
731                                 }
732                                 //spliteval += timer();
733
734                                 dcount++;
735                         }
736
737                         //insert the last point too (just set tangent for now
738                         is = curind[0].curind;
739
740                         //first point inherits current tangent status
741                         v = df[is - i0];
742                         if(v.mag_squared() > EPSILON)
743                                 v *= (curind[0].tangentscale/v.mag());
744
745                         if(!breaktan)
746                                 a.set_tangent(v);
747                         else a.set_tangent2(v);
748
749                         a.set_vertex(f[is]);
750                         if(setwidth)a.set_width(f_w[is]);
751
752                         out.push_back(a);
753                         a.set_split_tangent_flag(false); //won't need to break anymore
754                         breaktan = false;
755
756                         for(i = 1; i < (int)curind.size()-1; ++i)
757                         {
758                                 is = curind[i].curind;
759
760                                 //first point inherits current tangent status
761                                 v = df[is-i0];
762                                 if(v.mag_squared() > EPSILON)
763                                         v *= (curind[i].tangentscale/v.mag());
764
765                                 a.set_tangent(v); // always inside, so guaranteed to be smooth
766                                 a.set_vertex(f[is]);
767                                 if(setwidth)a.set_width(f_w[is]);
768
769                                 out.push_back(a);
770                         }
771
772                         //set the last point's data
773                         is = curind.back().curind; //should already be this
774
775                         v = df[is-i0];
776                         if(v.mag_squared() > EPSILON)
777                                 v *= (curind.back().tangentscale/v.mag());
778
779                         a.set_tangent1(v);
780                         a.set_split_tangent_flag(true);
781                         breaktan = true;
782
783                         //will get the vertex and tangent 2 from next round
784                 }
785
786                 a.set_vertex(f[i3]);
787                 a.set_split_tangent_flag(false);
788                 if(setwidth)
789                         a.set_width(f_w[i3]);
790                 out.push_back(a);
791
792                 /*etl::clock::value_type totaltime = total(),
793                                                            misctime = totaltime - initialprocess - curveval - breakeval - disteval
794                                                                           - preproceval - tesseval - erroreval - spliteval;
795
796                 synfig::info(
797                         "Curve Convert Profile:\n"
798                         "\tInitial Preprocess:    %f\n"
799                         "\tCurvature Calculation: %f\n"
800                         "\tBreak Calculation:     %f\n"
801                         "\tDistance Calculation:  %f\n"
802                         "  Algorithm: (numtimes,totaltime)\n"
803                         "\tPreprocess step:      (%d,%f)\n"
804                         "\tTessellation step:    (%d,%f)\n"
805                         "\tError step:           (%d,%f)\n"
806                         "\tSplit step:           (%d,%f)\n"
807                         "  Num Input: %d, Num Output: %d\n"
808                         "  Total time: %f, Misc time: %f\n",
809                         initialprocess, curveval,breakeval,disteval,
810                         numpre,preproceval,numtess,tesseval,numerror,erroreval,numsplit,spliteval,
811                         in.size(),out.size(),
812                         totaltime,misctime);*/
813
814                 return;
815         }
816 }
817
818 void synfigapp::BLineConverter::EnforceMinWidth(std::list<synfig::BLinePoint> &bline, synfig::Real min_pressure)
819 {
820         std::list<synfig::BLinePoint>::iterator i = bline.begin(),
821                                                                                         end = bline.end();
822
823         for(i = bline.begin(); i != end; ++i)
824                 if(i->get_width() < min_pressure)
825                         i->set_width(min_pressure);
826 }