1 /* === S I N F G =========================================================== */
5 ** $Id: outline.cpp,v 1.1.1.1 2005/01/04 01:23:10 darco Exp $
8 ** Copyright (c) 2002 Robert B. Quattlebaum Jr.
10 ** This software and associated documentation
11 ** are CONFIDENTIAL and PROPRIETARY property of
12 ** the above-mentioned copyright holder.
14 ** You may not copy, print, publish, or in any
15 ** other way distribute this software without
16 ** a prior written agreement with
17 ** the copyright holder.
20 /* ========================================================================= */
22 //! \note This whole file should be rewritten at some point (darco)
24 /* === H E A D E R S ======================================================= */
34 #include <sinfg/string.h>
35 #include <sinfg/time.h>
36 #include <sinfg/context.h>
37 #include <sinfg/paramdesc.h>
38 #include <sinfg/renddesc.h>
39 #include <sinfg/surface.h>
40 #include <sinfg/value.h>
41 #include <sinfg/valuenode.h>
43 #include <ETL/calculus>
45 #include <ETL/hermite>
48 #include <sinfg/valuenode_bline.h>
54 /* === M A C R O S ========================================================= */
57 #define ROUND_END_FACTOR (4)
58 #define CUSP_THRESHOLD (0.40)
59 #define SPIKE_AMOUNT (4)
60 #define NO_LOOP_COOKIE sinfg::Vector(84951305,7836658)
61 #define EPSILON (0.000000001)
62 #define CUSP_TANGENT_ADJUST (0.025)
64 /* === G L O B A L S ======================================================= */
66 SINFG_LAYER_INIT(Outline);
67 SINFG_LAYER_SET_NAME(Outline,"outline");
68 SINFG_LAYER_SET_LOCAL_NAME(Outline,_("Outline"));
69 SINFG_LAYER_SET_CATEGORY(Outline,_("Geometry"));
70 SINFG_LAYER_SET_VERSION(Outline,"0.2");
71 SINFG_LAYER_SET_CVS_ID(Outline,"$Id: outline.cpp,v 1.1.1.1 2005/01/04 01:23:10 darco Exp $");
73 /* === P R O C E D U R E S ================================================= */
75 // This function was adapted from what was
76 // described on http://www.whisqu.se/per/docs/math28.htm
77 Point line_intersection(
84 const float& x0(p1[0]);
85 const float& y0(p1[1]);
87 const float x1(p1[0]+t1[0]);
88 const float y1(p1[1]+t1[1]);
90 const float& x2(p2[0]);
91 const float& y2(p2[1]);
93 const float x3(p2[0]+t2[0]);
94 const float y3(p2[1]+t2[1]);
96 const float near_infinity((float)1e+10);
98 float m1,m2; // the slopes of each line
100 // compute slopes, note the cludge for infinity, however, this will
104 m1 = (y1-y0)/(x1-x0);
109 m2 = (y3-y2)/(x3-x2);
116 const float b1(-1.0f);
117 const float b2(-1.0f);
118 const float c1(y0-m1*x0);
119 const float c2(y2-m2*x2);
121 // compute the inverse of the determinate
122 const float det_inv(1.0f/(a1*b2 - a2*b1));
124 // use Kramers rule to compute the intersection
126 ((b1*c2 - b2*c1)*det_inv),
127 ((a2*c1 - a1*c2)*det_inv)
129 } // end Intersect_Lines
131 /* === M E T H O D S ======================================================= */
143 homogeneous_width=true;
146 vector<BLinePoint> bline_point_list;
147 bline_point_list.push_back(BLinePoint());
148 bline_point_list.push_back(BLinePoint());
149 bline_point_list.push_back(BLinePoint());
150 bline_point_list[0].set_vertex(Point(0,1));
151 bline_point_list[1].set_vertex(Point(0,-1));
152 bline_point_list[2].set_vertex(Point(1,0));
153 bline_point_list[0].set_tangent(bline_point_list[1].get_vertex()-bline_point_list[2].get_vertex()*0.5f);
154 bline_point_list[1].set_tangent(bline_point_list[2].get_vertex()-bline_point_list[0].get_vertex()*0.5f);
155 bline_point_list[2].set_tangent(bline_point_list[0].get_vertex()-bline_point_list[1].get_vertex()*0.5f);
156 bline_point_list[0].set_width(1.0f);
157 bline_point_list[1].set_width(1.0f);
158 bline_point_list[2].set_width(1.0f);
159 bline=bline_point_list;
165 /*! The Sync() function takes the values
166 ** and creates a polygon to be rendered
167 ** with the polygon layer.
176 const bool loop(bline.get_loop());
177 const vector<sinfg::BLinePoint> bline_(bline.get_list().begin(),bline.get_list().end());
180 vector<BLinePoint>::const_iterator
184 const vector<BLinePoint>::const_iterator
196 Vector last_tangent=iter->get_tangent1();
198 for(bool first=!loop;next!=end;iter=next++,first=false)
200 Vector prev_t(iter->get_tangent1());
201 Vector iter_t(iter->get_tangent2());
202 Vector next_t(next->get_tangent1());
204 bool split_flag(iter->get_split_tangent_flag());
206 if(iter_t.is_equal_to(Vector::zero()) && next_t.is_equal_to(Vector::zero()))
208 iter_t=next_t=next->get_vertex()-iter->get_vertex();
213 hermite<Vector> curve(
221 iter_w((iter->get_width()*width)*0.5f+expand),
222 next_w((next->get_width()*width)*0.5f+expand);
224 const derivative< hermite<Vector> > deriv(curve);
226 // Make cusps as necessary
227 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()))
229 Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
231 const Vector t1(last_tangent.perp().norm());
232 const Vector t2(curr_tangent.perp().norm());
234 Real cross(t1*t2.perp());
235 Real perp((t1-t2).mag());
236 if(cross>CUSP_THRESHOLD)
238 const Point p1(iter->get_vertex()+t1*iter_w);
239 const Point p2(iter->get_vertex()+t2*iter_w);
241 side_a.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
243 else if(cross<-CUSP_THRESHOLD)
245 const Point p1(iter->get_vertex()-t1*iter_w);
246 const Point p2(iter->get_vertex()-t2*iter_w);
248 side_b.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
250 else if(cross>0 && perp>1)
252 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
254 side_a.push_back(iter->get_vertex()+(t1+t2).norm()*iter_w*amount);
256 else if(cross<0 && perp>1)
258 float amount(max(0.0f,(float)(-cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
260 side_b.push_back(iter->get_vertex()-(t1+t2).norm()*iter_w*amount);
265 if(homogeneous_width)
267 const float length(curve.length());
270 for(float n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
272 const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
273 const Vector p(curve(n));
276 dist+=(p-lastpoint).mag();
278 const float w(((next_w-iter_w)*(dist/length)+iter_w));
280 side_a.push_back(p+d*w);
281 side_b.push_back(p-d*w);
287 for(float n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
289 const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
290 const Vector p(curve(n));
291 const float w(((next_w-iter_w)*n+iter_w));
293 side_a.push_back(p+d*w);
294 side_b.push_back(p-d*w);
296 last_tangent=deriv(1.0-CUSP_TANGENT_ADJUST);
297 side_a.push_back(curve(1.0)+last_tangent.perp().norm()*next_w);
298 side_b.push_back(curve(1.0)-last_tangent.perp().norm()*next_w);
303 reverse(side_b.begin(),side_b.end());
309 // Insert code for adding end tip
310 if(round_tip[1] && !loop)
312 // remove the last point
315 const Point vertex(bline.back().get_vertex());
316 const Vector tangent(last_tangent.norm());
317 const float w((bline.back().get_width()*width)*0.5f+expand);
319 hermite<Vector> curve(
320 vertex+tangent.perp()*w,
321 vertex-tangent.perp()*w,
322 tangent*w*ROUND_END_FACTOR,
323 -tangent*w*ROUND_END_FACTOR
326 for(float n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
327 side_a.push_back(curve(n));
329 // remove the last point
333 for(;!side_b.empty();side_b.pop_back())
334 side_a.push_back(side_b.back());
336 // Insert code for adding begin tip
337 if(round_tip[0] && !loop)
339 // remove the last point
342 const Point vertex(bline.front().get_vertex());
343 const Vector tangent(bline.front().get_tangent2().norm());
344 const float w((bline.front().get_width()*width)*0.5f+expand);
346 hermite<Vector> curve(
347 vertex-tangent.perp()*w,
348 vertex+tangent.perp()*w,
349 -tangent*w*ROUND_END_FACTOR,
350 tangent*w*ROUND_END_FACTOR
353 for(float n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
354 side_a.push_back(curve(n));
356 // remove the last point
366 if(bline.get_contained_type()==ValueBase::TYPE_BLINEPOINT)
368 ValueBase value(bline);
372 value.set_loop(false);
376 loop_=value.get_loop();
378 segment_list=convert_bline_to_segment_list(value);
379 width_list=convert_bline_to_width_list(value);
389 if(segment_list.empty())
391 sinfg::warning("Outline: segment_list is empty, layer disabled");
397 // Repair the width list if we need to
400 if(width_list.empty())
403 default_width=width_list.back();
405 while(width_list.size()<segment_list.size()+1)
406 width_list.push_back(default_width);
407 while(width_list.size()>segment_list.size()+1)
408 width_list.pop_back();
412 // Repair the zero tangents (if any)
414 vector<Segment>::iterator iter;
415 for(iter=segment_list.begin();iter!=segment_list.end();++iter)
417 if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
418 iter->t1=iter->t2=iter->p2-iter->p1;
422 vector<Real>::iterator iter;
423 vector<Real> scaled_width_list;
424 for(iter=width_list.begin();iter!=width_list.end();++iter)
426 scaled_width_list.push_back((*iter*width+expand)*0.5f);
429 Vector::value_type n;
430 etl::hermite<Vector> curve;
431 vector<Point> vector_list;
432 Vector last_tangent(segment_list.back().t2);
436 last_tangent=NO_LOOP_COOKIE;
439 vector<Segment>::iterator iter;
440 vector<Real>::iterator witer;
442 iter=segment_list.begin(),
443 witer=scaled_width_list.begin();
444 iter!=segment_list.end();
447 if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
449 vector_list.push_back(iter->p1-(iter->p2-iter->p1).perp().norm()*witer[0]);
450 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]));
451 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]));
452 vector_list.push_back(iter->p2-(iter->p2-iter->p1).perp().norm()*witer[1]);
462 etl::derivative<etl::hermite<Vector> > deriv(curve);
464 // without this if statement, the broken tangents would
466 if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t1))
468 //Vector curr_tangent(iter->t1);
469 Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
471 const Vector t1(last_tangent.perp().norm());
472 const Vector t2(curr_tangent.perp().norm());
474 Point p1(iter->p1+t1*witer[0]);
475 Point p2(iter->p1+t2*witer[0]);
477 Real cross(t1*t2.perp());
479 if(cross>CUSP_THRESHOLD)
480 vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
483 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
484 // Push back something to make it look vaguely round;
485 //vector_list.push_back(iter->p1+(t1*1.25+t2).norm()*witer[0]*amount);
486 vector_list.push_back(iter->p1+(t1+t2).norm()*witer[0]*amount);
487 //vector_list.push_back(iter->p1+(t1+t2*1.25).norm()*witer[0]*amount);
490 //last_tangent=iter->t2;
491 last_tangent=deriv(1.0f-CUSP_TANGENT_ADJUST);
493 for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
494 vector_list.push_back(curve(n)+deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm()*((witer[1]-witer[0])*n+witer[0]) );
495 vector_list.push_back(curve(1.0)+deriv(1.0-CUSP_TANGENT_ADJUST).perp().norm()*witer[1]);
499 if(round_tip[1] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
501 // remove the last point
502 vector_list.pop_back();
506 curve.p1()=iter->p2+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
507 curve.p2()=iter->p2-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
508 curve.t2()=-(curve.t1()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
510 for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
511 vector_list.push_back(curve(n));
513 // remove the last point
514 vector_list.pop_back();
519 last_tangent=NO_LOOP_COOKIE;
522 add_polygon(vector_list);
524 last_tangent=segment_list.front().t1;
528 // last_tangent=segment_list.back().t2;
531 vector<Segment>::reverse_iterator iter;
532 vector<Real>::reverse_iterator witer;
534 iter=segment_list.rbegin(),
535 witer=scaled_width_list.rbegin(),++witer;
536 !(iter==segment_list.rend());
540 if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
542 vector_list.push_back(iter->p2+(iter->p2-iter->p1).perp().norm()*witer[0]);
543 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]));
544 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]));
545 vector_list.push_back(iter->p1+(iter->p2-iter->p1).perp().norm()*witer[-1]);
555 etl::derivative<etl::hermite<Vector> > deriv(curve);
557 // without this if statement, the broken tangents would
559 if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t2))
561 //Vector curr_tangent(iter->t2);
562 Vector curr_tangent(deriv(1.0f-CUSP_TANGENT_ADJUST));
564 const Vector t1(last_tangent.perp().norm());
565 const Vector t2(curr_tangent.perp().norm());
567 Point p1(iter->p2-t1*witer[-1]);
568 Point p2(iter->p2-t2*witer[-1]);
570 Real cross(t1*t2.perp());
572 //if(last_tangent.perp().norm()*curr_tangent.norm()<-CUSP_THRESHOLD)
573 if(cross>CUSP_THRESHOLD)
574 vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
577 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
578 // Push back something to make it look vaguely round;
579 //vector_list.push_back(iter->p2-(t1*1.25+t2).norm()*witer[-1]*amount);
580 vector_list.push_back(iter->p2-(t1+t2).norm()*witer[-1]*amount);
581 //vector_list.push_back(iter->p2-(t1+t2*1.25).norm()*witer[-1]*amount);
584 //last_tangent=iter->t1;
585 last_tangent=deriv(CUSP_TANGENT_ADJUST);
587 for(n=1.0f;n>CUSP_TANGENT_ADJUST;n-=1.0f/SAMPLES)
588 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]) );
589 vector_list.push_back(curve(0.0f)-deriv(CUSP_TANGENT_ADJUST).perp().norm()*witer[0]);
592 if(round_tip[0] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
594 // remove the last point
595 vector_list.pop_back();
599 curve.p1()=iter->p1+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
600 curve.p2()=iter->p1-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
601 curve.t1()=-(curve.t2()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
604 for(n=1.0;n>0.0;n-=1.0/SAMPLES)
605 vector_list.push_back(curve(n));
607 // remove the last point
608 vector_list.pop_back();
613 // reverse(vector_list.begin(),vector_list.end());
617 vector<Point>::iterator iter;
618 for(iter=vector_list.begin();iter!=vector_list.end();++iter)
619 if(!iter->is_valid())
621 sinfg::error("Outline::sync(): Bad point in vector_list!");
623 //sinfg::info("BLEHH__________--- x:%f, y:%f",vector_list.front()[0],vector_list.front()[1]);
627 add_polygon(vector_list);
631 } catch (...) { sinfg::error("Outline::sync(): Exception thrown"); throw; }
637 Outline::set_param(const String & param, const ValueBase &value)
639 if(param=="segment_list")
641 if(dynamic_param_list().count("segment_list"))
643 connect_dynamic_param("bline",dynamic_param_list().find("segment_list")->second);
644 disconnect_dynamic_param("segment_list");
645 sinfg::warning("Outline::set_param(): Updated valuenode connection to use the new \"bline\" parameter.");
648 sinfg::warning("Outline::set_param(): The parameter \"segment_list\" is deprecated. Use \"bline\" instead.");
651 if( (param=="segment_list" || param=="bline") && value.get_type()==ValueBase::TYPE_LIST)
653 //if(value.get_contained_type()!=ValueBase::TYPE_BLINEPOINT)
661 if( param=="seg" && value.get_type()==ValueBase::TYPE_SEGMENT)
663 if(!segment_list.empty())
664 segment_list.clear();
666 segment_list.push_back(value.get(Segment()));
671 if( param=="w[0]" && value.get_type()==ValueBase::TYPE_REAL)
673 if(width_list.size()<2)
675 width_list.push_back(value.get(Real()));
676 width_list.push_back(value.get(Real()));
680 width_list[0]=value.get(Real());
687 if( param=="w[1]" && value.get_type()==ValueBase::TYPE_REAL)
689 if(width_list.size()<2)
691 width_list.push_back(value.get(Real()));
692 width_list.push_back(value.get(Real()));
696 width_list[1]=value.get(Real());
703 if( param=="width_list" && value.same_as(width_list))
711 IMPORT(round_tip[0]);
712 IMPORT(round_tip[1]);
714 IMPORT_PLUS(width,if(old_version){width*=2.0;});
717 IMPORT(homogeneous_width);
719 if(param!="vector_list")
720 return Layer_Polygon::set_param(param,value);
726 Outline::set_time(Context context, Time time)const
728 const_cast<Outline*>(this)->sync();
729 context.set_time(time);
733 Outline::set_time(Context context, Time time, Vector pos)const
735 const_cast<Outline*>(this)->sync();
736 context.set_time(time,pos);
740 Outline::get_param(const String& param)const
744 //EXPORT(width_list);
745 //EXPORT(segment_list);
746 EXPORT(homogeneous_width);
747 EXPORT(round_tip[0]);
748 EXPORT(round_tip[1]);
756 if(param!="vector_list")
757 return Layer_Polygon::get_param(param);
762 Outline::get_param_vocab()const
764 Layer::Vocab ret(Layer_Polygon::get_param_vocab());
766 // Pop off the polygon parameter from the polygon vocab
769 ret.push_back(ParamDesc("bline")
770 .set_local_name(_("Vertices"))
771 .set_origin("offset")
773 .set_description(_("A list of BLine Points"))
777 ret.push_back(ParamDesc("width_list")
778 .set_local_name(_("Point Widths"))
779 .set_origin("segment_list")
785 ret.push_back(ParamDesc("width")
787 .set_local_name(_("Outline Width"))
790 ret.push_back(ParamDesc("expand")
792 .set_local_name(_("Expand"))
795 ret.push_back(ParamDesc("sharp_cusps")
796 .set_local_name(_("Sharp Cusps"))
797 .set_description(_("Determines cusp type"))
800 ret.push_back(ParamDesc("round_tip[0]")
801 .set_local_name(_("Rounded Begin"))
802 .set_description(_("Round off the tip"))
805 ret.push_back(ParamDesc("round_tip[1]")
806 .set_local_name(_("Rounded End"))
807 .set_description(_("Round off the tip"))
809 ret.push_back(ParamDesc("loopyness")
810 .set_local_name(_("Loopyness"))
812 ret.push_back(ParamDesc("homogeneous_width")
813 .set_local_name(_("Homogeneous"))