1 /* === S Y N F I G ========================================================= */
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
10 ** This package is free software; you can redistribute it and/or
11 ** modify it under the terms of the GNU General Public License as
12 ** published by the Free Software Foundation; either version 2 of
13 ** the License, or (at your option) any later version.
15 ** This package is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ** General Public License for more details.
21 /* ========================================================================= */
23 //! \note This whole file should be rewritten at some point (darco)
25 /* === H E A D E R S ======================================================= */
35 #include <synfig/string.h>
36 #include <synfig/time.h>
37 #include <synfig/context.h>
38 #include <synfig/paramdesc.h>
39 #include <synfig/renddesc.h>
40 #include <synfig/surface.h>
41 #include <synfig/value.h>
42 #include <synfig/valuenode.h>
44 #include <ETL/calculus>
46 #include <ETL/hermite>
49 #include <synfig/valuenode_bline.h>
55 /* === M A C R O S ========================================================= */
58 #define ROUND_END_FACTOR (4)
59 #define CUSP_THRESHOLD (0.40)
60 #define SPIKE_AMOUNT (4)
61 #define NO_LOOP_COOKIE synfig::Vector(84951305,7836658)
62 #define EPSILON (0.000000001)
63 #define CUSP_TANGENT_ADJUST (0.025)
65 /* === G L O B A L S ======================================================= */
67 SYNFIG_LAYER_INIT(Outline);
68 SYNFIG_LAYER_SET_NAME(Outline,"outline");
69 SYNFIG_LAYER_SET_LOCAL_NAME(Outline,_("Outline"));
70 SYNFIG_LAYER_SET_CATEGORY(Outline,_("Geometry"));
71 SYNFIG_LAYER_SET_VERSION(Outline,"0.2");
72 SYNFIG_LAYER_SET_CVS_ID(Outline,"$Id$");
74 /* === P R O C E D U R E S ================================================= */
76 // This function was adapted from what was
77 // described on http://www.whisqu.se/per/docs/math28.htm
78 Point line_intersection(
85 const float& x0(p1[0]);
86 const float& y0(p1[1]);
88 const float x1(p1[0]+t1[0]);
89 const float y1(p1[1]+t1[1]);
91 const float& x2(p2[0]);
92 const float& y2(p2[1]);
94 const float x3(p2[0]+t2[0]);
95 const float y3(p2[1]+t2[1]);
97 const float near_infinity((float)1e+10);
99 float m1,m2; // the slopes of each line
101 // compute slopes, note the cludge for infinity, however, this will
105 m1 = (y1-y0)/(x1-x0);
110 m2 = (y3-y2)/(x3-x2);
117 const float b1(-1.0f);
118 const float b2(-1.0f);
119 const float c1(y0-m1*x0);
120 const float c2(y2-m2*x2);
122 // compute the inverse of the determinate
123 const float det_inv(1.0f/(a1*b2 - a2*b1));
125 // use Kramers rule to compute the intersection
127 ((b1*c2 - b2*c1)*det_inv),
128 ((a2*c1 - a1*c2)*det_inv)
130 } // end Intersect_Lines
132 /* === M E T H O D S ======================================================= */
144 homogeneous_width=true;
147 vector<BLinePoint> bline_point_list;
148 bline_point_list.push_back(BLinePoint());
149 bline_point_list.push_back(BLinePoint());
150 bline_point_list.push_back(BLinePoint());
151 bline_point_list[0].set_vertex(Point(0,1));
152 bline_point_list[1].set_vertex(Point(0,-1));
153 bline_point_list[2].set_vertex(Point(1,0));
154 bline_point_list[0].set_tangent(bline_point_list[1].get_vertex()-bline_point_list[2].get_vertex()*0.5f);
155 bline_point_list[1].set_tangent(bline_point_list[2].get_vertex()-bline_point_list[0].get_vertex()*0.5f);
156 bline_point_list[2].set_tangent(bline_point_list[0].get_vertex()-bline_point_list[1].get_vertex()*0.5f);
157 bline_point_list[0].set_width(1.0f);
158 bline_point_list[1].set_width(1.0f);
159 bline_point_list[2].set_width(1.0f);
160 bline=bline_point_list;
166 /*! The Sync() function takes the values
167 ** and creates a polygon to be rendered
168 ** with the polygon layer.
175 if (!bline.get_list().size())
177 synfig::warning(string("Outline::sync():")+_("No verticies in outline " + string("\"") + get_description() + string("\"")));
184 const bool loop(bline.get_loop());
185 const vector<synfig::BLinePoint> bline_(bline.get_list().begin(),bline.get_list().end());
188 vector<BLinePoint>::const_iterator
192 const vector<BLinePoint>::const_iterator
209 Vector first_tangent=bline.front().get_tangent2();
210 Vector last_tangent=iter->get_tangent1();
212 // if we are looped and drawing sharp cusps, we'll need a value for the incoming tangent
213 if (loop && sharp_cusps && last_tangent.is_equal_to(Vector::zero()))
215 hermite<Vector> curve((iter-1)->get_vertex(), iter->get_vertex(), (iter-1)->get_tangent2(), iter->get_tangent1());
216 const derivative< hermite<Vector> > deriv(curve);
217 last_tangent=deriv(1.0-CUSP_TANGENT_ADJUST);
220 // `first' is for making the cusps; don't do that for the first point if we're not looped
221 for(bool first=!loop; next!=end; iter=next++)
223 Vector prev_t(iter->get_tangent1());
224 Vector iter_t(iter->get_tangent2());
225 Vector next_t(next->get_tangent1());
227 bool split_flag(iter->get_split_tangent_flag());
229 // if iter.t2 == 0 and next.t1 == 0, this is a straight line
230 if(iter_t.is_equal_to(Vector::zero()) && next_t.is_equal_to(Vector::zero()))
232 iter_t=next_t=next->get_vertex()-iter->get_vertex();
235 // if the two points are on top of each other, ignore this segment
236 // leave `first' true if was before
237 if (iter_t.is_equal_to(Vector::zero()))
242 hermite<Vector> curve(
250 iter_w((iter->get_width()*width)*0.5f+expand),
251 next_w((next->get_width()*width)*0.5f+expand);
253 const derivative< hermite<Vector> > deriv(curve);
256 first_tangent = deriv(CUSP_TANGENT_ADJUST);
258 // Make cusps as necessary
259 if(!first && sharp_cusps && split_flag && (!prev_t.is_equal_to(iter_t) || iter_t.is_equal_to(Vector::zero())) && !last_tangent.is_equal_to(Vector::zero()))
261 Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
263 const Vector t1(last_tangent.perp().norm());
264 const Vector t2(curr_tangent.perp().norm());
266 Real cross(t1*t2.perp());
267 Real perp((t1-t2).mag());
268 if(cross>CUSP_THRESHOLD)
270 const Point p1(iter->get_vertex()+t1*iter_w);
271 const Point p2(iter->get_vertex()+t2*iter_w);
273 side_a.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
275 else if(cross<-CUSP_THRESHOLD)
277 const Point p1(iter->get_vertex()-t1*iter_w);
278 const Point p2(iter->get_vertex()-t2*iter_w);
280 side_b.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
282 else if(cross>0 && perp>1)
284 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
286 side_a.push_back(iter->get_vertex()+(t1+t2).norm()*iter_w*amount);
288 else if(cross<0 && perp>1)
290 float amount(max(0.0f,(float)(-cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
292 side_b.push_back(iter->get_vertex()-(t1+t2).norm()*iter_w*amount);
297 if(homogeneous_width)
299 const float length(curve.length());
302 for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
304 const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
305 const Vector p(curve(n));
308 dist+=(p-lastpoint).mag();
310 const float w(((next_w-iter_w)*(dist/length)+iter_w));
312 side_a.push_back(p+d*w);
313 side_b.push_back(p-d*w);
319 for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
321 const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
322 const Vector p(curve(n));
323 const float w(((next_w-iter_w)*n+iter_w));
325 side_a.push_back(p+d*w);
326 side_b.push_back(p-d*w);
328 last_tangent=deriv(1.0-CUSP_TANGENT_ADJUST);
329 side_a.push_back(curve(1.0)+last_tangent.perp().norm()*next_w);
330 side_b.push_back(curve(1.0)-last_tangent.perp().norm()*next_w);
337 reverse(side_b.begin(),side_b.end());
343 // Insert code for adding end tip
344 if(round_tip[1] && !loop && side_a.size())
346 // remove the last point
349 const Point vertex(bline.back().get_vertex());
350 const Vector tangent(last_tangent.norm());
351 const float w((bline.back().get_width()*width)*0.5f+expand);
353 hermite<Vector> curve(
354 vertex+tangent.perp()*w,
355 vertex-tangent.perp()*w,
356 tangent*w*ROUND_END_FACTOR,
357 -tangent*w*ROUND_END_FACTOR
360 for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
361 side_a.push_back(curve(n));
364 for(;!side_b.empty();side_b.pop_back())
365 side_a.push_back(side_b.back());
367 // Insert code for adding begin tip
368 if(round_tip[0] && !loop && side_a.size())
370 // remove the last point
373 const Point vertex(bline.front().get_vertex());
374 const Vector tangent(first_tangent.norm());
375 const float w((bline.front().get_width()*width)*0.5f+expand);
377 hermite<Vector> curve(
378 vertex-tangent.perp()*w,
379 vertex+tangent.perp()*w,
380 -tangent*w*ROUND_END_FACTOR,
381 tangent*w*ROUND_END_FACTOR
384 for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
385 side_a.push_back(curve(n));
394 if(bline.get_contained_type()==ValueBase::TYPE_BLINEPOINT)
396 ValueBase value(bline);
400 value.set_loop(false);
404 loop_=value.get_loop();
406 segment_list=convert_bline_to_segment_list(value);
407 width_list=convert_bline_to_width_list(value);
417 if(segment_list.empty())
419 synfig::warning("Outline: segment_list is empty, layer disabled");
425 // Repair the width list if we need to
428 if(width_list.empty())
431 default_width=width_list.back();
433 while(width_list.size()<segment_list.size()+1)
434 width_list.push_back(default_width);
435 while(width_list.size()>segment_list.size()+1)
436 width_list.pop_back();
440 // Repair the zero tangents (if any)
442 vector<Segment>::iterator iter;
443 for(iter=segment_list.begin();iter!=segment_list.end();++iter)
445 if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
446 iter->t1=iter->t2=iter->p2-iter->p1;
450 vector<Real>::iterator iter;
451 vector<Real> scaled_width_list;
452 for(iter=width_list.begin();iter!=width_list.end();++iter)
454 scaled_width_list.push_back((*iter*width+expand)*0.5f);
457 Vector::value_type n;
458 etl::hermite<Vector> curve;
459 vector<Point> vector_list;
460 Vector last_tangent(segment_list.back().t2);
464 last_tangent=NO_LOOP_COOKIE;
467 vector<Segment>::iterator iter;
468 vector<Real>::iterator witer;
470 iter=segment_list.begin(),
471 witer=scaled_width_list.begin();
472 iter!=segment_list.end();
475 if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
477 vector_list.push_back(iter->p1-(iter->p2-iter->p1).perp().norm()*witer[0]);
478 vector_list.push_back((iter->p2-iter->p1)*0.05+iter->p1-(iter->p2-iter->p1).perp().norm()*((witer[1]-witer[0])*0.05+witer[0]));
479 vector_list.push_back((iter->p2-iter->p1)*0.95+iter->p1-(iter->p2-iter->p1).perp().norm()*((witer[1]-witer[0])*0.95+witer[0]));
480 vector_list.push_back(iter->p2-(iter->p2-iter->p1).perp().norm()*witer[1]);
490 etl::derivative<etl::hermite<Vector> > deriv(curve);
492 // without this if statement, the broken tangents would
494 if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t1))
496 //Vector curr_tangent(iter->t1);
497 Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
499 const Vector t1(last_tangent.perp().norm());
500 const Vector t2(curr_tangent.perp().norm());
502 Point p1(iter->p1+t1*witer[0]);
503 Point p2(iter->p1+t2*witer[0]);
505 Real cross(t1*t2.perp());
507 if(cross>CUSP_THRESHOLD)
508 vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
511 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
512 // Push back something to make it look vaguely round;
513 //vector_list.push_back(iter->p1+(t1*1.25+t2).norm()*witer[0]*amount);
514 vector_list.push_back(iter->p1+(t1+t2).norm()*witer[0]*amount);
515 //vector_list.push_back(iter->p1+(t1+t2*1.25).norm()*witer[0]*amount);
518 //last_tangent=iter->t2;
519 last_tangent=deriv(1.0f-CUSP_TANGENT_ADJUST);
521 for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
522 vector_list.push_back(curve(n)+deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm()*((witer[1]-witer[0])*n+witer[0]) );
523 vector_list.push_back(curve(1.0)+deriv(1.0-CUSP_TANGENT_ADJUST).perp().norm()*witer[1]);
527 if(round_tip[1] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
529 // remove the last point
530 vector_list.pop_back();
534 curve.p1()=iter->p2+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
535 curve.p2()=iter->p2-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
536 curve.t2()=-(curve.t1()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
538 for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
539 vector_list.push_back(curve(n));
541 // remove the last point
542 vector_list.pop_back();
547 last_tangent=NO_LOOP_COOKIE;
550 add_polygon(vector_list);
552 last_tangent=segment_list.front().t1;
556 // last_tangent=segment_list.back().t2;
559 vector<Segment>::reverse_iterator iter;
560 vector<Real>::reverse_iterator witer;
562 iter=segment_list.rbegin(),
563 witer=scaled_width_list.rbegin(),++witer;
564 !(iter==segment_list.rend());
568 if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
570 vector_list.push_back(iter->p2+(iter->p2-iter->p1).perp().norm()*witer[0]);
571 vector_list.push_back((iter->p2-iter->p1)*0.95+iter->p1+(iter->p2-iter->p1).perp().norm()*((witer[-1]-witer[0])*0.95+witer[0]));
572 vector_list.push_back((iter->p2-iter->p1)*0.05+iter->p1+(iter->p2-iter->p1).perp().norm()*((witer[-1]-witer[0])*0.05+witer[0]));
573 vector_list.push_back(iter->p1+(iter->p2-iter->p1).perp().norm()*witer[-1]);
583 etl::derivative<etl::hermite<Vector> > deriv(curve);
585 // without this if statement, the broken tangents would
587 if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t2))
589 //Vector curr_tangent(iter->t2);
590 Vector curr_tangent(deriv(1.0f-CUSP_TANGENT_ADJUST));
592 const Vector t1(last_tangent.perp().norm());
593 const Vector t2(curr_tangent.perp().norm());
595 Point p1(iter->p2-t1*witer[-1]);
596 Point p2(iter->p2-t2*witer[-1]);
598 Real cross(t1*t2.perp());
600 //if(last_tangent.perp().norm()*curr_tangent.norm()<-CUSP_THRESHOLD)
601 if(cross>CUSP_THRESHOLD)
602 vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
605 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
606 // Push back something to make it look vaguely round;
607 //vector_list.push_back(iter->p2-(t1*1.25+t2).norm()*witer[-1]*amount);
608 vector_list.push_back(iter->p2-(t1+t2).norm()*witer[-1]*amount);
609 //vector_list.push_back(iter->p2-(t1+t2*1.25).norm()*witer[-1]*amount);
612 //last_tangent=iter->t1;
613 last_tangent=deriv(CUSP_TANGENT_ADJUST);
615 for(n=1.0f;n>CUSP_TANGENT_ADJUST;n-=1.0f/SAMPLES)
616 vector_list.push_back(curve(n)-deriv(1-n>CUSP_TANGENT_ADJUST?n:1-CUSP_TANGENT_ADJUST).perp().norm()*((witer[-1]-witer[0])*n+witer[0]) );
617 vector_list.push_back(curve(0.0f)-deriv(CUSP_TANGENT_ADJUST).perp().norm()*witer[0]);
620 if(round_tip[0] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
622 // remove the last point
623 vector_list.pop_back();
627 curve.p1()=iter->p1+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
628 curve.p2()=iter->p1-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
629 curve.t1()=-(curve.t2()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
632 for(n=1.0;n>0.0;n-=1.0/SAMPLES)
633 vector_list.push_back(curve(n));
635 // remove the last point
636 vector_list.pop_back();
641 // reverse(vector_list.begin(),vector_list.end());
645 vector<Point>::iterator iter;
646 for(iter=vector_list.begin();iter!=vector_list.end();++iter)
647 if(!iter->is_valid())
649 synfig::error("Outline::sync(): Bad point in vector_list!");
651 //synfig::info("BLEHH__________--- x:%f, y:%f",vector_list.front()[0],vector_list.front()[1]);
655 add_polygon(vector_list);
659 } catch (...) { synfig::error("Outline::sync(): Exception thrown"); throw; }
665 Outline::set_param(const String & param, const ValueBase &value)
667 if(param=="segment_list")
669 if(dynamic_param_list().count("segment_list"))
671 connect_dynamic_param("bline",dynamic_param_list().find("segment_list")->second);
672 disconnect_dynamic_param("segment_list");
673 synfig::warning("Outline::set_param(): Updated valuenode connection to use the new \"bline\" parameter.");
676 synfig::warning("Outline::set_param(): The parameter \"segment_list\" is deprecated. Use \"bline\" instead.");
679 if( (param=="segment_list" || param=="bline") && value.get_type()==ValueBase::TYPE_LIST)
681 //if(value.get_contained_type()!=ValueBase::TYPE_BLINEPOINT)
689 if( param=="seg" && value.get_type()==ValueBase::TYPE_SEGMENT)
691 if(!segment_list.empty())
692 segment_list.clear();
694 segment_list.push_back(value.get(Segment()));
699 if( param=="w[0]" && value.get_type()==ValueBase::TYPE_REAL)
701 if(width_list.size()<2)
703 width_list.push_back(value.get(Real()));
704 width_list.push_back(value.get(Real()));
708 width_list[0]=value.get(Real());
715 if( param=="w[1]" && value.get_type()==ValueBase::TYPE_REAL)
717 if(width_list.size()<2)
719 width_list.push_back(value.get(Real()));
720 width_list.push_back(value.get(Real()));
724 width_list[1]=value.get(Real());
731 if( param=="width_list" && value.same_as(width_list))
739 IMPORT(round_tip[0]);
740 IMPORT(round_tip[1]);
742 IMPORT_PLUS(width,if(old_version){width*=2.0;});
745 IMPORT(homogeneous_width);
747 if(param!="vector_list")
748 return Layer_Polygon::set_param(param,value);
754 Outline::set_time(Context context, Time time)const
756 const_cast<Outline*>(this)->sync();
757 context.set_time(time);
761 Outline::set_time(Context context, Time time, Vector pos)const
763 const_cast<Outline*>(this)->sync();
764 context.set_time(time,pos);
768 Outline::get_param(const String& param)const
772 //EXPORT(width_list);
773 //EXPORT(segment_list);
774 EXPORT(homogeneous_width);
775 EXPORT(round_tip[0]);
776 EXPORT(round_tip[1]);
784 if(param!="vector_list")
785 return Layer_Polygon::get_param(param);
790 Outline::get_param_vocab()const
792 Layer::Vocab ret(Layer_Polygon::get_param_vocab());
794 // Pop off the polygon parameter from the polygon vocab
797 ret.push_back(ParamDesc("bline")
798 .set_local_name(_("Vertices"))
799 .set_origin("offset")
801 .set_description(_("A list of BLine Points"))
805 ret.push_back(ParamDesc("width_list")
806 .set_local_name(_("Point Widths"))
807 .set_origin("segment_list")
813 ret.push_back(ParamDesc("width")
815 .set_local_name(_("Outline Width"))
818 ret.push_back(ParamDesc("expand")
820 .set_local_name(_("Expand"))
823 ret.push_back(ParamDesc("sharp_cusps")
824 .set_local_name(_("Sharp Cusps"))
825 .set_description(_("Determines cusp type"))
828 ret.push_back(ParamDesc("round_tip[0]")
829 .set_local_name(_("Rounded Begin"))
830 .set_description(_("Round off the tip"))
833 ret.push_back(ParamDesc("round_tip[1]")
834 .set_local_name(_("Rounded End"))
835 .set_description(_("Round off the tip"))
837 ret.push_back(ParamDesc("loopyness")
838 .set_local_name(_("Loopyness"))
840 ret.push_back(ParamDesc("homogeneous_width")
841 .set_local_name(_("Homogeneous"))