Add my copyright to files I've modified.
[synfig.git] / synfig-core / trunk / src / modules / mod_gradient / curvegradient.cpp
index 623074f..2fb682f 100644 (file)
@@ -6,6 +6,7 @@
 **
 **     \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
@@ -72,7 +73,7 @@ inline float calculate_distance(const synfig::BLinePoint& a,const synfig::BLineP
 #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());
@@ -83,10 +84,10 @@ inline float calculate_distance(const std::vector<synfig::BLinePoint>& bline)
 
        next=bline.begin();
 
-       //if(loop)
-       //      iter=--bline.end();
-       //else
-       iter=next++;
+       if(bline_loop)
+               iter=--bline.end();
+       else
+               iter=next++;
 
        for(;next!=end;iter=next++)
        {
@@ -105,7 +106,7 @@ inline float calculate_distance(const std::vector<synfig::BLinePoint>& bline)
 }
 
 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());
@@ -118,6 +119,7 @@ find_closest(const std::vector<synfig::BLinePoint>& bline,const Point& p,bool lo
        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)
@@ -149,23 +151,43 @@ find_closest(const std::vector<synfig::BLinePoint>& bline,const Point& p,bool lo
                        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;
@@ -176,7 +198,7 @@ find_closest(const std::vector<synfig::BLinePoint>& bline,const Point& p,bool lo
 inline void
 CurveGradient::sync()
 {
-       curve_length_=calculate_distance(bline);
+       curve_length_=calculate_distance(bline, bline_loop);
 }
 
 
@@ -186,7 +208,8 @@ CurveGradient::CurveGradient():
        gradient(Color::black(), Color::white()),
        loop(false),
        zigzag(false),
-       perpendicular(false)
+       perpendicular(false),
+       fast(true)
 {
        bline.push_back(BLinePoint());
        bline.push_back(BLinePoint());
@@ -215,6 +238,7 @@ CurveGradient::color_func(const Point &point_, int quality, float supersample)co
        Real dist;
 
        float perp_dist;
+       bool edge_case = false;
 
        if(bline.size()==0)
                return Color::alpha();
@@ -226,6 +250,7 @@ CurveGradient::color_func(const Point &point_, int quality, float supersample)co
        }
        else
        {
+               float t;
                Point point(point_-offset);
 
                std::vector<synfig::BLinePoint>::const_iterator iter,next;
@@ -234,12 +259,12 @@ CurveGradient::color_func(const Point &point_, int quality, float supersample)co
                // 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++;
@@ -262,26 +287,51 @@ CurveGradient::color_func(const Point &point_, int quality, float supersample)co
                  else if(quality<=2)search_iterations=10;
                  else if(quality<=4)search_iterations=8;
                */
-               if(!perpendicular)
+               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;
                }
-               else
-               {
-                       if(quality>7)
-                               search_iterations=4;
-               }
 
                // Figure out the closest point on the curve
-               const float t(curve.find_closest(point,search_iterations));
-
+               if (fast)
+                       t = curve.find_closest(fast, point,search_iterations);
 
                // Calculate our values
-               p1=curve(t);
-               tangent=deriv(t).norm();
+               p1=curve(t);                     // the closest point on the curve
+               tangent=deriv(t).norm(); // the unit tangent at that point
+
+               // 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)
+                       {
+                               if (iter->get_split_tangent_flag())
+                               {
+                                       tangent=(iter->get_tangent1().norm()+tangent).norm();
+                                       edge_case=true;
+                               }
+                       }
+                       else
+                       {
+                               if (next->get_split_tangent_flag())
+                               {
+                                       tangent=(next->get_tangent2().norm()+tangent).norm();
+                                       edge_case=true;
+                               }
+                       }
+               }
 
                if(perpendicular)
                {
@@ -289,23 +339,12 @@ CurveGradient::color_func(const Point &point_, int quality, float supersample)co
                        p1-=tangent*perp_dist;
                        tangent=-tangent.perp();
                }
-               else
-               {
+               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();
-               }
-               //}
        }
 
-       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(perpendicular)
        {
                if(quality>7)
                {
@@ -326,6 +365,23 @@ CurveGradient::color_func(const Point &point_, int quality, float supersample)co
                        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)
                dist-=floor(dist);
@@ -362,7 +418,7 @@ CurveGradient::color_func(const Point &point_, int quality, float supersample)co
 }
 
 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;
 }
@@ -386,6 +442,7 @@ CurveGradient::set_param(const String & param, const ValueBase &value)
 
        IMPORT(offset);
        IMPORT(perpendicular);
+       IMPORT(fast);
 
        if(param=="bline" && value.get_type()==ValueBase::TYPE_LIST)
        {
@@ -413,6 +470,7 @@ CurveGradient::get_param(const String & param)const
        EXPORT(zigzag);
        EXPORT(width);
        EXPORT(perpendicular);
+       EXPORT(fast);
 
        EXPORT_NAME();
        EXPORT_VERSION();
@@ -446,6 +504,8 @@ CurveGradient::get_param_vocab()const
                                  .set_local_name(_("ZigZag")));
        ret.push_back(ParamDesc("perpendicular")
                                  .set_local_name(_("Perpendicular")));
+       ret.push_back(ParamDesc("fast")
+                                 .set_local_name(_("Fast")));
 
        return ret;
 }