ParamDesc::set_scalar(String) and ParamDesc::set_hint(String) were identical. Got...
[synfig.git] / synfig-core / trunk / src / modules / mod_geometry / outline.cpp
blobca45878db965b6008c9eeb520761edf2209e806c
1 /* === S Y N F I G ========================================================= */
2 /*! \file outline.cpp
3 ** \brief Implementation of the "Outline" layer
4 **
5 ** $Id$
6 **
7 ** \legal
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007 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.
20 ** \endlegal
22 /* ========================================================================= */
24 //! \note This whole file should be rewritten at some point (darco)
26 /* === H E A D E R S ======================================================= */
28 #ifdef USING_PCH
29 # include "pch.h"
30 #else
31 #ifdef HAVE_CONFIG_H
32 # include <config.h>
33 #endif
35 #include "outline.h"
36 #include <synfig/string.h>
37 #include <synfig/time.h>
38 #include <synfig/context.h>
39 #include <synfig/paramdesc.h>
40 #include <synfig/renddesc.h>
41 #include <synfig/surface.h>
42 #include <synfig/value.h>
43 #include <synfig/valuenode.h>
45 #include <ETL/calculus>
46 #include <ETL/bezier>
47 #include <ETL/hermite>
48 #include <vector>
50 #include <synfig/valuenode_bline.h>
52 #endif
54 using namespace etl;
56 /* === M A C R O S ========================================================= */
58 #define SAMPLES 50
59 #define ROUND_END_FACTOR (4)
60 #define CUSP_THRESHOLD (0.40)
61 #define SPIKE_AMOUNT (4)
62 #define NO_LOOP_COOKIE synfig::Vector(84951305,7836658)
63 #define EPSILON (0.000000001)
64 #define CUSP_TANGENT_ADJUST (0.025)
66 /* === G L O B A L S ======================================================= */
68 SYNFIG_LAYER_INIT(Outline);
69 SYNFIG_LAYER_SET_NAME(Outline,"outline");
70 SYNFIG_LAYER_SET_LOCAL_NAME(Outline,N_("Outline"));
71 SYNFIG_LAYER_SET_CATEGORY(Outline,N_("Geometry"));
72 SYNFIG_LAYER_SET_VERSION(Outline,"0.2");
73 SYNFIG_LAYER_SET_CVS_ID(Outline,"$Id$");
75 /* === P R O C E D U R E S ================================================= */
77 // This function was adapted from what was
78 // described on http://www.whisqu.se/per/docs/math28.htm
79 Point line_intersection(
80 const Point& p1,
81 const Vector& t1,
82 const Point& p2,
83 const Vector& t2
86 const float& x0(p1[0]);
87 const float& y0(p1[1]);
89 const float x1(p1[0]+t1[0]);
90 const float y1(p1[1]+t1[1]);
92 const float& x2(p2[0]);
93 const float& y2(p2[1]);
95 const float x3(p2[0]+t2[0]);
96 const float y3(p2[1]+t2[1]);
98 const float near_infinity((float)1e+10);
100 float m1,m2; // the slopes of each line
102 // compute slopes, note the kluge for infinity, however, this will
103 // be close enough
105 if ((x1-x0)!=0)
106 m1 = (y1-y0)/(x1-x0);
107 else
108 m1 = near_infinity;
110 if ((x3-x2)!=0)
111 m2 = (y3-y2)/(x3-x2);
112 else
113 m2 = near_infinity;
115 // compute constants
116 const float& a1(m1);
117 const float& a2(m2);
118 const float b1(-1.0f);
119 const float b2(-1.0f);
120 const float c1(y0-m1*x0);
121 const float c2(y2-m2*x2);
123 // compute the inverse of the determinate
124 const float det_inv(1.0f/(a1*b2 - a2*b1));
126 // use Kramers rule to compute the intersection
127 return Point(
128 ((b1*c2 - b2*c1)*det_inv),
129 ((a2*c1 - a1*c2)*det_inv)
131 } // end Intersect_Lines
133 /* === M E T H O D S ======================================================= */
136 Outline::Outline()
138 old_version=false;
139 round_tip[0]=true;
140 round_tip[1]=true;
141 sharp_cusps=true;
142 width=1.0f;
143 loopyness=1.0f;
144 expand=0;
145 homogeneous_width=true;
146 clear();
148 vector<BLinePoint> bline_point_list;
149 bline_point_list.push_back(BLinePoint());
150 bline_point_list.push_back(BLinePoint());
151 bline_point_list.push_back(BLinePoint());
152 bline_point_list[0].set_vertex(Point(0,1));
153 bline_point_list[1].set_vertex(Point(0,-1));
154 bline_point_list[2].set_vertex(Point(1,0));
155 bline_point_list[0].set_tangent(bline_point_list[1].get_vertex()-bline_point_list[2].get_vertex()*0.5f);
156 bline_point_list[1].set_tangent(bline_point_list[2].get_vertex()-bline_point_list[0].get_vertex()*0.5f);
157 bline_point_list[2].set_tangent(bline_point_list[0].get_vertex()-bline_point_list[1].get_vertex()*0.5f);
158 bline_point_list[0].set_width(1.0f);
159 bline_point_list[1].set_width(1.0f);
160 bline_point_list[2].set_width(1.0f);
161 bline=bline_point_list;
163 needs_sync=true;
167 /*! The Sync() function takes the values
168 ** and creates a polygon to be rendered
169 ** with the polygon layer.
171 void
172 Outline::sync()
174 clear();
176 if (!bline.get_list().size())
178 synfig::warning(string("Outline::sync():")+N_("No vertices in outline " + string("\"") + get_description() + string("\"")));
179 return;
182 try {
183 #if 1
185 const bool loop(bline.get_loop());
186 const vector<synfig::BLinePoint> bline_(bline.get_list().begin(),bline.get_list().end());
187 #define bline bline_
189 vector<BLinePoint>::const_iterator
190 iter,
191 next(bline.begin());
193 const vector<BLinePoint>::const_iterator
194 end(bline.end());
196 vector<Point>
197 side_a,
198 side_b;
200 if(loop)
201 iter=--bline.end();
202 else
203 iter=next++;
205 // iter next
206 // ---- ----
207 // looped nth 1st
208 // !looped 1st 2nd
210 Vector first_tangent=bline.front().get_tangent2();
211 Vector last_tangent=iter->get_tangent1();
213 // if we are looped and drawing sharp cusps, we'll need a value for the incoming tangent
214 if (loop && sharp_cusps && last_tangent.is_equal_to(Vector::zero()))
216 hermite<Vector> curve((iter-1)->get_vertex(), iter->get_vertex(), (iter-1)->get_tangent2(), iter->get_tangent1());
217 const derivative< hermite<Vector> > deriv(curve);
218 last_tangent=deriv(1.0-CUSP_TANGENT_ADJUST);
221 // `first' is for making the cusps; don't do that for the first point if we're not looped
222 for(bool first=!loop; next!=end; iter=next++)
224 Vector prev_t(iter->get_tangent1());
225 Vector iter_t(iter->get_tangent2());
226 Vector next_t(next->get_tangent1());
228 bool split_flag(iter->get_split_tangent_flag());
230 // if iter.t2 == 0 and next.t1 == 0, this is a straight line
231 if(iter_t.is_equal_to(Vector::zero()) && next_t.is_equal_to(Vector::zero()))
233 iter_t=next_t=next->get_vertex()-iter->get_vertex();
234 // split_flag=true;
236 // if the two points are on top of each other, ignore this segment
237 // leave `first' true if was before
238 if (iter_t.is_equal_to(Vector::zero()))
239 continue;
242 // Setup the curve
243 hermite<Vector> curve(
244 iter->get_vertex(),
245 next->get_vertex(),
246 iter_t,
247 next_t
250 const float
251 iter_w((iter->get_width()*width)*0.5f+expand),
252 next_w((next->get_width()*width)*0.5f+expand);
254 const derivative< hermite<Vector> > deriv(curve);
256 if (first)
257 first_tangent = deriv(CUSP_TANGENT_ADJUST);
259 // Make cusps as necessary
260 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()))
262 Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
264 const Vector t1(last_tangent.perp().norm());
265 const Vector t2(curr_tangent.perp().norm());
267 Real cross(t1*t2.perp());
268 Real perp((t1-t2).mag());
269 if(cross>CUSP_THRESHOLD)
271 const Point p1(iter->get_vertex()+t1*iter_w);
272 const Point p2(iter->get_vertex()+t2*iter_w);
274 side_a.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
276 else if(cross<-CUSP_THRESHOLD)
278 const Point p1(iter->get_vertex()-t1*iter_w);
279 const Point p2(iter->get_vertex()-t2*iter_w);
281 side_b.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
283 else if(cross>0 && perp>1)
285 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
287 side_a.push_back(iter->get_vertex()+(t1+t2).norm()*iter_w*amount);
289 else if(cross<0 && perp>1)
291 float amount(max(0.0f,(float)(-cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
293 side_b.push_back(iter->get_vertex()-(t1+t2).norm()*iter_w*amount);
297 // Make the outline
298 if(homogeneous_width)
300 const float length(curve.length());
301 float dist(0);
302 Point lastpoint;
303 for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
305 const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
306 const Vector p(curve(n));
308 if(n)
309 dist+=(p-lastpoint).mag();
311 const float w(((next_w-iter_w)*(dist/length)+iter_w));
313 side_a.push_back(p+d*w);
314 side_b.push_back(p-d*w);
316 lastpoint=p;
319 else
320 for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
322 const Vector d(deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm());
323 const Vector p(curve(n));
324 const float w(((next_w-iter_w)*n+iter_w));
326 side_a.push_back(p+d*w);
327 side_b.push_back(p-d*w);
329 last_tangent=deriv(1.0-CUSP_TANGENT_ADJUST);
330 side_a.push_back(curve(1.0)+last_tangent.perp().norm()*next_w);
331 side_b.push_back(curve(1.0)-last_tangent.perp().norm()*next_w);
333 first=false;
336 if(loop)
338 reverse(side_b.begin(),side_b.end());
339 add_polygon(side_a);
340 add_polygon(side_b);
341 return;
344 // Insert code for adding end tip
345 if(round_tip[1] && !loop && side_a.size())
347 // remove the last point
348 side_a.pop_back();
350 const Point vertex(bline.back().get_vertex());
351 const Vector tangent(last_tangent.norm());
352 const float w((bline.back().get_width()*width)*0.5f+expand);
354 hermite<Vector> curve(
355 vertex+tangent.perp()*w,
356 vertex-tangent.perp()*w,
357 tangent*w*ROUND_END_FACTOR,
358 -tangent*w*ROUND_END_FACTOR
361 for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
362 side_a.push_back(curve(n));
365 for(;!side_b.empty();side_b.pop_back())
366 side_a.push_back(side_b.back());
368 // Insert code for adding begin tip
369 if(round_tip[0] && !loop && side_a.size())
371 // remove the last point
372 side_a.pop_back();
374 const Point vertex(bline.front().get_vertex());
375 const Vector tangent(first_tangent.norm());
376 const float w((bline.front().get_width()*width)*0.5f+expand);
378 hermite<Vector> curve(
379 vertex-tangent.perp()*w,
380 vertex+tangent.perp()*w,
381 -tangent*w*ROUND_END_FACTOR,
382 tangent*w*ROUND_END_FACTOR
385 for(float n=0.0f;n<0.999999f;n+=1.0f/SAMPLES)
386 side_a.push_back(curve(n));
389 add_polygon(side_a);
392 #else /* 1 */
394 bool loop_;
395 if(bline.get_contained_type()==ValueBase::TYPE_BLINEPOINT)
397 ValueBase value(bline);
399 if(loopyness<0.5f)
401 value.set_loop(false);
402 loop_=false;
404 else
405 loop_=value.get_loop();
407 segment_list=convert_bline_to_segment_list(value);
408 width_list=convert_bline_to_width_list(value);
410 else
412 clear();
413 return;
418 if(segment_list.empty())
420 synfig::warning("Outline: segment_list is empty, layer disabled");
421 clear();
422 return;
426 // Repair the width list if we need to
428 Real default_width;
429 if(width_list.empty())
430 default_width=0.01;
431 else
432 default_width=width_list.back();
434 while(width_list.size()<segment_list.size()+1)
435 width_list.push_back(default_width);
436 while(width_list.size()>segment_list.size()+1)
437 width_list.pop_back();
441 // Repair the zero tangents (if any)
443 vector<Segment>::iterator iter;
444 for(iter=segment_list.begin();iter!=segment_list.end();++iter)
446 if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
447 iter->t1=iter->t2=iter->p2-iter->p1;
451 vector<Real>::iterator iter;
452 vector<Real> scaled_width_list;
453 for(iter=width_list.begin();iter!=width_list.end();++iter)
455 scaled_width_list.push_back((*iter*width+expand)*0.5f);
458 Vector::value_type n;
459 etl::hermite<Vector> curve;
460 vector<Point> vector_list;
461 Vector last_tangent(segment_list.back().t2);
462 clear();
464 if(!loop_)
465 last_tangent=NO_LOOP_COOKIE;
468 vector<Segment>::iterator iter;
469 vector<Real>::iterator witer;
470 for(
471 iter=segment_list.begin(),
472 witer=scaled_width_list.begin();
473 iter!=segment_list.end();
474 ++iter,++witer)
476 if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
478 vector_list.push_back(iter->p1-(iter->p2-iter->p1).perp().norm()*witer[0]);
479 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]));
480 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]));
481 vector_list.push_back(iter->p2-(iter->p2-iter->p1).perp().norm()*witer[1]);
483 else
485 curve.p1()=iter->p1;
486 curve.t1()=iter->t1;
487 curve.p2()=iter->p2;
488 curve.t2()=iter->t2;
489 curve.sync();
491 etl::derivative<etl::hermite<Vector> > deriv(curve);
493 // without this if statement, the broken tangents would
494 // have boxed edges
495 if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t1))
497 //Vector curr_tangent(iter->t1);
498 Vector curr_tangent(deriv(CUSP_TANGENT_ADJUST));
500 const Vector t1(last_tangent.perp().norm());
501 const Vector t2(curr_tangent.perp().norm());
503 Point p1(iter->p1+t1*witer[0]);
504 Point p2(iter->p1+t2*witer[0]);
506 Real cross(t1*t2.perp());
508 if(cross>CUSP_THRESHOLD)
509 vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
510 else if(cross>0)
512 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
513 // Push back something to make it look vaguely round;
514 //vector_list.push_back(iter->p1+(t1*1.25+t2).norm()*witer[0]*amount);
515 vector_list.push_back(iter->p1+(t1+t2).norm()*witer[0]*amount);
516 //vector_list.push_back(iter->p1+(t1+t2*1.25).norm()*witer[0]*amount);
519 //last_tangent=iter->t2;
520 last_tangent=deriv(1.0f-CUSP_TANGENT_ADJUST);
522 for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
523 vector_list.push_back(curve(n)+deriv(n>CUSP_TANGENT_ADJUST?n:CUSP_TANGENT_ADJUST).perp().norm()*((witer[1]-witer[0])*n+witer[0]) );
524 vector_list.push_back(curve(1.0)+deriv(1.0-CUSP_TANGENT_ADJUST).perp().norm()*witer[1]);
528 if(round_tip[1] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
530 // remove the last point
531 vector_list.pop_back();
533 iter--;
535 curve.p1()=iter->p2+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
536 curve.p2()=iter->p2-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
537 curve.t2()=-(curve.t1()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
538 curve.sync();
539 for(n=0.0f;n<1.0f;n+=1.0f/SAMPLES)
540 vector_list.push_back(curve(n));
542 // remove the last point
543 vector_list.pop_back();
547 if(!loop_)
548 last_tangent=NO_LOOP_COOKIE;
549 else
551 add_polygon(vector_list);
552 vector_list.clear();
553 last_tangent=segment_list.front().t1;
556 //else
557 // last_tangent=segment_list.back().t2;
560 vector<Segment>::reverse_iterator iter;
561 vector<Real>::reverse_iterator witer;
562 for(
563 iter=segment_list.rbegin(),
564 witer=scaled_width_list.rbegin(),++witer;
565 !(iter==segment_list.rend());
566 ++iter,++witer)
569 if(iter->t1.mag_squared()<=EPSILON && iter->t2.mag_squared()<=EPSILON)
571 vector_list.push_back(iter->p2+(iter->p2-iter->p1).perp().norm()*witer[0]);
572 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]));
573 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]));
574 vector_list.push_back(iter->p1+(iter->p2-iter->p1).perp().norm()*witer[-1]);
576 else
578 curve.p1()=iter->p1;
579 curve.t1()=iter->t1;
580 curve.p2()=iter->p2;
581 curve.t2()=iter->t2;
582 curve.sync();
584 etl::derivative<etl::hermite<Vector> > deriv(curve);
586 // without this if statement, the broken tangents would
587 // have boxed edges
588 if(sharp_cusps && last_tangent!=NO_LOOP_COOKIE && !last_tangent.is_equal_to(iter->t2))
590 //Vector curr_tangent(iter->t2);
591 Vector curr_tangent(deriv(1.0f-CUSP_TANGENT_ADJUST));
593 const Vector t1(last_tangent.perp().norm());
594 const Vector t2(curr_tangent.perp().norm());
596 Point p1(iter->p2-t1*witer[-1]);
597 Point p2(iter->p2-t2*witer[-1]);
599 Real cross(t1*t2.perp());
601 //if(last_tangent.perp().norm()*curr_tangent.norm()<-CUSP_THRESHOLD)
602 if(cross>CUSP_THRESHOLD)
603 vector_list.push_back(line_intersection(p1,last_tangent,p2,curr_tangent));
604 else if(cross>0)
606 float amount(max(0.0f,(float)(cross/CUSP_THRESHOLD))*(SPIKE_AMOUNT-1)+1);
607 // Push back something to make it look vaguely round;
608 //vector_list.push_back(iter->p2-(t1*1.25+t2).norm()*witer[-1]*amount);
609 vector_list.push_back(iter->p2-(t1+t2).norm()*witer[-1]*amount);
610 //vector_list.push_back(iter->p2-(t1+t2*1.25).norm()*witer[-1]*amount);
613 //last_tangent=iter->t1;
614 last_tangent=deriv(CUSP_TANGENT_ADJUST);
616 for(n=1.0f;n>CUSP_TANGENT_ADJUST;n-=1.0f/SAMPLES)
617 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]) );
618 vector_list.push_back(curve(0.0f)-deriv(CUSP_TANGENT_ADJUST).perp().norm()*witer[0]);
621 if(round_tip[0] && !loop_/* && (!sharp_cusps || segment_list.front().p1!=segment_list.back().p2)*/)
623 // remove the last point
624 vector_list.pop_back();
625 iter--;
626 witer--;
628 curve.p1()=iter->p1+Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer);
629 curve.p2()=iter->p1-(Vector(last_tangent[1],-last_tangent[0]).norm()*(*witer));
630 curve.t1()=-(curve.t2()=last_tangent/last_tangent.mag()*(*witer)*ROUND_END_FACTOR);
631 curve.sync();
633 for(n=1.0;n>0.0;n-=1.0/SAMPLES)
634 vector_list.push_back(curve(n));
636 // remove the last point
637 vector_list.pop_back();
641 //if(loop_)
642 // reverse(vector_list.begin(),vector_list.end());
644 #ifdef _DEBUG
646 vector<Point>::iterator iter;
647 for(iter=vector_list.begin();iter!=vector_list.end();++iter)
648 if(!iter->is_valid())
650 synfig::error("Outline::sync(): Bad point in vector_list!");
652 //synfig::info("BLEHH__________--- x:%f, y:%f",vector_list.front()[0],vector_list.front()[1]);
654 #endif /* _DEBUG */
656 add_polygon(vector_list);
659 #endif /* 1 */
660 } catch (...) { synfig::error("Outline::sync(): Exception thrown"); throw; }
663 #undef bline
665 bool
666 Outline::set_param(const String & param, const ValueBase &value)
668 if(param=="segment_list")
670 if(dynamic_param_list().count("segment_list"))
672 connect_dynamic_param("bline",dynamic_param_list().find("segment_list")->second);
673 disconnect_dynamic_param("segment_list");
674 synfig::warning("Outline::set_param(): Updated valuenode connection to use the new \"bline\" parameter.");
676 else
677 synfig::warning("Outline::set_param(): The parameter \"segment_list\" is deprecated. Use \"bline\" instead.");
680 if( (param=="segment_list" || param=="bline") && value.get_type()==ValueBase::TYPE_LIST)
682 //if(value.get_contained_type()!=ValueBase::TYPE_BLINEPOINT)
683 // return false;
685 bline=value;
687 return true;
690 if( param=="seg" && value.get_type()==ValueBase::TYPE_SEGMENT)
692 if(!segment_list.empty())
693 segment_list.clear();
695 segment_list.push_back(value.get(Segment()));
696 loop_=false;
697 //sync();
698 return true;
700 if( param=="w[0]" && value.get_type()==ValueBase::TYPE_REAL)
702 if(width_list.size()<2)
704 width_list.push_back(value.get(Real()));
705 width_list.push_back(value.get(Real()));
707 else
709 width_list[0]=value.get(Real());
711 width=1;
712 //sync();
713 return true;
716 if( param=="w[1]" && value.get_type()==ValueBase::TYPE_REAL)
718 if(width_list.size()<2)
720 width_list.push_back(value.get(Real()));
721 width_list.push_back(value.get(Real()));
723 else
725 width_list[1]=value.get(Real());
727 width=1;
728 //sync();
729 return true;
732 if( param=="width_list" && value.same_type_as(width_list))
734 width_list=value;
735 //sync();
736 return true;
740 IMPORT(round_tip[0]);
741 IMPORT(round_tip[1]);
742 IMPORT(sharp_cusps);
743 IMPORT_PLUS(width,if(old_version){width*=2.0;});
744 IMPORT(loopyness);
745 IMPORT(expand);
746 IMPORT(homogeneous_width);
748 if(param!="vector_list")
749 return Layer_Polygon::set_param(param,value);
751 return false;
754 void
755 Outline::set_time(Context context, Time time)const
757 const_cast<Outline*>(this)->sync();
758 context.set_time(time);
761 void
762 Outline::set_time(Context context, Time time, Vector pos)const
764 const_cast<Outline*>(this)->sync();
765 context.set_time(time,pos);
768 ValueBase
769 Outline::get_param(const String& param)const
771 EXPORT(bline);
772 EXPORT(expand);
773 //EXPORT(width_list);
774 //EXPORT(segment_list);
775 EXPORT(homogeneous_width);
776 EXPORT(round_tip[0]);
777 EXPORT(round_tip[1]);
778 EXPORT(sharp_cusps);
779 EXPORT(width);
780 EXPORT(loopyness);
782 EXPORT_NAME();
783 EXPORT_VERSION();
785 if(param!="vector_list")
786 return Layer_Polygon::get_param(param);
787 return ValueBase();
790 Layer::Vocab
791 Outline::get_param_vocab()const
793 Layer::Vocab ret(Layer_Polygon::get_param_vocab());
795 // Pop off the polygon parameter from the polygon vocab
796 ret.pop_back();
798 ret.push_back(ParamDesc("bline")
799 .set_local_name(_("Vertices"))
800 .set_origin("offset")
801 .set_hint("width")
802 .set_description(_("A list of BLine Points"))
806 ret.push_back(ParamDesc("width_list")
807 .set_local_name(_("Point Widths"))
808 .set_origin("segment_list")
809 .hidden()
810 .not_critical()
814 ret.push_back(ParamDesc("width")
815 .set_is_distance()
816 .set_local_name(_("Outline Width"))
819 ret.push_back(ParamDesc("expand")
820 .set_is_distance()
821 .set_local_name(_("Expand"))
824 ret.push_back(ParamDesc("sharp_cusps")
825 .set_local_name(_("Sharp Cusps"))
826 .set_description(_("Determines cusp type"))
829 ret.push_back(ParamDesc("round_tip[0]")
830 .set_local_name(_("Rounded Begin"))
831 .set_description(_("Round off the tip"))
834 ret.push_back(ParamDesc("round_tip[1]")
835 .set_local_name(_("Rounded End"))
836 .set_description(_("Round off the tip"))
838 ret.push_back(ParamDesc("loopyness")
839 .set_local_name(_("Loopyness"))
841 ret.push_back(ParamDesc("homogeneous_width")
842 .set_local_name(_("Homogeneous"))
845 return ret;