File Coverage

blib/lib/SVG/Graph/Glyph/bezier.pm
Criterion Covered Total %
statement 100 102 98.0
branch 18 20 90.0
condition 8 11 72.7
subroutine 11 11 100.0
pod 8 8 100.0
total 145 152 95.3


line stmt bran cond sub pod time code
1             package SVG::Graph::Glyph::bezier;
2              
3 1     1   6 use base SVG::Graph::Glyph;
  1         1  
  1         539  
4 1     1   6 use strict;
  1         2  
  1         35  
5 1     1   707 use Math::Spline;
  1         1989  
  1         1517  
6              
7             =head2 draw
8              
9             Title : draw
10             Usage :
11             Function:
12             Example :
13             Returns :
14             Args :
15              
16              
17             =cut
18              
19             sub draw{
20 1     1 1 2 my ($self,@args) = @_;
21              
22 1         6 my $id = 'n'.sprintf("%07d",int(rand(9999999)));
23 1         5 my $group = $self->svg->group(id=>"bezier$id");
24              
25 1         56 my $xscale = $self->xsize / $self->group->xrange;
26 1         9 my $yscale = $self->ysize / $self->group->yrange;
27              
28              
29 1         11 my @p = map {[$_->x,$_->y]} sort {$a->x <=> $b->x} $self->group->data;
  20         35  
  64         131  
30 1         4 my $c = spline_generate(@p);
31              
32 1         2 my @c;
33 1         2 foreach my $i (1..$#p){
34 19         1118 my($x1,$y1) = @{$p[$i-1]};
  19         43  
35 19         17 my($x2,$y2) = @{$p[$i] };
  19         29  
36              
37 19         16 my $e = 0;
38 19         19 my($cx,$cy,$cx_sum,$cy_sum);
39 19         42 for(my $d = 2; $d <= 100 ; $d += 2){
40 950 50       1467 next if $d == 0;
41 950         1200 my $delta = ($x2 - $x1) / $d;
42 950         1899 my $y1d = spline_evaluate($x1+$delta,$c,@p);
43 950         1913 my $y2d = spline_evaluate($x2-$delta,$c,@p);
44              
45 950         2418 my($int,$t1,$t2) = Intersection(Segment([$x1,$y1],[$x1+$delta,$y1d]),Segment([$x2-$delta,$y2d],[$x2,$y2]));
46 950 100       3412 next unless ref($int);
47 44 100 66     198 next unless $x1 <= $int->[0] and $x2 >= $int->[0];
48              
49 30         27 $e++;
50 30         32 $cx_sum += $int->[0];
51 30         79 $cy_sum += $int->[1];
52             }
53              
54 19         89 $x1 = (($x1 - $self->group->xmin) * $xscale) + $self->xoffset;
55 19         49 $y1 = ($self->ysize - ($y1 - $self->group->ymin) * $yscale) + $self->yoffset;
56 19         50 $x2 = (($x2 - $self->group->xmin) * $xscale) + $self->xoffset;
57 19         40 $y2 = ($self->ysize - ($y2 - $self->group->ymin) * $yscale) + $self->yoffset;
58              
59             #warn $cx_sum,"\t",$e;
60             #next unless $e;
61 19 100       34 if($e){
62 1         3 $cx = $cx_sum / $e;
63 1         2 $cy = $cy_sum / $e;
64              
65 1         3 $cx = (($cx - $self->group->xmin) * $xscale) + $self->xoffset;
66 1         3 $cy = ($self->ysize - ($cy - $self->group->ymin) * $yscale) + $self->yoffset;
67              
68 1         26 $group->path(d=>"M$x1,$y1 Q$cx,$cy $x2,$y2",
69             style=>{$self->_style},
70             );
71             } else {
72 18         57 $group->line(x1=>$x1,y1=>$y1,x2=>$x2,y2=>$y2,style=>{$self->_style});
73             }
74             }
75              
76              
77             }
78              
79             =head2 spline_generate
80              
81             Title : spline_generate
82             Usage :
83             Function:
84             Example :
85             Returns :
86             Args :
87              
88              
89             =cut
90              
91             sub spline_generate{
92 1     1 1 7 my (@points) = @_;
93              
94 1         2 my ($i, $delta, $temp, @factors, @coeffs);
95 1         2 $coeffs[0] = $factors[0] = 0;
96              
97             # Decomposition phase of the tridiagonal system of equations
98 1         5 for ($i = 1; $i < @points - 1; $i++) {
99              
100             #ad
101 18 100       50 next unless ($points[$i+1][0] - $points[$i-1][0]);
102              
103 2         6 $delta = ($points[$i][0] - $points[$i-1][0]) / (($points[$i+1][0] - $points[$i-1][0]));
104              
105              
106 2   100     10 $temp = $delta * ($coeffs[$i-1] || 0) + 2;
107              
108              
109 2         5 $coeffs[$i] = ($delta - 1) / @points;
110              
111             #ad
112 2 100       8 next unless ($points[$i+1][0] - $points[$i][0]);
113 1 50       6 next unless ($points[$i][0] - $points[$i-1][0]);
114              
115 0         0 $factors[$i] = ($points[$i+1][1] - $points[$i][1]) / (($points[$i+1][0] - $points[$i][0]))
116             -
117             ($points[$i][1] - $points[$i-1][1]) / (($points[$i][0] - $points[$i-1][0]));
118              
119              
120 0         0 $factors[$i] = ( 6 * $factors[$i] / (($points[$i+1][0] - $points[$i-1][0]))
121             -
122             $delta * $factors[$i-1] ) / $temp;
123             }
124              
125             # Backsubstitution phase of the tridiagonal system
126             #
127 1         3 $coeffs[$#points] = 0;
128 1         4 for ($i = @points - 2; $i >= 0; $i--) {
129 19   100     156 $coeffs[$i] = ($coeffs[$i] || 0) * ($coeffs[$i+1] || 0) + ($factors[$i] || 0);
      50        
      50        
130             }
131 1         4 return \@coeffs;
132             }
133              
134             =head2 spline_evaluate
135              
136             Title : spline_evaluate
137             Usage :
138             Function:
139             Example :
140             Returns :
141             Args :
142              
143              
144             =cut
145              
146             sub spline_evaluate{
147 1900     1900 1 4399 my ($x, $coeffs, @points) = @_;
148 1900         1809 my ($i, $delta, $mult);
149              
150             # Which section of the spline are we in?
151             #
152 1900         3657 for ($i = @points - 2; $i >= 1; $i--) {
153 10900 100       23206 last if $x >= $points[$i][0];
154             }
155              
156 1900         2470 $delta = $points[$i+1][0] - $points[$i][0];
157              
158             #ad
159 1900 100       3845 return 0 unless $delta;
160              
161 1000         2041 $mult = ( $coeffs->[$i]/2 ) + ($x - $points[$i][0]) * ($coeffs->[$i+1] - $coeffs->[$i]) / (6 * $delta);
162 1000         1119 $mult *= $x - $points[$i][0];
163 1000         1521 $mult += ($points[$i+1][1] - $points[$i][1]) / ($delta);
164 1000         1425 $mult -= ($coeffs->[$i+1] + 2 * $coeffs->[$i]) * $delta / 6;
165 1000         2495 return $points[$i][1] + $mult * ($x - $points[$i][0]);
166             }
167              
168             =head2 Segment
169              
170             Title : Segment
171             Usage :
172             Function:
173             Example :
174             Returns :
175             Args :
176              
177              
178             =cut
179              
180             sub Segment {
181 1900     1900 1 4109 [ $_[0], $_[1] ];
182             }
183              
184             =head2 VectorSum
185              
186             Title : VectorSum
187             Usage :
188             Function:
189             Example :
190             Returns :
191             Args :
192              
193              
194             =cut
195              
196             sub VectorSum {
197 1944     1944 1 1950 my ($x, $y) = (0, 0);
198 1944         2573 for (@_) {
199 3888         3744 $x += $_->[0];
200 3888         5065 $y += $_->[1];
201             }
202 1944         3451 [ $x, $y ];
203             }
204              
205             =head2 ScalarProd
206              
207             Title : ScalarProd
208             Usage :
209             Function:
210             Example :
211             Returns :
212             Args :
213              
214              
215             =cut
216              
217             sub ScalarProd {
218 44     44 1 38 my $s = shift;
219 44         40 my $v = shift;
220 44         119 [ $v->[0] * $s, $v->[1] * $s ];
221             }
222              
223             =head2 Minus
224              
225             Title : Minus
226             Usage :
227             Function:
228             Example :
229             Returns :
230             Args :
231              
232              
233             =cut
234              
235             sub Minus {
236 1900     1900 1 1693 my $v = shift;
237 1900         4160 [ - $v->[0], - $v->[1] ];
238             }
239              
240             =head2 Intersection
241              
242             Title : Intersection
243             Usage :
244             Function:
245             Example :
246             Returns :
247             Args :
248              
249              
250             =cut
251              
252             sub Intersection {
253 950     950 1 917 my $l1 = shift;
254 950         791 my $l2 = shift;
255 950         1083 my ($bp1, $op1) = @$l1;
256 950         979 my ($bp2, $op2) = @$l2;
257 950         1277 my $v1 = VectorSum($op1, Minus($bp1));
258 950         1576 my $v2 = VectorSum($op2, Minus($bp2));
259 950         1298 my ($x1, $y1) = @$v1;
260 950         975 my ($x2, $y2) = @$v2;
261              
262 950         1104 my $DEN = $x1 * $y2 - $x2 * $y1;
263              
264             # Lines are parallel.
265 950 100       2740 return undef if $DEN == 0;
266              
267 44         49 my ($bx1, $by1) = @$bp1;
268 44         44 my ($bx2, $by2) = @$bp2;
269            
270 44         72 my $t1 = (($bx2 - $bx1) * $y2 - ($by2 - $by1) * $x2) / $DEN;
271 44         66 my $t2 = (($bx2 - $bx1) * $y1 - ($by2 - $by1) * $x1) / $DEN;
272              
273 44         76 my $RESULT = VectorSum($bp1, ScalarProd($t1, $v1));
274 44         135 ($RESULT, $t1, $t2);
275             }
276              
277             1;