1 /* === S Y N F I G ========================================================= */
2 /*! \file curvewarp.cpp
3 ** \brief Implementation of the "Curve Warp" layer
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007-2008 Chris Moore
11 ** This package is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU General Public License as
13 ** published by the Free Software Foundation; either version 2 of
14 ** the License, or (at your option) any later version.
16 ** This package is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** General Public License for more details.
22 ** === N O T E S ===========================================================
24 ** ========================================================================= */
26 /* === H E A D E R S ======================================================= */
35 #include "curvewarp.h"
37 #include <synfig/context.h>
38 #include <synfig/paramdesc.h>
39 #include <synfig/surface.h>
40 #include <synfig/valuenode.h>
41 #include <ETL/calculus>
45 /* === M A C R O S ========================================================= */
47 #define FAKE_TANGENT_STEP 0.000001
49 /* === G L O B A L S ======================================================= */
51 SYNFIG_LAYER_INIT(CurveWarp);
52 SYNFIG_LAYER_SET_NAME(CurveWarp,"curve_warp");
53 SYNFIG_LAYER_SET_LOCAL_NAME(CurveWarp,N_("Curve Warp"));
54 SYNFIG_LAYER_SET_CATEGORY(CurveWarp,N_("Distortions"));
55 SYNFIG_LAYER_SET_VERSION(CurveWarp,"0.0");
56 SYNFIG_LAYER_SET_CVS_ID(CurveWarp,"$Id$");
58 /* === P R O C E D U R E S ================================================= */
60 inline float calculate_distance(const std::vector<synfig::BLinePoint>& bline)
62 std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
63 std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
67 if (bline.empty()) return dist;
72 for(;next!=end;iter=next++)
75 etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
82 std::vector<synfig::BLinePoint>::const_iterator
83 find_closest_to_bline(bool fast, const std::vector<synfig::BLinePoint>& bline,const Point& p,float& t, float& len, bool& extreme)
85 std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
86 std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
89 float dist(100000000000.0);
91 float best_pos(0), best_len(0);
92 etl::hermite<Vector> best_curve;
96 bool first = true, last = false;
99 for(;next!=end;iter=next++)
102 etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
108 #define POINT_CHECK(x) bp=curve(x); thisdist=(bp-p).mag_squared(); if(thisdist<dist) { extreme = (first&&x<0.01); ret=iter; best_len = total_len; dist=thisdist; best_curve=curve; last=true; best_pos=x;}
109 POINT_CHECK(0.0001); POINT_CHECK((1.0/6)); POINT_CHECK((2.0/6)); POINT_CHECK((3.0/6));
110 POINT_CHECK((4.0/6)); POINT_CHECK((5.0/6)); POINT_CHECK(0.9999);
114 float pos = curve.find_closest(fast, p);
115 thisdist=(curve(pos)-p).mag_squared();
118 extreme = (first && pos == 0);
123 best_len = total_len;
127 total_len += curve.length();
134 len = best_len + best_curve.find_distance(0,best_curve.find_closest(fast, p));
135 if (last && t > .99) extreme = true;
139 len = best_len + best_curve.find_distance(0,best_pos);
140 if (last && t == 1) extreme = true;
145 /* === M E T H O D S ======================================================= */
150 curve_length_=calculate_distance(bline);
151 perp_ = (end_point - start_point).perp().norm();
154 CurveWarp::CurveWarp():
157 start_point(-2.5,-0.5),
161 bline.push_back(BLinePoint());
162 bline.push_back(BLinePoint());
163 bline[0].set_vertex(Point(-2.5,0));
164 bline[1].set_vertex(Point( 2.5,0));
165 bline[0].set_tangent(Point(1, 0.1));
166 bline[1].set_tangent(Point(1, -0.1));
167 bline[0].set_width(1.0f);
168 bline[1].set_width(1.0f);
174 CurveWarp::transform(const Point &point_, int quality, float supersample)const
181 bool edge_case = false;
188 else if(bline.size()==1)
190 tangent=bline.front().get_tangent1();
191 p1=bline.front().get_vertex();
192 thickness=bline.front().get_width();
198 Point point(point_-origin);
200 std::vector<synfig::BLinePoint>::const_iterator iter,next;
202 // Figure out the BLinePoint we will be using,
203 next=find_closest_to_bline(fast,bline,point,t,len,extreme);
206 if(next==bline.end()) next=bline.begin();
209 etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
211 // Setup the derivative function
212 etl::derivative<etl::hermite<Vector> > deriv(curve);
214 int search_iterations(7);
216 if(quality<=6)search_iterations=7;
217 else if(quality<=7)search_iterations=6;
218 else if(quality<=8)search_iterations=5;
219 else search_iterations=4;
221 // Figure out the closest point on the curve
222 if (fast) t = curve.find_closest(fast, point,search_iterations);
224 // Calculate our values
225 p1=curve(t); // the closest point on the curve
226 tangent=deriv(t); // the tangent at that point
228 // if the point we're nearest to is at either end of the
229 // bline, our distance from the curve is the distance from the
230 // point on the curve. we need to know which side of the
231 // curve we're on, so find the average of the two tangents at
233 if (t<0.00001 || t>0.99999)
235 bool zero_tangent = (tangent[0] == 0 && tangent[1] == 0);
239 if (iter->get_split_tangent_flag() || zero_tangent)
241 // fake the current tangent if we need to
242 if (zero_tangent) tangent = curve(FAKE_TANGENT_STEP) - curve(0);
244 // calculate the other tangent
245 Vector other_tangent(iter->get_tangent1());
246 if (other_tangent[0] == 0 && other_tangent[1] == 0)
248 // find the previous blinepoint
249 std::vector<synfig::BLinePoint>::const_iterator prev;
250 if (iter != bline.begin()) (prev = iter)--;
253 etl::hermite<Vector> other_curve(prev->get_vertex(), iter->get_vertex(), prev->get_tangent2(), iter->get_tangent1());
254 other_tangent = other_curve(1) - other_curve(1-FAKE_TANGENT_STEP);
257 // normalise and sum the two tangents
258 tangent=(other_tangent.norm()+tangent.norm());
264 if (next->get_split_tangent_flag() || zero_tangent)
266 // fake the current tangent if we need to
267 if (zero_tangent) tangent = curve(1) - curve(1-FAKE_TANGENT_STEP);
269 // calculate the other tangent
270 Vector other_tangent(next->get_tangent2());
271 if (other_tangent[0] == 0 && other_tangent[1] == 0)
273 // find the next blinepoint
274 std::vector<synfig::BLinePoint>::const_iterator next2(next);
275 if (++next2 == bline.end())
278 etl::hermite<Vector> other_curve(next->get_vertex(), next2->get_vertex(), next->get_tangent2(), next2->get_tangent1());
279 other_tangent = other_curve(FAKE_TANGENT_STEP) - other_curve(0);
282 // normalise and sum the two tangents
283 tangent=(other_tangent.norm()+tangent.norm());
288 tangent = tangent.norm();
290 // the width of the bline at the closest point on the curve
291 thickness=(next->get_width()-iter->get_width())*t+iter->get_width();
300 std::vector<synfig::BLinePoint>::const_iterator iter(bline.begin());
301 tangent = iter->get_tangent1().norm();
306 std::vector<synfig::BLinePoint>::const_iterator iter(--bline.end());
307 tangent = iter->get_tangent2().norm();
310 diff = tangent.perp()*thickness*perp_width;
311 len += (point_-origin - p1)*tangent;
315 diff=(p1-(point_-origin));
316 if(diff*tangent.perp()<0) diff=-diff;
317 diff=diff.norm()*thickness*perp_width;
320 diff=tangent.perp()*thickness*perp_width;
322 const Real mag(diff.inv_mag());
323 supersample=supersample*mag;
325 dist=(point_-origin - p1)*diff;
326 len /= curve_length_;
327 return (start_point + (end_point - start_point) * len) + perp_ * dist;
331 CurveWarp::calc_supersample(const synfig::Point &/*x*/, float pw,float /*ph*/)const
336 synfig::Layer::Handle
337 CurveWarp::hit_check(synfig::Context context, const synfig::Point &point)const
339 return context.hit_check(transform(point));
343 CurveWarp::set_param(const String & param, const ValueBase &value)
351 if(param=="bline" && value.get_type()==ValueBase::TYPE_LIST)
359 IMPORT_AS(origin,"offset");
365 CurveWarp::get_param(const String & param)const
381 CurveWarp::get_param_vocab()const
385 ret.push_back(ParamDesc("origin")
386 .set_local_name(_("Origin")));
388 ret.push_back(ParamDesc("perp_width")
389 .set_local_name(_("Width"))
390 .set_origin("start_point"));
392 ret.push_back(ParamDesc("start_point")
393 .set_local_name(_("Start Point"))
394 .set_connect("end_point"));
396 ret.push_back(ParamDesc("end_point")
397 .set_local_name(_("End Point")));
399 ret.push_back(ParamDesc("bline")
400 .set_local_name(_("Vertices"))
401 .set_origin("origin")
403 .set_description(_("A list of BLine Points")));
405 ret.push_back(ParamDesc("fast")
406 .set_local_name(_("Fast")));
412 CurveWarp::get_color(Context context, const Point &point)const
414 return context.get_color(transform(point));
418 CurveWarp::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
420 SuperCallback stageone(cb,0,9000,10000);
421 SuperCallback stagetwo(cb,9000,10000,10000);
425 const Real pw(renddesc.get_pw()),ph(renddesc.get_ph());
426 Point tl(renddesc.get_tl());
427 Point br(renddesc.get_br());
428 const int w(renddesc.get_w());
429 const int h(renddesc.get_h());
431 // find a bounding rectangle for the context we need to render
432 // todo: find a better way of doing this - this way doesn't work
433 Rect src_rect(transform(tl));
436 // look along the top and bottom edges
437 pos1[0] = pos2[0] = tl[0]; pos1[1] = tl[1]; pos2[1] = br[1];
438 for (x = 0; x < w; x++, pos1[0] += pw, pos2[0] += pw)
440 src_rect.expand(transform(pos1));
441 src_rect.expand(transform(pos2));
444 // look along the left and right edges
445 pos1[0] = tl[0]; pos2[0] = br[0]; pos1[1] = pos2[1] = tl[1];
446 for (y = 0; y < h; y++, pos1[1] += ph, pos2[1] += ph)
448 src_rect.expand(transform(pos1));
449 src_rect.expand(transform(pos2));
452 // look at each blinepoint
453 std::vector<synfig::BLinePoint>::const_iterator iter;
454 for (iter=bline.begin(); iter!=bline.end(); iter++)
455 src_rect.expand(transform(iter->get_vertex()+origin));
457 Point src_tl(src_rect.get_min());
458 Point src_br(src_rect.get_max());
459 int src_w = w; // todo: what should we use for src_w
460 int src_h = h; // and src_h?
461 Real src_pw((src_br[0] - src_tl[0]) / src_w);
462 Real src_ph((src_br[1] - src_tl[1]) / src_h);
464 // this is an attempt to remove artifacts around tile edges - the
465 // cubic interpolation uses at most 2 pixels either side of the
466 // target pixel, so add an extra 2 pixels around the tile on all
468 src_tl -= (Point(src_pw,src_ph)*2);
469 src_br += (Point(src_pw,src_ph)*2);
472 src_pw = (src_br[0] - src_tl[0]) / src_w;
473 src_ph = (src_br[1] - src_tl[1]) / src_h;
475 // set up a renddesc for the context to render
476 RendDesc src_desc(renddesc);
477 src_desc.clear_flags();
478 src_desc.set_tl(src_tl);
479 src_desc.set_br(src_br);
480 src_desc.set_wh(src_w, src_h);
482 // render the context onto a new surface
484 source.set_wh(src_w,src_h);
485 if(!context.accelerated_render(&source,quality,src_desc,&stageone))
491 surface->set_wh(w,h);
494 if(quality<=4) // CUBIC
495 for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
497 for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
500 u=(tmp[0]-src_tl[0])/src_pw;
501 v=(tmp[1]-src_tl[1])/src_ph;
502 if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v))
503 (*surface)[y][x]=context.get_color(tmp);
505 (*surface)[y][x]=source.cubic_sample(u,v);
507 if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false;
509 else if (quality<=6) // INTERPOLATION_LINEAR
510 for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
512 for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
515 u=(tmp[0]-src_tl[0])/src_pw;
516 v=(tmp[1]-src_tl[1])/src_ph;
517 if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v))
518 (*surface)[y][x]=context.get_color(tmp);
520 (*surface)[y][x]=source.linear_sample(u,v);
522 if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false;
524 else // NEAREST_NEIGHBOR
525 for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
527 for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
530 u=(tmp[0]-src_tl[0])/src_pw;
531 v=(tmp[1]-src_tl[1])/src_ph;
532 if(u<0 || v<0 || u>=src_w || v>=src_h || isnan(u) || isnan(v))
533 (*surface)[y][x]=context.get_color(tmp);
535 (*surface)[y][x]=source[floor_to_int(v)][floor_to_int(u)];
537 if((y&31)==0 && cb && !stagetwo.amount_complete(y,h)) return false;
540 // Mark our progress as finished
541 if(cb && !cb->amount_complete(10000,10000))