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