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