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/string.h>
38 #include <synfig/time.h>
39 #include <synfig/context.h>
40 #include <synfig/paramdesc.h>
41 #include <synfig/renddesc.h>
42 #include <synfig/surface.h>
43 #include <synfig/value.h>
44 #include <synfig/valuenode.h>
46 #include <ETL/hermite>
47 #include <ETL/calculus>
51 /* === M A C R O S ========================================================= */
53 #define FAKE_TANGENT_STEP 0.000001
55 /* === G L O B A L S ======================================================= */
57 SYNFIG_LAYER_INIT(CurveWarp);
58 SYNFIG_LAYER_SET_NAME(CurveWarp,"curve_warp");
59 SYNFIG_LAYER_SET_LOCAL_NAME(CurveWarp,N_("Curve Warp"));
60 SYNFIG_LAYER_SET_CATEGORY(CurveWarp,N_("Distortions"));
61 SYNFIG_LAYER_SET_VERSION(CurveWarp,"0.0");
62 SYNFIG_LAYER_SET_CVS_ID(CurveWarp,"$Id$");
64 /* === P R O C E D U R E S ================================================= */
66 inline float calculate_distance(const std::vector<synfig::BLinePoint>& bline)
68 std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
69 std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
73 if (bline.empty()) return dist;
78 for(;next!=end;iter=next++)
81 etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
88 std::vector<synfig::BLinePoint>::const_iterator
89 find_closest_to_bline(bool fast, const std::vector<synfig::BLinePoint>& bline,const Point& p,float& t, float& len, bool& extreme)
91 std::vector<synfig::BLinePoint>::const_iterator iter,next,ret;
92 std::vector<synfig::BLinePoint>::const_iterator end(bline.end());
95 float dist(100000000000.0);
97 float best_pos(0), best_len(0);
98 etl::hermite<Vector> best_curve;
102 bool first = true, last = false;
105 // printf(" loop\n");
106 for(;next!=end;iter=next++)
110 etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
116 #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;}
117 POINT_CHECK(0.0001); POINT_CHECK((1.0/6)); POINT_CHECK((2.0/6)); POINT_CHECK((3.0/6));
118 POINT_CHECK((4.0/6)); POINT_CHECK((5.0/6)); POINT_CHECK(0.9999);
122 float pos = curve.find_closest(fast, p);
123 thisdist=(curve(pos)-p).mag_squared();
126 extreme = (first && pos == 0);
131 // printf("set best_curve\n");
132 best_len = total_len;
134 // printf(" *last\n");
137 total_len += curve.length();
140 // printf(" after\n");
145 len = best_len + best_curve.find_distance(0,best_curve.find_closest(fast, p));
146 // printf("fast set len = %f + %f = %f\n", best_len, best_curve.find_distance(0,best_curve.find_closest(fast, p)), len);
147 if (last && t > .99) extreme = true;
151 len = best_len + best_curve.find_distance(0,best_pos);
152 // printf("slow set len = %f + %f = %f\n", best_len, best_curve.find_distance(0,best_pos), len);
153 // printf("find_distance from 0 to %f is %f\n", best_pos, best_curve.find_distance(0,best_pos));
154 // if (last) printf("last\n");
156 if (last && t > .999)
159 // printf("extreme end\n");
165 /* === M E T H O D S ======================================================= */
170 curve_length_=calculate_distance(bline);
171 perp_ = (end_point - start_point).perp().norm();
172 // printf("curve_length_ = %f\n", curve_length_);
175 CurveWarp::CurveWarp():
182 bline.push_back(BLinePoint());
183 bline.push_back(BLinePoint());
184 bline[0].set_vertex(Point(-2.5,0));
185 bline[1].set_vertex(Point(2.5,0));
186 bline[0].set_tangent(Point(1, 1));
187 bline[1].set_tangent(Point(1, -1));
188 bline[0].set_width(1.0f);
189 bline[1].set_width(1.0f);
195 CurveWarp::transform(const Point &point_, int quality, float supersample)const
202 bool edge_case = false;
209 else if(bline.size()==1)
211 tangent=bline.front().get_tangent1();
212 p1=bline.front().get_vertex();
213 thickness=bline.front().get_width();
219 Point point(point_-origin);
221 std::vector<synfig::BLinePoint>::const_iterator iter,next;
223 // Figure out the BLinePoint we will be using,
224 next=find_closest_to_bline(fast,bline,point,t,len,extreme);
227 if(next==bline.end()) next=bline.begin();
230 etl::hermite<Vector> curve(iter->get_vertex(), next->get_vertex(), iter->get_tangent2(), next->get_tangent1());
232 // Setup the derivative function
233 etl::derivative<etl::hermite<Vector> > deriv(curve);
235 int search_iterations(7);
237 if(quality<=6)search_iterations=7;
238 else if(quality<=7)search_iterations=6;
239 else if(quality<=8)search_iterations=5;
240 else search_iterations=4;
242 // Figure out the closest point on the curve
243 if (fast) t = curve.find_closest(fast, point,search_iterations);
245 // Calculate our values
246 p1=curve(t); // the closest point on the curve
247 tangent=deriv(t); // the tangent at that point
249 // if the point we're nearest to is at either end of the
250 // bline, our distance from the curve is the distance from the
251 // point on the curve. we need to know which side of the
252 // curve we're on, so find the average of the two tangents at
254 if (t<0.00001 || t>0.99999)
256 bool zero_tangent = (tangent[0] == 0 && tangent[1] == 0);
260 if (iter->get_split_tangent_flag() || zero_tangent)
262 // fake the current tangent if we need to
263 if (zero_tangent) tangent = curve(FAKE_TANGENT_STEP) - curve(0);
265 // calculate the other tangent
266 Vector other_tangent(iter->get_tangent1());
267 if (other_tangent[0] == 0 && other_tangent[1] == 0)
269 // find the previous blinepoint
270 std::vector<synfig::BLinePoint>::const_iterator prev;
271 if (iter != bline.begin()) (prev = iter)--;
274 etl::hermite<Vector> other_curve(prev->get_vertex(), iter->get_vertex(), prev->get_tangent2(), iter->get_tangent1());
275 other_tangent = other_curve(1) - other_curve(1-FAKE_TANGENT_STEP);
278 // normalise and sum the two tangents
279 tangent=(other_tangent.norm()+tangent.norm());
285 if (next->get_split_tangent_flag() || zero_tangent)
287 // fake the current tangent if we need to
288 if (zero_tangent) tangent = curve(1) - curve(1-FAKE_TANGENT_STEP);
290 // calculate the other tangent
291 Vector other_tangent(next->get_tangent2());
292 if (other_tangent[0] == 0 && other_tangent[1] == 0)
294 // find the next blinepoint
295 std::vector<synfig::BLinePoint>::const_iterator next2(next);
296 if (++next2 == bline.end())
299 etl::hermite<Vector> other_curve(next->get_vertex(), next2->get_vertex(), next->get_tangent2(), next2->get_tangent1());
300 other_tangent = other_curve(FAKE_TANGENT_STEP) - other_curve(0);
303 // normalise and sum the two tangents
304 tangent=(other_tangent.norm()+tangent.norm());
309 tangent = tangent.norm();
311 // the width of the bline at the closest point on the curve
312 thickness=(next->get_width()-iter->get_width())*t+iter->get_width();
317 Vector tangent, perp;
321 synfig::BLinePoint start(bline[0]);
322 // Point a(start.get_vertex());
323 tangent = start.get_tangent1().norm();
324 diff = tangent.perp()*thickness*width;
325 len = (point_-origin - p1)*tangent;
329 std::vector<synfig::BLinePoint>::const_iterator iter;
332 tangent = iter->get_tangent2().norm();
333 diff = tangent.perp()*thickness*width;
334 len = (point_-origin - p1)*tangent + curve_length_;
339 diff=(p1-(point_-origin));
340 if(diff*tangent.perp()<0) diff=-diff;
341 diff=diff.norm()*thickness*width;
344 diff=tangent.perp()*thickness*width;
346 const Real mag(diff.inv_mag());
347 supersample=supersample*mag;
349 dist=(point_-origin - p1)*diff;
351 len /= curve_length_;
353 // printf("len %6.2f dist %6.2f\n", len, dist);
354 return ((start_point + (end_point - start_point) * len) +
359 CurveWarp::calc_supersample(const synfig::Point &/*x*/, float pw,float /*ph*/)const
364 synfig::Layer::Handle
365 CurveWarp::hit_check(synfig::Context context, const synfig::Point &point)const
367 return context.hit_check(transform(point));
371 CurveWarp::set_param(const String & param, const ValueBase &value)
379 if(param=="bline" && value.get_type()==ValueBase::TYPE_LIST)
387 IMPORT_AS(origin,"offset");
393 CurveWarp::get_param(const String & param)const
409 CurveWarp::get_param_vocab()const
413 ret.push_back(ParamDesc("origin")
414 .set_local_name(_("Origin")));
416 ret.push_back(ParamDesc("width")
417 .set_local_name(_("Width")));
419 ret.push_back(ParamDesc("start_point")
420 .set_local_name(_("Start Point")));
422 ret.push_back(ParamDesc("end_point")
423 .set_local_name(_("End Point")));
425 ret.push_back(ParamDesc("bline")
426 .set_local_name(_("Vertices"))
427 .set_origin("origin")
429 .set_description(_("A list of BLine Points")));
431 ret.push_back(ParamDesc("fast")
432 .set_local_name(_("Fast")));
438 CurveWarp::get_color(Context context, const Point &point)const
440 return context.get_color(transform(point));
444 CurveWarp::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
446 SuperCallback supercb(cb,0,9500,10000);
448 if(!context.accelerated_render(surface,quality,renddesc,&supercb))
453 const Real pw(renddesc.get_pw()),ph(renddesc.get_ph());
455 Point tl(renddesc.get_tl());
456 const int w(surface->get_w());
457 const int h(surface->get_h());
459 for(y=0,pos[1]=tl[1];y<h;y++,pos[1]+=ph)
460 for(x=0,pos[0]=tl[0];x<w;x++,pos[0]+=pw)
462 (*surface)[y][x]=context.get_color(transform(pos));
464 (*surface)[y][x]=context.get_color(transform(pos));
466 // Mark our progress as finished
467 if(cb && !cb->amount_complete(10000,10000))