/* === S Y N F I G ========================================================= */ /*! \file curvegradient.cpp ** \brief Template Header ** ** $Id: curvegradient.cpp 472 2007-04-18 09:14:33Z dooglus $ ** ** \legal ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley ** ** 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 ** ** === N O T E S =========================================================== ** ** ========================================================================= */ /* === H E A D E R S ======================================================= */ #ifdef USING_PCH # include "pch.h" #else #ifdef HAVE_CONFIG_H # include #endif #include "curvegradient.h" #include #include #include #include #include #include #include #include #include #include #include #endif /* === M A C R O S ========================================================= */ /* === G L O B A L S ======================================================= */ SYNFIG_LAYER_INIT(CurveGradient); SYNFIG_LAYER_SET_NAME(CurveGradient,"curve_gradient"); SYNFIG_LAYER_SET_LOCAL_NAME(CurveGradient,_("Curve Gradient")); SYNFIG_LAYER_SET_CATEGORY(CurveGradient,_("Gradients")); SYNFIG_LAYER_SET_VERSION(CurveGradient,"0.0"); SYNFIG_LAYER_SET_CVS_ID(CurveGradient,"$Id: curvegradient.cpp 472 2007-04-18 09:14:33Z dooglus $"); /* === P R O C E D U R E S ================================================= */ inline float calculate_distance(const synfig::BLinePoint& a,const synfig::BLinePoint& b) { #if 1 const Point& c1(a.get_vertex()); const Point c2(a.get_vertex()+a.get_tangent2()/3); const Point c3(b.get_vertex()-b.get_tangent1()/3); const Point& c4(b.get_vertex()); return (c1-c2).mag()+(c2-c3).mag()+(c3-c4).mag(); #else #endif } inline float calculate_distance(const std::vector& bline) { std::vector::const_iterator iter,next,ret; std::vector::const_iterator end(bline.end()); float dist(0); if (bline.empty()) return dist; next=bline.begin(); //if(loop) // iter=--bline.end(); //else iter=next++; for(;next!=end;iter=next++) { // Setup the curve etl::hermite curve( iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1()); // dist+=calculate_distance(*iter,*next); dist+=curve.length(); } return dist; } std::vector::const_iterator find_closest(bool fast, const std::vector& bline,const Point& p,float& t,bool loop=false,float *bline_dist_ret=0) { std::vector::const_iterator iter,next,ret; std::vector::const_iterator end(bline.end()); ret=bline.end(); float dist(100000000000.0); next=bline.begin(); float best_bline_dist(0); float best_bline_len(0); float total_bline_dist(0); float best_pos(0); etl::hermite best_curve; if(loop) iter=--bline.end(); else iter=next++; Point bp; for(;next!=end;iter=next++) { // Setup the curve etl::hermite curve( iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1()); /* const float t(curve.find_closest(p,6,0.01,0.99)); bp=curve(t);if((bp-p).mag_squared()::const_iterator iter,next; // Figure out the BLinePoints we will be using, // Taking into account looping. if(perpendicular) { next=find_closest(fast,bline,point,t,bline_loop,&perp_dist); perp_dist/=curve_length_; } else { next=find_closest(fast,bline,point,t,bline_loop); } iter=next++; if(next==bline.end()) next=bline.begin(); // Setup the curve etl::hermite curve( iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1() ); // Setup the derivative function etl::derivative > deriv(curve); int search_iterations(7); /*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; } // 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(); if(perpendicular) { tangent*=curve_length_; p1-=tangent*perp_dist; tangent=-tangent.perp(); } else { thickness=(next->get_width()-iter->get_width())*t+iter->get_width(); } //} } 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); } else { if(quality>7) { dist=perp_dist; /* diff=tangent.perp(); const Real mag(diff.inv_mag()); supersample=supersample*mag; */ supersample=0; } else { 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); } } if(loop) dist-=floor(dist); if(zigzag) { dist*=2.0; supersample*=2.0; if(dist>1)dist=2.0-dist; } if(loop) { if(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) { 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(); } } return gradient(dist,supersample); } float CurveGradient::calc_supersample(const synfig::Point &x, float pw,float ph)const { return pw; } synfig::Layer::Handle CurveGradient::hit_check(synfig::Context context, const synfig::Point &point)const { if(get_blend_method()==Color::BLEND_STRAIGHT && get_amount()>=0.5) return const_cast(this); if(get_amount()==0.0) return context.hit_check(point); if((get_blend_method()==Color::BLEND_STRAIGHT || get_blend_method()==Color::BLEND_COMPOSITE|| get_blend_method()==Color::BLEND_ONTO) && color_func(point).get_a()>0.5) return const_cast(this); return context.hit_check(point); } bool CurveGradient::set_param(const String & param, const ValueBase &value) { IMPORT(offset); IMPORT(perpendicular); IMPORT(fast); if(param=="bline" && value.get_type()==ValueBase::TYPE_LIST) { bline=value; bline_loop=value.get_loop(); sync(); return true; } IMPORT(width); IMPORT(gradient); IMPORT(loop); IMPORT(zigzag); return Layer_Composite::set_param(param,value); } ValueBase CurveGradient::get_param(const String & param)const { EXPORT(offset); EXPORT(bline); EXPORT(gradient); EXPORT(loop); EXPORT(zigzag); EXPORT(width); EXPORT(perpendicular); EXPORT(fast); EXPORT_NAME(); EXPORT_VERSION(); return Layer_Composite::get_param(param); } Layer::Vocab CurveGradient::get_param_vocab()const { Layer::Vocab ret(Layer_Composite::get_param_vocab()); ret.push_back(ParamDesc("offset") .set_local_name(_("Offset"))); ret.push_back(ParamDesc("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"))); ret.push_back(ParamDesc("gradient") .set_local_name(_("Gradient"))); ret.push_back(ParamDesc("loop") .set_local_name(_("Loop"))); ret.push_back(ParamDesc("zigzag") .set_local_name(_("ZigZag"))); ret.push_back(ParamDesc("perpendicular") .set_local_name(_("Perpendicular"))); ret.push_back(ParamDesc("fast") .set_local_name(_("Fast"))); return ret; } Color CurveGradient::get_color(Context context, const Point &point)const { const Color color(color_func(point,0)); if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT) return color; else return Color::blend(color,context.get_color(point),get_amount(),get_blend_method()); } bool CurveGradient::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const { SuperCallback supercb(cb,0,9500,10000); if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT) { surface->set_wh(renddesc.get_w(),renddesc.get_h()); } else { if(!context.accelerated_render(surface,quality,renddesc,&supercb)) return false; if(get_amount()==0) return true; } int x,y; Surface::pen pen(surface->begin()); const Real pw(renddesc.get_pw()),ph(renddesc.get_ph()); Point pos; Point tl(renddesc.get_tl()); const int w(surface->get_w()); const int h(surface->get_h()); if(get_amount()==1.0 && get_blend_method()==Color::BLEND_STRAIGHT) { for(y=0,pos[1]=tl[1];yamount_complete(10000,10000)) return false; return true; }