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