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