File Coverage

blib/lib/GD/Graph/smoothlines.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package GD::Graph::smoothlines;
2              
3 1     1   24169 use strict;
  1         3  
  1         34  
4 1     1   4 use warnings;
  1         2  
  1         27  
5              
6 1     1   406 use GD;
  0            
  0            
7             use GD::Graph::lines;
8              
9             @GD::Graph::smoothlines::ISA = qw( GD::Graph::lines );
10              
11             use vars qw($VERSION);
12             $VERSION = '1.6';
13              
14             # Bezier smoothed plottype
15             # http://homepages.borland.com/efg2lab/Graphics/Jean-YvesQueinecBezierCurves.htm - description of bezier curves
16              
17             sub smoothFactor {
18             $_[0]->{_smoothFactor} = $_[1] if ( defined $_[1] );
19             return exists $_[0]->{_smoothFactor} ? $_[0]->{_smoothFactor} : 0.75;
20             }
21             sub bezierCurvePoints {
22             $_[0]->{_bezierCurvePoints} = $_[1] if ( defined $_[1] );
23             return exists $_[0]->{_bezierCurvePoints} ? $_[0]->{_bezierCurvePoints} : 50;
24             }
25              
26             sub _mid
27             {
28             my $self = shift;
29             my ( $p1, $p2 ) = @_;
30             return ( $p1 + $p2 ) / 2;
31             }
32             sub _mirror
33             {
34             my $self = shift;
35             my ( $p1, $p2, $factor ) = @_;
36             $factor = 1 unless ( defined $factor );
37             return $p2 + $factor * ( $p2 - $p1 );
38             }
39             sub _controlPoint
40             {
41             my $self = shift;
42             my ( $p1, $p2, $p3 ) = @_;
43            
44             my $sa = $self->_mirror( $p1, $p2, $self->smoothFactor );
45             my $sb = $self->_mid( $p2, $sa );
46            
47             my $m = $self->_mid( $p2, $p3 );
48            
49             my $pC = $self->_mid( $sb, $m );
50            
51             return $pC;
52             }
53             sub _bezier
54             {
55             my $self = shift;
56             my ( $t, $p1, $p2, $p3, $p4 ) = @_;
57             return ((1 - $t)**3) * $p1 +3 * ((1 - $t)**2) * $t * $p2 +3 * (1 - $t) * (($t)**2) * $p3 +(($t)**3) * $p4;
58             }
59             sub _getPoints
60             {
61             my $self = shift;
62             my ( $ds, $_dataset, $bUseValues ) = @_;
63            
64             my ( $x_min, $x_max );# = $self->{_data}->get_min_max_x( $ds );
65             my ( $y_min, $y_max );# = $self->{_data}->get_min_max_y( $ds );
66            
67             for ( @$_dataset ) {
68             my $x = $_->{x};
69             my $y = $_->{y};
70            
71             $x_min = $x if( ! defined $x_min || $x < $x_min );
72             $y_min = $y if( ! defined $y_min || $y < $y_min );
73            
74             $x_max = $x if( ! defined $x_max || $x > $x_max );
75             $y_max = $y if( ! defined $y_max || $y > $y_max );
76             }
77            
78             unless ( $bUseValues ) {
79             ( $x_min, $y_min ) = $self->val_to_pixel( $x_min, $y_min, $ds );
80             ( $x_max, $y_max ) = $self->val_to_pixel( $x_max, $y_max, $ds );
81             }
82            
83             my @plotarea = ();
84            
85             my $rightEdge = 0;
86            
87             my $_count = scalar( @$_dataset );
88             for my $index ( 0 .. ( $_count - 1 ) )
89             {
90             my $p0 = $index < 1 ? undef : $_dataset->[ $index - 1 ];
91             my $p1 = $_dataset->[ $index ];
92             my $p2 = $_dataset->[ $index + 1 ];
93             my $p3 = $_dataset->[ $index + 2 ];
94            
95             if ( ! defined $p0 && defined $p1 && defined $p2 ) {
96             $p0->{x} = $p1->{x} - abs( $p2->{x} - $p1->{x} );
97             $p0->{y} = $p1->{y};
98             }
99             elsif ( ! defined $p3 && defined $p2 && defined $p1 ) {
100             $p3->{x} = $p2->{x} + abs( $p2->{x} - $p1->{x} );
101             $p3->{y} = $p2->{y};
102             } else {
103             if ( ! defined $p2 ) {
104             $p2 = { x => $p1->{x} + abs( $p1->{x} - $p0->{x} ), y => $p1->{y} };
105             }
106             if ( ! defined $p3 ) {
107             $p3 = { x => $p1->{x} + 2 * abs( $p1->{x} - $p0->{x} ), y => $p1->{y} };
108             }
109             }
110            
111             my $pC1 = {};
112             my $pC2 = {};
113            
114             $pC1->{x} = $self->_controlPoint( $p0->{x}, $p1->{x}, $p2->{x} );
115             $pC1->{y} = $self->_controlPoint( $p0->{y}, $p1->{y}, $p2->{y} );
116             $pC2->{x} = $self->_controlPoint( $p3->{x}, $p2->{x}, $p1->{x} );
117             $pC2->{y} = $self->_controlPoint( $p3->{y}, $p2->{y}, $p1->{y} );
118            
119             $rightEdge = 0;
120             for ( my $t = 0; $t <= 1; $t = $t +1 / $self->bezierCurvePoints ) {
121             my $b = {};
122            
123             $b->{x} = $self->_bezier( $t, $p1->{x}, $pC1->{x}, $pC2->{x}, $p2->{x} );
124             $b->{y} = $self->_bezier( $t, $p1->{y}, $pC1->{y}, $pC2->{y}, $p2->{y} );
125            
126             if (
127             $b->{x} >= $x_min && $b->{x} <= $x_max
128             # &&
129             # $b->{y} >= $y_min && $b->{y} <= $y_max
130             ) {
131             $rightEdge = $rightEdge >= $b->{x} ? $rightEdge : $b->{x};
132             push( @plotarea, { x => $b->{x} , y => $b->{y} } );
133             }
134             }
135            
136             $index++;
137             }
138            
139             return \@plotarea;
140             }
141              
142             sub draw_data_set
143             {
144             my $self = shift;
145             my $ds = shift;
146            
147             my @values = $self->{_data}->y_values($ds) or
148             return $self->_set_error(
149             "Impossible illegal data set: $ds",
150             $self->{_data}->error
151             )
152             ;
153            
154             my $dsci = $self->set_clr($self->pick_data_clr($ds) );
155             my $type = $self->pick_line_type($ds);
156            
157             my ($xb, $yb);
158            
159             my @_points = ();
160            
161             for (my $i = 0; $i < @values; $i++)
162             {
163             if (!defined $values[$i])
164             {
165             ($xb, $yb) = () if $self->{skip_undef};
166             next;
167             }
168            
169             my ($xe, $ye);
170            
171             if (defined($self->{x_min_value}) && defined($self->{x_max_value}))
172             {
173             ($xe, $ye) = ( $self->{_data}->get_x($i), $values[$i] );
174             }
175             else
176             {
177             ($xe, $ye) = ( $i+1, $values[$i] );
178             }
179            
180             if (defined $xb)
181             {
182             my @asd = ( $xb, $yb );
183             push @_points, { x => $asd[0], y => $asd[1] };
184            
185             # TODO: is this correct?!
186             $self->{_hotspots}->[$ds]->[$i] = ['line', $xb, $yb, $xe, $ye, $self->{line_width}];
187             }
188             ($xb, $yb) = ($xe, $ye);
189             }
190            
191             my @asd = ( $xb, $yb );
192             push @_points, { x => $asd[0], y => $asd[1] };
193            
194             if ( scalar( @_points ) > 2 ) {
195             @_points = @{ $self->_getPoints( $ds, \@_points, 1 ) };
196             }
197            
198             my $asd = shift @_points;
199             ( $xb, $yb ) = $self->val_to_pixel( $asd->{x}, $asd->{y} );
200             for $asd ( @_points ) {
201             my ( $xe, $ye ) = $self->val_to_pixel( $asd->{x}, $asd->{y} );
202             $self->draw_line( $xb, $yb, $xe, $ye, $type, $dsci ) if defined $dsci;
203             ($xb, $yb) = ($xe, $ye);
204             }
205            
206             return $ds;
207             }
208              
209             1;
210              
211             __END__