Remove ancient trunk folder from svn repository
[synfig.git] / synfig-core / src / modules / mod_geometry / outline.cpp
diff --git a/synfig-core/src/modules/mod_geometry/outline.cpp b/synfig-core/src/modules/mod_geometry/outline.cpp
new file mode 100644 (file)
index 0000000..e84effb
--- /dev/null
@@ -0,0 +1,854 @@
+/* === S Y N F I G ========================================================= */
+/*!    \file outline.cpp
+**     \brief Implementation of the "Outline" layer
+**
+**     $Id$
+**
+**     \legal
+**     Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
+**     Copyright (c) 2007, 2008 Chris Moore
+**
+**     This package is free software; you can redistribute it and/or
+**     modify it under the terms of the GNU General Public License as
+**     published by the Free Software Foundation; either version 2 of
+**     the License, or (at your option) any later version.
+**
+**     This package is distributed in the hope that it will be useful,
+**     but WITHOUT ANY WARRANTY; without even the implied warranty of
+**     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+**     General Public License for more details.
+**     \endlegal
+*/
+/* ========================================================================= */
+
+//! \note This whole file should be rewritten at some point (darco)
+
+/* === H E A D E R S ======================================================= */
+
+#ifdef USING_PCH
+#      include "pch.h"
+#else
+#ifdef HAVE_CONFIG_H
+#      include <config.h>
+#endif
+
+#include "outline.h"
+#include <synfig/string.h>
+#include <synfig/time.h>
+#include <synfig/context.h>
+#include <synfig/paramdesc.h>
+#include <synfig/renddesc.h>
+#include <synfig/surface.h>
+#include <synfig/value.h>
+#include <synfig/valuenode.h>
+
+#include <ETL/calculus>
+#include <ETL/bezier>
+#include <ETL/hermite>
+#include <vector>
+
+#include <synfig/valuenode_bline.h>
+
+#endif
+
+using namespace etl;
+
+/* === M A C R O S ========================================================= */
+
+#define SAMPLES                50
+#define ROUND_END_FACTOR       (4)
+#define CUSP_THRESHOLD         (0.40)
+#define SPIKE_AMOUNT           (4)
+#define NO_LOOP_COOKIE         synfig::Vector(84951305,7836658)
+#define EPSILON                                (0.000000001)
+#define CUSP_TANGENT_ADJUST    (0.025)
+
+/* === G L O B A L S ======================================================= */
+
+SYNFIG_LAYER_INIT(Outline);
+SYNFIG_LAYER_SET_NAME(Outline,"outline");
+SYNFIG_LAYER_SET_LOCAL_NAME(Outline,N_("Outline"));
+SYNFIG_LAYER_SET_CATEGORY(Outline,N_("Geometry"));
+SYNFIG_LAYER_SET_VERSION(Outline,"0.2");
+SYNFIG_LAYER_SET_CVS_ID(Outline,"$Id$");
+
+/* === P R O C E D U R E S ================================================= */
+
+// This function was adapted from what was
+// described on http://www.whisqu.se/per/docs/math28.htm
+Point line_intersection(
+       const Point& p1,
+       const Vector& t1,
+       const Point& p2,
+       const Vector& t2
+)
+{
+       const float& x0(p1[0]);
+       const float& y0(p1[1]);
+
+       const float x1(p1[0]+t1[0]);
+       const float y1(p1[1]+t1[1]);
+
+       const float& x2(p2[0]);
+       const float& y2(p2[1]);
+
+       const float x3(p2[0]+t2[0]);
+       const float y3(p2[1]+t2[1]);
+
+       const float near_infinity((float)1e+10);
+
+       float m1,m2;    // the slopes of each line
+
+       // compute slopes, note the kluge for infinity, however, this will
+       // be close enough
+
+       if ((x1-x0)!=0)
+          m1 = (y1-y0)/(x1-x0);
+       else
+          m1 = near_infinity;
+
+       if ((x3-x2)!=0)
+          m2 = (y3-y2)/(x3-x2);
+       else
+          m2 = near_infinity;
+
+       // compute constants
+       const float& a1(m1);
+       const float& a2(m2);
+       const float b1(-1.0f);
+       const float b2(-1.0f);
+       const float c1(y0-m1*x0);
+       const float c2(y2-m2*x2);
+
+       // compute the inverse of the determinate
+       const float det_inv(1.0f/(a1*b2 - a2*b1));
+
+       // use Kramers rule to compute the intersection
+       return Point(
+               ((b1*c2 - b2*c1)*det_inv),
+               ((a2*c1 - a1*c2)*det_inv)
+       );
+} // end Intersect_Lines
+
+/* === M E T H O D S ======================================================= */
+
+
+Outline::Outline()
+{
+       old_version=false;
+       round_tip[0]=true;
+       round_tip[1]=true;
+       sharp_cusps=true;
+       width=1.0f;
+       loopyness=1.0f;
+       expand=0;
+       homogeneous_width=true;
+       clear();
+
+       vector<BLinePoint> bline_point_list;
+       bline_point_list.push_back(BLinePoint());
+       bline_point_list.push_back(BLinePoint());
+       bline_point_list.push_back(BLinePoint());
+       bline_point_list[0].set_vertex(Point(0,1));
+       bline_point_list[1].set_vertex(Point(0,-1));
+       bline_point_list[2].set_vertex(Point(1,0));
+       bline_point_list[0].set_tangent(bline_point_list[1].get_vertex()-bline_point_list[2].get_vertex()*0.5f);
+       bline_point_list[1].set_tangent(bline_point_list[2].get_vertex()-bline_point_list[0].get_vertex()*0.5f);
+       bline_point_list[2].set_tangent(bline_point_list[0].get_vertex()-bline_point_list[1].get_vertex()*0.5f);
+       bline_point_list[0].set_width(1.0f);
+       bline_point_list[1].set_width(1.0f);
+       bline_point_list[2].set_width(1.0f);
+       bline=bline_point_list;
+
+       needs_sync=true;
+}
+
+
+/*! The Sync() function takes the values
+**     and creates a polygon to be rendered
+**     with the polygon layer.
+*/
+void
+Outline::sync()
+{
+       clear();
+
+       if (!bline.get_list().size())
+       {
+               synfig::warning(string("Outline::sync():")+N_("No vertices in outline " + string("\"") + get_description() + string("\"")));
+               return;
+       }
+
+       try {
+#if 1
+
+       const bool loop(bline.get_loop());
+
+       ValueNode_BLine::Handle bline_valuenode;
+       if (bline.get_contained_type() == ValueBase::TYPE_SEGMENT)
+       {
+               bline_valuenode = ValueNode_BLine::create(bline);
+               bline = (*bline_valuenode)(0);
+       }
+
+       const vector<synfig::BLinePoint> bline_(bline.get_list().begin(),bline.get_list().end());
+#define bline bline_
+
+       vector<BLinePoint>::const_iterator
+               iter,
+               next(bline.begin());
+
+       const vector<BLinePoint>::const_iterator
+               end(bline.end());
+
+       vector<Point>
+               side_a,
+               side_b;
+
+       if(loop)
+               iter=--bline.end();
+       else
+               iter=next++;
+
+       //                              iter    next
+       //                              ----    ----
+       // looped               nth             1st
+       // !looped              1st             2nd
+
+       Vector first_tangent=bline.front().get_tangent2();
+       Vector last_tangent=iter->get_tangent1();
+
+       // if we are looped and drawing sharp cusps, we'll need a value for the incoming tangent
+       if (loop && sharp_cusps && last_tangent.is_equal_to(Vector::zero()))
+       {
+               hermite<Vector> curve((iter-1)->get_vertex(), iter->get_vertex(), (iter-1)->get_tangent2(), iter->get_tangent1());
+               const derivative< hermite<Vector> > deriv(curve);
+               last_tangent=deriv(1.0-CUSP_TANGENT_ADJUST);
+       }
+
+       // `first' is for making the cusps; don't do that for the first point if we're not looped
+       for(bool first=!loop; next!=end; iter=next++)
+       {
+               Vector prev_t(iter->get_tangent1());
+               Vector iter_t(iter->get_tangent2());
+               Vector next_t(next->get_tangent1());
+
+               bool split_flag(iter->get_split_tangent_flag());
+
+               // if iter.t2 == 0 and next.t1 == 0, this is a straight line
+               if(iter_t.is_equal_to(Vector::zero()) && next_t.is_equal_to(Vector::zero()))
+               {
+                       iter_t=next_t=next->get_vertex()-iter->get_vertex();
+                       // split_flag=true;
+
+                       // if the two points are on top of each other, ignore this segment
+                       // leave `first' true if was before
+                       if (iter_t.is_equal_to(Vector::zero()))
+                               continue;
+               }
+
+               // Setup the curve
+               hermite<Vector> curve(
+                       iter->get_vertex(),
+                       next->get_vertex(),
+                       iter_t,
+                       next_t
+               );
+
+               const float
+                       iter_w((iter->get_width()*width)*0.5f+expand),
+                       next_w((next->get_width()*width)*0.5f+expand);
+
+               const derivative< hermite<Vector> > deriv(curve);
+
+               if (first)
+                       first_tangent = deriv(CUSP_TANGENT_ADJUST);
+
+               // Make cusps as necessary
+               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()))
+               {
+                       Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
+
+                       const Vector t1(last_tangent.perp().norm());
+                       const Vector t2(curr_tangent.perp().norm());
+
+                       Real cross(t1*t2.perp());
+                       Real perp((t1-t2).mag());
+                       if(cross>CUSP_THRESHOLD)
+                       {
+                               const Point p1(iter->get_vertex()+t1*iter_w);
+                               const Point p2(iter->get_vertex()+t2*iter_w);
+
+                               side_a.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
+                       }
+                       else if(cross<-CUSP_THRESHOLD)
+                       {
+                               const Point p1(iter->get_vertex()-t1*iter_w);
+                               const Point p2(iter->get_vertex()-t2*iter_w);
+
+                               side_b.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
+                       }
+                       else if(cross>0 && perp>1)
+                       {
+                               float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
+
+                               side_a.push_back(iter->get_vertex()+(t1+t2).norm()*iter_w*amount);
+                       }
+                       else if(cross<0 && perp>1)
+                       {
+                               float amount(max(0.0f,(float)(-cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
+
+                               side_b.push_back(iter->get_vertex()-(t1+t2).norm()*iter_w*amount);
+                       }
+               }
+
+               // Make the outline
+               if(homogeneous_width)
+               {
+                       const float length(curve.length());
+                       float dist(0);
+                       Point lastpoint;
+                       for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
+                       {
+                               const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
+                               const Vector p(curve(n));
+
+                               if(n)
+                                       dist+=(p-lastpoint).mag();
+
+                               const float w(((next_w-iter_w)*(dist/length)+iter_w));
+
+                               side_a.push_back(p+d*w);
+                               side_b.push_back(p-d*w);
+
+                               lastpoint=p;
+                       }
+               }
+               else
+                       for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
+                       {
+                               const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
+                               const Vector p(curve(n));
+                               const float w(((next_w-iter_w)*n+iter_w));
+
+                               side_a.push_back(p+d*w);
+                               side_b.push_back(p-d*w);
+                       }
+               last_tangent=deriv(1.0-CUSP_TANGENT_ADJUST);
+               side_a.push_back(curve(1.0)+last_tangent.perp().norm()*next_w);
+               side_b.push_back(curve(1.0)-last_tangent.perp().norm()*next_w);
+
+               first=false;
+       }
+
+       if(loop)
+       {
+               reverse(side_b.begin(),side_b.end());
+               add_polygon(side_a);
+               add_polygon(side_b);
+               return;
+       }
+
+       // Insert code for adding end tip
+       if(round_tip[1] && !loop && side_a.size())
+       {
+               // remove the last point
+               side_a.pop_back();
+
+               const Point vertex(bline.back().get_vertex());
+               const Vector tangent(last_tangent.norm());
+               const float w((bline.back().get_width()*width)*0.5f+expand);
+
+               hermite<Vector> curve(
+                       vertex+tangent.perp()*w,
+                       vertex-tangent.perp()*w,
+                       tangent*w*ROUND_END_FACTOR,
+                       -tangent*w*ROUND_END_FACTOR
+               );
+
+               for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
+                       side_a.push_back(curve(n));
+       }
+
+       for(;!side_b.empty();side_b.pop_back())
+               side_a.push_back(side_b.back());
+
+       // Insert code for adding begin tip
+       if(round_tip[0] && !loop && side_a.size())
+       {
+               // remove the last point
+               side_a.pop_back();
+
+               const Point vertex(bline.front().get_vertex());
+               const Vector tangent(first_tangent.norm());
+               const float w((bline.front().get_width()*width)*0.5f+expand);
+
+               hermite<Vector> curve(
+                       vertex-tangent.perp()*w,
+                       vertex+tangent.perp()*w,
+                       -tangent*w*ROUND_END_FACTOR,
+                       tangent*w*ROUND_END_FACTOR
+               );
+
+               for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
+                       side_a.push_back(curve(n));
+       }
+
+       add_polygon(side_a);
+
+
+#else /* 1 */
+
+       bool loop_;
+       if(bline.get_contained_type()==ValueBase::TYPE_BLINEPOINT)
+       {
+               ValueBase value(bline);
+
+               if(loopyness<0.5f)
+               {
+                       value.set_loop(false);
+                       loop_=false;
+               }
+               else
+                       loop_=value.get_loop();
+
+               segment_list=convert_bline_to_segment_list(value);
+               width_list=convert_bline_to_width_list(value);
+       }
+       else
+       {
+               clear();
+               return;
+       }
+
+
+
+       if(segment_list.empty())
+       {
+               synfig::warning("Outline: segment_list is empty, layer disabled");
+               clear();
+               return;
+       }
+
+
+       // Repair the width list if we need to
+       {
+               Real default_width;
+               if(width_list.empty())
+                       default_width=0.01;
+               else
+                       default_width=width_list.back();
+
+               while(width_list.size()<segment_list.size()+1)
+                       width_list.push_back(default_width);
+               while(width_list.size()>segment_list.size()+1)
+                       width_list.pop_back();
+
+       }
+
+       // Repair the zero tangents (if any)
+       {
+               vector<Segment>::iterator iter;
+               for(iter=segment_list.begin();iter!=segment_list.end();++iter)
+               {
+                       if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
+                               iter->t1=iter->t2=iter->p2-iter->p1;
+               }
+       }
+
+       vector<Real>::iterator iter;
+       vector<Real> scaled_width_list;
+       for(iter=width_list.begin();iter!=width_list.end();++iter)
+       {
+               scaled_width_list.push_back((*iter*width+expand)*0.5f);
+       }
+
+       Vector::value_type n;
+       etl::hermite<Vector> curve;
+       vector<Point> vector_list;
+       Vector last_tangent(segment_list.back().t2);
+       clear();
+
+       if(!loop_)
+               last_tangent=NO_LOOP_COOKIE;
+
+       {
+               vector<Segment>::iterator iter;
+               vector<Real>::iterator witer;
+               for(
+                       iter=segment_list.begin(),
+                       witer=scaled_width_list.begin();
+                       iter!=segment_list.end();
+                       ++iter,++witer)
+               {
+                       if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
+                       {
+                               vector_list.push_back(iter->p1-(iter->p2-iter->p1).perp().norm()*witer[0]);
+                               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]));
+                               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]));
+                               vector_list.push_back(iter->p2-(iter->p2-iter->p1).perp().norm()*witer[1]);
+                       }
+                       else
+                       {
+                               curve.p1()=iter->p1;
+                               curve.t1()=iter->t1;
+                               curve.p2()=iter->p2;
+                               curve.t2()=iter->t2;
+                               curve.sync();
+
+                               etl::derivative<etl::hermite<Vector> > deriv(curve);
+
+                               // without this if statement, the broken tangents would
+                               // have boxed edges
+                               if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t1))
+                               {
+                                       //Vector curr_tangent(iter->t1);
+                                       Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
+
+                                       const Vector t1(last_tangent.perp().norm());
+                                       const Vector t2(curr_tangent.perp().norm());
+
+                                       Point p1(iter->p1+t1*witer[0]);
+                                       Point p2(iter->p1+t2*witer[0]);
+
+                                       Real cross(t1*t2.perp());
+
+                                       if(cross>CUSP_THRESHOLD)
+                                               vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
+                                       else if(cross>0)
+                                       {
+                                               float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
+                                               // Push back something to make it look vaguely round;
+                                               //vector_list.push_back(iter->p1+(t1*1.25+t2).norm()*witer[0]*amount);
+                                               vector_list.push_back(iter->p1+(t1+t2).norm()*witer[0]*amount);
+                                               //vector_list.push_back(iter->p1+(t1+t2*1.25).norm()*witer[0]*amount);
+                                       }
+                               }
+                               //last_tangent=iter->t2;
+                               last_tangent=deriv(1.0f-CUSP_TANGENT_ADJUST);
+
+                               for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
+                                       vector_list.push_back(curve(n)+deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm()*((witer[1]-witer[0])*n+witer[0]) );
+                               vector_list.push_back(curve(1.0)+deriv(1.0-CUSP_TANGENT_ADJUST).perp().norm()*witer[1]);
+
+                       }
+               }
+               if(round_tip[1] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
+               {
+                       // remove the last point
+                       vector_list.pop_back();
+
+                       iter--;
+
+                       curve.p1()=iter->p2+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
+                       curve.p2()=iter->p2-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
+                       curve.t2()=-(curve.t1()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
+                       curve.sync();
+                       for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
+                               vector_list.push_back(curve(n));
+
+                       // remove the last point
+                       vector_list.pop_back();
+               }
+       }
+
+       if(!loop_)
+               last_tangent=NO_LOOP_COOKIE;
+       else
+       {
+               add_polygon(vector_list);
+               vector_list.clear();
+               last_tangent=segment_list.front().t1;
+       }
+
+       //else
+       //      last_tangent=segment_list.back().t2;
+
+       {
+               vector<Segment>::reverse_iterator iter;
+               vector<Real>::reverse_iterator witer;
+               for(
+                       iter=segment_list.rbegin(),
+                       witer=scaled_width_list.rbegin(),++witer;
+                       !(iter==segment_list.rend());
+                       ++iter,++witer)
+               {
+
+                       if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
+                       {
+                               vector_list.push_back(iter->p2+(iter->p2-iter->p1).perp().norm()*witer[0]);
+                               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]));
+                               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]));
+                               vector_list.push_back(iter->p1+(iter->p2-iter->p1).perp().norm()*witer[-1]);
+                       }
+                       else
+                       {
+                               curve.p1()=iter->p1;
+                               curve.t1()=iter->t1;
+                               curve.p2()=iter->p2;
+                               curve.t2()=iter->t2;
+                               curve.sync();
+
+                               etl::derivative<etl::hermite<Vector> > deriv(curve);
+
+                               // without this if statement, the broken tangents would
+                               // have boxed edges
+                               if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t2))
+                               {
+                                       //Vector curr_tangent(iter->t2);
+                                       Vector curr_tangent(deriv(1.0f-CUSP_TANGENT_ADJUST));
+
+                                       const Vector t1(last_tangent.perp().norm());
+                                       const Vector t2(curr_tangent.perp().norm());
+
+                                       Point p1(iter->p2-t1*witer[-1]);
+                                       Point p2(iter->p2-t2*witer[-1]);
+
+                                       Real cross(t1*t2.perp());
+
+                                       //if(last_tangent.perp().norm()*curr_tangent.norm()<-CUSP_THRESHOLD)
+                                       if(cross>CUSP_THRESHOLD)
+                                               vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
+                                       else if(cross>0)
+                                       {
+                                               float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
+                                               // Push back something to make it look vaguely round;
+                                               //vector_list.push_back(iter->p2-(t1*1.25+t2).norm()*witer[-1]*amount);
+                                               vector_list.push_back(iter->p2-(t1+t2).norm()*witer[-1]*amount);
+                                               //vector_list.push_back(iter->p2-(t1+t2*1.25).norm()*witer[-1]*amount);
+                                       }
+                               }
+                               //last_tangent=iter->t1;
+                               last_tangent=deriv(CUSP_TANGENT_ADJUST);
+
+                               for(n=1.0f;n>CUSP_TANGENT_ADJUST;n-=1.0f/SAMPLES)
+                                       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]) );
+                               vector_list.push_back(curve(0.0f)-deriv(CUSP_TANGENT_ADJUST).perp().norm()*witer[0]);
+                       }
+               }
+               if(round_tip[0] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
+               {
+                       // remove the last point
+                       vector_list.pop_back();
+                       iter--;
+                       witer--;
+
+                       curve.p1()=iter->p1+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
+                       curve.p2()=iter->p1-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
+                       curve.t1()=-(curve.t2()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
+                       curve.sync();
+
+                       for(n=1.0;n>0.0;n-=1.0/SAMPLES)
+                               vector_list.push_back(curve(n));
+
+                       // remove the last point
+                       vector_list.pop_back();
+               }
+       }
+
+       //if(loop_)
+       //      reverse(vector_list.begin(),vector_list.end());
+
+#ifdef _DEBUG
+       {
+               vector<Point>::iterator iter;
+               for(iter=vector_list.begin();iter!=vector_list.end();++iter)
+                       if(!iter->is_valid())
+                       {
+                               synfig::error("Outline::sync(): Bad point in vector_list!");
+                       }
+               //synfig::info("BLEHH__________--- x:%f, y:%f",vector_list.front()[0],vector_list.front()[1]);
+       }
+#endif /* _DEBUG */
+
+       add_polygon(vector_list);
+
+
+#endif /* 1 */
+       } catch (...) { synfig::error("Outline::sync(): Exception thrown"); throw; }
+}
+
+#undef bline
+
+bool
+Outline::set_param(const String & param, const ValueBase &value)
+{
+       if(param=="segment_list")
+       {
+               if(dynamic_param_list().count("segment_list"))
+               {
+                       connect_dynamic_param("bline",dynamic_param_list().find("segment_list")->second);
+                       disconnect_dynamic_param("segment_list");
+                       synfig::warning("Outline::set_param(): Updated valuenode connection to use the new \"bline\" parameter.");
+               }
+               else
+                       synfig::warning("Outline::set_param(): The parameter \"segment_list\" is deprecated. Use \"bline\" instead.");
+       }
+
+       if(     (param=="segment_list" || param=="bline") && value.get_type()==ValueBase::TYPE_LIST)
+       {
+               //if(value.get_contained_type()!=ValueBase::TYPE_BLINEPOINT)
+               //      return false;
+
+               bline=value;
+
+               return true;
+       }
+       /*
+       if(     param=="seg" && value.get_type()==ValueBase::TYPE_SEGMENT)
+       {
+               if(!segment_list.empty())
+                       segment_list.clear();
+
+               segment_list.push_back(value.get(Segment()));
+               loop_=false;
+               //sync();
+               return true;
+       }
+       if(     param=="w[0]" && value.get_type()==ValueBase::TYPE_REAL)
+       {
+               if(width_list.size()<2)
+               {
+                       width_list.push_back(value.get(Real()));
+                       width_list.push_back(value.get(Real()));
+               }
+               else
+               {
+                       width_list[0]=value.get(Real());
+               }
+               width=1;
+               //sync();
+               return true;
+       }
+
+       if(     param=="w[1]" && value.get_type()==ValueBase::TYPE_REAL)
+       {
+               if(width_list.size()<2)
+               {
+                       width_list.push_back(value.get(Real()));
+                       width_list.push_back(value.get(Real()));
+               }
+               else
+               {
+                       width_list[1]=value.get(Real());
+               }
+               width=1;
+               //sync();
+               return true;
+       }
+
+       if(     param=="width_list" && value.same_type_as(width_list))
+       {
+               width_list=value;
+               //sync();
+               return true;
+       }
+       */
+
+       IMPORT(round_tip[0]);
+       IMPORT(round_tip[1]);
+       IMPORT(sharp_cusps);
+       IMPORT_PLUS(width,if(old_version){width*=2.0;});
+       IMPORT(loopyness);
+       IMPORT(expand);
+       IMPORT(homogeneous_width);
+
+       if(param!="vector_list")
+               return Layer_Polygon::set_param(param,value);
+
+       return false;
+}
+
+void
+Outline::set_time(Context context, Time time)const
+{
+       const_cast<Outline*>(this)->sync();
+       context.set_time(time);
+}
+
+void
+Outline::set_time(Context context, Time time, Vector pos)const
+{
+       const_cast<Outline*>(this)->sync();
+       context.set_time(time,pos);
+}
+
+ValueBase
+Outline::get_param(const String& param)const
+{
+       EXPORT(bline);
+       EXPORT(expand);
+       //EXPORT(width_list);
+       //EXPORT(segment_list);
+       EXPORT(homogeneous_width);
+       EXPORT(round_tip[0]);
+       EXPORT(round_tip[1]);
+       EXPORT(sharp_cusps);
+       EXPORT(width);
+       EXPORT(loopyness);
+
+       EXPORT_NAME();
+       EXPORT_VERSION();
+
+       if(param!="vector_list")
+               return Layer_Polygon::get_param(param);
+       return ValueBase();
+}
+
+Layer::Vocab
+Outline::get_param_vocab()const
+{
+       Layer::Vocab ret(Layer_Polygon::get_param_vocab());
+
+       // Pop off the polygon parameter from the polygon vocab
+       ret.pop_back();
+
+       ret.push_back(ParamDesc("bline")
+               .set_local_name(_("Vertices"))
+               .set_origin("origin")
+               .set_hint("width")
+               .set_description(_("A list of BLine Points"))
+       );
+
+       /*
+       ret.push_back(ParamDesc("width_list")
+               .set_local_name(_("Point Widths"))
+               .set_origin("segment_list")
+               .hidden()
+               .not_critical()
+       );
+       */
+
+       ret.push_back(ParamDesc("width")
+               .set_is_distance()
+               .set_local_name(_("Outline Width"))
+       );
+
+       ret.push_back(ParamDesc("expand")
+               .set_is_distance()
+               .set_local_name(_("Expand"))
+       );
+
+       ret.push_back(ParamDesc("sharp_cusps")
+               .set_local_name(_("Sharp Cusps"))
+               .set_description(_("Determines cusp type"))
+       );
+
+       ret.push_back(ParamDesc("round_tip[0]")
+               .set_local_name(_("Rounded Begin"))
+               .set_description(_("Round off the tip"))
+       );
+
+       ret.push_back(ParamDesc("round_tip[1]")
+               .set_local_name(_("Rounded End"))
+               .set_description(_("Round off the tip"))
+       );
+       ret.push_back(ParamDesc("loopyness")
+               .set_local_name(_("Loopyness"))
+       );
+       ret.push_back(ParamDesc("homogeneous_width")
+               .set_local_name(_("Homogeneous"))
+       );
+
+       return ret;
+}