Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-core / tags / synfig_0_61_03 / synfig-core / src / modules / mod_geometry / outline.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file bline.cpp
3 **      \brief Template
4 **
5 **      $Id: outline.cpp,v 1.1.1.1 2005/01/04 01:23:10 darco Exp $
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: outline.cpp,v 1.1.1.1 2005/01/04 01:23:10 darco Exp $");
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         Vector last_tangent=iter->get_tangent1();
198         
199         for(bool first=!loop;next!=end;iter=next++,first=false)
200         {
201                 Vector prev_t(iter->get_tangent1());
202                 Vector iter_t(iter->get_tangent2());
203                 Vector next_t(next->get_tangent1());
204                 
205                 bool split_flag(iter->get_split_tangent_flag());
206                 
207                 if(iter_t.is_equal_to(Vector::zero()) && next_t.is_equal_to(Vector::zero()))
208                 {
209                         iter_t=next_t=next->get_vertex()-iter->get_vertex();
210                         split_flag=true;
211                 }
212                 
213                 // Setup the curve
214                 hermite<Vector> curve(
215                         iter->get_vertex(),
216                         next->get_vertex(),
217                         iter_t,
218                         next_t
219                 );
220                 
221                 const float
222                         iter_w((iter->get_width()*width)*0.5f+expand),
223                         next_w((next->get_width()*width)*0.5f+expand);
224
225                 const derivative< hermite<Vector> > deriv(curve);
226                 
227                 // Make cusps as necessary
228                 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()))
229                 {
230                         Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
231                         
232                         const Vector t1(last_tangent.perp().norm());
233                         const Vector t2(curr_tangent.perp().norm());
234                                                 
235                         Real cross(t1*t2.perp());
236                         Real perp((t1-t2).mag());
237                         if(cross>CUSP_THRESHOLD)                                
238                         {
239                                 const Point p1(iter->get_vertex()+t1*iter_w);
240                                 const Point p2(iter->get_vertex()+t2*iter_w);
241                                 
242                                 side_a.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
243                         }
244                         else if(cross<-CUSP_THRESHOLD)
245                         {
246                                 const Point p1(iter->get_vertex()-t1*iter_w);
247                                 const Point p2(iter->get_vertex()-t2*iter_w);
248                                 
249                                 side_b.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
250                         }
251                         else if(cross>0 && perp>1)
252                         {
253                                 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
254
255                                 side_a.push_back(iter->get_vertex()+(t1+t2).norm()*iter_w*amount);
256                         }
257                         else if(cross<0 && perp>1)
258                         {
259                                 float amount(max(0.0f,(float)(-cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
260
261                                 side_b.push_back(iter->get_vertex()-(t1+t2).norm()*iter_w*amount);
262                         }                       
263                 }
264                                 
265                 // Make the outline
266                 if(homogeneous_width)
267                 {
268                         const float length(curve.length());
269                         float dist(0);
270                         Point lastpoint;
271                         for(float n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
272                         {
273                                 const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
274                                 const Vector p(curve(n));
275
276                                 if(n)
277                                         dist+=(p-lastpoint).mag();
278
279                                 const float w(((next_w-iter_w)*(dist/length)+iter_w));
280
281                                 side_a.push_back(p+d*w);
282                                 side_b.push_back(p-d*w);
283                                                                 
284                                 lastpoint=p;
285                         }
286                 }
287                 else
288                         for(float n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
289                         {
290                                 const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
291                                 const Vector p(curve(n));
292                                 const float w(((next_w-iter_w)*n+iter_w));
293                                 
294                                 side_a.push_back(p+d*w);
295                                 side_b.push_back(p-d*w);
296                         }
297                 last_tangent=deriv(1.0-CUSP_TANGENT_ADJUST);
298                 side_a.push_back(curve(1.0)+last_tangent.perp().norm()*next_w);
299                 side_b.push_back(curve(1.0)-last_tangent.perp().norm()*next_w);
300         }
301         
302         if(loop)
303         {
304                 reverse(side_b.begin(),side_b.end());
305                 add_polygon(side_a);
306                 add_polygon(side_b);
307                 return;
308         }
309         
310         // Insert code for adding end tip
311         if(round_tip[1] && !loop)
312         {
313                 // remove the last point
314                 side_a.pop_back();
315                 
316                 const Point vertex(bline.back().get_vertex());
317                 const Vector tangent(last_tangent.norm());
318                 const float w((bline.back().get_width()*width)*0.5f+expand);
319                 
320                 hermite<Vector> curve(
321                         vertex+tangent.perp()*w,
322                         vertex-tangent.perp()*w,
323                         tangent*w*ROUND_END_FACTOR,
324                         -tangent*w*ROUND_END_FACTOR
325                 );
326
327                 for(float n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
328                         side_a.push_back(curve(n));
329
330                 // remove the last point
331                 side_a.pop_back();
332         }
333         
334         for(;!side_b.empty();side_b.pop_back())
335                 side_a.push_back(side_b.back());
336
337         // Insert code for adding begin tip
338         if(round_tip[0] && !loop)
339         {
340                 // remove the last point
341                 side_a.pop_back();
342                 
343                 const Point vertex(bline.front().get_vertex());
344                 const Vector tangent(bline.front().get_tangent2().norm());
345                 const float w((bline.front().get_width()*width)*0.5f+expand);
346                 
347                 hermite<Vector> curve(
348                         vertex-tangent.perp()*w,
349                         vertex+tangent.perp()*w,
350                         -tangent*w*ROUND_END_FACTOR,
351                         tangent*w*ROUND_END_FACTOR
352                 );
353
354                 for(float n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
355                         side_a.push_back(curve(n));
356
357                 // remove the last point
358                 side_a.pop_back();
359         }
360         
361         add_polygon(side_a);
362         
363         
364 #else
365                 
366         bool loop_;
367         if(bline.get_contained_type()==ValueBase::TYPE_BLINEPOINT)
368         {
369                 ValueBase value(bline);
370
371                 if(loopyness<0.5f)
372                 {
373                         value.set_loop(false);
374                         loop_=false;
375                 }
376                 else
377                         loop_=value.get_loop();
378                 
379                 segment_list=convert_bline_to_segment_list(value);
380                 width_list=convert_bline_to_width_list(value);
381         }
382         else
383         {
384                 clear();
385                 return;
386         }
387
388
389
390         if(segment_list.empty())
391         {
392                 synfig::warning("Outline: segment_list is empty, layer disabled");
393                 clear();
394                 return;
395         }
396         
397         
398         // Repair the width list if we need to
399         {
400                 Real default_width;
401                 if(width_list.empty())
402                         default_width=0.01;
403                 else
404                         default_width=width_list.back();
405                 
406                 while(width_list.size()<segment_list.size()+1)
407                         width_list.push_back(default_width);
408                 while(width_list.size()>segment_list.size()+1)
409                         width_list.pop_back();
410                 
411         }
412
413         // Repair the zero tangents (if any)
414         {
415                 vector<Segment>::iterator iter;
416                 for(iter=segment_list.begin();iter!=segment_list.end();++iter)
417                 {
418                         if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
419                                 iter->t1=iter->t2=iter->p2-iter->p1;
420                 }
421         }
422         
423         vector<Real>::iterator iter;
424         vector<Real> scaled_width_list;
425         for(iter=width_list.begin();iter!=width_list.end();++iter)
426         {
427                 scaled_width_list.push_back((*iter*width+expand)*0.5f);
428         }
429
430         Vector::value_type n;
431         etl::hermite<Vector> curve;
432         vector<Point> vector_list;
433         Vector last_tangent(segment_list.back().t2);
434         clear();
435         
436         if(!loop_)
437                 last_tangent=NO_LOOP_COOKIE;
438         
439         {
440                 vector<Segment>::iterator iter;
441                 vector<Real>::iterator witer;
442                 for(
443                         iter=segment_list.begin(),
444                         witer=scaled_width_list.begin();
445                         iter!=segment_list.end();
446                         ++iter,++witer)
447                 {                       
448                         if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
449                         {
450                                 vector_list.push_back(iter->p1-(iter->p2-iter->p1).perp().norm()*witer[0]);
451                                 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]));
452                                 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]));
453                                 vector_list.push_back(iter->p2-(iter->p2-iter->p1).perp().norm()*witer[1]);
454                         }
455                         else
456                         {
457                                 curve.p1()=iter->p1;
458                                 curve.t1()=iter->t1;
459                                 curve.p2()=iter->p2;
460                                 curve.t2()=iter->t2;
461                                 curve.sync();
462         
463                                 etl::derivative<etl::hermite<Vector> > deriv(curve);
464                                 
465                                 // without this if statement, the broken tangents would
466                                 // have boxed edges
467                                 if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t1))
468                                 {
469                                         //Vector curr_tangent(iter->t1);
470                                         Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
471                                         
472                                         const Vector t1(last_tangent.perp().norm());
473                                         const Vector t2(curr_tangent.perp().norm());
474                                         
475                                         Point p1(iter->p1+t1*witer[0]);
476                                         Point p2(iter->p1+t2*witer[0]);
477                                         
478                                         Real cross(t1*t2.perp());
479                                         
480                                         if(cross>CUSP_THRESHOLD)                                
481                                                 vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
482                                         else if(cross>0)
483                                         {
484                                                 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
485                                                 // Push back something to make it look vaguely round;
486                                                 //vector_list.push_back(iter->p1+(t1*1.25+t2).norm()*witer[0]*amount);
487                                                 vector_list.push_back(iter->p1+(t1+t2).norm()*witer[0]*amount);
488                                                 //vector_list.push_back(iter->p1+(t1+t2*1.25).norm()*witer[0]*amount);
489                                         }
490                                 }
491                                 //last_tangent=iter->t2;
492                                 last_tangent=deriv(1.0f-CUSP_TANGENT_ADJUST);
493                                 
494                                 for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
495                                         vector_list.push_back(curve(n)+deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm()*((witer[1]-witer[0])*n+witer[0]) );
496                                 vector_list.push_back(curve(1.0)+deriv(1.0-CUSP_TANGENT_ADJUST).perp().norm()*witer[1]);
497                                 
498                         }
499                 }
500                 if(round_tip[1] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
501                 {
502                         // remove the last point
503                         vector_list.pop_back();
504                         
505                         iter--;
506
507                         curve.p1()=iter->p2+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
508                         curve.p2()=iter->p2-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
509                         curve.t2()=-(curve.t1()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
510                         curve.sync();
511                         for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
512                                 vector_list.push_back(curve(n));
513
514                         // remove the last point
515                         vector_list.pop_back();
516                 }
517         }
518         
519         if(!loop_)
520                 last_tangent=NO_LOOP_COOKIE;
521         else
522         {
523                 add_polygon(vector_list);
524                 vector_list.clear();
525                 last_tangent=segment_list.front().t1;
526         }
527         
528         //else
529         //      last_tangent=segment_list.back().t2;
530         
531         {
532                 vector<Segment>::reverse_iterator iter;
533                 vector<Real>::reverse_iterator witer;
534                 for(
535                         iter=segment_list.rbegin(),
536                         witer=scaled_width_list.rbegin(),++witer;
537                         !(iter==segment_list.rend());
538                         ++iter,++witer)
539                 {
540                         
541                         if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
542                         {
543                                 vector_list.push_back(iter->p2+(iter->p2-iter->p1).perp().norm()*witer[0]);
544                                 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]));
545                                 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]));
546                                 vector_list.push_back(iter->p1+(iter->p2-iter->p1).perp().norm()*witer[-1]);
547                         }
548                         else
549                         {
550                                 curve.p1()=iter->p1;
551                                 curve.t1()=iter->t1;
552                                 curve.p2()=iter->p2;
553                                 curve.t2()=iter->t2;
554                                 curve.sync();
555         
556                                 etl::derivative<etl::hermite<Vector> > deriv(curve);
557
558                                 // without this if statement, the broken tangents would
559                                 // have boxed edges
560                                 if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t2))
561                                 {
562                                         //Vector curr_tangent(iter->t2);
563                                         Vector curr_tangent(deriv(1.0f-CUSP_TANGENT_ADJUST));
564                                         
565                                         const Vector t1(last_tangent.perp().norm());
566                                         const Vector t2(curr_tangent.perp().norm());
567
568                                         Point p1(iter->p2-t1*witer[-1]);
569                                         Point p2(iter->p2-t2*witer[-1]);
570                                         
571                                         Real cross(t1*t2.perp());
572                                         
573                                         //if(last_tangent.perp().norm()*curr_tangent.norm()<-CUSP_THRESHOLD)
574                                         if(cross>CUSP_THRESHOLD)                                
575                                                 vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
576                                         else if(cross>0)
577                                         {
578                                                 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
579                                                 // Push back something to make it look vaguely round;
580                                                 //vector_list.push_back(iter->p2-(t1*1.25+t2).norm()*witer[-1]*amount);
581                                                 vector_list.push_back(iter->p2-(t1+t2).norm()*witer[-1]*amount);
582                                                 //vector_list.push_back(iter->p2-(t1+t2*1.25).norm()*witer[-1]*amount);
583                                         }
584                                 }
585                                 //last_tangent=iter->t1;
586                                 last_tangent=deriv(CUSP_TANGENT_ADJUST);
587                                 
588                                 for(n=1.0f;n>CUSP_TANGENT_ADJUST;n-=1.0f/SAMPLES)
589                                         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]) );
590                                 vector_list.push_back(curve(0.0f)-deriv(CUSP_TANGENT_ADJUST).perp().norm()*witer[0]);
591                         }
592                 }
593                 if(round_tip[0] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
594                 {
595                         // remove the last point
596                         vector_list.pop_back();
597                         iter--;
598                         witer--;
599                         
600                         curve.p1()=iter->p1+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
601                         curve.p2()=iter->p1-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
602                         curve.t1()=-(curve.t2()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
603                         curve.sync();
604
605                         for(n=1.0;n>0.0;n-=1.0/SAMPLES)
606                                 vector_list.push_back(curve(n));
607                         
608                         // remove the last point
609                         vector_list.pop_back();
610                 }
611         }
612
613         //if(loop_)
614         //      reverse(vector_list.begin(),vector_list.end());
615
616 #ifdef _DEBUG
617         {
618                 vector<Point>::iterator iter;
619                 for(iter=vector_list.begin();iter!=vector_list.end();++iter)
620                         if(!iter->is_valid())
621                         {
622                                 synfig::error("Outline::sync(): Bad point in vector_list!");
623                         }
624                 //synfig::info("BLEHH__________--- x:%f, y:%f",vector_list.front()[0],vector_list.front()[1]);
625         }
626 #endif
627         
628         add_polygon(vector_list);
629
630
631 #endif
632         } catch (...) { synfig::error("Outline::sync(): Exception thrown"); throw; }
633 }
634
635 #undef bline
636
637 bool
638 Outline::set_param(const String & param, const ValueBase &value)
639 {
640         if(param=="segment_list")
641         {
642                 if(dynamic_param_list().count("segment_list"))
643                 {
644                         connect_dynamic_param("bline",dynamic_param_list().find("segment_list")->second);
645                         disconnect_dynamic_param("segment_list");
646                         synfig::warning("Outline::set_param(): Updated valuenode connection to use the new \"bline\" parameter.");
647                 }
648                 else
649                         synfig::warning("Outline::set_param(): The parameter \"segment_list\" is deprecated. Use \"bline\" instead.");
650         }
651
652         if(     (param=="segment_list" || param=="bline") && value.get_type()==ValueBase::TYPE_LIST)
653         {
654                 //if(value.get_contained_type()!=ValueBase::TYPE_BLINEPOINT)
655                 //      return false;
656                         
657                 bline=value;
658
659                 return true;
660         }
661         /*
662         if(     param=="seg" && value.get_type()==ValueBase::TYPE_SEGMENT)
663         {
664                 if(!segment_list.empty())
665                         segment_list.clear();
666
667                 segment_list.push_back(value.get(Segment()));
668                 loop_=false;
669                 //sync();
670                 return true;
671         }
672         if(     param=="w[0]" && value.get_type()==ValueBase::TYPE_REAL)
673         {
674                 if(width_list.size()<2)
675                 {
676                         width_list.push_back(value.get(Real()));
677                         width_list.push_back(value.get(Real()));
678                 }
679                 else
680                 {
681                         width_list[0]=value.get(Real());
682                 }
683                 width=1;
684                 //sync();
685                 return true;
686         }
687
688         if(     param=="w[1]" && value.get_type()==ValueBase::TYPE_REAL)
689         {
690                 if(width_list.size()<2)
691                 {
692                         width_list.push_back(value.get(Real()));
693                         width_list.push_back(value.get(Real()));
694                 }
695                 else
696                 {
697                         width_list[1]=value.get(Real());
698                 }
699                 width=1;
700                 //sync();
701                 return true;
702         }
703         
704         if(     param=="width_list" && value.same_as(width_list))
705         {
706                 width_list=value;
707                 //sync();
708                 return true;
709         }
710         */
711
712         IMPORT(round_tip[0]);
713         IMPORT(round_tip[1]);
714         IMPORT(sharp_cusps);
715         IMPORT_PLUS(width,if(old_version){width*=2.0;});
716         IMPORT(loopyness);
717         IMPORT(expand);
718         IMPORT(homogeneous_width);
719         
720         if(param!="vector_list")
721                 return Layer_Polygon::set_param(param,value);
722
723         return false;
724 }
725
726 void
727 Outline::set_time(Context context, Time time)const
728 {
729         const_cast<Outline*>(this)->sync();
730         context.set_time(time);
731 }
732
733 void
734 Outline::set_time(Context context, Time time, Vector pos)const
735 {
736         const_cast<Outline*>(this)->sync();
737         context.set_time(time,pos);
738 }
739
740 ValueBase
741 Outline::get_param(const String& param)const
742 {
743         EXPORT(bline);
744         EXPORT(expand);
745         //EXPORT(width_list);
746         //EXPORT(segment_list);
747         EXPORT(homogeneous_width);
748         EXPORT(round_tip[0]);
749         EXPORT(round_tip[1]);
750         EXPORT(sharp_cusps);
751         EXPORT(width);
752         EXPORT(loopyness);
753         
754         EXPORT_NAME();
755         EXPORT_VERSION();
756
757         if(param!="vector_list")
758                 return Layer_Polygon::get_param(param);
759         return ValueBase();
760 }
761
762 Layer::Vocab
763 Outline::get_param_vocab()const
764 {
765         Layer::Vocab ret(Layer_Polygon::get_param_vocab());
766
767         // Pop off the polygon parameter from the polygon vocab
768         ret.pop_back();
769
770         ret.push_back(ParamDesc("bline")
771                 .set_local_name(_("Vertices"))
772                 .set_origin("offset")
773                 .set_scalar("width")
774                 .set_description(_("A list of BLine Points"))
775         );
776
777         /*
778         ret.push_back(ParamDesc("width_list")
779                 .set_local_name(_("Point Widths"))
780                 .set_origin("segment_list")
781                 .hidden()
782                 .not_critical()
783         );
784         */
785
786         ret.push_back(ParamDesc("width")
787                 .set_is_distance()
788                 .set_local_name(_("Outline Width"))
789         );
790
791         ret.push_back(ParamDesc("expand")
792                 .set_is_distance()
793                 .set_local_name(_("Expand"))
794         );
795
796         ret.push_back(ParamDesc("sharp_cusps")
797                 .set_local_name(_("Sharp Cusps"))
798                 .set_description(_("Determines cusp type"))
799         );
800
801         ret.push_back(ParamDesc("round_tip[0]")
802                 .set_local_name(_("Rounded Begin"))
803                 .set_description(_("Round off the tip"))
804         );
805
806         ret.push_back(ParamDesc("round_tip[1]")
807                 .set_local_name(_("Rounded End"))
808                 .set_description(_("Round off the tip"))
809         );
810         ret.push_back(ParamDesc("loopyness")
811                 .set_local_name(_("Loopyness"))
812         );
813         ret.push_back(ParamDesc("homogeneous_width")
814                 .set_local_name(_("Homogeneous"))
815         );
816
817         return ret;
818 }