-/* === S I N F G =========================================================== */
-/*! \file bline.cpp
-** \brief Template
+/* === S Y N F I G ========================================================= */
+/*! \file outline.cpp
+** \brief Implementation of the "Outline" layer
**
-** $Id: outline.cpp,v 1.1.1.1 2005/01/04 01:23:10 darco Exp $
+** $Id$
**
** \legal
-** Copyright (c) 2002 Robert B. Quattlebaum Jr.
+** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
+** Copyright (c) 2007, 2008 Chris Moore
**
-** This software and associated documentation
-** are CONFIDENTIAL and PROPRIETARY property of
-** the above-mentioned copyright holder.
+** 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.
**
-** You may not copy, print, publish, or in any
-** other way distribute this software without
-** a prior written agreement with
-** the copyright holder.
+** 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
*/
/* ========================================================================= */
#endif
#include "outline.h"
-#include <sinfg/string.h>
-#include <sinfg/time.h>
-#include <sinfg/context.h>
-#include <sinfg/paramdesc.h>
-#include <sinfg/renddesc.h>
-#include <sinfg/surface.h>
-#include <sinfg/value.h>
-#include <sinfg/valuenode.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 <sinfg/valuenode_bline.h>
+#include <synfig/valuenode_bline.h>
#endif
#define ROUND_END_FACTOR (4)
#define CUSP_THRESHOLD (0.40)
#define SPIKE_AMOUNT (4)
-#define NO_LOOP_COOKIE sinfg::Vector(84951305,7836658)
+#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 ======================================================= */
-SINFG_LAYER_INIT(Outline);
-SINFG_LAYER_SET_NAME(Outline,"outline");
-SINFG_LAYER_SET_LOCAL_NAME(Outline,_("Outline"));
-SINFG_LAYER_SET_CATEGORY(Outline,_("Geometry"));
-SINFG_LAYER_SET_VERSION(Outline,"0.2");
-SINFG_LAYER_SET_CVS_ID(Outline,"$Id: outline.cpp,v 1.1.1.1 2005/01/04 01:23:10 darco Exp $");
+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
+// This function was adapted from what was
// described on http://www.whisqu.se/per/docs/math28.htm
Point line_intersection(
const Point& p1,
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 cludge for infinity, however, this will
+
+ // 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 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
+
+ // use Kramers rule to compute the intersection
return Point(
((b1*c2 - b2*c1)*det_inv),
((a2*c1 - a1*c2)*det_inv)
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[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_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;
}
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());
- const vector<sinfg::BLinePoint> bline_(bline.get_list().begin(),bline.get_list().end());
+
+ 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());
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();
-
- for(bool first=!loop;next!=end;iter=next++,first=false)
+
+ // 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;
+ // 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(),
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)
+ 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_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<1.0f;n+=1.0f/SAMPLES)
+ 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));
side_a.push_back(p+d*w);
side_b.push_back(p-d*w);
-
+
lastpoint=p;
}
}
else
- for(float n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
+ 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_b);
return;
}
-
+
// Insert code for adding end tip
- if(round_tip[1] && !loop)
+ 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
);
- for(float n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
+ for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
side_a.push_back(curve(n));
-
- // remove the last point
- side_a.pop_back();
}
-
+
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)
+ 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(bline.front().get_tangent2().norm());
+ 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
);
- for(float n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
+ for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
side_a.push_back(curve(n));
-
- // remove the last point
- side_a.pop_back();
}
-
+
add_polygon(side_a);
-
-
-#else
-
+
+
+#else /* 1 */
+
bool loop_;
if(bline.get_contained_type()==ValueBase::TYPE_BLINEPOINT)
{
}
else
loop_=value.get_loop();
-
+
segment_list=convert_bline_to_segment_list(value);
width_list=convert_bline_to_width_list(value);
}
if(segment_list.empty())
{
- sinfg::warning("Outline: segment_list is empty, layer disabled");
+ synfig::warning("Outline: segment_list is empty, layer disabled");
clear();
return;
}
-
-
+
+
// Repair the width list if we need to
{
Real default_width;
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)
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)
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;
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]);
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)
+
+ if(cross>CUSP_THRESHOLD)
vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
else if(cross>0)
{
}
//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);
vector_list.pop_back();
}
}
-
+
if(!loop_)
last_tangent=NO_LOOP_COOKIE;
else
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;
!(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]);
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
{
//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)
+ if(cross>CUSP_THRESHOLD)
vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
else if(cross>0)
{
}
//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]);
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);
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();
}
for(iter=vector_list.begin();iter!=vector_list.end();++iter)
if(!iter->is_valid())
{
- sinfg::error("Outline::sync(): Bad point in vector_list!");
+ synfig::error("Outline::sync(): Bad point in vector_list!");
}
- //sinfg::info("BLEHH__________--- x:%f, y:%f",vector_list.front()[0],vector_list.front()[1]);
+ //synfig::info("BLEHH__________--- x:%f, y:%f",vector_list.front()[0],vector_list.front()[1]);
}
-#endif
-
+#endif /* _DEBUG */
+
add_polygon(vector_list);
-#endif
- } catch (...) { sinfg::error("Outline::sync(): Exception thrown"); throw; }
+#endif /* 1 */
+ } catch (...) { synfig::error("Outline::sync(): Exception thrown"); throw; }
}
#undef bline
{
connect_dynamic_param("bline",dynamic_param_list().find("segment_list")->second);
disconnect_dynamic_param("segment_list");
- sinfg::warning("Outline::set_param(): Updated valuenode connection to use the new \"bline\" parameter.");
+ synfig::warning("Outline::set_param(): Updated valuenode connection to use the new \"bline\" parameter.");
}
else
- sinfg::warning("Outline::set_param(): The parameter \"segment_list\" is deprecated. Use \"bline\" instead.");
+ 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;
//sync();
return true;
}
-
- if( param=="width_list" && value.same_as(width_list))
+
+ if( param=="width_list" && value.same_type_as(width_list))
{
width_list=value;
//sync();
IMPORT(loopyness);
IMPORT(expand);
IMPORT(homogeneous_width);
-
+
if(param!="vector_list")
return Layer_Polygon::set_param(param,value);
EXPORT(sharp_cusps);
EXPORT(width);
EXPORT(loopyness);
-
+
EXPORT_NAME();
EXPORT_VERSION();
ret.push_back(ParamDesc("bline")
.set_local_name(_("Vertices"))
- .set_origin("offset")
- .set_scalar("width")
+ .set_origin("origin")
+ .set_hint("width")
.set_description(_("A list of BLine Points"))
);