Tidying. Trying to understand the code, and tidying it up a little as I do so. ...
[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         point_cache.clear();
381         width_cache.clear();
382         ftemp.clear();
383         deriv.clear();
384         cvt.clear();
385         brk.clear();
386         cum_dist.clear();
387         this_dist.clear();
388         work.clear();
389         curind.clear();
390 }
391
392 void
393 synfigapp::BLineConverter::operator()(std::list<synfig::BLinePoint>  &blinepoints_out,
394                                                                           const std::list<synfig::Point> &points_in,
395                                                                           const std::list<synfig::Real>  &widths_in)
396 {
397         //Profiling information
398         /*etl::clock::value_type initialprocess=0, curveval=0, breakeval=0, disteval=0;
399         etl::clock::value_type preproceval=0, tesseval=0, erroreval=0, spliteval=0;
400         unsigned int                    numpre=0, numtess=0, numerror=0, numsplit=0;
401         etl::clock_realtime timer,total;*/
402
403         //total.reset();
404         if (points_in.size() <= 1)
405                 return;
406
407         clear();
408
409         //removing digitization error harder than expected
410
411         //intended to fix little pen errors caused by the way people draw (tiny juts in opposite direction)
412         //Different solutions
413         //      Average at both end points (will probably eliminate many points at each end of the samples)
414         //      Average after the break points are found (weird points would still affect the curve)
415         //      Just always get rid of breaks at the beginning and end if they are a certain distance apart
416         //              This is will be current approach so all we do now is try to remove duplicate points
417
418         //remove duplicate points - very bad for fitting
419
420         //timer.reset();
421
422         {
423                 std::list<synfig::Point>::const_iterator point_iter = points_in.begin(), end = points_in.end();
424                 std::list<synfig::Real>::const_iterator width_iter = widths_in.begin();
425                 synfig::Point c;
426
427                 if (points_in.size() == widths_in.size())
428                 {
429                         for(;point_iter != end; ++point_iter,++width_iter)
430                                 if(*point_iter != c)            // eliminate duplicate points
431                                 {
432                                         point_cache.push_back(c = *point_iter);
433                                         width_cache.push_back(*width_iter);
434                                 }
435                 }
436                 else
437                         for(;point_iter != end; ++point_iter)
438                                 if(*point_iter != c)            // eliminate duplicate points
439                                         point_cache.push_back(c = *point_iter);
440         }
441         //initialprocess = timer();
442
443         if (point_cache.size() < 7)
444         {
445                 info("only %d unique points - giving up", point_cache.size());
446                 return;
447         }
448
449         //get curvature information
450         //timer.reset();
451
452         {
453                 int i,i0,i1;
454                 synfig::Vector v1,v2;
455
456                 cvt.resize(point_cache.size());
457
458                 cvt.front() = 1;
459                 cvt.back() = 1;
460
461                 for(i = 1; i < (int)point_cache.size()-1; ++i)
462                 {
463                         i0 = std::max(0,i - 2);
464                         i1 = std::min((int)(point_cache.size()-1),i + 2);
465
466                         v1 = point_cache[i] - point_cache[i0];
467                         v2 = point_cache[i1] - point_cache[i];
468
469                         cvt[i] = (v1*v2)/(v1.mag()*v2.mag());
470                 }
471         }
472
473         //curveval = timer();
474         //synfig::info("calculated curvature");
475
476         //find corner points and interpolate inside those
477         //timer.reset();
478         {
479                 //break at sharp derivative points
480                 //TODO tolerance should be set based upon digitization resolution (length dependent index selection)
481                 Real    tol = 0;                //break tolerance, for the cosine of the change in angle (really high curvature or something)
482                 Real    fixdistsq = 4*width*width; //the distance to ignore breaks at the end points (for fixing stuff)
483                 unsigned int i = 0;
484
485                 int             maxi = -1, last=0;
486                 Real    minc = 1;
487
488                 brk.push_back(0);
489
490                 for(i = 1; i < cvt.size()-1; ++i)
491                 {
492                         //insert if too sharp (we need to break the tangents to insert onto the break list)
493
494                         if(cvt[i] < tol)
495                         {
496                                 if(cvt[i] < minc)
497                                 {
498                                         minc = cvt[i];
499                                         maxi = i;
500                                 }
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 = point_cache[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 = (point_cache[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 = point_cache[brk.back()];
533                 for(i = brk.size()-2; i > 0; --i) //start at one in from the end
534                 {
535                         d = (point_cache[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=point_cache[0];
552
553                 cum_dist.resize(point_cache.size()); this_dist.resize(point_cache.size());
554                 Real d = 0;
555                 for(unsigned int i = 0; i < point_cache.size();)
556                 {
557                         d += (this_dist[i] = (p2-p1).mag());
558                         cum_dist[i] = d;
559
560                         p1=p2;
561                         //! \todo is this legal?  it reads off the end of the vector
562                         p2=point_cache[++i];
563                 }
564         }
565         //disteval = timer();
566         //synfig::info("calculated distance");
567
568         //now break at every point - calculate new derivatives each time
569
570         //TODO
571         //must be sure that the break points are 3 or more apart
572         //then must also store the breaks which are not smooth, etc.
573         //and figure out tangents between there
574
575         //for each pair of break points (as long as they are far enough apart) recursively subdivide stuff
576         //ignore the detected intermediate points
577         {
578                 unsigned int i0=0,i3=0,is=0;
579                 int i=0,j=0;
580
581                 bool done = false;
582
583                 Real errortol = smoothness*pixelwidth; //???? what the hell should this value be
584
585                 BLinePoint a;
586                 synfig::Vector v;
587
588                 //intemp = f; //don't want to smooth out the corners
589
590                 bool breaktan = false, setwidth;
591                 a.set_split_tangent_flag(false);
592                 //a.set_width(width);
593                 a.set_width(1.0f);
594
595                 setwidth = (point_cache.size() == width_cache.size());
596
597                 for(j = 0; j < (int)brk.size() - 1; ++j)
598                 {
599                         //for b[j] to b[j+1] subdivide and stuff
600                         i0 = brk[j];
601                         i3 = brk[j+1];
602
603                         unsigned int size = i3-i0+1; //must include the end points
604
605                         //new derivatives
606                         //timer.reset();
607                         ftemp.assign(point_cache.begin()+i0, point_cache.begin()+i3+1);
608                         for(i=0;i<20;++i)
609                                 gaussian_blur_3(ftemp.begin(),ftemp.end(),false);
610
611                         deriv.resize(size);
612
613                         // Wondering whether the modification of the deriv vector
614                         // using a char* pointer and pointer arithmetric was safe,
615                         // I looked it up...
616                         // 
617                         // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2369.pdf tells me:
618                         // 
619                         //      23.2.5  Class template vector [vector]
620                         // 
621                         //      [...] The elements of a vector are stored contiguously,
622                         //      meaning that if v is a vector<T,Allocator> where T is
623                         //      some type other than bool, then it obeys the identity
624                         //      &v[n] == &v[0] + n for all 0 <= n < v.size().
625                         // 
626                         GetFirstDerivatives(ftemp,0,size,(char*)&deriv[0],sizeof(deriv[0]));
627
628                         //GetSimpleDerivatives(ftemp,0,size,deriv,0,cum_dist);
629                         //< don't have to worry about indexing stuff as it is all being taken care of right now
630                         //preproceval += timer();
631                         //numpre++;
632
633                         work.resize(size*2-1); //guarantee that all points will be tessellated correctly (one point in between every 2 adjacent points)
634
635                         //if size of work is size*2-1, the step size should be 1/(size*2 - 2)
636                         //Real step = 1/(Real)(size*2 - 1);
637
638                         //start off with break points as indices
639                         curind.clear();
640                         curind.push_back(cpindex(i0,cum_dist[i3]-cum_dist[i0],0)); //0 error because no curve on the left
641                         curind.push_back(cpindex(i3,cum_dist[i3]-cum_dist[i0],-1)); //error needs to be reevaluated
642                         done = false; //we want to loop
643
644                         unsigned int dcount = 0;
645
646                         //while there are still enough points between us, and the error is too high subdivide (and invalidate neighbors that share tangents)
647                         while(!done)
648                         {
649                                 //tessellate all curves with invalid error values
650                                 work[0] = point_cache[i0];
651
652                                 //timer.reset();
653                                 /*numtess += */tessellate_curves(curind,point_cache,deriv,work);
654                                 //tesseval += timer();
655
656                                 //now get all error values
657                                 //timer.reset();
658                                 for(i = 1; i < (int)curind.size(); ++i)
659                                 {
660                                         if(curind[i].error < 0) //must have been retessellated, so now recalculate error value
661                                         {
662                                                 //evaluate error from points (starting at current index)
663                                                 int size = curind[i].curind - curind[i-1].curind + 1;
664                                                 curind[i].error = CurveError(&point_cache[curind[i-1].curind], size,
665                                                                                                          work,(curind[i-1].curind - i0)*2,(curind[i].curind - i0)*2+1);
666
667                                                 /*if(curind[i].error > 1.0e5)
668                                                 {
669                                                         synfig::info("Holy crap %d-%d error %f",curind[i-1].curind,curind[i].curind,curind[i].error);
670                                                         curind[i].error = -1;
671                                                         numtess += tessellate_curves(curind,f,deriv,work);
672                                                         curind[i].error = CurveError(&point_cache[curind[i-1].curind], size,
673                                                                                                          work,0,work.size());//(curind[i-1].curind - i0)*2,(curind[i].curind - i0)*2+1);
674                                                 }*/
675                                                 //numerror++;
676                                         }
677                                 }
678                                 //erroreval += timer();
679
680                                 //assume we're done
681                                 done = true;
682
683                                 //check each error to see if it's too big, if so, then subdivide etc.
684                                 int indsize = (int)curind.size();
685                                 Real maxrelerror = 0;
686                                 int maxi = -1;//, numpoints;
687
688                                 //timer.reset();
689                                 //get the maximum error and split there
690                                 for(i = 1; i < indsize; ++i)
691                                 {
692                                         //numpoints = curind[i].curind - curind[i-1].curind + 1;
693
694                                         if(curind[i].error > maxrelerror && curind[i].curind - curind[i-1].curind > 2) //only accept if it's valid
695                                         {
696                                                 maxrelerror = curind[i].error;
697                                                 maxi = i;
698                                         }
699                                 }
700
701                                 //split if error is too great
702                                 if(maxrelerror > errortol)
703                                 {
704                                         //add one to the left etc
705                                         unsigned int    ibase = curind[maxi-1].curind, itop = curind[maxi].curind,
706                                                                         ibreak = (ibase + itop)/2;
707                                         Real scale, scale2;
708
709                                         assert(ibreak < point_cache.size());
710
711                                         //synfig::info("Split %d -%d- %d, error: %f", ibase,ibreak,itop,maxrelerror);
712
713                                         if(ibase != itop)
714                                         {
715                                                 //invalidate current error of the changed tangents and add an extra segment
716                                                 //enforce minimum tangents property
717                                                 curind[maxi].error = -1;
718                                                 curind[maxi-1].error = -1;
719                                                 if(maxi+1 < indsize) curind[maxi+1].error = -1; //if there is a curve segment beyond this it will be effected as well
720
721                                                 scale = cum_dist[itop] - cum_dist[ibreak];
722                                                 scale2 = maxi+1 < indsize ? cum_dist[curind[maxi+1].curind] - cum_dist[itop] : scale; //to the right valid?
723                                                 curind[maxi].tangentscale = std::min(scale, scale2);
724
725                                                 scale = cum_dist[ibreak] - cum_dist[ibase];
726                                                 scale2 = maxi >= 2 ? cum_dist[ibase] - cum_dist[curind[maxi-2].curind] : scale; // to the left valid -2 ?
727                                                 curind[maxi-1].tangentscale = std::min(scale, scale2);
728
729                                                 scale = std::min(cum_dist[ibreak] - cum_dist[ibase], cum_dist[itop] - cum_dist[ibreak]);
730
731                                                 curind.insert(curind.begin()+maxi,cpindex(ibreak, scale, -1));
732                                                 //curind.push_back(cpindex(ibreak, scale, -1));
733                                                 //std::sort(curind.begin(), curind.end());
734
735                                                 done = false;
736                                                 //numsplit++;
737                                         }
738                                 }
739                                 //spliteval += timer();
740
741                                 dcount++;
742                         }
743
744                         //insert the last point too (just set tangent for now
745                         is = curind[0].curind;
746
747                         //first point inherits current tangent status
748                         v = deriv[is - i0];
749                         if(v.mag_squared() > EPSILON)
750                                 v *= (curind[0].tangentscale/v.mag());
751
752                         if(!breaktan)
753                                 a.set_tangent(v);
754                         else a.set_tangent2(v);
755
756                         a.set_vertex(point_cache[is]);
757                         if(setwidth)a.set_width(width_cache[is]);
758
759                         blinepoints_out.push_back(a);
760                         a.set_split_tangent_flag(false); //won't need to break anymore
761                         breaktan = false;
762
763                         for(i = 1; i < (int)curind.size()-1; ++i)
764                         {
765                                 is = curind[i].curind;
766
767                                 //first point inherits current tangent status
768                                 v = deriv[is-i0];
769                                 if(v.mag_squared() > EPSILON)
770                                         v *= (curind[i].tangentscale/v.mag());
771
772                                 a.set_tangent(v); // always inside, so guaranteed to be smooth
773                                 a.set_vertex(point_cache[is]);
774                                 if(setwidth)a.set_width(width_cache[is]);
775
776                                 blinepoints_out.push_back(a);
777                         }
778
779                         //set the last point's data
780                         is = curind.back().curind; //should already be this
781
782                         v = deriv[is-i0];
783                         if(v.mag_squared() > EPSILON)
784                                 v *= (curind.back().tangentscale/v.mag());
785
786                         a.set_tangent1(v);
787                         a.set_split_tangent_flag(true);
788                         breaktan = true;
789
790                         //will get the vertex and tangent 2 from next round
791                 }
792
793                 a.set_vertex(point_cache[i3]);
794                 a.set_split_tangent_flag(false);
795                 if(setwidth)
796                         a.set_width(width_cache[i3]);
797                 blinepoints_out.push_back(a);
798
799                 /*etl::clock::value_type totaltime = total(),
800                                                            misctime = totaltime - initialprocess - curveval - breakeval - disteval
801                                                                           - preproceval - tesseval - erroreval - spliteval;
802
803                 synfig::info(
804                         "Curve Convert Profile:\n"
805                         "\tInitial Preprocess:    %f\n"
806                         "\tCurvature Calculation: %f\n"
807                         "\tBreak Calculation:     %f\n"
808                         "\tDistance Calculation:  %f\n"
809                         "  Algorithm: (numtimes,totaltime)\n"
810                         "\tPreprocess step:      (%d,%f)\n"
811                         "\tTessellation step:    (%d,%f)\n"
812                         "\tError step:           (%d,%f)\n"
813                         "\tSplit step:           (%d,%f)\n"
814                         "  Num Input: %d, Num Output: %d\n"
815                         "  Total time: %f, Misc time: %f\n",
816                         initialprocess, curveval,breakeval,disteval,
817                         numpre,preproceval,numtess,tesseval,numerror,erroreval,numsplit,spliteval,
818                         points_in.size(),blinepoints_out.size(),
819                         totaltime,misctime);*/
820
821                 return;
822         }
823 }
824
825 void synfigapp::BLineConverter::EnforceMinWidth(std::list<synfig::BLinePoint> &bline, synfig::Real min_pressure)
826 {
827         std::list<synfig::BLinePoint>::iterator i = bline.begin(),
828                                                                                         end = bline.end();
829
830         for(i = bline.begin(); i != end; ++i)
831                 if(i->get_width() < min_pressure)
832                         i->set_width(min_pressure);
833 }