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