/*! \file curvegradient.cpp
** \brief Template Header
**
+** $Id$
+**
** \legal
** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
+** Copyright (c) 2007 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
#endif
}
-inline float calculate_distance(const std::vector<synfig::BLinePoint>& bline)
+inline float calculate_distance(const std::vector<synfig::BLinePoint>& bline, bool bline_loop)
{
std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
float dist(0);
+ if (bline.empty()) return dist;
+
next=bline.begin();
- //if(loop)
- // iter=--bline.end();
- //else
+ if(bline_loop)
+ iter=--bline.end();
+ else
iter=next++;
for(;next!=end;iter=next++)
iter->get_vertex(),
next->get_vertex(),
iter->get_tangent2(),
- next->get_tangent1()
- );
+ next->get_tangent1());
// dist+=calculate_distance(*iter,*next);
dist+=curve.length();
}
std::vector<synfig::BLinePoint>::const_iterator
-find_closest(const std::vector<synfig::BLinePoint>& bline,const Point& p,bool loop=false,float *bline_dist_ret=0)
+find_closest(bool fast, const std::vector<synfig::BLinePoint>& bline,const Point& p,float& t,bool loop=false,float *bline_dist_ret=0)
{
std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
float best_bline_dist(0);
float best_bline_len(0);
float total_bline_dist(0);
+ float best_pos(0);
etl::hermite<Vector> best_curve;
if(loop)
iter->get_vertex(),
next->get_vertex(),
iter->get_tangent2(),
- next->get_tangent1()
- );
+ next->get_tangent1());
/*
const float t(curve.find_closest(p,6,0.01,0.99));
len=curve.length();
}
+ if (fast)
+ {
#define POINT_CHECK(x) bp=curve(x); thisdist=(bp-p).mag_squared(); if(thisdist<dist) { ret=iter; dist=thisdist; best_bline_dist=total_bline_dist; best_bline_len=len; best_curve=curve; }
-
- POINT_CHECK(0.0001);
- POINT_CHECK((1.0/6.0));
- POINT_CHECK((2.0/6.0));
- POINT_CHECK((3.0/6.0));
- POINT_CHECK((4.0/6.0));
- POINT_CHECK((5.0/6.0));
- POINT_CHECK(0.9999);
+ POINT_CHECK(0.0001);
+ POINT_CHECK((1.0/6.0));
+ POINT_CHECK((2.0/6.0));
+ POINT_CHECK((3.0/6.0));
+ POINT_CHECK((4.0/6.0));
+ POINT_CHECK((5.0/6.0));
+ POINT_CHECK(0.9999);
+ }
+ else
+ {
+ float pos = curve.find_closest(fast, p);
+ thisdist=(curve(pos)-p).mag_squared();
+ if(thisdist<dist)
+ {
+ ret=iter;
+ dist=thisdist;
+ best_bline_dist=total_bline_dist;
+ best_bline_len=len;
+ best_curve=curve;
+ best_pos = pos;
+ }
+ }
total_bline_dist+=len;
}
+ t = best_pos;
+
if(bline_dist_ret)
{
- *bline_dist_ret=best_bline_dist+best_curve.find_distance(0,best_curve.find_closest(p));
-// *bline_dist_ret=best_bline_dist+best_curve.find_closest(p)*best_bline_len;
+ //! \todo is this a redundant call to find_closest()?
+ // note bline_dist_ret is null except when 'perpendicular' is true
+ *bline_dist_ret=best_bline_dist+best_curve.find_distance(0,best_curve.find_closest(fast, p));
+// *bline_dist_ret=best_bline_dist+best_curve.find_closest(fast, p)*best_bline_len;
}
return ret;
inline void
CurveGradient::sync()
{
- diff=(p2-p1);
- const Real mag(diff.inv_mag());
- diff*=mag*mag;
-
- curve_length_=calculate_distance(bline);
+ curve_length_=calculate_distance(bline, bline_loop);
}
CurveGradient::CurveGradient():
- p1(1,1),
- p2(-1,-1),
offset(0,0),
width(0.25),
gradient(Color::black(), Color::white()),
loop(false),
zigzag(false),
- perpendicular(false)
+ perpendicular(false),
+ fast(true)
{
bline.push_back(BLinePoint());
bline.push_back(BLinePoint());
Real dist;
float perp_dist;
+ bool edge_case = false;
if(bline.size()==0)
return Color::alpha();
}
else
{
+ float t;
Point point(point_-offset);
std::vector<synfig::BLinePoint>::const_iterator iter,next;
// Taking into account looping.
if(perpendicular)
{
- next=find_closest(bline,point,bline_loop,&perp_dist);
+ next=find_closest(fast,bline,point,t,bline_loop,&perp_dist);
perp_dist/=curve_length_;
}
- else
+ else // not perpendicular
{
- next=find_closest(bline,point,bline_loop);
+ next=find_closest(fast,bline,point,t,bline_loop);
}
- iter=next++;
- if(next==bline.end()) next=bline.begin();
-
- // Setup the curve
- etl::hermite<Vector> curve(
- iter->get_vertex(),
- next->get_vertex(),
- iter->get_tangent2(),
- next->get_tangent1()
- );
- // Setup the derivative function
- etl::derivative<etl::hermite<Vector> > deriv(curve);
+ iter=next++;
+ if(next==bline.end()) next=bline.begin();
+
+ // Setup the curve
+ etl::hermite<Vector> curve(
+ iter->get_vertex(),
+ next->get_vertex(),
+ iter->get_tangent2(),
+ next->get_tangent1()
+ );
- int search_iterations(7);
+ // Setup the derivative function
+ etl::derivative<etl::hermite<Vector> > deriv(curve);
- /*if(quality==0)search_iterations=8;
- else if(quality<=2)search_iterations=10;
- else if(quality<=4)search_iterations=8;
- */
- if(!perpendicular)
- {
- if(quality<=6)search_iterations=7;
- else if(quality<=7)search_iterations=6;
- else if(quality<=8)search_iterations=5;
- else search_iterations=4;
- }
- else
- {
- if(quality>7)
- search_iterations=4;
- }
+ int search_iterations(7);
- // Figure out the closest point on the curve
- const float t(curve.find_closest(point,search_iterations));
+ /*if(quality==0)search_iterations=8;
+ else if(quality<=2)search_iterations=10;
+ else if(quality<=4)search_iterations=8;
+ */
+ if(perpendicular)
+ {
+ if(quality>7)
+ search_iterations=4;
+ }
+ else // not perpendicular
+ {
+ if(quality<=6)search_iterations=7;
+ else if(quality<=7)search_iterations=6;
+ else if(quality<=8)search_iterations=5;
+ else search_iterations=4;
+ }
+ // Figure out the closest point on the curve
+ if (fast)
+ t = curve.find_closest(fast, point,search_iterations);
- // Calculate our values
- p1=curve(t);
- tangent=deriv(t).norm();
+ // Calculate our values
+ p1=curve(t); // the closest point on the curve
+ tangent=deriv(t).norm(); // the unit tangent at that point
- if(perpendicular)
+ // if the point we're nearest to is at either end of the
+ // bline, our distance from the curve is the distance from the
+ // point on the curve. we need to know which side of the
+ // curve we're on, so find the average of the two tangents at
+ // this point
+ if (t<0.00001 || t>0.99999)
+ {
+ if (t<0.5)
{
- tangent*=curve_length_;
- p1-=tangent*perp_dist;
- tangent=-tangent.perp();
+ if (iter->get_split_tangent_flag())
+ {
+ tangent=(iter->get_tangent1().norm()+tangent).norm();
+ edge_case=true;
+ }
}
else
{
- thickness=(next->get_width()-iter->get_width())*t+iter->get_width();
+ if (next->get_split_tangent_flag())
+ {
+ tangent=(next->get_tangent2().norm()+tangent).norm();
+ edge_case=true;
+ }
}
- //}
- }
-
+ }
- if(!perpendicular)
- {
- diff=tangent.perp()*thickness*width;
- p1-=diff*0.5;
- const Real mag(diff.inv_mag());
- supersample=supersample*mag;
- diff*=mag*mag;
- dist=((point_-offset)*diff-p1*diff);
+ if(perpendicular)
+ {
+ tangent*=curve_length_;
+ p1-=tangent*perp_dist;
+ tangent=-tangent.perp();
+ }
+ else // not perpendicular
+ // the width of the bline at the closest point on the curve
+ thickness=(next->get_width()-iter->get_width())*t+iter->get_width();
}
- else
+
+ if(perpendicular)
{
if(quality>7)
{
}
else
{
- diff=tangent.perp();
- //p1-=diff*0.5;
+ diff=tangent.perp();
+ //p1-=diff*0.5;
+ const Real mag(diff.inv_mag());
+ supersample=supersample*mag;
+ diff*=mag*mag;
+ dist=((point_-offset)*diff-p1*diff);
+ }
+ }
+ else // not perpendicular
+ {
+ if (edge_case)
+ {
+ diff=(p1-(point_-offset));
+ if(diff*tangent.perp()<0) diff=-diff;
+ diff=diff.norm()*thickness*width;
+ }
+ else
+ diff=tangent.perp()*thickness*width;
+
+ p1-=diff*0.5;
const Real mag(diff.inv_mag());
supersample=supersample*mag;
diff*=mag*mag;
dist=((point_-offset)*diff-p1*diff);
- }
}
if(loop)
{
if(dist+supersample*0.5>1.0)
{
- Color pool(gradient(dist,supersample*0.5).premult_alpha()*(1.0-(dist-supersample*0.5)));
- pool+=gradient((dist+supersample*0.5)-1.0,supersample*0.5).premult_alpha()*((dist+supersample*0.5)-1.0);
+ float left(supersample*0.5-(dist-1.0));
+ float right(supersample*0.5+(dist-1.0));
+ Color pool(gradient(1.0-(left*0.5),left).premult_alpha()*left/supersample);
+ if (zigzag) pool+=gradient(1.0-right*0.5,right).premult_alpha()*right/supersample;
+ else pool+=gradient(right*0.5,right).premult_alpha()*right/supersample;
return pool.demult_alpha();
}
if(dist-supersample*0.5<0.0)
{
- Color pool(gradient(dist,supersample*0.5).premult_alpha()*(dist+supersample*0.5));
- pool+=gradient(1.0-(dist-supersample*0.5),supersample*0.5).premult_alpha()*(-(dist-supersample*0.5));
+ float left(supersample*0.5-dist);
+ float right(supersample*0.5+dist);
+ Color pool(gradient(right*0.5,right).premult_alpha()*right/supersample);
+ if (zigzag) pool+=gradient(left*0.5,left).premult_alpha()*left/supersample;
+ else pool+=gradient(1.0-left*0.5,left).premult_alpha()*left/supersample;
return pool.demult_alpha();
}
}
}
float
-CurveGradient::calc_supersample(const synfig::Point &x, float pw,float ph)const
+CurveGradient::calc_supersample(const synfig::Point &/*x*/, float pw,float /*ph*/)const
{
-// return pw/(p2-p1).mag();
return pw;
}
bool
CurveGradient::set_param(const String & param, const ValueBase &value)
{
- if(param=="p1" && value.same_as(p1))
- {
- p1=value.get(p1);
- sync();
- return true;
- }
- if(param=="p2" && value.same_as(p2))
- {
- p2=value.get(p2);
- sync();
- return true;
- }
- //IMPORT(p1);
- //IMPORT(p2);
IMPORT(offset);
IMPORT(perpendicular);
+ IMPORT(fast);
if(param=="bline" && value.get_type()==ValueBase::TYPE_LIST)
{
{
EXPORT(offset);
EXPORT(bline);
- EXPORT(p1);
- EXPORT(p2);
EXPORT(gradient);
EXPORT(loop);
EXPORT(zigzag);
EXPORT(width);
EXPORT(perpendicular);
+ EXPORT(fast);
EXPORT_NAME();
EXPORT_VERSION();
{
Layer::Vocab ret(Layer_Composite::get_param_vocab());
- ret.push_back(ParamDesc("offset")
- .set_local_name(_("Offset"))
- );
+ ret.push_back(ParamDesc("offset")
+ .set_local_name(_("Offset")));
ret.push_back(ParamDesc("width")
- .set_is_distance()
- .set_local_name(_("Width"))
- );
+ .set_is_distance()
+ .set_local_name(_("Width")));
ret.push_back(ParamDesc("bline")
- .set_local_name(_("Vertices"))
- .set_origin("offset")
- .set_scalar("width")
- .set_description(_("A list of BLine Points"))
- );
-
+ .set_local_name(_("Vertices"))
+ .set_origin("offset")
+ .set_scalar("width")
+ .set_description(_("A list of BLine Points")));
ret.push_back(ParamDesc("gradient")
- .set_local_name(_("Gradient"))
- );
+ .set_local_name(_("Gradient")));
ret.push_back(ParamDesc("loop")
- .set_local_name(_("Loop"))
- );
+ .set_local_name(_("Loop")));
ret.push_back(ParamDesc("zigzag")
- .set_local_name(_("ZigZag"))
- );
+ .set_local_name(_("ZigZag")));
ret.push_back(ParamDesc("perpendicular")
- .set_local_name(_("Perpendicular"))
- );
+ .set_local_name(_("Perpendicular")));
+ ret.push_back(ParamDesc("fast")
+ .set_local_name(_("Fast")));
return ret;
}