Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-core / tags / synfig_0_61_06 / src / modules / mod_geometry / outline.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file outline.cpp
3 **      \brief Template
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 //! \note This whole file should be rewritten at some point (darco)
24
25 /* === H E A D E R S ======================================================= */
26
27 #ifdef USING_PCH
28 #       include "pch.h"
29 #else
30 #ifdef HAVE_CONFIG_H
31 #       include <config.h>
32 #endif
33
34 #include "outline.h"
35 #include <synfig/string.h>
36 #include <synfig/time.h>
37 #include <synfig/context.h>
38 #include <synfig/paramdesc.h>
39 #include <synfig/renddesc.h>
40 #include <synfig/surface.h>
41 #include <synfig/value.h>
42 #include <synfig/valuenode.h>
43
44 #include <ETL/calculus>
45 #include <ETL/bezier>
46 #include <ETL/hermite>
47 #include <vector>
48
49 #include <synfig/valuenode_bline.h>
50
51 #endif
52
53 using namespace etl;
54
55 /* === M A C R O S ========================================================= */
56
57 #define SAMPLES         50
58 #define ROUND_END_FACTOR        (4)
59 #define CUSP_THRESHOLD          (0.40)
60 #define SPIKE_AMOUNT            (4)
61 #define NO_LOOP_COOKIE          synfig::Vector(84951305,7836658)
62 #define EPSILON                         (0.000000001)
63 #define CUSP_TANGENT_ADJUST     (0.025)
64
65 /* === G L O B A L S ======================================================= */
66
67 SYNFIG_LAYER_INIT(Outline);
68 SYNFIG_LAYER_SET_NAME(Outline,"outline");
69 SYNFIG_LAYER_SET_LOCAL_NAME(Outline,_("Outline"));
70 SYNFIG_LAYER_SET_CATEGORY(Outline,_("Geometry"));
71 SYNFIG_LAYER_SET_VERSION(Outline,"0.2");
72 SYNFIG_LAYER_SET_CVS_ID(Outline,"$Id$");
73
74 /* === P R O C E D U R E S ================================================= */
75
76 // This function was adapted from what was
77 // described on http://www.whisqu.se/per/docs/math28.htm
78 Point line_intersection(
79         const Point& p1,
80         const Vector& t1,
81         const Point& p2,
82         const Vector& t2
83 )
84 {
85         const float& x0(p1[0]);
86         const float& y0(p1[1]);
87
88         const float x1(p1[0]+t1[0]);
89         const float y1(p1[1]+t1[1]);
90
91         const float& x2(p2[0]);
92         const float& y2(p2[1]);
93
94         const float x3(p2[0]+t2[0]);
95         const float y3(p2[1]+t2[1]);
96
97         const float near_infinity((float)1e+10);
98
99         float m1,m2;    // the slopes of each line
100
101         // compute slopes, note the cludge for infinity, however, this will
102         // be close enough
103
104         if ((x1-x0)!=0)
105            m1 = (y1-y0)/(x1-x0);
106         else
107            m1 = near_infinity;
108
109         if ((x3-x2)!=0)
110            m2 = (y3-y2)/(x3-x2);
111         else
112            m2 = near_infinity;
113
114         // compute constants
115         const float& a1(m1);
116         const float& a2(m2);
117         const float b1(-1.0f);
118         const float b2(-1.0f);
119         const float c1(y0-m1*x0);
120         const float c2(y2-m2*x2);
121
122         // compute the inverse of the determinate
123         const float det_inv(1.0f/(a1*b2 - a2*b1));
124
125         // use Kramers rule to compute the intersection
126         return Point(
127                 ((b1*c2 - b2*c1)*det_inv),
128                 ((a2*c1 - a1*c2)*det_inv)
129         );
130 } // end Intersect_Lines
131
132 /* === M E T H O D S ======================================================= */
133
134
135 Outline::Outline()
136 {
137         old_version=false;
138         round_tip[0]=true;
139         round_tip[1]=true;
140         sharp_cusps=true;
141         width=1.0f;
142         loopyness=1.0f;
143         expand=0;
144         homogeneous_width=true;
145         clear();
146
147         vector<BLinePoint> bline_point_list;
148         bline_point_list.push_back(BLinePoint());
149         bline_point_list.push_back(BLinePoint());
150         bline_point_list.push_back(BLinePoint());
151         bline_point_list[0].set_vertex(Point(0,1));
152         bline_point_list[1].set_vertex(Point(0,-1));
153         bline_point_list[2].set_vertex(Point(1,0));
154         bline_point_list[0].set_tangent(bline_point_list[1].get_vertex()-bline_point_list[2].get_vertex()*0.5f);
155         bline_point_list[1].set_tangent(bline_point_list[2].get_vertex()-bline_point_list[0].get_vertex()*0.5f);
156         bline_point_list[2].set_tangent(bline_point_list[0].get_vertex()-bline_point_list[1].get_vertex()*0.5f);
157         bline_point_list[0].set_width(1.0f);
158         bline_point_list[1].set_width(1.0f);
159         bline_point_list[2].set_width(1.0f);
160         bline=bline_point_list;
161
162         needs_sync=true;
163 }
164
165
166 /*! The Sync() function takes the values
167 **      and creates a polygon to be rendered
168 **      with the polygon layer.
169 */
170 void
171 Outline::sync()
172 {
173         clear();
174         try {
175 #if 1
176
177         const bool loop(bline.get_loop());
178         const vector<synfig::BLinePoint> bline_(bline.get_list().begin(),bline.get_list().end());
179 #define bline bline_
180
181         vector<BLinePoint>::const_iterator
182                 iter,
183                 next(bline.begin());
184
185         const vector<BLinePoint>::const_iterator
186                 end(bline.end());
187
188         vector<Point>
189                 side_a,
190                 side_b;
191
192         if(loop)
193                 iter=--bline.end();
194         else
195                 iter=next++;
196
197         //                              iter    next
198         //                              ----    ----
199         // looped               nth             1st
200         // !looped              1st             2nd
201
202         Vector first_tangent=bline.front().get_tangent2();
203         Vector last_tangent=iter->get_tangent1();
204
205         // if we are looped and drawing sharp cusps, we'll need a value for the incoming tangent
206         if (loop && sharp_cusps && last_tangent.is_equal_to(Vector::zero()))
207         {
208                 hermite<Vector> curve((iter-1)->get_vertex(), iter->get_vertex(), (iter-1)->get_tangent2(), iter->get_tangent1());
209                 const derivative< hermite<Vector> > deriv(curve);
210                 last_tangent=deriv(1.0-CUSP_TANGENT_ADJUST);
211         }
212
213         // `first' is for making the cusps; don't do that for the first point if we're not looped
214         for(bool first=!loop; next!=end; iter=next++)
215         {
216                 Vector prev_t(iter->get_tangent1());
217                 Vector iter_t(iter->get_tangent2());
218                 Vector next_t(next->get_tangent1());
219
220                 bool split_flag(iter->get_split_tangent_flag());
221
222                 // if iter.t2 == 0 and next.t1 == 0, this is a straight line
223                 if(iter_t.is_equal_to(Vector::zero()) && next_t.is_equal_to(Vector::zero()))
224                 {
225                         iter_t=next_t=next->get_vertex()-iter->get_vertex();
226                         // split_flag=true;
227
228                         // if the two points are on top of each other, ignore this segment
229                         // leave `first' true if was before
230                         if (iter_t.is_equal_to(Vector::zero()))
231                                 continue;
232                 }
233
234                 // Setup the curve
235                 hermite<Vector> curve(
236                         iter->get_vertex(),
237                         next->get_vertex(),
238                         iter_t,
239                         next_t
240                 );
241
242                 const float
243                         iter_w((iter->get_width()*width)*0.5f+expand),
244                         next_w((next->get_width()*width)*0.5f+expand);
245
246                 const derivative< hermite<Vector> > deriv(curve);
247
248                 if (first)
249                         first_tangent = deriv(CUSP_TANGENT_ADJUST);
250
251                 // Make cusps as necessary
252                 if(!first && sharp_cusps && split_flag && (!prev_t.is_equal_to(iter_t) || iter_t.is_equal_to(Vector::zero())) && !last_tangent.is_equal_to(Vector::zero()))
253                 {
254                         Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
255
256                         const Vector t1(last_tangent.perp().norm());
257                         const Vector t2(curr_tangent.perp().norm());
258
259                         Real cross(t1*t2.perp());
260                         Real perp((t1-t2).mag());
261                         if(cross>CUSP_THRESHOLD)
262                         {
263                                 const Point p1(iter->get_vertex()+t1*iter_w);
264                                 const Point p2(iter->get_vertex()+t2*iter_w);
265
266                                 side_a.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
267                         }
268                         else if(cross<-CUSP_THRESHOLD)
269                         {
270                                 const Point p1(iter->get_vertex()-t1*iter_w);
271                                 const Point p2(iter->get_vertex()-t2*iter_w);
272
273                                 side_b.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
274                         }
275                         else if(cross>0 && perp>1)
276                         {
277                                 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
278
279                                 side_a.push_back(iter->get_vertex()+(t1+t2).norm()*iter_w*amount);
280                         }
281                         else if(cross<0 && perp>1)
282                         {
283                                 float amount(max(0.0f,(float)(-cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
284
285                                 side_b.push_back(iter->get_vertex()-(t1+t2).norm()*iter_w*amount);
286                         }
287                 }
288
289                 // Make the outline
290                 if(homogeneous_width)
291                 {
292                         const float length(curve.length());
293                         float dist(0);
294                         Point lastpoint;
295                         for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
296                         {
297                                 const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
298                                 const Vector p(curve(n));
299
300                                 if(n)
301                                         dist+=(p-lastpoint).mag();
302
303                                 const float w(((next_w-iter_w)*(dist/length)+iter_w));
304
305                                 side_a.push_back(p+d*w);
306                                 side_b.push_back(p-d*w);
307
308                                 lastpoint=p;
309                         }
310                 }
311                 else
312                         for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
313                         {
314                                 const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
315                                 const Vector p(curve(n));
316                                 const float w(((next_w-iter_w)*n+iter_w));
317
318                                 side_a.push_back(p+d*w);
319                                 side_b.push_back(p-d*w);
320                         }
321                 last_tangent=deriv(1.0-CUSP_TANGENT_ADJUST);
322                 side_a.push_back(curve(1.0)+last_tangent.perp().norm()*next_w);
323                 side_b.push_back(curve(1.0)-last_tangent.perp().norm()*next_w);
324
325                 first=false;
326         }
327
328         if(loop)
329         {
330                 reverse(side_b.begin(),side_b.end());
331                 add_polygon(side_a);
332                 add_polygon(side_b);
333                 return;
334         }
335
336         // Insert code for adding end tip
337         if(round_tip[1] && !loop && side_a.size())
338         {
339                 // remove the last point
340                 side_a.pop_back();
341
342                 const Point vertex(bline.back().get_vertex());
343                 const Vector tangent(last_tangent.norm());
344                 const float w((bline.back().get_width()*width)*0.5f+expand);
345
346                 hermite<Vector> curve(
347                         vertex+tangent.perp()*w,
348                         vertex-tangent.perp()*w,
349                         tangent*w*ROUND_END_FACTOR,
350                         -tangent*w*ROUND_END_FACTOR
351                 );
352
353                 for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
354                         side_a.push_back(curve(n));
355         }
356
357         for(;!side_b.empty();side_b.pop_back())
358                 side_a.push_back(side_b.back());
359
360         // Insert code for adding begin tip
361         if(round_tip[0] && !loop && side_a.size())
362         {
363                 // remove the last point
364                 side_a.pop_back();
365
366                 const Point vertex(bline.front().get_vertex());
367                 const Vector tangent(first_tangent.norm());
368                 const float w((bline.front().get_width()*width)*0.5f+expand);
369
370                 hermite<Vector> curve(
371                         vertex-tangent.perp()*w,
372                         vertex+tangent.perp()*w,
373                         -tangent*w*ROUND_END_FACTOR,
374                         tangent*w*ROUND_END_FACTOR
375                 );
376
377                 for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
378                         side_a.push_back(curve(n));
379         }
380
381         add_polygon(side_a);
382
383
384 #else /* 1 */
385
386         bool loop_;
387         if(bline.get_contained_type()==ValueBase::TYPE_BLINEPOINT)
388         {
389                 ValueBase value(bline);
390
391                 if(loopyness<0.5f)
392                 {
393                         value.set_loop(false);
394                         loop_=false;
395                 }
396                 else
397                         loop_=value.get_loop();
398
399                 segment_list=convert_bline_to_segment_list(value);
400                 width_list=convert_bline_to_width_list(value);
401         }
402         else
403         {
404                 clear();
405                 return;
406         }
407
408
409
410         if(segment_list.empty())
411         {
412                 synfig::warning("Outline: segment_list is empty, layer disabled");
413                 clear();
414                 return;
415         }
416
417
418         // Repair the width list if we need to
419         {
420                 Real default_width;
421                 if(width_list.empty())
422                         default_width=0.01;
423                 else
424                         default_width=width_list.back();
425
426                 while(width_list.size()<segment_list.size()+1)
427                         width_list.push_back(default_width);
428                 while(width_list.size()>segment_list.size()+1)
429                         width_list.pop_back();
430
431         }
432
433         // Repair the zero tangents (if any)
434         {
435                 vector<Segment>::iterator iter;
436                 for(iter=segment_list.begin();iter!=segment_list.end();++iter)
437                 {
438                         if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
439                                 iter->t1=iter->t2=iter->p2-iter->p1;
440                 }
441         }
442
443         vector<Real>::iterator iter;
444         vector<Real> scaled_width_list;
445         for(iter=width_list.begin();iter!=width_list.end();++iter)
446         {
447                 scaled_width_list.push_back((*iter*width+expand)*0.5f);
448         }
449
450         Vector::value_type n;
451         etl::hermite<Vector> curve;
452         vector<Point> vector_list;
453         Vector last_tangent(segment_list.back().t2);
454         clear();
455
456         if(!loop_)
457                 last_tangent=NO_LOOP_COOKIE;
458
459         {
460                 vector<Segment>::iterator iter;
461                 vector<Real>::iterator witer;
462                 for(
463                         iter=segment_list.begin(),
464                         witer=scaled_width_list.begin();
465                         iter!=segment_list.end();
466                         ++iter,++witer)
467                 {
468                         if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
469                         {
470                                 vector_list.push_back(iter->p1-(iter->p2-iter->p1).perp().norm()*witer[0]);
471                                 vector_list.push_back((iter->p2-iter->p1)*0.05+iter->p1-(iter->p2-iter->p1).perp().norm()*((witer[1]-witer[0])*0.05+witer[0]));
472                                 vector_list.push_back((iter->p2-iter->p1)*0.95+iter->p1-(iter->p2-iter->p1).perp().norm()*((witer[1]-witer[0])*0.95+witer[0]));
473                                 vector_list.push_back(iter->p2-(iter->p2-iter->p1).perp().norm()*witer[1]);
474                         }
475                         else
476                         {
477                                 curve.p1()=iter->p1;
478                                 curve.t1()=iter->t1;
479                                 curve.p2()=iter->p2;
480                                 curve.t2()=iter->t2;
481                                 curve.sync();
482
483                                 etl::derivative<etl::hermite<Vector> > deriv(curve);
484
485                                 // without this if statement, the broken tangents would
486                                 // have boxed edges
487                                 if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t1))
488                                 {
489                                         //Vector curr_tangent(iter->t1);
490                                         Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
491
492                                         const Vector t1(last_tangent.perp().norm());
493                                         const Vector t2(curr_tangent.perp().norm());
494
495                                         Point p1(iter->p1+t1*witer[0]);
496                                         Point p2(iter->p1+t2*witer[0]);
497
498                                         Real cross(t1*t2.perp());
499
500                                         if(cross>CUSP_THRESHOLD)
501                                                 vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
502                                         else if(cross>0)
503                                         {
504                                                 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
505                                                 // Push back something to make it look vaguely round;
506                                                 //vector_list.push_back(iter->p1+(t1*1.25+t2).norm()*witer[0]*amount);
507                                                 vector_list.push_back(iter->p1+(t1+t2).norm()*witer[0]*amount);
508                                                 //vector_list.push_back(iter->p1+(t1+t2*1.25).norm()*witer[0]*amount);
509                                         }
510                                 }
511                                 //last_tangent=iter->t2;
512                                 last_tangent=deriv(1.0f-CUSP_TANGENT_ADJUST);
513
514                                 for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
515                                         vector_list.push_back(curve(n)+deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm()*((witer[1]-witer[0])*n+witer[0]) );
516                                 vector_list.push_back(curve(1.0)+deriv(1.0-CUSP_TANGENT_ADJUST).perp().norm()*witer[1]);
517
518                         }
519                 }
520                 if(round_tip[1] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
521                 {
522                         // remove the last point
523                         vector_list.pop_back();
524
525                         iter--;
526
527                         curve.p1()=iter->p2+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
528                         curve.p2()=iter->p2-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
529                         curve.t2()=-(curve.t1()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
530                         curve.sync();
531                         for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
532                                 vector_list.push_back(curve(n));
533
534                         // remove the last point
535                         vector_list.pop_back();
536                 }
537         }
538
539         if(!loop_)
540                 last_tangent=NO_LOOP_COOKIE;
541         else
542         {
543                 add_polygon(vector_list);
544                 vector_list.clear();
545                 last_tangent=segment_list.front().t1;
546         }
547
548         //else
549         //      last_tangent=segment_list.back().t2;
550
551         {
552                 vector<Segment>::reverse_iterator iter;
553                 vector<Real>::reverse_iterator witer;
554                 for(
555                         iter=segment_list.rbegin(),
556                         witer=scaled_width_list.rbegin(),++witer;
557                         !(iter==segment_list.rend());
558                         ++iter,++witer)
559                 {
560
561                         if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
562                         {
563                                 vector_list.push_back(iter->p2+(iter->p2-iter->p1).perp().norm()*witer[0]);
564                                 vector_list.push_back((iter->p2-iter->p1)*0.95+iter->p1+(iter->p2-iter->p1).perp().norm()*((witer[-1]-witer[0])*0.95+witer[0]));
565                                 vector_list.push_back((iter->p2-iter->p1)*0.05+iter->p1+(iter->p2-iter->p1).perp().norm()*((witer[-1]-witer[0])*0.05+witer[0]));
566                                 vector_list.push_back(iter->p1+(iter->p2-iter->p1).perp().norm()*witer[-1]);
567                         }
568                         else
569                         {
570                                 curve.p1()=iter->p1;
571                                 curve.t1()=iter->t1;
572                                 curve.p2()=iter->p2;
573                                 curve.t2()=iter->t2;
574                                 curve.sync();
575
576                                 etl::derivative<etl::hermite<Vector> > deriv(curve);
577
578                                 // without this if statement, the broken tangents would
579                                 // have boxed edges
580                                 if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t2))
581                                 {
582                                         //Vector curr_tangent(iter->t2);
583                                         Vector curr_tangent(deriv(1.0f-CUSP_TANGENT_ADJUST));
584
585                                         const Vector t1(last_tangent.perp().norm());
586                                         const Vector t2(curr_tangent.perp().norm());
587
588                                         Point p1(iter->p2-t1*witer[-1]);
589                                         Point p2(iter->p2-t2*witer[-1]);
590
591                                         Real cross(t1*t2.perp());
592
593                                         //if(last_tangent.perp().norm()*curr_tangent.norm()<-CUSP_THRESHOLD)
594                                         if(cross>CUSP_THRESHOLD)
595                                                 vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
596                                         else if(cross>0)
597                                         {
598                                                 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
599                                                 // Push back something to make it look vaguely round;
600                                                 //vector_list.push_back(iter->p2-(t1*1.25+t2).norm()*witer[-1]*amount);
601                                                 vector_list.push_back(iter->p2-(t1+t2).norm()*witer[-1]*amount);
602                                                 //vector_list.push_back(iter->p2-(t1+t2*1.25).norm()*witer[-1]*amount);
603                                         }
604                                 }
605                                 //last_tangent=iter->t1;
606                                 last_tangent=deriv(CUSP_TANGENT_ADJUST);
607
608                                 for(n=1.0f;n>CUSP_TANGENT_ADJUST;n-=1.0f/SAMPLES)
609                                         vector_list.push_back(curve(n)-deriv(1-n>CUSP_TANGENT_ADJUST?n:1-CUSP_TANGENT_ADJUST).perp().norm()*((witer[-1]-witer[0])*n+witer[0]) );
610                                 vector_list.push_back(curve(0.0f)-deriv(CUSP_TANGENT_ADJUST).perp().norm()*witer[0]);
611                         }
612                 }
613                 if(round_tip[0] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
614                 {
615                         // remove the last point
616                         vector_list.pop_back();
617                         iter--;
618                         witer--;
619
620                         curve.p1()=iter->p1+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
621                         curve.p2()=iter->p1-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
622                         curve.t1()=-(curve.t2()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
623                         curve.sync();
624
625                         for(n=1.0;n>0.0;n-=1.0/SAMPLES)
626                                 vector_list.push_back(curve(n));
627
628                         // remove the last point
629                         vector_list.pop_back();
630                 }
631         }
632
633         //if(loop_)
634         //      reverse(vector_list.begin(),vector_list.end());
635
636 #ifdef _DEBUG
637         {
638                 vector<Point>::iterator iter;
639                 for(iter=vector_list.begin();iter!=vector_list.end();++iter)
640                         if(!iter->is_valid())
641                         {
642                                 synfig::error("Outline::sync(): Bad point in vector_list!");
643                         }
644                 //synfig::info("BLEHH__________--- x:%f, y:%f",vector_list.front()[0],vector_list.front()[1]);
645         }
646 #endif /* _DEBUG */
647
648         add_polygon(vector_list);
649
650
651 #endif /* 1 */
652         } catch (...) { synfig::error("Outline::sync(): Exception thrown"); throw; }
653 }
654
655 #undef bline
656
657 bool
658 Outline::set_param(const String & param, const ValueBase &value)
659 {
660         if(param=="segment_list")
661         {
662                 if(dynamic_param_list().count("segment_list"))
663                 {
664                         connect_dynamic_param("bline",dynamic_param_list().find("segment_list")->second);
665                         disconnect_dynamic_param("segment_list");
666                         synfig::warning("Outline::set_param(): Updated valuenode connection to use the new \"bline\" parameter.");
667                 }
668                 else
669                         synfig::warning("Outline::set_param(): The parameter \"segment_list\" is deprecated. Use \"bline\" instead.");
670         }
671
672         if(     (param=="segment_list" || param=="bline") && value.get_type()==ValueBase::TYPE_LIST)
673         {
674                 //if(value.get_contained_type()!=ValueBase::TYPE_BLINEPOINT)
675                 //      return false;
676
677                 bline=value;
678
679                 return true;
680         }
681         /*
682         if(     param=="seg" && value.get_type()==ValueBase::TYPE_SEGMENT)
683         {
684                 if(!segment_list.empty())
685                         segment_list.clear();
686
687                 segment_list.push_back(value.get(Segment()));
688                 loop_=false;
689                 //sync();
690                 return true;
691         }
692         if(     param=="w[0]" && value.get_type()==ValueBase::TYPE_REAL)
693         {
694                 if(width_list.size()<2)
695                 {
696                         width_list.push_back(value.get(Real()));
697                         width_list.push_back(value.get(Real()));
698                 }
699                 else
700                 {
701                         width_list[0]=value.get(Real());
702                 }
703                 width=1;
704                 //sync();
705                 return true;
706         }
707
708         if(     param=="w[1]" && value.get_type()==ValueBase::TYPE_REAL)
709         {
710                 if(width_list.size()<2)
711                 {
712                         width_list.push_back(value.get(Real()));
713                         width_list.push_back(value.get(Real()));
714                 }
715                 else
716                 {
717                         width_list[1]=value.get(Real());
718                 }
719                 width=1;
720                 //sync();
721                 return true;
722         }
723
724         if(     param=="width_list" && value.same_as(width_list))
725         {
726                 width_list=value;
727                 //sync();
728                 return true;
729         }
730         */
731
732         IMPORT(round_tip[0]);
733         IMPORT(round_tip[1]);
734         IMPORT(sharp_cusps);
735         IMPORT_PLUS(width,if(old_version){width*=2.0;});
736         IMPORT(loopyness);
737         IMPORT(expand);
738         IMPORT(homogeneous_width);
739
740         if(param!="vector_list")
741                 return Layer_Polygon::set_param(param,value);
742
743         return false;
744 }
745
746 void
747 Outline::set_time(Context context, Time time)const
748 {
749         const_cast<Outline*>(this)->sync();
750         context.set_time(time);
751 }
752
753 void
754 Outline::set_time(Context context, Time time, Vector pos)const
755 {
756         const_cast<Outline*>(this)->sync();
757         context.set_time(time,pos);
758 }
759
760 ValueBase
761 Outline::get_param(const String& param)const
762 {
763         EXPORT(bline);
764         EXPORT(expand);
765         //EXPORT(width_list);
766         //EXPORT(segment_list);
767         EXPORT(homogeneous_width);
768         EXPORT(round_tip[0]);
769         EXPORT(round_tip[1]);
770         EXPORT(sharp_cusps);
771         EXPORT(width);
772         EXPORT(loopyness);
773
774         EXPORT_NAME();
775         EXPORT_VERSION();
776
777         if(param!="vector_list")
778                 return Layer_Polygon::get_param(param);
779         return ValueBase();
780 }
781
782 Layer::Vocab
783 Outline::get_param_vocab()const
784 {
785         Layer::Vocab ret(Layer_Polygon::get_param_vocab());
786
787         // Pop off the polygon parameter from the polygon vocab
788         ret.pop_back();
789
790         ret.push_back(ParamDesc("bline")
791                 .set_local_name(_("Vertices"))
792                 .set_origin("offset")
793                 .set_scalar("width")
794                 .set_description(_("A list of BLine Points"))
795         );
796
797         /*
798         ret.push_back(ParamDesc("width_list")
799                 .set_local_name(_("Point Widths"))
800                 .set_origin("segment_list")
801                 .hidden()
802                 .not_critical()
803         );
804         */
805
806         ret.push_back(ParamDesc("width")
807                 .set_is_distance()
808                 .set_local_name(_("Outline Width"))
809         );
810
811         ret.push_back(ParamDesc("expand")
812                 .set_is_distance()
813                 .set_local_name(_("Expand"))
814         );
815
816         ret.push_back(ParamDesc("sharp_cusps")
817                 .set_local_name(_("Sharp Cusps"))
818                 .set_description(_("Determines cusp type"))
819         );
820
821         ret.push_back(ParamDesc("round_tip[0]")
822                 .set_local_name(_("Rounded Begin"))
823                 .set_description(_("Round off the tip"))
824         );
825
826         ret.push_back(ParamDesc("round_tip[1]")
827                 .set_local_name(_("Rounded End"))
828                 .set_description(_("Round off the tip"))
829         );
830         ret.push_back(ParamDesc("loopyness")
831                 .set_local_name(_("Loopyness"))
832         );
833         ret.push_back(ParamDesc("homogeneous_width")
834                 .set_local_name(_("Homogeneous"))
835         );
836
837         return ret;
838 }