Remove .gitignore do nothing is ignored.
[synfig.git] / synfig-core / trunk / src / modules / lyr_std / curvewarp.cpp
index 6b544e6..82c213d 100644 (file)
 
 #include "curvewarp.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/bezier>
-#include <ETL/hermite>
 #include <ETL/calculus>
 
 #endif
@@ -51,6 +45,7 @@
 /* === M A C R O S ========================================================= */
 
 #define FAKE_TANGENT_STEP 0.000001
+#define TOO_THIN 0.01
 
 /* === G L O B A L S ======================================================= */
 
@@ -102,10 +97,8 @@ find_closest_to_bline(bool fast, const std::vector<synfig::BLinePoint>& bline,co
        bool first = true, last = false;
        extreme = false;
 
-       // printf("  loop\n");
        for(;next!=end;iter=next++)
        {
-               // printf("    top\n");
                // Setup the curve
                etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
                float thisdist(0);
@@ -128,36 +121,24 @@ find_closest_to_bline(bool fast, const std::vector<synfig::BLinePoint>& bline,co
                                dist=thisdist;
                                best_pos = pos;
                                best_curve = curve;
-                               // printf("set best_curve\n");
                                best_len = total_len;
                                last = true;
-                               // printf("      *last\n");
                        }
                }
                total_len += curve.length();
                first = false;
        }
-       // printf("  after\n");
 
        t = best_pos;
        if (fast)
        {
                len = best_len + best_curve.find_distance(0,best_curve.find_closest(fast, p));
-               // printf("fast set len = %f + %f = %f\n", best_len, best_curve.find_distance(0,best_curve.find_closest(fast, p)), len);
                if (last && t > .99) extreme = true;
        }
        else
        {
                len = best_len + best_curve.find_distance(0,best_pos);
-               // printf("slow set len = %f + %f = %f\n", best_len, best_curve.find_distance(0,best_pos), len);
-               // printf("find_distance from 0 to %f is %f\n", best_pos, best_curve.find_distance(0,best_pos));
-               // if (last) printf("last\n");
-
-               if (last && t > .999)
-               {
-                       extreme = true;
-                       // printf("extreme end\n");
-               }
+               if (last && t == 1) extreme = true;
        }
        return ret;
 }
@@ -169,22 +150,21 @@ CurveWarp::sync()
 {
        curve_length_=calculate_distance(bline);
        perp_ = (end_point - start_point).perp().norm();
-       // printf("curve_length_ = %f\n", curve_length_);
 }
 
 CurveWarp::CurveWarp():
        origin(0,0),
-       width(1),
-       start_point(-3,-1),
-       end_point(3,1),
+       perp_width(1),
+       start_point(-2.5,-0.5),
+       end_point(2.5,-0.3),
        fast(true)
 {
        bline.push_back(BLinePoint());
        bline.push_back(BLinePoint());
        bline[0].set_vertex(Point(-2.5,0));
-       bline[1].set_vertex(Point(2.5,0));
-       bline[0].set_tangent(Point(1, 1));
-       bline[1].set_tangent(Point(1, -1));
+       bline[1].set_vertex(Point( 2.5,0));
+       bline[0].set_tangent(Point(1,  0.1));
+       bline[1].set_tangent(Point(1, -0.1));
        bline[0].set_width(1.0f);
        bline[1].set_width(1.0f);
 
@@ -192,13 +172,12 @@ CurveWarp::CurveWarp():
 }
 
 inline Point
-CurveWarp::transform(const Point &point_, int quality, float supersample)const
+CurveWarp::transform(const Point &point_, Real *dist, Real *along, int quality)const
 {
        Vector tangent;
        Vector diff;
        Point p1;
        Real thickness;
-       Real dist;
        bool edge_case = false;
        float len(0);
        bool extreme;
@@ -312,53 +291,46 @@ CurveWarp::transform(const Point &point_, int quality, float supersample)const
                thickness=(next->get_width()-iter->get_width())*t+iter->get_width();
        }
 
+       if (thickness < TOO_THIN && thickness > -TOO_THIN)
+       {
+               if (thickness > 0) thickness = TOO_THIN;
+               else thickness = -TOO_THIN;
+       }
+
        if (extreme)
        {
-               Vector tangent, perp;
+               Vector tangent;
 
                if (t < 0.5)
                {
-                       synfig::BLinePoint start(bline[0]);
-                       // Point a(start.get_vertex());
-                       tangent = start.get_tangent1().norm();
-                       diff = tangent.perp()*thickness*width;
-                       len = (point_-origin - p1)*tangent;
+                       std::vector<synfig::BLinePoint>::const_iterator iter(bline.begin());
+                       tangent = iter->get_tangent1().norm();
+                       len = 0;
                }
                else
                {
-                       std::vector<synfig::BLinePoint>::const_iterator iter;
-                       iter=bline.end();
-                       iter--;
+                       std::vector<synfig::BLinePoint>::const_iterator iter(--bline.end());
                        tangent = iter->get_tangent2().norm();
-                       diff = tangent.perp()*thickness*width;
-                       len = (point_-origin - p1)*tangent + curve_length_;
+                       len = curve_length_;
                }
+               len += (point_-origin - p1)*tangent;
+               diff = tangent.perp();
        }
        else if (edge_case)
        {
                diff=(p1-(point_-origin));
                if(diff*tangent.perp()<0) diff=-diff;
-               diff=diff.norm()*thickness*width;
+               diff=diff.norm();
        }
        else
-               diff=tangent.perp()*thickness*width;
-
-               const Real mag(diff.inv_mag());
-               supersample=supersample*mag;
-               diff*=mag*mag;
-               dist=(point_-origin - p1)*diff;
-
-       len /= curve_length_;
-
-       // printf("len %6.2f dist %6.2f\n", len, dist);
-       return ((start_point + (end_point - start_point) * len) +
-                       perp_ * dist);
-}
-
-float
-CurveWarp::calc_supersample(const synfig::Point &/*x*/, float pw,float /*ph*/)const
-{
-       return pw;
+               diff=tangent.perp();
+
+       // diff is a unit vector perpendicular to the bline
+       const Real unscaled_distance((point_-origin - p1)*diff);
+       if (dist) *dist = unscaled_distance;
+       if (along) *along = len;
+       return ((start_point + (end_point - start_point) * len / curve_length_) +
+                       perp_ * unscaled_distance/(thickness*perp_width));
 }
 
 synfig::Layer::Handle
@@ -374,7 +346,7 @@ CurveWarp::set_param(const String & param, const ValueBase &value)
        IMPORT(start_point);
        IMPORT(end_point);
        IMPORT(fast);
-       IMPORT(width);
+       IMPORT(perp_width);
 
        if(param=="bline" && value.get_type()==ValueBase::TYPE_LIST)
        {
@@ -397,7 +369,7 @@ CurveWarp::get_param(const String & param)const
        EXPORT(end_point);
        EXPORT(bline);
        EXPORT(fast);
-       EXPORT(width);
+       EXPORT(perp_width);
 
        EXPORT_NAME();
        EXPORT_VERSION();
@@ -413,11 +385,13 @@ CurveWarp::get_param_vocab()const
        ret.push_back(ParamDesc("origin")
                                  .set_local_name(_("Origin")));
 
-       ret.push_back(ParamDesc("width")
-                                 .set_local_name(_("Width")));
+       ret.push_back(ParamDesc("perp_width")
+                                 .set_local_name(_("Width"))
+                                 .set_origin("start_point"));
 
        ret.push_back(ParamDesc("start_point")
-                                 .set_local_name(_("Start Point")));
+                                 .set_local_name(_("Start Point"))
+                                 .set_connect("end_point"));
 
        ret.push_back(ParamDesc("end_point")
                                  .set_local_name(_("End Point")));
@@ -443,25 +417,171 @@ CurveWarp::get_color(Context context, const Point &point)const
 bool
 CurveWarp::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
 {
-       SuperCallback supercb(cb,0,9500,10000);
-
-       if(!context.accelerated_render(surface,quality,renddesc,&supercb))
-               return false;
+       SuperCallback stageone(cb,0,9000,10000);
+       SuperCallback stagetwo(cb,9000,10000,10000);
 
        int x,y;
 
        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());
+       Point br(renddesc.get_br());
+       const int w(renddesc.get_w());
+       const int h(renddesc.get_h());
+
+       // find a bounding rectangle for the context we need to render
+       // todo: find a better way of doing this - this way doesn't work
+       Rect src_rect(transform(tl));
+       Point pos1, pos2;
+       Real dist, along;
+       Real min_dist(999999), max_dist(-999999), min_along(999999), max_along(-999999);
+
+#define UPDATE_DIST \
+       if (dist < min_dist) min_dist = dist; \
+       if (dist > max_dist) max_dist = dist; \
+       if (along < min_along) min_along = along; \
+       if (along > max_along) max_along = along
+
+       // look along the top and bottom edges
+       pos1[0] = pos2[0] = tl[0]; pos1[1] = tl[1]; pos2[1] = br[1];
+       for (x = 0; x < w; x++, pos1[0] += pw, pos2[0] += pw)
+       {
+               src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST;
+               src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST;
+       }
 
-       for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
-               for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
-                       if (1)
-                               (*surface)[y][x]=context.get_color(transform(pos));
-                       else
-                               (*surface)[y][x]=context.get_color(transform(pos));
+       // look along the left and right edges
+       pos1[0] = tl[0]; pos2[0] = br[0]; pos1[1] = pos2[1] = tl[1];
+       for (y = 0; y < h; y++, pos1[1] += ph, pos2[1] += ph)
+       {
+               src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST;
+               src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST;
+       }
+
+       // look along the diagonals
+       const int max_wh(std::max(w,h));
+       const Real inc_x((br[0]-tl[0])/max_wh),inc_y((br[1]-tl[1])/max_wh);
+       pos1[0] = pos2[0] = tl[0]; pos1[1] = tl[1]; pos2[1] = br[1];
+       for (x = 0; x < max_wh; x++, pos1[0] += inc_x, pos2[0] = pos1[0], pos1[1]+=inc_y, pos2[1]-=inc_y)
+       {
+               src_rect.expand(transform(pos1, &dist, &along)); UPDATE_DIST;
+               src_rect.expand(transform(pos2, &dist, &along)); UPDATE_DIST;
+       }
+
+#if 0
+       // look at each blinepoint
+       std::vector<synfig::BLinePoint>::const_iterator iter;
+       for (iter=bline.begin(); iter!=bline.end(); iter++)
+               src_rect.expand(transform(iter->get_vertex()+origin, &dist, &along)); UPDATE_DIST;
+#endif
+
+       Point src_tl(src_rect.get_min());
+       Point src_br(src_rect.get_max());
+
+       Vector ab((end_point - start_point).norm());
+       Angle::tan ab_angle(ab[1], ab[0]);
+
+       Real used_length = max_along - min_along;
+       Real render_width = max_dist - min_dist;
+
+       int src_w = (abs(used_length*Angle::cos(ab_angle).get()) +
+                                abs(render_width*Angle::sin(ab_angle).get())) / abs(pw);
+       int src_h = (abs(used_length*Angle::sin(ab_angle).get()) +
+                                abs(render_width*Angle::cos(ab_angle).get())) / abs(ph);
+
+       Real src_pw((src_br[0] - src_tl[0]) / src_w);
+       Real src_ph((src_br[1] - src_tl[1]) / src_h);
+
+       if (src_pw > abs(pw))
+       {
+               src_w = int((src_br[0] - src_tl[0]) / abs(pw));
+               src_pw = (src_br[0] - src_tl[0]) / src_w;
+       }
+
+       if (src_ph > abs(ph))
+       {
+               src_h = int((src_br[1] - src_tl[1]) / abs(ph));
+               src_ph = (src_br[1] - src_tl[1]) / src_h;
+       }
+
+#define MAXPIX 10000
+       if (src_w > MAXPIX) src_w = MAXPIX;
+       if (src_h > MAXPIX) src_h = MAXPIX;
+
+       // this is an attempt to remove artifacts around tile edges - the
+       // cubic interpolation uses at most 2 pixels either side of the
+       // target pixel, so add an extra 2 pixels around the tile on all
+       // sides
+       src_tl -= (Point(src_pw,src_ph)*2);
+       src_br += (Point(src_pw,src_ph)*2);
+       src_w += 4;
+       src_h += 4;
+       src_pw = (src_br[0] - src_tl[0]) / src_w;
+       src_ph = (src_br[1] - src_tl[1]) / src_h;
+
+       // set up a renddesc for the context to render
+       RendDesc src_desc(renddesc);
+       src_desc.clear_flags();
+       src_desc.set_tl(src_tl);
+       src_desc.set_br(src_br);
+       src_desc.set_wh(src_w, src_h);
+
+       // render the context onto a new surface
+       Surface source;
+       source.set_wh(src_w,src_h);
+       if(!context.accelerated_render(&source,quality,src_desc,&stageone))
+               return false;
+
+       float u,v;
+       Point pos, tmp;
+
+       surface->set_wh(w,h);
+       surface->clear();
+
+       if(quality<=4)                          // CUBIC
+               for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
+               {
+                       for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
+                       {
+                               tmp=transform(pos);
+                               u=(tmp[0]-src_tl[0])/src_pw;
+                               v=(tmp[1]-src_tl[1])/src_ph;
+                               if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v))
+                                       (*surface)[y][x]=context.get_color(tmp);
+                               else
+                                       (*surface)[y][x]=source.cubic_sample(u,v);
+                       }
+                       if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false;
+               }
+       else if (quality<=6)            // INTERPOLATION_LINEAR
+               for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
+               {
+                       for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
+                       {
+                               tmp=transform(pos);
+                               u=(tmp[0]-src_tl[0])/src_pw;
+                               v=(tmp[1]-src_tl[1])/src_ph;
+                               if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v))
+                                       (*surface)[y][x]=context.get_color(tmp);
+                               else
+                                       (*surface)[y][x]=source.linear_sample(u,v);
+                       }
+                       if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false;
+               }
+       else                                            // NEAREST_NEIGHBOR
+               for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
+               {
+                       for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
+                       {
+                               tmp=transform(pos);
+                               u=(tmp[0]-src_tl[0])/src_pw;
+                               v=(tmp[1]-src_tl[1])/src_ph;
+                               if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v))
+                                       (*surface)[y][x]=context.get_color(tmp);
+                               else
+                                       (*surface)[y][x]=source[floor_to_int(v)][floor_to_int(u)];
+                       }
+                       if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false;
+               }
 
        // Mark our progress as finished
        if(cb && !cb->amount_complete(10000,10000))