File Coverage

blib/lib/Chart/Gnuplot.pm
Criterion Covered Total %
statement 649 1164 55.7
branch 325 740 43.9
condition 91 183 49.7
subroutine 49 79 62.0
pod 24 26 92.3
total 1138 2192 51.9


line stmt bran cond sub pod time code
1             package Chart::Gnuplot;
2 21     21   825759 use strict;
  21         110  
  21         1212  
3 21     21   234 use vars qw($VERSION);
  21         46  
  21         1427  
4 21     21   122 use Carp;
  21         40  
  21         3496  
5 21     21   27828 use File::Copy qw(move);
  21         203670  
  21         3115  
6 21     21   45390 use File::Temp qw(tempdir);
  21         671733  
  21         2223  
7 21     21   14304 use Chart::Gnuplot::Util qw(_lineType _pointType _borderCode _fillStyle _copy);
  21         97  
  21         316683  
8             $VERSION = '0.21';
9              
10             # Constructor
11             sub new
12             {
13 35     35 1 17921 my ($class, %hash) = @_;
14              
15             # Create temporary file to store Gnuplot instructions
16 35 50       154 if (!defined $hash{_multiplot}) # if not in multiplot mode
17             {
18 35         184 my $dirTmp = tempdir(CLEANUP => 1);
19 35 50       22371 ($^O =~ /MSWin/)? ($dirTmp .= '\\'): ($dirTmp .= '/');
20 35         142 $hash{_script} = $dirTmp . "plot";
21             }
22              
23             # Default terminal: postscript terminal with color drawing elements
24 35 50 33     260 if (!defined $hash{terminal} && !defined $hash{term})
25             {
26 35         92 $hash{terminal} = "postscript enhanced color";
27 35         86 $hash{_terminal} = 'auto';
28             }
29              
30             # Default setting
31 35 50       152 if (defined $hash{output})
32             {
33 35         258 my @a = split(/\./, $hash{output});
34 35         115 my $ext = $a[-1];
35 35 50 33     1895 $hash{terminal} .= " eps" if ($hash{terminal} =~ /^post/ &&
36             $ext eq 'eps');
37             }
38              
39 35         81 my $self = \%hash;
40 35         166 return bless($self, $class);
41             }
42              
43              
44             # Generic attribute methods
45             sub AUTOLOAD
46             {
47 46     46   154 my ($self, $key) = @_;
48 46         115 my $attr = our $AUTOLOAD;
49 46         239 $attr =~ s/.*:://;
50 46 50       148 return if ($attr eq 'DESTROY'); # ignore destructor
51 46 50       169 $self->{$attr} = $key if (defined $key);
52 46         186 return($self->{$attr});
53             }
54              
55              
56             # General set method
57             sub set
58             {
59 1     1 1 10 my ($self, %opts) = @_;
60 1         3 foreach my $opt (keys %opts)
61             {
62 3 50       25 ($opts{$opt} eq 'on')? $self->$opt('') : $self->$opt($opts{$opt});
63             }
64 1         13 return($self);
65             }
66              
67              
68             # Add a 2D data set to the chart object
69             # - used with multiplot
70             sub add2d
71             {
72 0     0 1 0 my ($self, @dataSet) = @_;
73 0         0 push(@{$self->{_dataSets2D}}, @dataSet);
  0         0  
74             }
75              
76              
77             # Add a 3D data set to the chart object
78             # - used with multiplot
79             sub add3d
80             {
81 0     0 1 0 my ($self, @dataSet) = @_;
82 0         0 push(@{$self->{_dataSets3D}}, @dataSet);
  0         0  
83             }
84              
85              
86             # Add a 2D data set to the chart object
87             # - redirect to &add2d
88             # - for backward compatibility
89 0     0 0 0 sub add {&add2d(@_);}
90              
91              
92             # Plot 2D graphs
93             # - call _setChart()
94             #
95             # TODO:
96             # - Consider using pipe instead of system call
97             # - support MS time format: %{yyyy}-%{mmm}-%{dd} %{HH}:%{MM}
98             sub plot2d
99             {
100 0     0 1 0 my ($self, @dataSet) = @_;
101 0         0 &_setChart($self, \@dataSet);
102              
103 0         0 my $plotString = join(', ', map {$_->_thaw($self)} @dataSet);
  0         0  
104 0 0       0 open(GPH, ">>$self->{_script}") || confess("Can't write $self->{_script}");
105 0         0 print GPH "\nplot $plotString\n";
106 0         0 close(GPH);
107              
108             # Generate image file
109 0         0 &execute($self);
110 0         0 return($self);
111             }
112              
113              
114             # Plot 3D graphs
115             # - call _setChart()
116             #
117             # TODO:
118             # - Consider using pipe instead of system call
119             # - support MS time format: %{yyyy}-%{mmm}-%{dd} %{HH}:%{MM}
120             sub plot3d
121             {
122 0     0 1 0 my ($self, @dataSet) = @_;
123 0         0 &_setChart($self, \@dataSet);
124              
125 0         0 my $plotString = join(', ', map {$_->_thaw($self)} @dataSet);
  0         0  
126 0 0       0 open(GPH, ">>$self->{_script}") || confess("Can't write $self->{_script}");
127 0         0 print GPH "\nsplot $plotString\n";
128 0         0 close(GPH);
129              
130             # Generate image file
131 0         0 &execute($self);
132 0         0 return($self);
133             }
134              
135              
136             # Plot multiple plots in one single chart
137             sub multiplot
138             {
139 0     0 1 0 my ($self, @charts) = @_;
140 0         0 &_setChart($self);
141 0         0 &_reset($self);
142              
143 0 0       0 open(PLT, ">>$self->{_script}") || confess("Can't write $self->{_script}");
144              
145             # Emulate the title when there is background color fill
146 0 0 0     0 if (defined $self->{title} && defined $self->{bg})
147             {
148 0         0 print PLT "set label \"$self->{title}\" at screen 0.5, screen 1 ".
149             "center offset 0,-1\n";
150             }
151              
152 0 0 0     0 if (scalar(@charts) == 1 && ref($charts[0]) eq 'ARRAY')
153             {
154 0         0 my $nrows = scalar(@{$charts[0]});
  0         0  
155 0         0 my $ncols = scalar(@{$charts[0][0]});
  0         0  
156 0         0 &_setMultiplot($self, $nrows, $ncols);
157            
158 0         0 for (my $r = 0; $r < $nrows; $r++)
159             {
160 0         0 for (my $c = 0; $c < $ncols; $c++)
161             {
162 0         0 my $chart = $charts[0][$r][$c];
163 0         0 $chart->_script($self->{_script});
164 0         0 $chart->_multiplot(1);
165 0         0 delete $chart->{bg};
166              
167 0         0 my $plot;
168             my @dataSet;
169 0 0       0 if (defined $chart->{_dataSets2D})
    0          
170             {
171 0         0 $plot = 'plot';
172 0         0 @dataSet = @{$chart->{_dataSets2D}};
  0         0  
173             }
174             elsif (defined $chart->{_dataSets3D})
175             {
176 0         0 $plot = 'splot';
177 0         0 @dataSet = @{$chart->{_dataSets3D}};
  0         0  
178             }
179              
180 0         0 &_setChart($chart, \@dataSet);
181 0 0       0 open(PLT, ">>$self->{_script}") ||
182             confess("Can't write $self->{_script}");
183 0         0 print PLT "\n$plot ";
184 0         0 print PLT join(', ', map {$_->_thaw($self)} @dataSet), "\n";
  0         0  
185 0         0 close(PLT);
186 0         0 &_reset($chart);
187             }
188             }
189             }
190             else
191             {
192             # Start multi-plot
193 0         0 &_setMultiplot($self);
194              
195 0         0 foreach my $chart (@charts)
196             {
197 0         0 $chart->_script($self->{_script});
198 0         0 $chart->_multiplot(1);
199 0         0 delete $chart->{bg};
200              
201 0         0 my $plot;
202             my @dataSet;
203 0 0       0 if (defined $chart->{_dataSets2D})
    0          
204             {
205 0         0 $plot = 'plot';
206 0         0 @dataSet = @{$chart->{_dataSets2D}};
  0         0  
207             }
208             elsif (defined $chart->{_dataSets3D})
209             {
210 0         0 $plot = 'splot';
211 0         0 @dataSet = @{$chart->{_dataSets3D}};
  0         0  
212             }
213            
214 0         0 &_setChart($chart, \@dataSet);
215 0 0       0 open(PLT, ">>$self->{_script}") ||
216             confess("Can't write $self->{_script}");
217 0         0 print PLT "\n$plot ";
218 0         0 print PLT join(', ', map {$_->_thaw($self)} @dataSet), "\n";
  0         0  
219 0         0 close(PLT);
220 0         0 &_reset($chart);
221             }
222             }
223 0         0 close(PLT);
224              
225             # Generate image file
226 0         0 &execute($self);
227 0         0 return($self);
228             }
229              
230              
231             # Pass generic commands
232             sub command
233             {
234 2     2 1 14 my ($self, $cmd) = @_;
235              
236 2 50       189 open(PLT, ">>$self->{_script}") || confess("Can't write $self->{_script}");
237 2 100       36 (ref($cmd) eq 'ARRAY')?
238             (print PLT join("\n", @$cmd), "\n"):
239             (print PLT "$cmd\n");
240 2         90 close(PLT);
241 2         6 return($self);
242             }
243              
244              
245             # Set how the chart looks like
246             # - call _setTitle(), _setAxisLabel(), _setTics(), _setGrid(), _setBorder(),
247             # _setTimestamp()
248             # - called by plot2d() and plot3d()
249             sub _setChart
250             {
251 37     37   210 my ($self, $dataSets) = @_;
252 37         91 my @sets = ();
253              
254             # Orientation
255 37 100       193 $self->{terminal} .= " $self->{orient}" if (defined $self->{orient});
256              
257             # Set canvas size
258 37 100       125 if (defined $self->{imagesize})
259             {
260 3         18 my ($ws, $hs) = split(/,\s?/, $self->{imagesize});
261 3 50 33     24 if (defined $self->{_terminal} && $self->{_terminal} eq 'auto')
262             {
263             # for post terminal
264 3 100 66     15 if (defined $self->{orient} && $self->{orient} eq 'portrait')
265             {
266 1 50       9 $ws *= 7 if ($ws =~ /^([1-9]\d*)?0?(\.\d+)?$/);
267 1 50       8 $hs *= 10 if ($hs =~ /^([1-9]\d*)?0?(\.\d+)?$/);
268             }
269             else
270             {
271 2 100       16 $ws *= 10 if ($ws =~ /^([1-9]\d*)?0?(\.\d+)?$/);
272 2 100       12 $hs *= 7 if ($hs =~ /^([1-9]\d*)?0?(\.\d+)?$/);
273             }
274             }
275 3         33 $self->{terminal} .= " size $ws,$hs";
276             }
277              
278             # Prevent changing terminal in multiplot mode
279 37 50       116 delete $self->{terminal} if (defined $self->{_multiplot});
280              
281             # Start writing gnuplot script
282 37         82 my $pltTmp = $self->{_script};
283 37 50       3269 open(PLT, ">>$pltTmp") || confess("Can't write gnuplot script $pltTmp");
284              
285             # Set character encoding
286             #
287             # Quote from Gnuplot manual:
288             # "Generally you must set the encoding before setting the terminal type."
289 37 50       164 if (defined $self->{encoding})
290             {
291 0         0 print PLT "set encoding $self->{encoding}\n";
292             }
293              
294             # Chart background color
295 37 100       126 if (defined $self->{bg})
296             {
297 2         3 my $bg = $self->{bg};
298 2 50       7 if (ref($bg) eq 'HASH')
299             {
300 2         34 print PLT "set object rect from screen 0, screen 0 to ".
301             "screen 1, screen 1 fillcolor rgb \"$$bg{color}\"";
302 2 50       31 print PLT " fillstyle solid $$bg{density}" if
303             (defined $$bg{density});
304 2         3 print PLT " behind\n";
305             }
306             else
307             {
308 0         0 print PLT "set object rect from screen 0, screen 0 to ".
309             "screen 1, screen 1 fillcolor rgb \"$bg\" behind\n";
310             }
311 2         7 push(@sets, 'object');
312             }
313              
314             # Plot area background color
315 37 100       137 if (defined $self->{plotbg})
316             {
317 2         4 my $bg = $self->{plotbg};
318 2 50       10 if (ref($bg) eq 'HASH')
319             {
320 2         13 print PLT "set object rect from graph 0, graph 0 to ".
321             "graph 1, graph 1 fillcolor rgb \"$$bg{color}\"";
322 2 50       21 print PLT " fillstyle solid $$bg{density}" if
323             (defined $$bg{density});
324 2         4 print PLT " behind\n";
325             }
326             else
327             {
328 0         0 print PLT "set object rect from graph 0, graph 0 to ".
329             "graph 1, graph 1 fillcolor rgb \"$bg\" behind\n";
330             }
331 2         5 push(@sets, 'object');
332             }
333              
334             # Set date/time data
335             #
336             # For xrange to work for time-sequence, time-axis ("set xdata time")
337             # and timeformat ("set timefmt '%Y-%m-%d'") MUST be set BEFORE
338             # the range command ("set xrange ['2009-01-01','2009-01-07']")
339             #
340             # Thanks to Holyspell
341 37 100       153 if (defined $self->{timeaxis})
342             {
343 2         9 my @axis = split(/,\s?/, $self->{timeaxis});
344 2         5 foreach my $axis (@axis)
345             {
346 2         19 print PLT "set $axis"."data time\n";
347 2         7 push(@sets, $axis."data");
348             }
349              
350 2         4 foreach my $ds (@$dataSets)
351             {
352 2 50       11 if (defined $ds->{timefmt})
353             {
354 2         5 print PLT "set timefmt \"$ds->{timefmt}\"\n";
355 2         4 last;
356             }
357             }
358             }
359              
360             # Parametric plot
361 37         129 foreach my $ds (@$dataSets)
362             {
363             # Determine if there is paramatric plot
364 5 100 100     41 if (defined $ds->{func} && ref($ds->{func}) eq 'HASH')
365             {
366 2         5 $self->{parametric} = '';
367 2         5 last;
368             }
369             }
370              
371 37         66 my $setGrid = 0; # detect whether _setGrid has been run
372              
373             # Loop and process other chart options
374 37         163 foreach my $attr (keys %$self)
375             {
376 204 100 100     5748 if ($attr eq 'output')
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
377             {
378 37         263 print PLT "set output \"$self->{output}\"\n";
379             }
380             elsif ($attr eq 'title')
381             {
382 8         29 print PLT "set title ".&_setTitle($self->{title})."\n";
383 8         22 push(@sets, 'title')
384             }
385             elsif ($attr =~ /^((x|y)2?|z)label$/)
386             {
387 6         16 print PLT "set $attr ".&_setAxisLabel($self->{$attr})."\n";
388 6         13 push(@sets, $attr);
389             }
390             elsif ($attr =~ /^((x|y)2?|z|t|u|v)range$/)
391             {
392 7 100       21 if (ref($self->{$attr}) eq 'ARRAY')
    50          
393             {
394             # Deal with ranges from array reference
395 6 50 33     28 if (defined $self->{timeaxis} &&
396             $self->{timeaxis} =~ /(^|,)\s*$1\s*(,|$)/)
397             {
398             # $1-axis is a time axis
399 0         0 print PLT "set $attr ['".join("':'", @{$self->{$attr}}).
  0         0  
400             "']\n";
401             }
402             else
403             {
404 6         11 print PLT "set $attr [".join(":", @{$self->{$attr}})."]\n";
  6         45  
405             }
406             }
407             elsif ($self->{$attr} eq 'reverse')
408             {
409 0         0 print PLT "set $attr [*:*] reverse\n";
410             }
411             else
412             {
413 1         4 print PLT "set $attr $self->{$attr}\n";
414             }
415 7         16 push(@sets, $attr);
416             }
417             elsif ($attr =~ /^(x|y|x2|y2|z)tics$/)
418             {
419 7 50       18 if (defined $self->{$attr})
420             {
421 7         35 my ($axis) = ($attr =~ /^(.+)tics$/);
422 7         31 print PLT "set $attr".&_setTics($self->{$attr})."\n";
423 7 100       25 if (ref($self->{$attr}) eq 'HASH')
424             {
425 4 100       5 if (defined ${$self->{$attr}}{labelfmt})
  4         19  
426             {
427 2         7 print PLT "set format $axis ".
428 2         6 "\"${$self->{$attr}}{labelfmt}\"\n";
429 2         5 push(@sets, 'format');
430             }
431 4 100       5 if (defined ${$self->{$attr}}{minor})
  4         13  
432             {
433 1         1 my $nTics = ${$self->{$attr}}{minor}+1;
  1         3  
434 1         5 print PLT "set m$axis"."tics $nTics\n";
435 1         3 push(@sets, "m$axis"."tics");
436             }
437             }
438 7         17 push(@sets, $attr);
439             }
440             else
441             {
442 0         0 print PLT "unset $attr\n";
443             }
444             }
445             elsif ($attr eq 'legend')
446             {
447 1         6 print PLT "set key".&_setLegend($self->{legend})."\n";
448 1         3 push(@sets, 'key');
449             }
450             elsif ($attr eq 'border')
451             {
452 2 50       6 if (defined $self->{border})
453             {
454 2         16 print PLT "set border";
455 2 100       15 print PLT " ".&_borderCode($self->{border}->{sides}) if
456             (defined $self->{border}->{sides});
457 2         9 print PLT &_setBorder($self->{border})."\n";
458 2         7 push(@sets, 'border');
459             }
460             else
461             {
462 0         0 print PLT "unset border\n";
463             }
464             }
465             elsif ($attr =~ /^(minor)?grid$/)
466             {
467 4 100       11 next if ($setGrid == 1);
468              
469 3         9 print PLT "set grid".&_setGrid($self)."\n";
470 3         7 push(@sets, 'grid');
471 3         5 $setGrid = 1;
472             }
473             elsif ($attr eq 'timestamp')
474             {
475 2         13 print PLT "set timestamp".&_setTimestamp($self->{timestamp})."\n";
476 2         5 push(@sets, 'timestamp');
477             }
478             elsif ($attr eq 'terminal')
479             {
480 37         269 print PLT "set $attr $self->{$attr}\n";
481             }
482             # Non-gnuplot options / options specially treated before
483             elsif (!grep(/^$attr$/, qw(
484             gnuplot
485             convert
486             encoding
487             imagesize
488             orient
489             bg
490             plotbg
491             timeaxis
492             )) &&
493             $attr !~ /^_/)
494             {
495 5 100 66     44 (defined $self->{$attr} && $self->{$attr} ne '')?
496             (print PLT "set $attr $self->{$attr}\n"):
497             (print PLT "set $attr\n");
498 5         14 push(@sets, $attr);
499             }
500             }
501              
502             # Write labels
503 37         96 my $isLabelSet = 0;
504 37         56 foreach my $label (@{$self->{_labels}})
  37         128  
505             {
506 4         11 print PLT "set label"."$label\n";
507 4 100       13 push(@sets, "label") if ($isLabelSet == 0);
508 4         8 $isLabelSet = 1;
509             }
510              
511             # Draw arrows
512 37         72 my $isArrowSet = 0;
513 37         66 foreach my $arrow (@{$self->{_arrows}})
  37         207  
514             {
515 0         0 print PLT "set arrow"."$arrow\n";
516 0 0       0 push(@sets, "arrow") if ($isArrowSet == 0);
517 0         0 $isArrowSet = 1;
518             }
519              
520             # Draw objects
521 37         73 my $isObjectSet = 0;
522 37         55 foreach my $object (@{$self->{_objects}})
  37         152  
523             {
524 0         0 print PLT "set object"."$object\n";
525 0 0       0 push(@sets, "object") if ($isObjectSet == 0);
526 0         0 $isObjectSet = 1;
527             }
528 37         2480 close(PLT);
529              
530 37         347 $self->_sets(\@sets);
531             }
532              
533              
534             # Set the details of the title
535             # - called by _setChart()
536             #
537             # Usage example:
538             # title => {
539             # text => "My title",
540             # font => "arial, 14",
541             # color => "brown",
542             # offset => "0, -1",
543             # },
544             sub _setTitle
545             {
546 8     8   14 my ($title) = @_;
547 8 100       18 if (ref($title))
548             {
549 1         5 my $out = "\"$$title{text}\"";
550 1 50       8 $out .= " offset $$title{offset}" if (defined $$title{offset});
551              
552             # Font and size
553 1         2 my $font;
554 1 50       4 $font = $$title{font} if (defined $$title{font});
555 1 50       4 $font .= ",$$title{fontsize}" if (defined $$title{fontsize});
556 1 50       5 $out .= " font \"$font\"" if (defined $font);
557              
558             # Color
559 1 50       7 $out .= " textcolor rgb \"$$title{color}\"" if (defined $$title{color});
560              
561             # Switch of the enhanced mode. Default: off
562 1 50 33     9 $out .= " noenhanced" if (!defined $$title{enhanced} ||
563             $$title{enhanced} ne 'on');
564 1         6 return($out);
565             }
566             else
567             {
568 7         31 return("\"$title\" noenhanced");
569             }
570             }
571              
572              
573             # Set the details of the axis labels
574             # - called by _setChart()
575             #
576             # Usage example:
577             # xlabel => {
578             # text => "My x-axis label",
579             # font => "arial, 14",
580             # color => "brown",
581             # offset => "0, -1",
582             # rotate => 45,
583             # },
584             #
585             # TODO
586             # - support radian and pi in "rotate"
587             sub _setAxisLabel
588             {
589 6     6   7 my ($label) = @_;
590 6 100       10 if (ref($label))
591             {
592 2         6 my $out = "\"$$label{text}\"";
593              
594             # Location offset
595 2 100       7 $out .= " offset $$label{offset}" if (defined $$label{offset});
596              
597             # Font and size
598 2         1 my $font;
599 2 100       7 $font = $$label{font} if (defined $$label{font});
600 2 50       5 $font .= ",$$label{fontsize}" if (defined $$label{fontsize});
601 2 100       6 $out .= " font \"$font\"" if (defined $font);
602              
603             # Color
604 2 100       7 $out .= " textcolor rgb \"$$label{color}\"" if (defined $$label{color});
605              
606             # Switch of the enhanced mode. Default: off
607 2 100 66     10 $out .= " noenhanced" if (!defined $$label{enhanced} ||
608             $$label{enhanced} ne 'on');
609              
610             # Text rotation
611 2 100       6 $out .= " rotate by $$label{rotate}" if (defined $$label{rotate});
612 2         7 return($out);
613             }
614             else
615             {
616 4         31 return("\"$label\" noenhanced");
617             }
618             }
619              
620              
621             # Set the details of the tics and tic labels
622             # - called by _setChart()
623             #
624             # Usage example:
625             # xtics => {
626             # labels => [-10, 15, 20, 25],
627             # labelfmt => "%3f",
628             # font => "arial",
629             # fontsize => 14,
630             # fontcolor => "brown",
631             # offset => "0, -1",
632             # rotate => 45,
633             # length => "2,1",
634             # along => 'axis',
635             # minor => 3,
636             # mirror => 'off',
637             # },
638             #
639             # TODO
640             # - able to replace the label for specified text
641             # - implement "add" option to add addition tics other than default
642             # - support radian and pi in "rotate"
643             sub _setTics
644             {
645 7     7   12 my ($tic) = @_;
646              
647 7         10 my $out = '';
648 7 100       30 if (ref($tic) eq 'HASH')
    100          
    50          
649             {
650 4 50       13 $out .= " $$tic{along}" if (defined $$tic{along});
651 4 100 66     27 $out .= " nomirror" if (
652             defined $$tic{mirror} && $$tic{mirror} eq 'off'
653             );
654 4 100       15 $out .= " scale $$tic{length}" if (defined $$tic{length});
655 4 100       13 $out .= " rotate by $$tic{rotate}" if (defined $$tic{rotate});
656 4 50       11 $out .= " offset $$tic{offset}" if (defined $$tic{offset});
657 4 100       10 $out .= " (". join(',', @{$$tic{labels}}) . ")" if
  1         26  
658             (defined $$tic{labels});
659              
660             # Font, font size and font color
661 4 100       14 if (defined $$tic{font})
662             {
663 1         3 my $font = $$tic{font};
664 1 50 33     6 $font = "$$tic{font},$$tic{fontsize}" if ($font !~ /\,/ &&
665             defined $$tic{fontsize});
666 1         4 $out .= " font \"$font\"";
667             }
668 4 100       14 $out .= " textcolor rgb \"$$tic{fontcolor}\"" if
669             (defined $$tic{fontcolor});
670             }
671             elsif (ref($tic) eq 'ARRAY')
672             {
673 1         7 $out = " (". join(',', @$tic) . ")";
674             }
675             elsif ($tic ne 'on')
676             {
677 0         0 $out = "\"$tic\"";
678             }
679 7         19 return($out);
680             }
681              
682              
683             # Set the details of the grid lines
684             # - called by _setChart()
685             #
686             # Usage example:
687             # grid => {
688             # type => 'dash, dot', # default: dot
689             # width => 2, # default: 0
690             # color => 'blue', # default: black
691             # xlines => 'on', # default: on
692             # ylines => 'off', # default: on
693             # zlines => 'off', # default:
694             # x2lines => 'off', # default: off
695             # y2lines => 'off', # default: off
696             # },
697             #
698             # minorgrid => {
699             # width => 1, # default: 0
700             # color => 'gray', # default: black
701             # xlines => 'on', # default: off
702             # ylines => 'on', # default: off
703             # x2lines => 'off', # default: off
704             # y2lines => 'off', # default: off
705             # }
706             #
707             # # OR
708             #
709             # grid => 'on',
710             #
711             # TODO:
712             # - support polar grid
713             # - support layer
714             sub _setGrid
715             {
716 3     3   4 my ($self) = @_;
717 3         7 my $grid = $self->{grid};
718 3 100       9 my $mgrid = $self->{minorgrid} if (defined $self->{minorgrid});
719              
720 3         6 my $out = '';
721 3 100 66     18 if (ref($grid) eq 'HASH' || ref($mgrid) eq 'HASH')
722             {
723 2         7 $grid = &_gridString2Hash($grid);
724 2         7 $mgrid = &_gridString2Hash($mgrid);
725              
726             # Set whether the major grid lines are drawn
727 2 50 33     16 (defined $$grid{xlines} && $$grid{xlines} =~ /^off/)?
728             ($out .= " noxtics"): ($out .= " xtics");
729 2 50 33     15 (defined $$grid{ylines} && $$grid{ylines} =~ /^off/)?
730             ($out .= " noytics"): ($out .= " ytics");
731 2 50 33     9 (defined $$grid{zlines} && $$grid{zlines} =~ /^off/)?
732             ($out .= " noztics"): ($out .= " ztics");
733              
734             # Set whether the vertical minor grid lines are drawn
735 2 50 66     28 $out .= " mxtics" if ( (defined $$grid{xlines} &&
      33        
      33        
      66        
736             $$grid{xlines} =~ /,\s?on$/) ||
737             (defined $self->{minorgrid} && (!defined $$mgrid{xlines} ||
738             $$mgrid{xlines} eq 'on')) );
739              
740             # Set whether the horizontal minor grid lines are drawn
741 2 100 33     30 $out .= " mytics" if ( (defined $$grid{ylines} &&
      66        
      33        
      33        
742             $$grid{ylines} =~ /,\s?on$/) ||
743             (defined $mgrid && (!defined $$mgrid{ylines} ||
744             $$mgrid{ylines} eq 'on')) );
745              
746             # Major grid on secondary axes
747 2 50 33     8 $out .= " x2tics" if (defined $$grid{x2lines} &&
748             $$grid{x2lines} eq 'on');
749 2 50 33     6 $out .= " y2tics" if (defined $$grid{y2lines} &&
750             $$grid{y2lines} eq 'on');
751              
752             # Minor grid on secondary axes
753 2 50 33     7 $out .= " mx2tics" if (defined $$mgrid{x2lines} &&
754             $$mgrid{x2lines} eq 'on');
755 2 50 33     8 $out .= " my2tics" if (defined $$mgrid{y2lines} &&
756             $$mgrid{y2lines} eq 'on');
757              
758             # Set the line type of the grid lines
759 2         5 my $major = my $minor = '';
760 2         3 my $majorType = my $minorType = 4; # dotted lines
761 2 50       7 if (defined $$grid{linetype})
762             {
763 0         0 $majorType = $minorType = $$grid{linetype};
764 0 0       0 ($majorType, $minorType) = split(/\,\s?/, $$grid{linetype}) if
765             ($$grid{linetype} =~ /\,/);
766             }
767 2 50       5 $minorType = $$mgrid{linetype} if (defined $$mgrid{linetype});
768 2         11 $major .= " linetype ".&_lineType($majorType);
769 2         7 $minor .= " linetype ".&_lineType($minorType);
770            
771             # Set the line width of the grid lines
772 2         4 my $majorWidth = my $minorWidth = 0;
773 2 50       7 if (defined $$grid{width})
774             {
775 2         5 $majorWidth = $minorWidth = $$grid{width};
776 2 100       14 ($majorWidth, $minorWidth) = split(/\,\s?/, $$grid{width}) if
777             ($$grid{width} =~ /\,/);
778             }
779 2 100       11 $minorWidth = $$mgrid{width} if (defined $$mgrid{width});
780 2         4 $major .= " linewidth $majorWidth";
781 2         4 $minor .= " linewidth $minorWidth";
782              
783             # Set the line color of the grid lines
784 2         5 my $majorColor = my $minorColor = 'black';
785 2 50       7 if (defined $$grid{color})
786             {
787 0         0 $majorColor = $minorColor = $$grid{color};
788 0 0       0 ($majorColor, $minorColor) = split(/\,\s?/, $$grid{color}) if
789             ($$grid{color} =~ /\,/);
790             }
791 2 50       6 $minorColor = $$mgrid{color} if (defined $$mgrid{color});
792 2         5 $major .= " linecolor rgb \"$majorColor\"";
793 2         4 $minor .= " linecolor rgb \"$minorColor\"";
794 2 50       9 $out .= "$major" if ($major ne '');
795 2 50       8 $out .= ",$minor" if ($minor ne '');
796             }
797             else
798             {
799 1 50       4 if (defined $grid)
800             {
801 1 50       8 return(" $grid") if ($grid !~ /^(on|off)$/);
802 1 50       3 ($grid eq 'off')? ($out = " noxtics noytics"):
803             ($out = " xtics ytics");
804             }
805 1 50 33     4 $out .= " mxtics mytics" if (defined $mgrid && $mgrid eq 'on');
806             }
807 3         26 return($out);
808             }
809              
810              
811             # Convert grid string to hash
812             # - called by _setGrid
813             sub _gridString2Hash
814             {
815 4     4   6 my ($grid) = @_;
816 4 100       15 return($grid) if (ref($grid) eq 'HASH');
817              
818 1         2 my %out;
819 1         4 $out{xlines} = $out{ylines} = $out{zlines} = $grid;
820 1         3 return(\%out);
821             }
822              
823              
824             # Set the details of the graph border and legend box border
825             # - called by _setChart()
826             #
827             # Usage example:
828             # border => {
829             # linetype => 3, # default: solid
830             # width => 2, # default: 0
831             # color => '#ff00ff', # default: system defined
832             # },
833             #
834             # Remark:
835             # - By default, the color of the axis tics would follow the border unless
836             # specified otherwise.
837             #
838             # TODO:
839             # - support layer
840             sub _setBorder
841             {
842 2     2   3 my ($border) = @_;
843              
844 2         5 my $out = '';
845 2 50       13 $out .= " linetype ".&_lineType($$border{linetype}) if
846             (defined $$border{linetype});
847 2 50       11 $out .= " linecolor rgb \"$$border{color}\"" if (defined $$border{color});
848 2 50       7 $out .= " linewidth $$border{width}" if (defined $$border{width});
849 2         6 return($out);
850             }
851              
852              
853             # Format the legend (key)
854             #
855             # Usage example:
856             # legend => {
857             # position => "outside bottom",
858             # width => 3,
859             # height => 4,
860             # align => "right",
861             # order => "horizontal reverse",
862             # title => "Title of the legend",
863             # sample => {
864             # length => 3,
865             # position => "left",
866             # spacing => 2,
867             # },
868             # border => {
869             # linetype => 2,
870             # width => 1,
871             # color => "blue",
872             # },
873             # },
874             sub _setLegend
875             {
876 1     1   3 my ($legend) = @_;
877              
878 1         2 my $out = '';
879 1 50       4 if (defined $$legend{position})
880             {
881 1 50       8 ($$legend{position} =~ /\d/)? ($out .= " at $$legend{position}"):
882             ($out .= " $$legend{position}");
883             }
884 1 50       10 $out .= " width $$legend{width}" if (defined $$legend{width});
885 1 50       6 $out .= " height $$legend{height}" if (defined $$legend{height});
886 1 50       3 if (defined $$legend{align})
887             {
888 1 50       4 $out .= " Left" if ($$legend{align} eq 'left');
889 1 50       4 $out .= " Right" if ($$legend{align} eq 'right');
890             }
891 1 50       4 if (defined $$legend{order})
892             {
893 1         2 my $order = $$legend{order};
894 1         5 $order =~ s/reverse/invert/;
895 1         3 $out .= " $order";
896             }
897 1 50       4 if (defined $$legend{title})
898             {
899 1 50       3 if (ref($$legend{title}) eq 'HASH')
900             {
901 0         0 my $title = $$legend{title};
902 0         0 $out .= " title \"$$title{text}\"";
903 0 0 0     0 $out .= " noenhanced" if (!defined $$title{enhanced} ||
904             $$title{enhanced} ne 'on');
905             }
906             else
907             {
908 1         4 $out .= " title \"$$legend{title}\" noenhanced";
909             }
910             }
911 1 50       4 if (defined $$legend{sample})
912             {
913 1 50       6 $out .= " samplen $$legend{sample}{length}" if
914             (defined $$legend{sample}{length});
915 1 50 33     5 $out .= " reverse" if (defined $$legend{sample}{position} ||
916             $$legend{sample}{position} eq "left");
917 1 50       14 $out .= " spacing $$legend{sample}{spacing}" if
918             (defined $$legend{sample}{spacing});
919             }
920 1 50       10 if (defined $$legend{border})
921             {
922 1 50       18 if (ref($$legend{border}) eq 'HASH')
    50          
    50          
923             {
924 0         0 $out .= " box ".&_setBorder($$legend{border});
925             }
926             elsif ($$legend{border} eq "off")
927             {
928 0         0 $out .= " no box";
929             }
930             elsif ($$legend{border} eq "on")
931             {
932 1         2 $out .= " box";
933             }
934             }
935 1         4 return($out);
936             }
937              
938              
939             # Set title and layout of the multiplot
940             sub _setMultiplot
941             {
942 0     0   0 my ($self, $nrows, $ncols) = @_;
943              
944 0 0       0 open(PLT, ">>$self->{_script}") || confess("Can't write $self->{_script}");
945 0         0 print PLT "set multiplot";
946 0 0       0 print PLT " title \"$self->{title}\"" if (defined $self->{title});
947 0 0       0 print PLT " layout $nrows, $ncols" if (defined $nrows);
948 0         0 print PLT "\n";
949 0         0 close(PLT);
950             }
951              
952              
953             # Usage example:
954             # timestamp => {
955             # fmt => '%d/%m/%y %H:%M',
956             # offset => "10,-3"
957             # font => "Helvetica",
958             # },
959             # # OR
960             # timestamp => 'on';
961             sub _setTimestamp
962             {
963 2     2   4 my ($ts) = @_;
964              
965 2         4 my $out = '';
966 2 100       9 if (ref($ts) eq 'HASH')
    50          
967             {
968 1 50       6 $out .= " \"$$ts{fmt}\"" if (defined $$ts{fmt});
969 1 50       6 $out .= " offset $$ts{offset}" if (defined $$ts{offset});
970 1 50       5 $out .= " font \"$$ts{font}\"" if (defined $$ts{font});
971             }
972             elsif ($ts ne 'on')
973             {
974 1         4 return($ts);
975             }
976 1         3 return($out);
977             }
978              
979              
980             # Call Gnuplot to generate the image file
981             sub execute
982             {
983 0     0 1 0 my ($self) = @_;
984              
985             # Try to find the executable of Gnuplot
986 0         0 my $gnuplot = 'gnuplot';
987 0 0       0 if (defined $self->{gnuplot})
988             {
989 0         0 $gnuplot = $self->{gnuplot};
990             }
991             else
992             {
993 0 0       0 if ($^O =~ /MSWin/)
994             {
995 0         0 my $gnuplotDir = 'C:\Program Files\gnuplot';
996 0 0       0 $gnuplotDir = 'C:\Program Files (x86)\gnuplot' if (!-e $gnuplotDir);
997              
998 0         0 my $binDir = $gnuplotDir.'\bin';
999 0 0       0 $binDir = $gnuplotDir.'\binary' if (!-e $binDir);
1000              
1001 0         0 $gnuplot = $binDir.'\gnuplot.exe';
1002 0 0       0 if (!-e $gnuplot)
1003             {
1004 0         0 $gnuplot = $binDir.'\wgnuplot.exe';
1005 0 0       0 confess("Gnuplot command not found.") if (!-e $gnuplot);
1006             }
1007             }
1008             }
1009              
1010             # Execute gnuplot
1011 0         0 my $cmd = qq("$gnuplot" "$self->{_script}");
1012 0 0       0 $cmd .= " -" if ($self->{terminal} =~ /^(ggi|pm|windows|wxt|x11)(\s|$)/);
1013 0         0 my $err = `$cmd 2>&1`;
1014             # my $err;
1015             # system("$cmd");
1016              
1017             # Capture and process error message from Gnuplot
1018 0 0 0     0 if (defined $err && $err ne '')
1019             {
1020 0         0 my ($errTmp) = ($err =~ /\", line \d+:\s(.+)/);
1021 0 0       0 die "$errTmp\n" if (defined $errTmp);
1022 0         0 warn "$err\n";
1023             }
1024              
1025             # Convert the image to the user-specified format
1026 0 0 0     0 if (defined $self->{_terminal} && $self->{_terminal} eq 'auto')
1027             {
1028 0         0 my @a = split(/\./, $self->{output});
1029 0         0 my $ext = $a[-1];
1030 0 0       0 &convert($self, $ext) if ($ext !~ /^e?ps$/);
1031             }
1032              
1033 0         0 return($self);
1034             }
1035              
1036              
1037             # Unset the chart properties
1038             # - called by multiplot()
1039             sub _reset
1040             {
1041 0     0   0 my ($self) = @_;
1042 0 0       0 open(PLT, ">>$self->{_script}") || confess("Can't write $self->{_script}");
1043 0         0 foreach my $opt (@{$self->{_sets}})
  0         0  
1044             {
1045 0         0 print PLT "unset $opt\n";
1046              
1047 0 0 0     0 if ($opt =~ /range$/)
    0          
1048             {
1049 0         0 print PLT "set $opt [*:*]\n";
1050             }
1051             elsif (!grep(/^$opt$/, qw(arrow grid label logscale object parametric))
1052             && $opt !~ /tics$/)
1053             {
1054 0         0 print PLT "set $opt\n";
1055             }
1056             }
1057 0         0 close(PLT);
1058             }
1059              
1060              
1061             # Arbitrary labels placed in the chart
1062             #
1063             # Usage example:
1064             # $chart->label(
1065             # text => "This is a label",
1066             # position => "0.2, 3 left",
1067             # offset => "2,2",
1068             # rotate => 45,
1069             # font => "arial, 15",
1070             # fontcolor => "dark-blue",
1071             # pointtype => 3,
1072             # pointsize => 5,
1073             # pointcolor => "blue",
1074             # );
1075             #
1076             # TODO:
1077             # - support layer
1078             sub label
1079             {
1080 4     4 1 50 my ($self, %label) = @_;
1081              
1082 4         13 my $out = " \"$label{text}\"";
1083 4 50       16 $out .= " at $label{position}" if (defined $label{position});
1084 4 100       15 $out .= " offset $label{offset}" if (defined $label{offset});
1085 4 50       15 $out .= " rotate by $label{rotate}" if (defined $label{rotate});
1086 4 50       12 $out .= " font \"$label{font}\"" if (defined $label{font});
1087 4 50       10 $out .= " textcolor rgb \"$label{fontcolor}\"" if
1088             (defined $label{fontcolor});
1089 4 50 33     23 $out .= " noenhanced" if (!defined $label{enhanced} ||
1090             $label{enhanced} ne 'on');
1091              
1092 4 50 66     20 if (defined $label{pointtype} || defined $label{pointsize} ||
      66        
1093             defined $label{pointcolor})
1094             {
1095 3         4 $out .= " point";
1096 3 50       19 $out .= " pointtype ".&_pointType($label{pointtype}) if
1097             (defined $label{pointtype});
1098 3 50       13 $out .= " pointsize $label{pointsize}" if (defined $label{pointsize});
1099 3 100       11 $out .= " linecolor rgb \"$label{pointcolor}\"" if
1100             (defined $label{pointcolor});
1101             }
1102              
1103 4         6 push(@{$self->{_labels}}, $out);
  4         16  
1104 4         20 return($self);
1105             }
1106              
1107              
1108             # Arbitrary arrows placed in the chart
1109             #
1110             # Usage example:
1111             # $chart->arrow(
1112             # from => "0,2",
1113             # to => "0.3,0.1",
1114             # linetype => 'dash',
1115             # width => 2,
1116             # color => "dark-blue",
1117             # head => {
1118             # size => 3,
1119             # angle => 30,
1120             # direction => 'back',
1121             # },
1122             # );
1123             #
1124             # TODO:
1125             # - support layer
1126             sub arrow
1127             {
1128 0     0 1 0 my ($self, %arrow) = @_;
1129 0 0       0 confess("Starting position of arrow not found") if (!defined $arrow{from});
1130              
1131 0         0 my $out = " from $arrow{from}";
1132 0 0       0 $out .= " to $arrow{to}" if (defined $arrow{to});
1133 0 0       0 $out .= " rto $arrow{rto}" if (defined $arrow{rto});
1134 0 0       0 $out .= " linetype ".&_lineType($arrow{linetype}) if
1135             (defined $arrow{linetype});
1136 0 0       0 $out .= " linewidth $arrow{width}" if (defined $arrow{width});
1137 0 0       0 $out .= " linecolor rgb \"$arrow{color}\"" if (defined $arrow{color});
1138 0 0       0 $out .= " size $arrow{headsize}" if (defined $arrow{headsize});
1139              
1140             # Set arrow head
1141 0 0       0 $out .= &_setArrowHead($arrow{head}) if (defined $arrow{head});
1142              
1143 0         0 push(@{$self->{_arrows}}, $out);
  0         0  
1144 0         0 return($self);
1145             }
1146              
1147              
1148             # Arbitrary lines placed in the chart
1149             #
1150             # Usage example:
1151             # $chart->line(
1152             # from => "0,2",
1153             # to => "0.3,0.1",
1154             # linetype => 'dash',
1155             # width => 2,
1156             # color => "dark-blue",
1157             # );
1158             #
1159             # TODO:
1160             # - support layer
1161             sub line
1162             {
1163 0     0 1 0 my ($self, %line) = @_;
1164 0 0       0 confess("Starting position of line not found") if (!defined $line{from});
1165              
1166 0         0 my $out = " from $line{from}";
1167 0 0       0 $out .= " to $line{to}" if (defined $line{to});
1168 0 0       0 $out .= " rto $line{rto}" if (defined $line{rto});
1169 0 0       0 $out .= " linetype ".&_lineType($line{linetype}) if
1170             (defined $line{linetype});
1171 0 0       0 $out .= " linewidth $line{width}" if (defined $line{width});
1172 0 0       0 $out .= " linecolor rgb \"$line{color}\"" if (defined $line{color});
1173              
1174 0         0 push(@{$self->{_arrows}}, "$out nohead"); # remove arrow head
  0         0  
1175 0         0 return($self);
1176             }
1177              
1178              
1179             # Set the options of arrow head
1180             sub _setArrowHead
1181             {
1182 0     0   0 my ($head) = @_;
1183 0         0 my $out = '';
1184              
1185             # Author's comments:
1186             # - The filling of arrow head does not follow the convention of fill style
1187             # of objects and plotting styles. Perhaps Gnuplot will change this in the
1188             # future.
1189             # - Back-angle is not meaningful if filling is "nofilled". This constraint
1190             # may be removed theoretically.
1191             # - Therefore, "backangle" and "fill" are disabled for the moment.
1192 0 0       0 if (ref($head) eq 'HASH')
1193             {
1194 0 0       0 my $size = (defined $$head{size})? $$head{size} : 0.45;
1195 0 0       0 my $angle = (defined $$head{angle})? $$head{angle} : 15;
1196 0 0       0 confess("arrow head size must be greater than 0") if ($size <= 0);
1197              
1198 0         0 $out .= " size $size";
1199 0 0       0 $out .= ",$angle" if ($size !~ /,/);
1200             # $out .= ",$$head{backangle}" if (defined $$head{backangle});
1201             # $out .= " $$head{fill}" if (defined $$head{fill});
1202 0 0       0 if (defined $$head{direction})
1203             {
1204 0 0       0 if ($$head{direction} eq 'back')
    0          
    0          
1205             {
1206 0         0 $out .= " backhead";
1207             }
1208             elsif ($$head{direction} eq 'both')
1209             {
1210 0         0 $out .= " heads";
1211             }
1212             elsif ($$head{direction} eq 'off')
1213             {
1214 0         0 $out .= " nohead";
1215             }
1216             }
1217             }
1218             else
1219             {
1220 0 0       0 if ($head eq 'off')
    0          
    0          
1221             {
1222 0         0 $out .= " nohead";
1223             }
1224             elsif ($head eq 'back')
1225             {
1226 0         0 $out .= " backhead";
1227             }
1228             elsif ($head eq 'both')
1229             {
1230 0         0 $out .= " heads";
1231             }
1232             }
1233              
1234 0         0 return($out);
1235             }
1236              
1237              
1238             # Arbitrary rectangles placed in the chart
1239             #
1240             # Usage example:
1241             # $chart->rectangle(
1242             # from => "screen 0.2, screen 0.2",
1243             # to => "screen 0.4, screen 0.4",
1244             # fill => {
1245             # density => 0.2,
1246             # color => "#11ff11",
1247             # },
1248             # border => {color => "blue"},
1249             # );
1250             sub rectangle
1251             {
1252 0     0 1 0 my ($self, %rect) = @_;
1253              
1254             # Position and dimension of the rectangle
1255 0         0 my $out = "";
1256 0 0       0 $out .= " $rect{index}" if (defined $rect{index});
1257 0         0 $out .= " rectangle";
1258              
1259 0 0       0 if (defined $rect{from})
    0          
1260             {
1261 0         0 $out .= " from $rect{from}";
1262            
1263 0 0       0 if (defined $rect{to})
    0          
1264             {
1265 0         0 $out .= " to $rect{to}";
1266             }
1267             elsif (defined $rect{rto})
1268             {
1269 0         0 $out .= " rto $rect{rto}";
1270             }
1271             else
1272             {
1273 0         0 confess("Rectangle dimension not complete");
1274             }
1275             }
1276             elsif (defined $rect{width})
1277             {
1278 0 0       0 $rect{at} = $rect{center} if (defined $rect{center});
1279 0 0       0 confess("Rectangle position not complete") if (!defined $rect{at});
1280 0 0 0     0 confess("Rectangle dimension not found") if (!defined $rect{width} ||
1281             !defined $rect{height});
1282              
1283 0         0 $out .= " at $rect{at} size $rect{width},$rect{height}";
1284             }
1285             else
1286             {
1287 0         0 confess("Rectangle position or dimension not complete");
1288             }
1289              
1290             # Process shared object options
1291 0         0 $out .= &_setObjOpt(\%rect);
1292              
1293 0         0 push(@{$self->{_objects}}, $out);
  0         0  
1294 0         0 return($self);
1295             }
1296              
1297              
1298             # Arbitrary ellipses placed in the chart
1299             #
1300             # Usage example:
1301             # $chart->ellipse(
1302             # at => "screen 0.2, screen 0.2",
1303             # width => 0.2,
1304             # height => 0.5
1305             # fill => {
1306             # density => 0.2,
1307             # color => "#11ff11",
1308             # },
1309             # border => {color => "blue"},
1310             # );
1311             sub ellipse
1312             {
1313 0     0 1 0 my ($self, %elli) = @_;
1314              
1315             # - Alias of "at": "center"
1316             # - Check position and dimension information
1317 0 0       0 $elli{at} = $elli{center} if (defined $elli{center});
1318 0 0       0 confess("Ellipse location not found") if (!defined $elli{at});
1319 0 0 0     0 confess("Ellipse dimension not found") if (!defined $elli{width} ||
1320             !defined $elli{height});
1321              
1322 0         0 my $out = "";
1323 0 0       0 $out .= " $elli{index}" if (defined $elli{index});
1324 0         0 $out .= " ellipse at $elli{at} size $elli{width},$elli{height}";
1325 0 0       0 $out .= " units $elli{units}" if (defined $elli{units});
1326              
1327             # Process shared object options
1328 0         0 $out .= &_setObjOpt(\%elli);
1329              
1330 0         0 push(@{$self->{_objects}}, $out);
  0         0  
1331 0         0 return($self);
1332             }
1333              
1334              
1335             # Arbitrary circles placed in the chart
1336             #
1337             # Usage example:
1338             # $chart->circle(
1339             # at => "screen 0.2, screen 0.2",
1340             # size => 0.5
1341             # fill => {
1342             # density => 0.2,
1343             # color => "#11ff11",
1344             # },
1345             # border => {color => "blue"},
1346             # );
1347             sub circle
1348             {
1349 0     0 1 0 my ($self, %cir) = @_;
1350              
1351             # - Alias of "at": "center"
1352             # - Check position and size information
1353 0 0       0 $cir{at} = $cir{center} if (defined $cir{center});
1354 0 0       0 confess("Circle location not found") if (!defined $cir{at});
1355 0 0       0 confess("Circle size not found") if (!defined $cir{size});
1356              
1357 0         0 my $out = "";
1358 0 0       0 $out .= " $cir{index}" if (defined $cir{index});
1359 0         0 $out .= " circle at $cir{at} size $cir{size}";
1360              
1361 0 0       0 if (defined $cir{arc})
1362             {
1363 0         0 (ref($cir{arc}) eq 'ARRAY')?
1364 0 0       0 ($out .= " arc [". join(':', @{$cir{arc}}) . "]"):
1365             ($out .= " arc [$cir{arc}]");
1366             }
1367              
1368             # Process shared object options
1369 0         0 $out .= &_setObjOpt(\%cir);
1370              
1371 0         0 push(@{$self->{_objects}}, $out);
  0         0  
1372 0         0 return($self);
1373             }
1374              
1375              
1376             # Arbitrary polygons placed in the chart
1377             #
1378             # Usage example:
1379             # $chart->polygon(
1380             # vertices => [
1381             # "0, -0.6",
1382             # {rto => "-1, 0.3"},
1383             # {to => [-4, 0.4]},
1384             # ],
1385             # );
1386             sub polygon
1387             {
1388 0     0 1 0 my ($self, %poly) = @_;
1389 0 0       0 confess("Polygon vertices not found") if (!defined $poly{vertices});
1390              
1391 0         0 my $v = $poly{vertices};
1392 0 0       0 confess("Polygon starting vertex not found") if (scalar(@$v) < 0.5);
1393              
1394 0         0 my $out = "";
1395 0 0       0 $out .= " $poly{index}" if (defined $poly{index});
1396 0         0 $out .= " polygon from $$v[0]";
1397              
1398             # Other vertices
1399 0         0 for (my $i = 1; $i < @$v; $i++)
1400             {
1401 0 0       0 if (ref($$v[$i]) eq 'HASH')
1402             {
1403 0         0 my @key = keys(%{$$v[$i]});
  0         0  
1404 0         0 my @val = values(%{$$v[$i]});
  0         0  
1405 0         0 $out .= " $key[0] $val[0]";
1406             }
1407             else
1408             {
1409 0         0 $out .= " to $$v[$i]";
1410             }
1411             }
1412              
1413             # Process shared object options
1414 0         0 $out .= &_setObjOpt(\%poly);
1415              
1416 0         0 push(@{$self->{_objects}}, $out);
  0         0  
1417 0         0 return($self);
1418             }
1419              
1420              
1421             # Set the details common to all objects
1422             sub _setObjOpt
1423             {
1424 0     0   0 my ($obj) = @_;
1425 0         0 my $out = "";
1426 0 0       0 $out .= " $$obj{layer}" if (defined $$obj{layer});
1427 0 0       0 $out .= " linewidth $$obj{linewidth}" if (defined $$obj{linewidth});
1428              
1429             # Set filling color / pattern and border
1430 0 0       0 if (defined $$obj{fill})
    0          
1431             {
1432 0         0 my $fill = $$obj{fill};
1433 0 0       0 $out .= " fillcolor rgb \"$$fill{color}\"" if (defined $$fill{color});
1434 0         0 $out .= " fillstyle". &_fillStyle($fill);
1435              
1436             # Set details of the border
1437 0 0       0 if (defined $$obj{border})
1438             {
1439 0 0       0 if (ref($$obj{border}) eq 'HASH')
    0          
1440             {
1441 0         0 $out .= " border";
1442 0 0       0 $out .= " linecolor rgb \"$$obj{border}{color}\"" if
1443             (defined $$obj{border}{color});
1444             }
1445             elsif ($$obj{border} =~ /^(off|no)$/)
1446             {
1447 0         0 $out .= " noborder";
1448             }
1449             }
1450             }
1451             elsif (defined $$obj{border})
1452             {
1453 0 0       0 if (ref($$obj{border}) eq 'HASH')
    0          
1454             {
1455 0         0 $out .= " fillstyle border";
1456 0 0       0 $out .= " linecolor rgb \"$$obj{border}{color}\"" if
1457             (defined $$obj{border}{color});
1458             }
1459             elsif ($$obj{border} =~ /^(off|no)$/)
1460             {
1461 0         0 $out .= " noborder";
1462             }
1463             }
1464 0         0 return($out);
1465             }
1466              
1467              
1468             # Output a test image for the terminal
1469             #
1470             # Usage example:
1471             # $chart = Chart::Gnuplot->new(output => "test.png");
1472             # $chart->test;
1473             sub test
1474             {
1475 0     0 0 0 my ($self) = @_;
1476              
1477 0         0 my $pltTmp = "$self->{_script}";
1478 0 0       0 open(PLT, ">$pltTmp") || confess("Can't write gnuplot script $pltTmp");
1479 0         0 print PLT "set terminal $self->{terminal}\n";
1480 0         0 print PLT "set output \"$self->{output}\"\n";
1481 0         0 print PLT "test\n";
1482 0         0 close(PLT);
1483              
1484             # Execute gnuplot
1485 0         0 my $gnuplot = 'gnuplot';
1486 0 0       0 $gnuplot = $self->{gnuplot} if (defined $self->{gnuplot});
1487 0         0 system("$gnuplot $pltTmp");
1488              
1489             # Convert the image to the user-specified format
1490 0 0 0     0 if (defined $self->{_terminal} && $self->{_terminal} eq 'auto')
1491             {
1492 0         0 my @a = split(/\./, $self->{output});
1493 0         0 my $ext = $a[-1];
1494 0 0       0 &convert($self, $ext) if ($ext !~ /^e?ps$/);
1495             }
1496 0         0 return($self);
1497             }
1498              
1499              
1500             # Create animated gif
1501             #
1502             # Usage example:
1503             # $chart->animate(
1504             # charts => \@charts, # sequence of chart object
1505             # delay => 10, # delay in units of 0.01 second
1506             # );
1507             sub animate
1508             {
1509 0     0 1 0 my ($self, %animate) = @_;
1510 0         0 my $charts = $animate{charts};
1511              
1512             # Force the terminal to be 'gif'
1513             # - Only the 'gif' terminal supports animation
1514 0 0 0     0 if (defined $self->{_terminal} && $self->{_terminal} eq 'auto')
    0          
1515             {
1516 0         0 $self->{terminal} = $self->{_terminal} = 'gif';
1517             }
1518             elsif ($self->{terminal} !~ /^gif/)
1519             {
1520 0         0 croak "animate() is supported only by the gif terminal";
1521             }
1522 0         0 $self->{terminal} .= " animate";
1523 0 0       0 $self->{terminal} .= " delay $animate{delay}" if (defined $animate{delay});
1524              
1525 0         0 &_setChart($self);
1526              
1527 0 0       0 open(PLT, ">>$self->{_script}") || confess("Can't write $self->{_script}");
1528              
1529 0         0 foreach my $chart (@$charts)
1530             {
1531 0         0 $chart->_script($self->{_script});
1532 0         0 $chart->_multiplot(1);
1533              
1534 0         0 my $plot;
1535             my @dataSet;
1536 0 0       0 if (defined $chart->{_dataSets2D})
    0          
1537             {
1538 0         0 $plot = 'plot';
1539 0         0 @dataSet = @{$chart->{_dataSets2D}};
  0         0  
1540             }
1541             elsif (defined $chart->{_dataSets3D})
1542             {
1543 0         0 $plot = 'splot';
1544 0         0 @dataSet = @{$chart->{_dataSets3D}};
  0         0  
1545             }
1546            
1547 0         0 &_setChart($chart, \@dataSet);
1548 0 0       0 open(PLT, ">>$self->{_script}") ||
1549             confess("Can't write $self->{_script}");
1550 0         0 print PLT "\n$plot ";
1551 0         0 print PLT join(', ', map {$_->_thaw($self)} @dataSet), "\n";
  0         0  
1552 0         0 close(PLT);
1553 0         0 &_reset($chart);
1554             }
1555              
1556             # Generate image file
1557 0         0 &execute($self);
1558 0         0 return($self);
1559             }
1560              
1561              
1562             # Change the image format
1563             # - called by plot2d()
1564             #
1565             # Usage example:
1566             # my $chart = Chart::Gnuplot->new(...);
1567             # my $data = Chart::Gnuplot::DataSet->new(...);
1568             # $chart->plot2d($data);
1569             # $chart->convert('gif');
1570             sub convert
1571             {
1572 0     0 1 0 my ($self, $imgfmt) = @_;
1573 0 0       0 return($self) if (!-e $self->{output});
1574              
1575             # Generate temp file
1576 0         0 my $temp = "$self->{_script}.tmp";
1577 0         0 move($self->{output}, $temp);
1578              
1579             # Execute gnuplot
1580 0         0 my $convert = 'convert';
1581 0 0       0 $convert = $self->{convert} if (defined $self->{convert});
1582              
1583             # Rotate 90 deg for landscape image
1584 0 0 0     0 if (defined $self->{orient} && $self->{orient} eq 'portrait')
1585             {
1586 0         0 my $cmd = qq("$convert" $temp $temp.$imgfmt 2>&1);
1587 0         0 my $err = `$cmd`;
1588 0 0 0     0 if (defined $err && $err ne '')
1589             {
1590 0 0       0 die "Unsupported image format ($imgfmt)\n" if
1591             ($err =~ /^convert: unable to open module file/);
1592              
1593 0         0 my ($errTmp) = ($err =~ /^convert: (.+)/);
1594 0 0       0 die "$errTmp Perhaps the image format is not supported\n" if
1595             (defined $errTmp);
1596 0         0 die "$err\n";
1597             }
1598             }
1599             else
1600             {
1601 0         0 my $cmd = qq("$convert" -rotate 90 $temp $temp.$imgfmt 2>&1);
1602 0         0 my $err = `$cmd`;
1603 0 0 0     0 if (defined $err && $err ne '')
1604             {
1605 0 0       0 die "Unsupported image format ($imgfmt)\n" if
1606             ($err =~ /^convert: unable to open module file/);
1607              
1608 0         0 my ($errTmp) = ($err =~ /^convert: (.+)/);
1609 0 0       0 die "$errTmp Perhaps the image format is not supported\n" if
1610             (defined $errTmp);
1611 0         0 die "$err\n";
1612             }
1613             }
1614              
1615             # Remove the temp file
1616 0         0 move("$temp.$imgfmt", $self->{output});
1617 0         0 unlink($temp);
1618 0         0 return($self);
1619             }
1620              
1621              
1622             # Change the image format to PNG
1623             #
1624             # Usage example:
1625             # my $chart = Chart::Gnuplot->new(...);
1626             # my $data = Chart::Gnuplot::DataSet->new(...);
1627             # $chart->plot2d($data)->png;
1628             sub png
1629             {
1630 0     0 1 0 my $self = shift;
1631 0         0 &convert($self, 'png');
1632 0         0 return($self)
1633             }
1634              
1635              
1636             # Change the image format to GIF
1637             #
1638             # Usage example:
1639             # my $chart = Chart::Gnuplot->new(...);
1640             # my $data = Chart::Gnuplot::DataSet->new(...);
1641             # $chart->plot2d($data)->gif;
1642             sub gif
1643             {
1644 0     0 1 0 my $self = shift;
1645 0         0 &convert($self, 'gif');
1646 0         0 return($self)
1647             }
1648              
1649              
1650             # Change the image format to JPG
1651             #
1652             # Usage example:
1653             # my $chart = Chart::Gnuplot->new(...);
1654             # my $data = Chart::Gnuplot::DataSet->new(...);
1655             # $chart->plot2d($data)->jpg;
1656             sub jpg
1657             {
1658 0     0 1 0 my $self = shift;
1659 0         0 &convert($self, 'jpg');
1660 0         0 return($self)
1661             }
1662              
1663              
1664             # Change the image format to PS
1665             #
1666             # Usage example:
1667             # my $chart = Chart::Gnuplot->new(...);
1668             # my $data = Chart::Gnuplot::DataSet->new(...);
1669             # $chart->plot2d($data)->ps;
1670             sub ps
1671             {
1672 0     0 1 0 my $self = shift;
1673 0         0 &convert($self, 'ps');
1674 0         0 return($self)
1675             }
1676              
1677              
1678             # Change the image format to PDF
1679             #
1680             # Usage example:
1681             # my $chart = Chart::Gnuplot->new(...);
1682             # my $data = Chart::Gnuplot::DataSet->new(...);
1683             # $chart->plot2d($data)->pdf;
1684             sub pdf
1685             {
1686 0     0 1 0 my $self = shift;
1687 0         0 &convert($self, 'pdf');
1688 0         0 return($self)
1689             }
1690              
1691              
1692             # Copy method of the chart object
1693             sub copy
1694             {
1695 2     2 1 12 my ($self, $num) = @_;
1696 2         10 my @clone = &_copy(@_);
1697              
1698 2         4 foreach my $clone (@clone)
1699             {
1700 4         10 my $dirTmp = tempdir(CLEANUP => 1);
1701 4 50       1609 ($^O =~ /MSWin/)? ($dirTmp .= '\\'): ($dirTmp .= '/');
1702 4         22 $clone->{_script} = $dirTmp . "plot";
1703             }
1704 2 100       9 return($clone[0]) if (!defined $num);
1705 1         6 return(@clone);
1706             }
1707              
1708             ################## Chart::Gnuplot::DataSet class ##################
1709              
1710             package Chart::Gnuplot::DataSet;
1711 21     21   335 use strict;
  21         53  
  21         1131  
1712 21     21   130 use Carp;
  21         47  
  21         2486  
1713 21     21   147 use File::Temp qw(tempdir);
  21         48  
  21         1252  
1714 21     21   122 use Chart::Gnuplot::Util qw(_lineType _pointType _fillStyle _copy);
  21         40  
  21         188000  
1715              
1716             # Constructor
1717             sub new
1718             {
1719 31     31   13908 my ($class, %hash) = @_;
1720              
1721 31         121 my $dirTmp = tempdir(CLEANUP => 1);
1722 31 50       15080 ($^O =~ /MSWin/)? ($dirTmp .= '\\'): ($dirTmp .= '/');
1723 31         93 $hash{_data} = $dirTmp . "data";
1724              
1725 31         60 my $self = \%hash;
1726 31         150 return bless($self, $class);
1727             }
1728              
1729              
1730             # Generic attribute methods
1731             sub AUTOLOAD
1732             {
1733 4     4   33 my ($self, $key) = @_;
1734 4         7 my $attr = our $AUTOLOAD;
1735 4         17 $attr =~ s/.*:://;
1736 4 50       16 return if ($attr eq 'DESTROY'); # ignore destructor
1737 4 50       12 $self->{$attr} = $key if (defined $key);
1738 4         12 return($self->{$attr});
1739             }
1740              
1741              
1742             # xdata get-set method
1743             sub xdata
1744             {
1745 0     0   0 my ($self, $xdata) = @_;
1746 0 0       0 return($self->{xdata}) if (!defined $xdata);
1747              
1748 0         0 delete $self->{points};
1749 0         0 delete $self->{datafile};
1750 0         0 delete $self->{func};
1751 0         0 $self->{xdata} = $xdata;
1752             }
1753              
1754              
1755             # ydata get-set method
1756             sub ydata
1757             {
1758 1     1   9 my ($self, $ydata) = @_;
1759 1 50       5 return($self->{ydata}) if (!defined $ydata);
1760              
1761 1         3 delete $self->{points};
1762 1         2 delete $self->{datafile};
1763 1         3 delete $self->{func};
1764 1         5 $self->{ydata} = $ydata;
1765             }
1766              
1767              
1768             # zdata get-set method
1769             sub zdata
1770             {
1771 0     0   0 my ($self, $zdata) = @_;
1772 0 0       0 return($self->{zdata}) if (!defined $zdata);
1773              
1774 0         0 delete $self->{points};
1775 0         0 delete $self->{datafile};
1776 0         0 delete $self->{func};
1777 0         0 $self->{zdata} = $zdata;
1778             }
1779              
1780              
1781             # points get-set method
1782             sub points
1783             {
1784 0     0   0 my ($self, $points) = @_;
1785 0 0       0 return($self->{points}) if (!defined $points);
1786              
1787 0         0 delete $self->{xdata};
1788 0         0 delete $self->{ydata};
1789 0         0 delete $self->{zdata};
1790 0         0 delete $self->{datafile};
1791 0         0 delete $self->{func};
1792 0         0 $self->{points} = $points;
1793             }
1794              
1795              
1796             # datafile get-set method
1797             sub datafile
1798             {
1799 0     0   0 my ($self, $datafile) = @_;
1800 0 0       0 return($self->{datafile}) if (!defined $datafile);
1801              
1802 0         0 delete $self->{xdata};
1803 0         0 delete $self->{ydata};
1804 0         0 delete $self->{zdata};
1805 0         0 delete $self->{points};
1806 0         0 delete $self->{func};
1807 0         0 $self->{datafile} = $datafile;
1808             }
1809              
1810              
1811             # func get-set method
1812             sub func
1813             {
1814 1     1   7 my ($self, $func) = @_;
1815 1 50       4 return($self->{func}) if (!defined $func);
1816              
1817 1         3 delete $self->{xdata};
1818 1         3 delete $self->{ydata};
1819 1         2 delete $self->{zdata};
1820 1         2 delete $self->{points};
1821 1         3 delete $self->{datafile};
1822 1         4 $self->{func} = $func;
1823             }
1824              
1825              
1826             # Copy method of the data set object
1827             sub copy
1828             {
1829 2     2   10 my ($self, $num) = @_;
1830 2         10 my @clone = &_copy(@_);
1831              
1832 2         6 foreach my $clone (@clone)
1833             {
1834 4         10 my $dirTmp = tempdir(CLEANUP => 1);
1835 4 50       1480 ($^O =~ /MSWin/)? ($dirTmp .= '\\'): ($dirTmp .= '/');
1836 4         15 $clone->{_data} = $dirTmp . "data";
1837             }
1838 2 100       11 return($clone[0]) if (!defined $num);
1839 1         5 return(@clone);
1840             }
1841              
1842              
1843             # Thaw the data set object
1844             # - call _fillStyle()
1845             # _ call different _thaw*()
1846             #
1847             # TODO:
1848             # - data file delimiter
1849             # - data labels
1850             sub _thaw
1851             {
1852 33     33   160 my ($self, $chart) = @_;
1853 33         41 my $string;
1854 33         56 my $using = '';
1855              
1856             # Data points stored in arrays
1857             # - in any case, ydata need to be defined
1858 33 100       183 if (defined $self->{ydata})
    100          
    100          
    50          
1859             {
1860 15         26 my $fileTmp = $self->{_data};
1861 15         79 $string = "'$fileTmp'";
1862              
1863             # Process 3D data set
1864             # - zdata is defined
1865 15 100 100     382 if (defined $self->{zdata})
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
      100        
      100        
      100        
1866             {
1867 2 100       11 $using = (ref($self->{xdata}->[0]) eq 'ARRAY')?
1868             &_thawXYZGrid($self) : &_thawXYZ($self);
1869             }
1870             # Treatment for financebars and candlesticks styles
1871             # - Both xdata and ydata are defined
1872             elsif (defined $self->{xdata} && defined $self->{style} &&
1873             $self->{style} =~ /^(financebars|candlesticks)$/)
1874             {
1875 1         4 $using = &_thawXYFinance($self);
1876             }
1877             # Treatment for errorbars and errorlines styles
1878             # - Both xdata and ydata are defined
1879             # - Style is defined and contain "error"
1880             elsif (defined $self->{xdata} && defined $self->{style} &&
1881             $self->{style} =~ /error/)
1882             {
1883             # Error bars along x-axis
1884 3 100       22 if ($self->{style} =~ /^xerror/)
    100          
    50          
1885             {
1886 1         13 $using = &_thawXYXError($self);
1887             }
1888             # Error bars along y-axis
1889             elsif ($self->{style} =~ /^(y|box)error/)
1890             {
1891 1         4 $using = &_thawXYYError($self);
1892             }
1893             # Error bars along both x and y-axis
1894             elsif ($self->{style} =~ /^(box)?xyerror/)
1895             {
1896 1         5 $using = &_thawXYXYError($self);
1897             }
1898             }
1899             # Treatment for hbars
1900             # - use "boxxyerrorbars" style to mimic
1901             elsif (defined $self->{xdata} && defined $self->{style} &&
1902             $self->{style} eq 'hbars')
1903             {
1904 1         5 &_thawXYHbars($self);
1905             }
1906             # Treatment for hlines
1907             # - use "boxxyerrorbars" style to mimic
1908             elsif (defined $self->{xdata} && defined $self->{style} &&
1909             $self->{style} eq 'hlines')
1910             {
1911 1         4 &_thawXYHlines($self);
1912             }
1913             elsif (defined $self->{xdata} && defined $self->{style} &&
1914             $self->{style} eq 'histograms')
1915             {
1916 1         4 $using = &_thawXYHistograms($self);
1917             }
1918             # Normal x-y plot
1919             # - Both xdata and ydata are defined
1920             elsif (defined $self->{xdata})
1921             {
1922 2         15 $using = &_thawXY($self);
1923             }
1924             # Only ydata is defined
1925             # - Plot ydata against index
1926             else
1927             {
1928             # Treatment for financebars and candlesticks styles
1929 4 100 100     56 if (defined $self->{style} &&
    100 100        
1930             $self->{style} =~ /^(financebars|candlesticks)$/)
1931             {
1932 1         5 &_thawYFinance($self);
1933             }
1934             # Treatment for errorbars and errorlines styles
1935             # - Style is defined and contain "error"
1936             elsif (defined $self->{style} && $self->{style} =~ /^yerror/)
1937             {
1938 1         4 &_thawYError($self);
1939             }
1940             # Other plotting styles
1941             else
1942             {
1943 2         15 &_thawY($self);
1944             }
1945 4 50       18 $using = "1:2" if (defined $self->{timefmt});
1946             }
1947             }
1948             # Data in points
1949             elsif (defined $self->{points})
1950             {
1951 5         8 my $pt = $self->{points};
1952 5         11 my $fileTmp = $self->{_data};
1953 5         13 $string = "'$fileTmp'";
1954              
1955             # Horizontal lines plotting style
1956 5 100 100     67 if (defined $self->{style} && $self->{style} eq 'hlines')
    100 100        
    100 66        
    100          
1957             {
1958 1         5 &_thawPointsHLines($self);
1959             }
1960             # Horizontal bars plotting style
1961             elsif (defined $self->{style} && $self->{style} eq 'hbars')
1962             {
1963 1         4 &_thawPointsHBars($self);
1964             }
1965             # Horizontal bars plotting style
1966             elsif (defined $self->{style} && $self->{style} eq 'histograms')
1967             {
1968 1         4 $using = &_thawPointsHistograms($self);
1969             }
1970             # 3D grid data points
1971             elsif (ref($$pt[0][0]) eq 'ARRAY')
1972             {
1973 1         4 $using = &_thawPointsGrid($self);
1974             }
1975             else
1976             {
1977 1         6 $using = &_thawPoints($self);
1978             }
1979             }
1980             # File
1981             elsif (defined $self->{datafile})
1982             {
1983 1         4 $string = "'$self->{datafile}'";
1984 1 50       6 $string .= " every $self->{every}" if (defined $self->{every});
1985 1 50       4 $string .= " index $self->{index}" if (defined $self->{index});
1986             }
1987             # Function
1988             elsif (defined $self->{func})
1989             {
1990             # Parametric function
1991 12 100       65 if (ref($self->{func}) eq 'HASH')
1992             {
1993 2 100       2 if (defined ${$self->{func}}{z})
  2         9  
1994             {
1995 1         3 $string = "${$self->{func}}{x},${$self->{func}}{y},".
  1         4  
  1         4  
1996 1         5 "${$self->{func}}{z}";
1997             }
1998             else
1999             {
2000 1         3 $string = "${$self->{func}}{x},${$self->{func}}{y}";
  1         3  
  1         3  
2001             }
2002             }
2003             else
2004             {
2005 10         99 $string = "$self->{func}";
2006             }
2007             }
2008             else
2009             {
2010 0         0 croak("Unknown or undefined data source");
2011             }
2012              
2013             # Process the Gnuplot "using" feature
2014 33 50       105 $using = $self->{using} if (defined $self->{using});
2015 33 100       84 $string .= " using $using" if ($using ne '');
2016              
2017             # Add title for the data sets
2018 33 100       103 (defined $self->{title})? ($string .= " title \"$self->{title}\""):
2019             ($string .= " title \"\"");
2020              
2021             # Change plotting style, color, width and point size
2022 33 100       91 $string .= " smooth $self->{smooth}" if (defined $self->{smooth});
2023 33 100       83 $string .= " axes $self->{axes}" if (defined $self->{axes});
2024 33 100       99 $string .= " with $self->{style}" if (defined $self->{style});
2025 33 100       87 $string .= " linetype ".&_lineType($self->{linetype}) if
2026             (defined $self->{linetype});
2027 33 100       78 $string .= " linecolor rgb \"$self->{color}\"" if (defined $self->{color});
2028 33 100       81 $string .= " linewidth $self->{width}" if (defined $self->{width});
2029 33 100       80 $string .= " pointtype ".&_pointType($self->{pointtype}) if
2030             (defined $self->{pointtype});
2031 33 100       86 $string .= " pointsize $self->{pointsize}" if (defined $self->{pointsize});
2032            
2033             # Filling style of the curve
2034 33 50       75 if (defined $self->{fill})
2035             {
2036 0         0 $string .= " fill".&_fillStyle($self->{fill});
2037              
2038             # Set details of the border
2039 0 0       0 if (defined $self->{border})
2040             {
2041 0 0       0 if (ref($self->{border}) eq 'HASH')
    0          
2042             {
2043 0         0 $string .= " border";
2044 0 0       0 $string .= " linecolor rgb \"$self->{border}{color}\"" if
2045             (defined $self->{border}{color});
2046             }
2047             elsif ($self->{border} =~ /^(off|no)$/)
2048             {
2049 0         0 $string .= " noborder";
2050             }
2051             }
2052             }
2053 33         100 return($string);
2054             }
2055              
2056              
2057             # Process input data of array of y
2058             sub _thawY
2059             {
2060 2     2   7 my ($ds) = @_;
2061 2         5 my $ydata = $ds->{ydata};
2062              
2063             # Write data into temp file
2064 2         4 my $fileTmp = $ds->{_data};
2065 2 50       258 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2066 2         10 for (my $i = 0; $i < @$ydata; $i++)
2067             {
2068 15         67 print DATA "$i $$ydata[$i]\n";
2069             }
2070 2         84 close(DATA);
2071             }
2072              
2073              
2074             # Process input data of array of y for plotting style "yerror..."
2075             sub _thawYError
2076             {
2077 1     1   2 my ($ds) = @_;
2078 1         3 my $ydata = $ds->{ydata};
2079              
2080             # Write data into temp file
2081 1         2 my $fileTmp = $ds->{_data};
2082 1 50       68 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2083 1         4 for (my $i = 0; $i < @{$$ydata[0]}; $i++)
  6         26  
2084             {
2085 5         20 print DATA "$i $$ydata[0][$i]";
2086 5         14 for (my $j = 1; $j < @$ydata; $j++)
2087             {
2088 5         33 print DATA " $$ydata[$j][$i]";
2089             }
2090 5         11 print DATA "\n";
2091             }
2092 1         40 close(DATA);
2093             }
2094              
2095              
2096             # Process input data of array of y for plotting financial time series
2097             sub _thawYFinance
2098             {
2099 1     1   3 my ($ds) = @_;
2100 1         3 my $ydata = $ds->{ydata};
2101              
2102             # Write data into temp file
2103 1         2 my $fileTmp = $ds->{_data};
2104 1 50       78 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2105 1         3 for (my $i = 0; $i < @{$$ydata[0]}; $i++)
  6         17  
2106             {
2107 5         28 print DATA "$i $$ydata[0][$i] $$ydata[1][$i] ".
2108             "$$ydata[2][$i] $$ydata[3][$i]\n";
2109             }
2110 1         59 close(DATA);
2111             }
2112              
2113              
2114             # Process input data of array of x and y
2115             sub _thawXY
2116             {
2117 2     2   4 my ($ds) = @_;
2118              
2119 2         5 my $xdata = $ds->{xdata};
2120 2         7 my $ydata = $ds->{ydata};
2121 2 50       9 croak("x-data and y-data have unequal length") if
2122             (scalar(@$ydata) != scalar(@$xdata));
2123              
2124             # Write data into temp file
2125 2         5 my $fileTmp = $ds->{_data};
2126 2 50       182 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2127 2         12 for (my $i = 0; $i < @$xdata; $i++)
2128             {
2129 26         891 print DATA "$$xdata[$i] $$ydata[$i]\n";
2130             }
2131 2         91 close(DATA);
2132              
2133             # Construst using statement for date-time data
2134 2         7 my $using = '';
2135 2 50       12 if (defined $ds->{timefmt})
2136             {
2137 0         0 my @a = split(/\s+/, $$xdata[0]);
2138 0         0 my $yCol = scalar(@a) + 1;
2139 0         0 $using = "1:$yCol";
2140             }
2141 2         9 return($using);
2142             }
2143              
2144              
2145             # Process input data of array of x and y for plotting style "xerror..."
2146             sub _thawXYXError
2147             {
2148 1     1   3 my ($ds) = @_;
2149              
2150 1         2 my $xdata = $ds->{xdata};
2151 1         2 my $ydata = $ds->{ydata};
2152 1         5 croak("x-data and y-data have unequal length") if
2153 1 50       2 (scalar(@{$$xdata[0]}) != scalar(@$ydata));
2154              
2155             # Write data into temp file
2156 1         2 my $fileTmp = $ds->{_data};
2157 1 50       69 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2158 1         5 for (my $i = 0; $i < @$ydata; $i++)
2159             {
2160 5         16 print DATA "$$xdata[0][$i] $$ydata[$i]";
2161 5         11 for (my $j = 1; $j < @$xdata; $j++)
2162             {
2163 5         27 print DATA " $$xdata[$j][$i]";
2164             }
2165 5         12 print DATA "\n";
2166             }
2167 1         33 close(DATA);
2168              
2169             # Construst using statement for date-time data
2170 1         3 my $using = '';
2171 1 50       4 if (defined $ds->{timefmt})
2172             {
2173 0 0       0 my ($xTmp) = (ref($$xdata[0]) eq 'ARRAY')? ($$xdata[0][0]):
2174             ($$xdata[0]);
2175 0         0 my @a = split(/\s+/, $xTmp);
2176 0         0 my $yCol = scalar(@a) + 1;
2177 0         0 $using = "1:$yCol";
2178             }
2179 1         4 return($using);
2180             }
2181              
2182              
2183             # Process input data of array of x and y for plotting style "yerror..."
2184             sub _thawXYYError
2185             {
2186 1     1   2 my ($ds) = @_;
2187              
2188 1         3 my $xdata = $ds->{xdata};
2189 1         2 my $ydata = $ds->{ydata};
2190 1         5 croak("x-data and y-data have unequal length") if
2191 1 50       1 (scalar(@{$$ydata[0]}) != scalar(@$xdata));
2192              
2193             # Write data into temp file
2194 1         2 my $fileTmp = $ds->{_data};
2195 1 50       92 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2196 1         5 for (my $i = 0; $i < @$xdata; $i++)
2197             {
2198 5         17 print DATA "$$xdata[$i] $$ydata[0][$i]";
2199 5         12 for (my $j = 1; $j < @$ydata; $j++)
2200             {
2201 5         25 print DATA " $$ydata[$j][$i]";
2202             }
2203 5         13 print DATA "\n";
2204             }
2205 1         33 close(DATA);
2206              
2207             # Construst using statement for date-time data
2208 1         3 my $using = '';
2209 1 50       4 if (defined $ds->{timefmt})
2210             {
2211 0 0       0 my ($xTmp) = (ref($$xdata[0]) eq 'ARRAY')? ($$xdata[0][0]):
2212             ($$xdata[0]);
2213 0         0 my @a = split(/\s+/, $xTmp);
2214 0         0 my $yCol = scalar(@a) + 1;
2215 0         0 $using = "1:$yCol";
2216             }
2217 1         3 return($using);
2218             }
2219              
2220              
2221             # Process input data of array of x and y for plotting style "xyerror..."
2222             sub _thawXYXYError
2223             {
2224 1     1   2 my ($ds) = @_;
2225              
2226 1         3 my $xdata = $ds->{xdata};
2227 1         1 my $ydata = $ds->{ydata};
2228              
2229             # Write data into temp file
2230 1         2 my $fileTmp = $ds->{_data};
2231 1 50       68 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2232 1 50       7 if (scalar(@$xdata) == scalar(@$ydata))
2233             {
2234 1         2 for (my $i = 0; $i < @{$$xdata[0]}; $i++)
  6         15  
2235             {
2236 5         16 print DATA "$$xdata[0][$i] $$ydata[0][$i]";
2237 5         14 for (my $j = 1; $j < @$ydata; $j++)
2238             {
2239 5         31 print DATA " $$xdata[$j][$i] $$ydata[$j][$i]";
2240             }
2241 5         11 print DATA "\n";
2242             }
2243             }
2244             else
2245             {
2246 0         0 for (my $i = 0; $i < @{$$xdata[0]}; $i++)
  0         0  
2247             {
2248 0         0 print DATA "$$xdata[0][$i] $$ydata[0][$i]";
2249 0 0       0 if (scalar(@$xdata) == 2)
2250             {
2251 0         0 my $ltmp = $$xdata[0][$i] - $$xdata[1][$i]*0.5;
2252 0         0 my $htmp = $$xdata[0][$i] + $$xdata[1][$i]*0.5;
2253 0         0 print DATA " $ltmp $htmp ".
2254             "$$ydata[1][$i] $$ydata[2][$i]\n";
2255             }
2256             else
2257             {
2258 0         0 my $ltmp = $$ydata[0][$i] - $$ydata[1][$i]*0.5;
2259 0         0 my $htmp = $$ydata[0][$i] - $$ydata[1][$i]*0.5;
2260 0         0 print DATA " $$xdata[1][$i] $$xdata[2][$i] ".
2261             "$ltmp $htmp\n";
2262             }
2263             }
2264             }
2265 1         32 close(DATA);
2266            
2267             # Construst using statement for date-time data
2268 1         2 my $using = '';
2269 1 50       5 if (defined $ds->{timefmt})
2270             {
2271 0 0       0 my ($xTmp) = (ref($$xdata[0]) eq 'ARRAY')? ($$xdata[0][0]):
2272             ($$xdata[0]);
2273 0         0 my @a = split(/\s+/, $xTmp);
2274 0         0 my $yCol = scalar(@a) + 1;
2275 0         0 $using = "1:$yCol";
2276             }
2277 1         4 return($using);
2278             }
2279              
2280              
2281             # Process input data of array of x and y for plotting financial time series
2282             sub _thawXYFinance
2283             {
2284 1     1   2 my ($ds) = @_;
2285              
2286 1         2 my $xdata = $ds->{xdata};
2287 1         3 my $ydata = $ds->{ydata};
2288 1         5 croak("x-data and y-data have unequal length") if
2289 1 50       2 (scalar(@{$$ydata[0]}) != scalar(@$xdata));
2290              
2291             # Write data into temp file
2292 1         2 my $fileTmp = $ds->{_data};
2293 1 50       69 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2294 1         6 for (my $i = 0; $i < @$xdata; $i++)
2295             {
2296 5         32 print DATA "$$xdata[$i] $$ydata[0][$i] $$ydata[1][$i] ".
2297             "$$ydata[2][$i] $$ydata[3][$i]\n";
2298             }
2299 1         34 close(DATA);
2300              
2301             # Construst using statement for date-time data
2302 1         2 my $using = '';
2303 1 50       6 if (defined $ds->{timefmt})
2304             {
2305 0         0 my @a = split(/\s+/, $$xdata[0]);
2306 0         0 my $yCol = scalar(@a) + 1;
2307 0         0 $using = "1:".join(':', ($yCol .. $yCol+3));
2308             }
2309 1         4 return($using);
2310             }
2311              
2312              
2313             # Process input data of arrays of x and y for plottiny style "hlines"
2314             sub _thawXYHlines
2315             {
2316 1     1   2 my ($ds) = @_;
2317 1         2 my $xdata = $ds->{xdata};
2318 1         2 my $ydata = $ds->{ydata};
2319              
2320             # Write data into temp file
2321 1         3 my $fileTmp = $ds->{_data};
2322 1 50       78 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2323 1         5 for (my $i = 0; $i < @$xdata; $i++)
2324             {
2325 5         25 print DATA "0 $$ydata[$i] 0 $$xdata[$i] $$ydata[$i] ".
2326             "$$ydata[$i]\n";
2327             }
2328 1         61 close(DATA);
2329 1         5 $ds->{style} = "boxxyerrorbars";
2330             }
2331              
2332              
2333             # Process input data of arrays of x and y for plottiny style "hbars"
2334             sub _thawXYHbars
2335             {
2336 1     1   2 my ($ds) = @_;
2337 1         4 my $xdata = $ds->{xdata};
2338 1         3 my $ydata = $ds->{ydata};
2339              
2340             # Put the corrdinates in a hash
2341 1         1 my %points;
2342 1         17 for (my $i = 0; $i < @$xdata; $i++)
2343             {
2344 5         54 $points{$$xdata[$i]} = $$ydata[$i];
2345             }
2346              
2347             # Sort x and y according to y values
2348 1         2 my (@sortX, @sortY) = ();
2349 1         10 foreach my $sx (sort {$points{$a} <=> $points{$b}} keys %points)
  8         14  
2350             {
2351 5         7 push(@sortX, $sx);
2352 5         10 push(@sortY, $points{$sx});
2353             }
2354              
2355 1         3 my $ylow = my $yhigh = $sortY[0];
2356 1 50       4 if (scalar(@sortY) > 1)
2357             {
2358 1         4 $ylow = 0.5*(3*$sortY[0]-$sortY[1]);
2359 1         3 $yhigh = 0.5*(3*$sortY[-1]-$sortY[-2]);
2360             }
2361              
2362             # Write data into temp file
2363 1         3 my $fileTmp = $ds->{_data};
2364 1 50       84 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2365 1         6 for (my $i = 0; $i < @$xdata; $i++)
2366             {
2367 5 100       16 $ylow = 0.5*($sortY[$i]+$sortY[$i-1]) if ($i > 0);
2368 5 100       17 $yhigh = ($i < $#sortY)?
2369             0.5*($sortY[$i]+$sortY[$i+1]) :
2370             2.0*$sortY[$i] - $ylow;
2371 5         48 print DATA "0 $sortY[$i] 0 $sortX[$i] $ylow $yhigh\n";
2372             }
2373 1         38 close(DATA);
2374 1         7 $ds->{style} = "boxxyerrorbars";
2375             }
2376              
2377              
2378             # Process input data of arrays of x and y for plottiny style "histograms"
2379             sub _thawXYHistograms
2380             {
2381 1     1   2 my ($ds) = @_;
2382 1         2 my $xdata = $ds->{xdata};
2383 1         3 my $ydata = $ds->{ydata};
2384 1 50       5 croak("x-data and y-data have unequal length") if
2385             (scalar(@$ydata) != scalar(@$xdata));
2386 1         2 my $using;
2387              
2388             # Write data into temp file
2389 1         2 my $fileTmp = $ds->{_data};
2390 1 50       79 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2391 1 50       7 if (ref($$ydata[0]) eq 'ARRAY')
2392             {
2393 0         0 for (my $i = 0; $i < @$xdata; $i++)
2394             {
2395 0         0 print DATA "\"$$xdata[$i]\" " . join(' ', @{$$ydata[$i]}) . "\n";
  0         0  
2396             }
2397 0         0 $using = join(':', (2 .. scalar(@{$$ydata[0]})+1)) . ":xticlabels(1)";
  0         0  
2398             }
2399             else
2400             {
2401 1         5 for (my $i = 0; $i < @$xdata; $i++)
2402             {
2403 5         24 print DATA "\"$$xdata[$i]\" $$ydata[$i]\n";
2404             }
2405 1         3 $using = "2:xticlabels(1)";
2406             }
2407 1         40 close(DATA);
2408              
2409 1         4 return($using);
2410             }
2411              
2412              
2413             # Process input data of array of x, y and z
2414             sub _thawXYZ
2415             {
2416 1     1   3 my ($ds) = @_;
2417              
2418 1         3 my $xdata = $ds->{xdata};
2419 1         2 my $ydata = $ds->{ydata};
2420 1         2 my $zdata = $ds->{zdata};
2421 1 50       4 croak("x-data and y-data have unequal length") if
2422             (scalar(@$ydata) != scalar(@$xdata));
2423 1 50       4 croak("y-data and z-data have unequal length") if
2424             (scalar(@$ydata) != scalar(@$zdata));
2425              
2426             # Write data into temp file
2427 1         3 my $fileTmp = $ds->{_data};
2428 1 50       145 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2429 1         6 for (my $i = 0; $i < @$xdata; $i++)
2430             {
2431 5         24 print DATA "$$xdata[$i] $$ydata[$i] $$zdata[$i]\n";
2432             }
2433 1         34 close(DATA);
2434              
2435             # Construst using statement for date-time data
2436 1         7 my $using = '';
2437 1 50       4 if (defined $ds->{timefmt})
2438             {
2439 0         0 my @a = split(/\s+/, $$xdata[0]);
2440 0         0 my $yCol = scalar(@a) + 1;
2441 0         0 $using = "1:$yCol";
2442              
2443 0         0 my @b = split(/\s+/, $$ydata[0]);
2444 0         0 my $zCol = scalar(@b) + $yCol;
2445 0         0 $using .= ":$zCol";
2446             }
2447 1         4 return($using);
2448             }
2449              
2450              
2451             # Process input data of matrice of x, y and z
2452             sub _thawXYZGrid
2453             {
2454 1     1   2 my ($ds) = @_;
2455              
2456 1         2 my $xdata = $ds->{xdata};
2457 1         2 my $ydata = $ds->{ydata};
2458 1         2 my $zdata = $ds->{zdata};
2459 1 50       4 croak("x-data and y-data have unequal length") if
2460             (scalar(@$ydata) != scalar(@$xdata));
2461 1 50       4 croak("y-data and z-data have unequal length") if
2462             (scalar(@$ydata) != scalar(@$zdata));
2463              
2464             # Write data into temp file
2465 1         2 my $fileTmp = $ds->{_data};
2466 1 50       66 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2467 1         5 for (my $i = 0; $i < @$xdata; $i++)
2468             {
2469 3         6 for (my $j = 0; $j < @{$$xdata[$i]}; $j++)
  12         26  
2470             {
2471 9         35 print DATA "$$xdata[$i][$j] $$ydata[$i][$j] $$zdata[$i][$j]\n";
2472             }
2473 3         7 print DATA "\n";
2474             }
2475 1         35 close(DATA);
2476              
2477             # Construst using statement for date-time data
2478 1         2 my $using = '';
2479 1 50       5 if (defined $ds->{timefmt})
2480             {
2481 0         0 my @a = split(/\s+/, $$xdata[0][0]);
2482 0         0 my $yCol = scalar(@a) + 1;
2483 0         0 $using = "1:$yCol";
2484              
2485 0         0 my @b = split(/\s+/, $$ydata[0][0]);
2486 0         0 my $zCol = scalar(@b) + $yCol;
2487 0         0 $using .= ":$zCol";
2488             }
2489 1         5 return($using);
2490             }
2491              
2492              
2493             # Process input data of array of points
2494             sub _thawPoints
2495             {
2496 1     1   2 my ($ds) = @_;
2497              
2498             # Write data into temp file
2499 1         4 my $pt = $ds->{points};
2500 1         3 my $fileTmp = $ds->{_data};
2501 1 50       90 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2502 1         6 for (my $i = 0; $i < @$pt; $i++)
2503             {
2504 6         9 print DATA join(" ", @{$$pt[$i]}), "\n";
  6         33  
2505             }
2506 1         318 close(DATA);
2507              
2508             # Construst using statement for date-time data
2509 1         4 my $using = '';
2510 1 50       6 if (defined $ds->{timefmt})
2511             {
2512 0         0 my $col = 1;
2513 0         0 $using = "1";
2514 0         0 for (my $i = 0; $i < @{$$pt[0]}-1; $i++)
  0         0  
2515             {
2516 0         0 my @a = split(/\s+/, $$pt[0][$i]);
2517 0         0 $col += scalar(@a);
2518 0         0 $using .= ":$col";
2519             }
2520             }
2521 1         5 return($using);
2522             }
2523              
2524              
2525             # Process input data of array of points for plotting style "hlines"
2526             sub _thawPointsHLines
2527             {
2528 1     1   2 my ($ds) = @_;
2529 1 50       5 confess("Data/time input data is not supported in hlines plotting style")
2530             if (defined $ds->{timefmt});
2531              
2532             # Write data into temp file
2533 1         2 my $pt = $ds->{points};
2534 1         2 my $fileTmp = $ds->{_data};
2535 1 50       70 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2536              
2537             # hlines plotting style
2538 1         5 for (my $i = 0; $i < @$pt; $i++)
2539             {
2540 5         27 print DATA "0 $$pt[$i][1] 0 $$pt[$i][0] $$pt[$i][1] $$pt[$i][1]\n";
2541             }
2542              
2543 1         3 $ds->{style} = "boxxyerrorbars";
2544 1         33 close(DATA);
2545             }
2546              
2547              
2548             # Process input data of array of points for plotting style "hbars"
2549             sub _thawPointsHBars
2550             {
2551 1     1   2 my ($ds) = @_;
2552 1 50       5 confess("Data/time input data is not supported in hbars plotting style")
2553             if (defined $ds->{timefmt});
2554              
2555 1         2 my $pt = $ds->{points};
2556              
2557             # Put the corrdinates in a hash
2558 1         2 my %points;
2559 1         4 for (my $i = 0; $i < @$pt; $i++)
2560             {
2561 5         18 $points{$$pt[$i][0]} = $$pt[$i][1];
2562             }
2563              
2564             # Sort x and y according to y values
2565 1         3 my (@sortX, @sortY) = ();
2566 1         7 foreach my $sx (sort {$points{$a} <=> $points{$b}} keys %points)
  7         11  
2567             {
2568 5         8 push(@sortX, $sx);
2569 5         8 push(@sortY, $points{$sx});
2570             }
2571              
2572 1         2 my $ylow = my $yhigh = $sortY[0];
2573 1 50       5 if (scalar(@sortY) > 1)
2574             {
2575 1         3 $ylow = 0.5*(3*$sortY[0]-$sortY[1]);
2576 1         3 $yhigh = 0.5*(3*$sortY[-1]-$sortY[-2]);
2577             }
2578              
2579             # Write data into temp file
2580 1         2 my $fileTmp = $ds->{_data};
2581 1 50       85 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2582 1         4 for (my $i = 0; $i < @$pt; $i++)
2583             {
2584 5 100       13 $ylow = 0.5*($sortY[$i]+$sortY[$i-1]) if ($i > 0);
2585 5 100       15 $yhigh = ($i < $#sortY)?
2586             0.5*($sortY[$i]+$sortY[$i+1]) :
2587             2.0*$sortY[$i] - $ylow;
2588 5         36 print DATA "0 $sortY[$i] 0 $sortX[$i] $ylow $yhigh\n";
2589             }
2590 1         36 close(DATA);
2591              
2592 1         3 $ds->{style} = "boxxyerrorbars";
2593 1         7 close(DATA);
2594             }
2595              
2596              
2597             # Process input data of array of points for plotting histograms
2598             sub _thawPointsHistograms
2599             {
2600 1     1   3 my ($ds) = @_;
2601              
2602             # Write data into temp file
2603 1         2 my $pt = $ds->{points};
2604 1         2 my $fileTmp = $ds->{_data};
2605 1 50       73 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2606 1         2 my $numCol = scalar(@{$$pt[0]});
  1         3  
2607 1         4 for (my $i = 0; $i < @$pt; $i++)
2608             {
2609 5         14 print DATA "\"$$pt[$i][0]\" ", join(' ', @{$$pt[$i]}[1 .. $numCol-1]),
  5         20  
2610             "\n";
2611             }
2612 1         33 close(DATA);
2613              
2614 1         4 my $using = join(':', (2 .. $numCol)) . ":xticlabels(1)";
2615 1         4 return($using);
2616             }
2617              
2618              
2619             # Process input data of a matrix of points
2620             sub _thawPointsGrid
2621             {
2622 1     1   2 my ($ds) = @_;
2623              
2624             # Write data into temp file
2625 1         3 my $pt = $ds->{points};
2626 1         2 my $fileTmp = $ds->{_data};
2627 1 50       80 open(DATA, ">$fileTmp") || confess("Can't write data to temp file");
2628 1         7 for (my $i = 0; $i < @$pt; $i++)
2629             {
2630 3         5 for (my $j = 0; $j < @{$$pt[$i]}; $j++)
  12         26  
2631             {
2632 9         11 print DATA join(" ", @{$$pt[$i][$j]}), "\n";
  9         31  
2633             }
2634 3         8 print DATA "\n";
2635             }
2636 1         37 close(DATA);
2637              
2638             # Construst using statement for date-time data
2639 1         3 my $using = '';
2640 1 50       5 if (defined $ds->{timefmt})
2641             {
2642 0         0 my $col = 1;
2643 0         0 $using = "1";
2644 0         0 for (my $i = 0; $i < @{$$pt[0][0]}-1; $i++)
  0         0  
2645             {
2646 0         0 my @a = split(/\s+/, $$pt[0][0][$i]);
2647 0         0 $col += scalar(@a);
2648 0         0 $using .= ":$col";
2649             }
2650             }
2651 1         4 return($using);
2652             }
2653              
2654              
2655             # Curve fitting method
2656             #
2657             # NOTICE: This feature is experimental and in alpha phase.
2658             #
2659             # Usage example:
2660             # my $dataSet = Chart::Gnuplot::DataSet->new(...);
2661             #
2662             # my $dataFit = $dataSet->fit(
2663             # func => "a*x + b", # linear fit
2664             # vars => 'x',
2665             # params => {a => -1, b => 0.5}, # seed
2666             # );
2667             #
2668             # print "a = $dataFit->{params}->{a}\n";
2669             # print "b = $dataFit->{params}->{b}\n";
2670             #
2671             # # Plot the raw data set and fitted curve
2672             # $chart->plot2d($dataSet, $dataFit);
2673             sub fit
2674             {
2675 0     0     my ($self, %hash) = @_;
2676 0           my $script = my $data = my $result = my $log = $self->{_data};
2677 0 0         my $styleTmp = (defined $self->{style})? $self->{style} : 'lines';
2678              
2679             # Filename of the temp files
2680 0           $script =~ s/\/data$/\/fit\.script/;
2681 0           $result =~ s/\/data$/\/fit\.result/;
2682 0           $log =~ s/\/data$/\/fit\.log/;
2683              
2684             # Prepare parameter and error string for printing
2685 0           my $paraString = my $paraList = my $errList = '';
2686 0           my @params = ();
2687 0           my $paraRef = ref($hash{params});
2688 0 0         if ($paraRef eq 'HASH')
    0          
2689             {
2690 0           @params = keys %{$hash{params}};
  0            
2691 0           my @err = ();
2692 0           my $parFile = $self->{_data};
2693 0           $parFile =~ s/\/data$/\/par\.dat/;
2694 0 0         open(PARA, ">$parFile") || confess "Can't write parameter to $parFile";
2695 0           foreach my $pTmp (@params)
2696             {
2697 0           my $vTmp = (defined ${$hash{params}}{$pTmp})?
  0            
2698 0 0         ${$hash{params}}{$pTmp} : 1.0;
2699 0           print PARA "$pTmp = $vTmp\n";
2700 0           push(@err, $pTmp . "_err");
2701             }
2702 0           close(PARA);
2703 0           $paraString = "\"$parFile\"";
2704 0           $paraList = join(',', @params);
2705 0           $errList = join(',', @err);
2706             }
2707             elsif ($paraRef eq 'ARRAY')
2708             {
2709 0           @params = @{$hash{params}};
  0            
2710 0           my @err = map {$_ . '_err'} @params;
  0            
2711 0           $paraString = $paraList = join(',', @params);
2712 0           $errList = join(',', @err);
2713             }
2714             else
2715             {
2716 0           @params = split(/,\s*/, $hash{params});
2717 0           my @err = map {$_ . '_err'} @params;
  0            
2718 0           $paraString = $paraList = $hash{params};
2719 0           $errList = join(',', @err);
2720             }
2721              
2722 0 0         if (!defined $hash{using})
2723             {
2724 0           my @col = split(/\s*,\s*/, $hash{vars});
2725 0           my $numCol = scalar(@col) + 1;
2726 0 0         if (ref($self->{ydata}->[0]) eq 'ARRAY')
2727             {
2728 0           $numCol++;
2729 0           $self->{style} = 'yerror'; # temp style for data file generation
2730             }
2731 0           $hash{using} = join(':', (1 .. $numCol));
2732             }
2733 0 0         $self->_thaw() if (!-e $data); # generate data file
2734 0           $self->{style} = $styleTmp;
2735              
2736             # Generate gnuplot script for curve fitting
2737 0 0         open(FIT, ">$script") || confess("Can't generate script to $script");
2738 0           print FIT "set fit logfile \"$log\" errorvariables\n";
2739 0           print FIT "set print \"$result\"\n";
2740 0           print FIT "fit $hash{func} \"$data\" using $hash{using}".
2741             " via $paraString\n";
2742 0           print FIT "print $paraList\n";
2743 0           print FIT "print $errList\n";
2744 0           close(FIT);
2745              
2746             # Call gnuplot
2747 0           system("gnuplot $script >& /dev/null");
2748              
2749             # Read and parse the result file
2750 0 0         open(RES, $result) || confess("Can't read fitting result $result");
2751 0           chomp(my ($pLine, $eLine) = );
2752 0           close(RES);
2753              
2754             # Save the result in DataSet object
2755 0           my %param;
2756 0           my @pVal = split(/\s+/, $pLine);
2757 0           my @eVal = split(/\s+/, $eLine);
2758 0           my $fitted = '';
2759 0           for (my $i = 0; $i < @pVal; $i++)
2760             {
2761 0           $param{$params[$i]} = $pVal[$i];
2762 0           $param{$params[$i]."_err"} = $eVal[$i];
2763 0           $fitted .= "$params[$i] = $pVal[$i],";
2764             }
2765              
2766 0           $fitted .= $hash{func};
2767 0           my $outDS = Chart::Gnuplot::DataSet->new(
2768             func => $fitted,
2769             params => \%param,
2770             );
2771 0           return($outDS);
2772             }
2773              
2774              
2775             1;
2776              
2777             __END__