File Coverage

blib/lib/Chart/Graph/Xmgrace.pm
Criterion Covered Total %
statement 238 492 48.3
branch 99 218 45.4
condition 1 6 16.6
subroutine 23 35 65.7
pod 0 1 0.0
total 361 752 48.0


line stmt bran cond sub pod time code
1             ## Xmgrace.pm is a sub-module of Graph.pm. It has all the subroutines
2             ## needed for the gnuplot part of the package.
3             ##
4             ## $Id: Xmgrace.pm,v 1.34 2006/06/07 21:09:33 emile Exp $ $Name: $
5             ##
6             ## This software product is developed by Esmond Lee and David Moore,
7             ## and copyrighted(C) 1998 by the University of California, San Diego
8             ## (UCSD), with all rights reserved. UCSD administers the CAIDA grant,
9             ## NCR-9711092, under which part of this code was developed.
10             ##
11             ## There is no charge for this software. You can redistribute it and/or
12             ## modify it under the terms of the GNU General Public License, v. 2 dated
13             ## June 1991 which is incorporated by reference herein. This software is
14             ## distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS, OF MERCHANTABILITY
15             ## OR FITNESS FOR A PARTICULAR PURPOSE or that the use of it will not
16             ## infringe on any third party's intellectual property rights.
17             ##
18             ## You should have received a copy of the GNU GPL along with this program.
19             ##
20             ##
21             ## IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY
22             ## PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
23             ## DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS
24             ## SOFTWARE, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF
25             ## THE POSSIBILITY OF SUCH DAMAGE.
26             ##
27             ## THE SOFTWARE PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND THE
28             ## UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
29             ## SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. THE UNIVERSITY
30             ## OF CALIFORNIA MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES
31             ## OF ANY KIND, EITHER IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED
32             ## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
33             ## PARTICULAR PURPOSE, OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE
34             ## ANY PATENT, TRADEMARK OR OTHER RIGHTS.
35             ##
36             ##
37             ## Contact: graph-dev@caida.org
38             ##
39             ##
40             package Chart::Graph::Xmgrace;
41 4     4   25 use Exporter();
  4         16  
  4         229  
42              
43             @ISA = qw(Exporter);
44             @EXPORT = qw();
45             @EXPORT_OK = qw(&xmgrace);
46              
47 4     4   25 use Carp; # for carp() and croak()
  4         10  
  4         293  
48 4     4   23 use Chart::Graph::Utils qw(:UTILS); # get global subs and variable
  4         7  
  4         1434  
49 4     4   25 use Chart::Graph::XrtUtils qw(:UTILS);
  4         8  
  4         853  
50 4     4   2778 use Chart::Graph::Xmgrace::Grace;
  4         13  
  4         127  
51 4     4   2860 use Chart::Graph::Xmgrace::Graph_Presentation_Type;
  4         13  
  4         132  
52 4     4   3228 use Chart::Graph::Xmgrace::Dataset;
  4         11  
  4         107  
53 4     4   24 use FileHandle;
  4         8  
  4         37  
54              
55             $cvs_Id = '$Id: Xmgrace.pm,v 1.34 2006/06/07 21:09:33 emile Exp $';
56             $cvs_Author = '$Author: emile $';
57             $cvs_Name = '$Name: $';
58             $cvs_Revision = '$Revision: 1.34 $';
59              
60             $VERSION = 3.2;
61              
62 4     4   1877 use strict;
  4         10  
  4         23028  
63              
64             #for debugging purposes.
65             my $stdout = 0;
66              
67             # these variables hold default options for xmgrace
68             my %def_xmgrace_global_opts = (
69             "title" => "untitled", # @description
70             "subtitle" => "",
71             "type of graph" => "XY graph", # XY graph, XY chart, Polar Graph, Smith Chart, Fixed
72             "output type" => "PNG", # png, jpeg, ps
73             "output file" => "untitled-grace",
74             "grace output file" => "untitled-grace.agr",
75             "xrange" => undef,
76             "yrange" => undef,
77             "x-axis label" => "x-axis",
78             "y-axis label" => "y-axis",
79             "alt x-axis label" => undef,
80             "alt y-axis label" => undef,
81             "logscale x" => undef,
82             "logscale y" => undef,
83             "xtics" => undef,
84             "ytics" => undef,
85             "alt xtics" => undef,
86             "alt ytics" => undef,
87             "stacked" => "false",
88             "extra opts" => undef,
89             );
90              
91              
92             my $def_graph_appearance = new Chart::Graph::Xmgrace::Graph_Presentation_Type;
93              
94             my %def_xmgrace_data_opts = (
95             "set presentation" => "XY",
96             "options" => $def_graph_appearance->{"XY graph"},
97             "title" => "untitled data set", # comment, legend
98             "data format" => undef, # columns, matrix, or file
99             );
100              
101             # New Hash of array references to hold list of options that might need to
102             # be checked against options. If Xmgrace program changes. Update here
103             my %def_xmgrace_available_options =
104             ( "type of graph" => ["XY graph", "XY chart", "Bar chart",
105             "Polar graph", "Smith chart", "Fixed",
106             # "Pie Chart", # Not yet implemented
107             ],
108             );
109              
110              
111             #
112             #
113             # Subroutine: xmgrace()
114             #
115             # Description: this is the main function you will be calling from
116             # our scripts. please see
117             # www.caida.org/Tools/Graph/graph_xmgrace.html for a
118             # full description and how-to of this subroutine
119             #
120              
121             sub xmgrace {
122 3     3 0 337 my ($user_global_opts_ref, @data_sets) = @_;
123 3         5 my (%data_opts, %global_opts,);
124 0         0 my ($plottype, $output_file, $plot_file, $output_type, $data_set_ref);
125             #my $autoscale = 1; # by default use autoscale
126             #my ($xscale, $yscale) = (0,0);
127 3         6 my $autoscale = "";
128              
129             # create a new filehandle to be used throughout package
130 3         117 my $handle = new FileHandle;
131 3         100 my $grace_output_file;
132              
133 3         27 my $grace = new Chart::Graph::Xmgrace::Grace("g0");
134              
135             # create tmpdir
136 3         13 _make_tmpdir("_Xmgrace_"); # grace files should be saved for user tweaking
137            
138             # set paths for external programs
139 3 100       17 if (not _set_xmgrace_paths()) {
140 2         7 _cleanup_tmpdir();
141 2         233 return 0;
142             }
143            
144             # check first arg for hash
145 1 50       5 if (ref($user_global_opts_ref) ne "HASH") {
146 0         0 carp "Global options must be a hash";
147 0         0 _cleanup_tmpdir();
148 0         0 return 0;
149             }
150            
151             # check for data sets
152 1 50       3 if (not @data_sets) {
153 0         0 carp "no data sets";
154 0         0 $handle->close;
155 0         0 _cleanup_tmpdir();
156 0         0 return 0;
157             }
158            
159 1         4 my $command_file = _make_tmpfile("command", "agr");
160             # remember to close the file if we return
161 1 50       7 if (not $handle->open(">$command_file")) {
162 0         0 carp "could not open file: $command_file";
163 0         0 _cleanup_tmpdir();
164 0         0 return 0;
165             }
166            
167             # process global options
168             # call to combine user options with default options
169 1         105 %global_opts = _mesh_opts($user_global_opts_ref, \%def_xmgrace_global_opts);
170            
171             # crunch on the global options
172 1         6 while (my ($key, $value) = each %global_opts) {
173            
174 20 100       43 if ($key eq "title") {
175 1         5 $grace->title_options->title($value);
176             }
177            
178 20 100       37 if ($key eq "subtitle") {
179 1         6 $grace->subtitle_options->title($value);
180             }
181            
182 20 100       38 if ($key eq "type of graph") {
183 1 50       5 unless(_is_available_option($key, $value,
184             \%def_xmgrace_available_options)) {
185 0         0 carp "Not a valid Xmgrace graph type";
186 0         0 $handle->close();
187 0         0 _cleanup_tmpdir();
188 0         0 return 0;
189             }
190 1 50       21 if ($value =~ "graph") {
    50          
191 0         0 $grace->graph_global_options->type("XY");
192             } elsif ($value =~ "chart") {
193 1         6 $grace->graph_global_options->type("Chart");
194             }
195             }
196            
197 20 100       31 if ($key eq "stacked") {
198 1 50 33     18 if ($value eq "true" || $value eq "false") {
199 1         5 $grace->graph_global_options->stacked("$value");
200             } else { # anything else is considered "false"
201 0         0 carp join ('',
202             "Warning: ambiguous setting for stacking",
203             "bar chart\nAssuming no stacking"
204             );
205 0         0 $grace->graph_global_options->stacked("false");
206             }
207             }
208            
209 20 100       38 if ($key eq "x-axis label") {
210 1         7 $grace->axes_options->xaxis->label_options->label($value);
211             }
212            
213 20 100       50 if ($key eq "y-axis label") {
214 1         4 $grace->axes_options->yaxis->label_options->label($value);
215             }
216            
217 20 100       37 if ($key eq "alt x-axis label") {
218 1 50       9 if (defined($value)) {
219 0         0 $grace->axes_options->altxaxis->status("on");
220 0         0 $grace->axes_options->altxaxis->label_options->label($value);
221             }
222             # else altxaxis off
223             }
224            
225 20 100       34 if ($key eq "alt y-axis label") {
226 1 50       6 if (defined($value)) {
227 0         0 $grace->axes_options->altyaxis->status("on");
228 0         0 $grace->axes_options->altyaxis->label_options->label($value);
229             }
230             # else altyaxis off
231             }
232              
233              
234 20 100       36 if ($key eq "xrange") {
235 1 50       15 if (defined($value)) {
236 0 0       0 if (ref($value) ne "ARRAY") {
237 0         0 carp "xrange values must be an ARRAY\n";
238 0         0 $handle->close();
239 0         0 _cleanup_tmpdir();
240 0         0 return 0;
241             }
242 0         0 $grace->world_options->xmin($value->[0]);
243 0         0 $grace->world_options->xmax($value->[1]);
244 0         0 $autoscale = "x";
245             }
246             }
247              
248 20 100       35 if ($key eq "yrange") {
249 1 50       5 if (defined($value)) {
250 0 0       0 if (ref($value) ne "ARRAY") {
251 0         0 carp "yrange values must be an ARRAY\n";
252 0         0 $handle->close();
253 0         0 _cleanup_tmpdir();
254 0         0 return 0;
255             }
256 0         0 $grace->world_options->ymin($value->[0]);
257 0         0 $grace->world_options->ymax($value->[1]);
258 0         0 $autoscale = "y";
259             }
260             }
261            
262 20 100       39 if ($key eq "xtics") {
263 1 50       4 if (defined($value)) {
264 0         0 $grace->axes_options->xaxis->tick_options->type("spec");
265 0         0 $grace->axes_options->xaxis->ticklabel_options->type("spec");
266 0         0 $grace->axes_options->xaxis->ticklabel_options->ticklabels($value);
267             }
268             }
269            
270            
271 20 100       33 if ($key eq "ytics") {
272 1 50       5 if (defined($value)) {
273 0         0 $grace->axes_options->yaxis->tick_options->type("spec");
274 0         0 $grace->axes_options->yaxis->ticklabel_options->type("spec");
275 0         0 $grace->axes_options->yaxis->ticklabel_options->ticklabels($value);
276             }
277             }
278            
279 20 100       47 if ($key eq "alt xtics") {
280 1 50       5 if (defined($value)) {
281 0         0 $grace->axes_options->altxaxis->tick_options->type("spec");
282 0         0 $grace->axes_options->altxaxis->ticklabel_options->type("spec");
283 0         0 $grace->axes_options->altxaxis->ticklabel_options->ticklabels($value);
284             }
285             }
286            
287            
288 20 100       33 if ($key eq "alt ytics") {
289 1 50       6 if (defined($value)) {
290 0         0 $grace->axes_options->altyaxis->tick_options->type("spec");
291 0         0 $grace->axes_options->altyaxis->ticklabel_options->type("spec");
292 0         0 $grace->axes_options->altyaxis->ticklabel_options->ticklabels($value);
293             }
294             }
295              
296 20 100       41 if ($key eq "logscale x") {
297 1 50       3 if (defined($value)) {
298 0         0 $grace->axes_options->xaxes->scale("Logarithmic");
299             }
300             }
301            
302 20 100       35 if ($key eq "logscale y") {
303 1 50       4 if (defined($value)) {
304 0         0 $grace->axes_options->yaxes->scale("Logarithmic");
305             }
306             }
307              
308 20 100       37 if ($key eq "output type") {
309 1         5 $output_type = uc($value);
310 1 50       4 if (not _check_output_type($output_type)) {
311 0         0 $output_type = $def_xmgrace_global_opts{"output type"};
312             }
313             #print "output type = $value\n";
314 1         5 $grace->output_type($output_type);
315             }
316            
317 20 100       40 if ($key eq "output file") {
318             #print "output file = $value\n";
319 1         6 $grace->output_file($value);
320             }
321            
322 20 100       33 if ($key eq "grace output file") {
323 1 50       3 if (defined($value)) {
324 1         20 $grace->grace_output_file(_make_gracefile("$value"));
325             #print "grace output file = $value\n";
326             }
327             }
328              
329 20 100       86 if ($key eq "extra opts") {
330 1 50       7 if (defined($value)) {
331 0         0 $grace->extra_options->extras($value);
332             }
333             }
334             }
335            
336             # process data sets
337 1         3 my @cum_data_set_objects;
338 1         2 my $dataset_count = 0;
339            
340 1         3 while (@data_sets) {
341 11         17 $data_set_ref = shift @data_sets;
342            
343 11 50       32 if (ref($data_set_ref) ne "ARRAY") {
344 0         0 carp "Data set must be an array";
345 0         0 $handle->close();
346 0         0 _cleanup_tmpdir();
347 0         0 return 0;
348             }
349            
350             # create a new Dataset object with each new datase
351 11         38 my $data_set_object = new Chart::Graph::Xmgrace::Dataset;
352 11         14 my ($user_data_opts_ref, @data) = @{$data_set_ref};
  11         28  
353            
354             # set values in the dataset object
355 11         51 $data_set_object->set_number($dataset_count);
356 11         53 $data_set_object->data(\@data);
357            
358             # process data
359 11         56 $data_set_object->data_format($user_data_opts_ref->{"data format"});
360 11         24 my $formatted_data = _xmgrace_data_set($data_set_object);
361            
362 11 50       22 if (not $formatted_data) {
363             # error message already printed
364 0         0 $handle->close();
365 0         0 _cleanup_tmpdir();
366 0         0 return 0;
367             }
368            
369             # stick formatted data back in the dataset object
370 11         46 $data_set_object->data($formatted_data);
371              
372             # set set_type
373 11         16 my $tog = $global_opts{"type of graph"}; # type of graph
374 11 50       53 if ($tog =~ "XY") {
    50          
375 0         0 $data_set_object->set_type("XY");
376             } elsif ($tog =~ m/BAR/i) {
377 11         51 $data_set_object->set_type("BAR");
378             } else {
379 0         0 carp "Untrapped graph type - Xmgrace.pm internal error";
380             }
381            
382             # need to create a new Presentation Type object for each dataset
383 11         45 my $gpt = new Chart::Graph::Xmgrace::Graph_Presentation_Type($dataset_count);
384 11         24 my $graph_appearance = $gpt->{$global_opts{"type of graph"}};
385 11         17 $def_xmgrace_data_opts{options} = $graph_appearance;
386            
387             # mesh data options
388 11         62 my $data_options_ref = _mesh_xmgrace_opts($user_data_opts_ref,
389             \%def_xmgrace_data_opts);
390            
391 11 50       24 if (not $data_options_ref) {
392             # error message already printed
393 0         0 $handle->close();
394 0         0 _cleanup_tmpdir();
395 0         0 return 0;
396             }
397              
398             # change the "title" into "comment" and "legend"
399 11         21 _set_title($data_options_ref);
400              
401             # set data options in the dataset object
402 11         53 $data_set_object->options($data_options_ref);
403              
404             # create new set presention for each data set
405 11         17 my $sp = $user_data_opts_ref->{"set presentation"};
406 11 50       22 if ($sp) {
407 0 0       0 if ($sp eq "XY") {
    0          
408 0         0 _set_XY($data_set_object);
409             } elsif ($sp =~ m/BAR/i) {
410 0         0 _set_BAR($data_set_object);
411             } else {
412 0         0 carp "Untrapped graph type - Xmgrace.pm internal error";
413             }
414             }
415              
416             # process data
417             # accumulate data objects into an array.
418 11         18 push @cum_data_set_objects, $data_set_object;
419 11         552 $dataset_count++;
420             }
421              
422             # print commandfile
423 1 50       4 if ($stdout) {
424 0         0 $handle = \*STDOUT;
425             }
426 1         8 $grace->print($handle);
427            
428             # we're done crunching on the data now print something!
429             # print data options
430              
431              
432 1         6 foreach my $set_object (@cum_data_set_objects) {
433             # XXX This code breaks under Perl 5.6 - not sure why XXX
434             # $set_object->{options}->{options}->print($handle,"s" .
435             # "$set_object->{set_number}");
436             }
437              
438             # print data sets
439 1         3 foreach my $set_object (@cum_data_set_objects) {
440 11         39 _printline($handle, "target G0.S$set_object->{\"set_number\"}\n");
441 11         31 _printline($handle, "type $set_object->{\"set_type\"}\n");
442 11 50       23 if (not _print_data_set($handle, $set_object)) {
443             # already printed error message
444 0         0 return 0;
445             }
446             }
447              
448             # commandfile successfully completed
449 1 50       12 $handle->close unless ($stdout);
450              
451             # if user chooses to, can save the .agr file
452 1 50       153 if (defined $grace->{grace_output_file}) {
453 1         5 _copy_commandfile($command_file, $grace->{grace_output_file});
454             }
455              
456             # run xmgrace with commandline args to create the specified graph type
457             # $xmgrace needs to be defined in the user's script
458              
459 1         25 _exec_xmgrace($command_file, $grace, $autoscale); # could probably put the autoscale flag inside the grace object
460              
461              
462             } # end of xmgrace()
463              
464              
465             ############################################################
466             #
467             # The following functions are internal functions used by this package. The
468             # user won't know about these functions.
469             #
470             ############################################################
471              
472             #
473             #
474             # Subroutine _is_available_option($$$)
475             #
476             # Description: Modest helper subroutine to loop through available
477             # options for more complex Xmgrace options and return
478             # true if a match is found.
479             #
480             #
481             sub _is_available_option($$$) {
482 1     1   3 my $key = shift;
483 1         1 my $to_match = shift;
484 1         2 my $avail_options_ref = shift;
485              
486 1 50       4 if (exists($avail_options_ref->{$key})) {
487 1         2 foreach my $option (@{$avail_options_ref->{$key}}) {
  1         4  
488 3 100       24 if ($to_match =~ m/$option/i) {
489 1         4 return($option);
490             }
491             }
492             }
493 0         0 return(undef);
494             }
495              
496              
497              
498             sub _exec_xmgrace ($$$) {
499 1     1   10 my ($command_file, $grace, $autoscale) = @_;
500 1         10 my ($childpid, $port);
501 1         8 my $display_env = $ENV{DISPLAY};
502 1         6 my $status;
503             my $framebuffer;
504              
505             #if ($Chart::Graph::use_xvfb) {
506             # start the virtual X server
507             #($childpid, $port) = _exec_xvfb();
508             # $status = system("$xmgrace -display :$port.0 < $command_file");
509            
510             #} else {
511             # use the local X server
512             # warning: colors might be messed up
513             # depending on your current setup
514             #$status = system("$Chart::Graph::xrt3d -display $display_env < $command_file");
515              
516             #}
517              
518             #my $status = system("$xrt -display :$port.0 < $command_file");
519             #if (not _chk_status($status)) {
520             # return 0;
521             # }
522              
523             #if ($Chart::Graph::use_xvfb) {
524             # kill('KILL', $childpid);
525             # }
526              
527              
528 1 50       11 if ($autoscale eq "") {
    0          
    0          
529 1 50       42 _run_app("$xmgrace", "-hardcopy -hdevice $grace->{output_type}",
530             "-printfile $grace->{output_file} $command_file -pexec autoscale") unless $stdout;
531 0 0       0 if ($Chart::Graph::debug) {
532 0         0 print STDERR "$xmgrace -hardcopy -hdevice $grace->{output_type} -printfile $grace->{output_file} $command_file -pexec autoscale\n";
533 0         0 print STDERR "done.\n";
534             ### fill in tmp files...
535             }
536 0         0 1;
537             } elsif ($autoscale eq "x") {
538 0 0       0 _run_app("$xmgrace", "-autoscale y", "-hardcopy -hdevice $grace->{output_type}",
539             "-printfile $grace->{output_file} $command_file -pexec autoscale") unless $stdout;
540 0         0 print STDERR "xscale\n";
541             } elsif ($autoscale eq "y") {
542 0 0       0 _run_app("$xmgrace", "-autoscale x", "-hardcopy -hdevice $grace->{output_type}",
543             "-printfile $grace->{output_file} $command_file -autoscale") unless $stdout;
544 0         0 print STDERR "yscale\n";
545             } else {
546 0 0       0 _run_app("$xmgrace", "-autoscale xy", "-hardcopy -hdevice $grace->{output_type}",
547             "-printfile $grace->{output_file} $command_file -autoscale") unless $stdout;
548 0         0 print STDERR "both\n";
549             }
550             }
551              
552              
553             #
554             # Subroutine: set_xmgrace_paths()
555             #
556             # Description: set paths for external programs required by gnuplot()
557             # if they are not defined already
558             #
559              
560             sub _set_xmgrace_paths {
561              
562 3 100   3   8 if (not defined($xmgrace)) {
563 1 50       6 if (not $xmgrace = _get_path("xmgrace")) {
564 1         5 return 0;
565             }
566             }
567              
568 2 100       7 if (not defined($xvfb)) {
569 1 50       4 if (not $xvfb = _get_path("Xvfb")) {
570 1         4 return 0;
571             }
572             }
573            
574             # make sure /usr/dt/lib is in the library path
575 1         6 _set_ldpath("/usr/dt/lib"); # defined in Xrtutils.pm
576              
577 1         2 return 1;
578             }
579              
580              
581             #
582             # Subroutine: _xmgrace_data_set()
583             #
584             # Description: this function sets up the data sets into a columns format
585             # and returns a formatted data reference
586             #
587             #
588              
589             sub _xmgrace_data_set {
590 11     11   14 my $data_set_object = shift;
591              
592 11         17 my $data_format = $data_set_object->{"data_format"};
593 11         12 my @data = @{$data_set_object->data};
  11         43  
594 11         14 my $formatted_data_ref;
595            
596             # we give the user 3 formats for supplying the data set
597             # 1) matrix
598             # 2) column
599             # 3) file
600             # please see the online docs for a description of these
601             # formats
602              
603 11 50       24 if ($data_format eq "matrix") {
    0          
    0          
    0          
604 11         22 $formatted_data_ref = _matrix_to_columns(@data);
605             } elsif ($data_format eq "columns") {
606 0         0 $formatted_data_ref = _columns_to_columns(@data);
607             } elsif ($data_format eq "file") {
608 0         0 $formatted_data_ref = _file_to_columns(@data); # single item list (filename)
609             } elsif ($data_format eq "") {
610 0         0 carp "Need to specify data set type";
611 0         0 return 0;
612             } else {
613 0         0 carp "Illegal data format: $data_format";
614 0         0 return 0;
615             }
616 11         20 return $formatted_data_ref;
617             }
618              
619             #
620             #
621             # Subroutine: matrix_to_columns()
622             #
623             # Description: converts the matrix data input into a an array
624             # of pairs of input. See www for the specific on
625             # the matrix format
626             #
627             #
628              
629             sub _matrix_to_columns (\@ ) {
630 11     11   14 my ($matrix_ref) = @_;
631 11         11 my $entry_ref;
632             my $matrix_len;
633 11         13 my @pairs = ();
634 11         16 my $single_pair = "";
635 11         13 my (@x_col, @y_col);
636              
637 11 50       27 if (ref($matrix_ref) ne "ARRAY") {
638 0         0 carp "Matrix data must be a reference to an array";
639 0         0 return 0;
640             }
641            
642 11         12 $matrix_len = @{$matrix_ref};
  11         18  
643 11         28 for (my $i = 0; $i < $matrix_len; $i++) {
644 99         120 $entry_ref = $matrix_ref->[$i];
645            
646 99 50       191 if (ref($entry_ref) ne "ARRAY") {
647 0         0 carp "Matrix entry must be a reference to an array";
648 0         0 return 0;
649             }
650            
651             # check that each entry ONLY has two entries
652 99 50       86 if (@{$entry_ref} != 2) {
  99         204  
653 0         0 carp "Each entry must be an array of size 2";
654 0         0 return 0;
655             }
656            
657 99         154 push @x_col, $entry_ref->[0];
658 99         136 push @y_col, $entry_ref->[1];
659              
660 99         262 @pairs = (\@x_col, \@y_col);
661             }
662              
663 11         29 return \@pairs;
664             }
665              
666             #
667             #
668             # Subroutine: columns_to_columns()
669             #
670             # Description: converts the column data input into a the gnuplot
671             # data file format. please see www page for specifics
672             # on this format.
673             #
674              
675             sub _columns_to_columns (\@ ) {
676 0     0   0 my ($x_col, $y_col) = @{$_[0]};
  0         0  
677 0         0 my ($x_len, $y_len);
678 0         0 my @pairs = ();
679 0         0 my $single_pair = "";
680            
681 0 0 0     0 if ((ref($x_col) ne "ARRAY") or (ref($y_col) ne "ARRAY")) {
682 0 0       0 unless($x_col) {
683 0         0 carp "No data in X column";
684             }
685 0 0       0 unless($y_col) {
686 0         0 carp "No data in Y column";
687             }
688 0         0 carp "Column data must be a reference to an array";
689 0         0 return 0;
690             }
691            
692 0         0 $x_len = @{$x_col};
  0         0  
693 0         0 $y_len = @{$y_col};
  0         0  
694            
695 0 0       0 if ($x_len != $y_len) {
696 0         0 carp "x and y columns must be of same length";
697 0         0 return 0;
698             }
699              
700 0 0       0 if ($x_len == 0) {
701 0         0 carp "Warning: x column has no data";
702             }
703            
704 0 0       0 if ($y_len == 0) {
705 0         0 carp "Warning: y column has no data";
706             }
707            
708 0         0 @pairs = ($x_col, $y_col);
709 0         0 return \@pairs;
710             }
711              
712             #
713             # Subroutine: file_to_columns()
714             #
715             # Description: If a gnuplot data set was given in
716             # file format, we simply copy the data
717             # and read it into
718             #
719              
720             sub _file_to_columns (\@ ) {
721 0     0   0 my ($file_in) = @_;
722 0         0 my $single_pair = "";
723 0         0 my @pairs = ();
724 0         0 my (@x_col, @y_col);
725 0         0 my ($x_len, $y_len);
726              
727 0 0       0 if (not $file_in) {
728 0         0 carp "File data format selected but data set file missing";
729 0         0 return 0;
730             }
731              
732 0 0       0 if (not -f $file_in) {
733 0         0 carp "Data set file, '$file_in', does not exist.";
734 0         0 return 0;
735             }
736            
737 0         0 my $fh = new FileHandle;
738            
739 0 0       0 if (not $fh->open("<$file_in")) {
740 0         0 carp "Could not open file: '$file_in'";
741 0         0 cleanup_tmpdir();
742 0         0 return 0;
743             }
744              
745 0         0 while (<$fh>) {
746 0         0 chomp;
747 0         0 my @data = split /\s+/;
748 0         0 push @x_col, $data[0];
749 0         0 push @y_col, $data[1];
750             }
751            
752 0         0 $x_len = @x_col;
753 0         0 $y_len = @y_col;
754              
755 0 0       0 if ($x_len != $y_len) {
756 0         0 carp "x and y columns must be of same length";
757 0         0 return 0;
758             }
759            
760 0 0       0 if ($x_len == 0) {
761 0         0 carp "Warning: x column has no data";
762             }
763            
764 0 0       0 if ($y_len == 0) {
765 0         0 carp "Warning: y column has no data";
766             }
767              
768 0         0 @pairs = (\@x_col, \@y_col);
769 0         0 $fh->close;
770 0         0 return \@pairs;
771             }
772              
773             #
774             # Subroutine: _printline()
775             #
776             # Description: prints out an Xmgrace formatted line
777             #
778             #
779            
780             sub _printline ($$$ ) {
781 22     22   31 my ($handle, $string, $length) = @_;
782              
783 22 50       38 unless ($length) {$length = 1;}
  22         23  
784 22         38 print $handle "@";
785 22         26 print $handle ' ' x $length;
786 22         24 print $handle "$string";
787              
788 22         29 return 1; # just for fun
789            
790             }
791              
792             #
793             # Subroutine: _copy_commandfile()
794             #
795             # Description: copies the temp commandfile into a grace
796             # output file (.agr)
797             #
798              
799             sub _copy_commandfile ($$ ) {
800            
801 1     1   4 my ($commandfile, $grace_output_file) = @_;
802 1         10273 my $status = system("cp", "$commandfile", "$grace_output_file");
803 1 50       64 if (not _chk_status($status)) {
804 0         0 return 0;
805             }
806             }
807              
808             #
809             # Subroutine: _make_gracefile()
810             #
811             # Description: constructs the gracefile.agr name
812             #
813              
814             sub _make_gracefile ($ ) {
815            
816 1     1   3 my $file = shift;
817 1         3 my $ext = ".agr";
818            
819 1 50       8 if ($file !~ m|\.agr$|) {
820 0         0 return "$file$ext";
821             } else {
822 1         7 return "$file";
823             }
824             }
825              
826             #
827             # Subroutine: _check_output_type()
828             #
829             # Description: checks user output type to known output types.
830             #
831              
832             sub _check_output_type ($ ) {
833            
834 1     1   3 my $user_type = shift;
835 1         3 my @types = ("PNG", "PS", "JPEG", "PNM");
836 1         3 my $seen = 0;
837            
838 1         3 foreach my $known_type (@types) {
839 1 50       28 if ($user_type =~ m|$known_type|i) {
840 1         3 $seen = 1;
841             }
842 1 50       4 if ($seen) {
843 1         5 return 1;
844             }
845             }
846 0         0 carp "Unknown output type: \'$user_type\'\nUsing default output type: \'PNG\'";
847 0         0 return 0;
848             }
849              
850              
851             #
852             # Subroutine: _run_app()
853             #
854             # Description: to simplify calls to external applications
855             #
856              
857             sub _run_app($@ ) {
858 1     1   10 my $application = shift;
859 1         7 my @arguments = @_;
860 1         4 my $command_str;
861              
862             # Run application
863             # Test if an executable file exists as specified location
864 1 50       66 if (-x "$application") {
865             # Try to run application. Quit if that fails.
866 0         0 $command_str = join(' ', $application, @arguments);
867 0         0 my $rc = 0xffff & system ($command_str);
868 0 0       0 if ($rc != 0) {
869 0         0 die "Execution error: $application";
870             }
871             } else {
872 1         1296 die "No such application: $application";
873             }
874             }
875              
876             #
877             # Subroutine: _mesh_xmgrace_opts
878             #
879             # Description: meshes the user's data_options with the default
880             # data options these "options" include: title, set
881             # presentation, options, and data format
882             #
883              
884             sub _mesh_xmgrace_opts {
885 11     11   17 my ($user_opts_ref, $default_opts_ref) = @_;
886              
887 11         11 my %user_opts = %{$user_opts_ref};
  11         54  
888 11         16 my %default_opts = %{$default_opts_ref};
  11         28  
889 11         19 my %opts;
890              
891             # check user opts against defaults and mesh
892             # the basic algorithm here is to override the
893             # the default options against the ones that
894             # the user has passed in.
895 11         30 while (my ($key, $value) = each %default_opts) {
896            
897 44 100       67 if ($key eq "options") {
898 11         15 my $opts_obj = $def_xmgrace_data_opts{options};
899 11         16 my $def_options = $opts_obj->{options};
900            
901 11 50       21 if (defined($user_opts{options})) {
902             # if user provides options for the datasets we mesh them
903 0         0 my $data_options_obj = _mesh_option_opts($user_opts_ref->{options},
904             $def_options);
905 0         0 $opts_obj->{options} = $data_options_obj;
906 0         0 $opts{options} = $opts_obj;
907 0         0 delete $user_opts{$key}; # remove options
908             } else {
909 11         22 $opts_obj->{options} = $def_options;
910 11         40 $opts{options} = $opts_obj;
911             }
912             } else {
913 33 100       55 if (defined($user_opts{$key})) {
914 22         26 $opts{$key} = $user_opts{$key};
915 22         68 delete $user_opts{$key}; # remove options
916             # that are matching
917             } else {
918 11         33 $opts{$key} = $default_opts{$key};
919             }
920             }
921             }
922            
923             # any left over options in the table are unknown
924             # if the user passes in illegal options then we
925             # warn them with an error message but still
926             # proceed.
927 11         35 while (my ($key, $value) = each %user_opts) {
928 0         0 carp "unknown option: $key";
929             }
930            
931 11         33 return \%opts;
932             }
933              
934             #
935             # Name: _mesh_option_opts
936             #
937             # Description: if the user introduces a "option" in their dataset
938             # hash, we use this function to mesh those options
939             # with the default options of a particular dataset.
940             # note: default options depend on the type of
941             # graph
942             #
943              
944             sub _mesh_option_opts {
945 0     0   0 my ($user_opts_ref, $default_opts_ref) = @_;
946 0         0 my %user_opts = %{$user_opts_ref};
  0         0  
947 0         0 my %def_options = %{$default_opts_ref};
  0         0  
948            
949             # check user opts against defaults and mesh
950             # the basic algorithm here is to override the
951             # the default options against the ones that
952             # the user has passed in.
953 0         0 while (my ($key, $value) = each %def_options) {
954 0 0       0 if (defined($user_opts{$key})) {
955            
956 0 0       0 if (!ref($value)) { # scalar
957 0         0 $def_options{$key} = $user_opts{$key};
958 0         0 delete $user_opts{$key}; # remove options
959             }
960            
961             else { # blessed object
962 0         0 my $return_obj;
963 0         0 my $class = ref($def_options{$key});
964 0         0 $return_obj = _mesh_option_opts($user_opts_ref->{$key},
965             $def_options{$key}->{options});
966 0         0 bless($return_obj, $class);
967 0         0 $def_options{$key}->{options} = $return_obj;
968 0         0 delete $user_opts{$key};
969             }
970             }
971             }
972            
973             # any left over options in the table are unknown
974             # if the user passes in illegal options then we
975             # warn them with an error message but still
976             # proceed.
977 0         0 while (my ($key, $value) = each %user_opts) {
978 0         0 carp "unknown option: $key";
979             }
980            
981 0         0 my $ret_obj = \%def_options;
982 0         0 return $ret_obj;
983             }
984              
985             #
986             # Name: _print_data_set
987             #
988             # Description: used to print out the data in the dataset in a
989             # formatted manner
990             #
991              
992             sub _print_data_set ($\@ ) {
993 11     11   14 my ($handle, $set_object) = @_;
994 11         12 my ($x_col, $y_col);
995 0         0 my $length;
996 0         0 my $i;
997 11         12 my $delimeter = "\t\t"; # set to 15 spaces, nice one xmgrace!
998 11         51 my $pairs = $set_object->data;
999              
1000 11         24 ($x_col, $y_col) = ($pairs->[0], $pairs->[1]);
1001 11         13 $length = $#{$x_col};
  11         16  
1002              
1003             #
1004             # data set format
1005             # @target G.S
1006             # @type $type_of_graph
1007             # x1 y1
1008             # . .
1009             # . .
1010             # xn yn
1011             # &
1012             #
1013              
1014             # we don't print out the hidden data set
1015             # XXX Protection against trying to access nonexistentant
1016             # XXX accessor function or OVERLOADED function?
1017 11 50       40 if (exists($set_object->{options}->{options}->{hidden})) {
1018             # This statement now breaks in 5.6 - unblessed reference.
1019 0 0       0 if ($set_object->{options}->{options}->hidden eq "true") {
1020 0         0 return 1;
1021             }
1022             }
1023              
1024 11         26 for ($i = 0; $i <= $length; $i++) {
1025 99         319 print $handle "$delimeter", "$x_col->[$i]", "$delimeter",
1026             "$y_col->[$i]\n";
1027             }
1028              
1029             # each data set is separated by a "&"
1030 11         12 print $handle "&\n";
1031 11         37 return 1;
1032             }
1033              
1034              
1035             #
1036             # Name: _set_BAR
1037             #
1038             # Description: sets up the necessary characteristics of a BAR
1039             # dataset type
1040             #
1041              
1042             sub _set_BAR ($ ) {
1043 0     0   0 my $ds_object = shift;
1044 0         0 my $data_options_ref = $ds_object->{options};
1045 0         0 $ds_object->set_type("BAR");
1046 0         0 $data_options_ref->{options}->type("BAR");
1047 0         0 $data_options_ref->{options}->symbol->fill_pattern("1");
1048 0         0 $data_options_ref->{options}->symbol->color("1");
1049 0         0 $data_options_ref->{options}->line->type("0");
1050 0         0 return 1;
1051             }
1052              
1053             #
1054             # Name: _set_XY
1055             #
1056             # Description: sets up the necessary characteristics of an XY
1057             # dataset type
1058             #
1059              
1060             sub _set_XY ($ ) {
1061 0     0   0 my $ds_object = shift;
1062 0         0 my $data_options_ref = $ds_object->{options};
1063 0         0 $ds_object->set_type("XY");
1064 0         0 $data_options_ref->{options}->type("XY");
1065 0         0 $data_options_ref->{options}->symbol->fill_pattern("0");
1066 0         0 $data_options_ref->{options}->line->type("1");
1067 0         0 return 1;
1068             }
1069              
1070             # Name: _set_title
1071             #
1072             # Description: sets up the necessary characteristics for the
1073             # title of the graph
1074             #
1075              
1076             sub _set_title ($ ) {
1077 11     11   12 my $ds_ref = shift;
1078 11         17 my $title = $ds_ref->{title};
1079 11         22 $ds_ref->{options}->{options}->{comment} = $title;
1080 11         17 $ds_ref->{options}->{options}->{legend} = $title;
1081 11         14 return 1;
1082             }
1083              
1084             ####################################################################
1085             #
1086             # The following functions are used to map an option value from
1087             # prose to it's corresponding number value which grace understands
1088             # Note: these routines are not used in the current version. the user
1089             # must use numbers (NOT prose) for now.
1090             #
1091             ###################################################################
1092              
1093              
1094             #
1095             # Subroutine: _get_color()
1096             #
1097             # Description: simply looks up a hash table and returns
1098             # a number that xmgrace understands that
1099             # corresponds to the color
1100             #
1101              
1102             sub _get_color ($ ) {
1103 0     0     my $color = shift;
1104 0           my %colortable = (
1105             "white" => "0",
1106             "black" => "1",
1107             "red" => "2",
1108             "green" => "3",
1109             "blue" => "4",
1110             "yellow" => "5",
1111             "brown" => "6",
1112             "grey" => "7",
1113             "violet" => "8",
1114             "cyan" => "9",
1115             "magenta" => "10",
1116             "orange" => "11",
1117             "indigo" => "12",
1118             "maroon" => "13",
1119             "turquoise" => "14",
1120             "green4" => "15",
1121             );
1122            
1123 0           $color = lc($color);
1124             # "\L$color\E"
1125             #color =~ y/A-Z/a-z/;
1126 0           my $retval = $colortable{$color};
1127            
1128 0 0         if ($retval) {
1129 0           return $retval;
1130             } else {
1131 0           return 0;
1132             }
1133             }
1134              
1135             #
1136             # Subroutine: _get_symbol()
1137             #
1138             # Description: simply looks up a hash table and returns
1139             # a number that xmgrace understands that
1140             # corresponds to the symbol
1141             #
1142              
1143             sub _get_symbol ($ ) {
1144 0     0     my $symbol = shift;
1145 0           my %symtab = (
1146             "none" => "0",
1147             "circle" => "1",
1148             "square" => "2",
1149             "diamond" => "3",
1150             "triangle up" => "4",
1151             "triangle left" => "5",
1152             "triangle down" => "6",
1153             "triangle right" => "7",
1154             "plus" => "8",
1155             "x" => "9",
1156             "star" => "10",
1157             "char" => "11",
1158             );
1159            
1160 0           $symbol = lc($symbol);
1161 0           my $retval = $symtab{$symbol};
1162            
1163 0 0         if ($retval) {
1164 0           return $retval;
1165             } else {
1166 0           return 0;
1167             }
1168             }
1169              
1170              
1171             #
1172             # Subroutine: _get_linetype()
1173             #
1174             # Description: simply looks up a hash table and returns
1175             # a number that xmgrace understands that
1176             # corresponds to the linetype
1177             #
1178              
1179             sub _get_linetype ($ ) {
1180 0     0     my $linetype = shift;
1181 0           my %linetypetab = (
1182             "none" => "0",
1183             "straight" => "1",
1184             "left stairs" => "2",
1185             "right stairs" => "3",
1186             "segments" => "4",
1187             "3-segments" => "5",
1188             );
1189            
1190 0           $linetype = lc($linetype);
1191 0           my $retval = $linetypetab{$linetype};
1192            
1193 0 0         if ($retval) {
1194 0           return $retval;
1195             } else {
1196 0           return 0;
1197             }
1198             }
1199              
1200             #
1201             # Subroutine: _get_linestyle()
1202             #
1203             # Description: simply looks up a hash table and returns
1204             # a number that xmgrace understands that
1205             # corresponds to the linestyle
1206             #
1207              
1208             sub _get_linestyle ($ ) {
1209 0     0     my $linestyle = shift;
1210 0           my %linestyletab = (
1211             "none" => "0",
1212             "solid" => "1",
1213             "dotted" => "2",
1214             "en dash" => "3",
1215             "em dash" => "4",
1216             "dot-en dash" => "5",
1217             "dot-em dash" => "6",
1218             "dot-en-dot dash" => "7",
1219             "en-dot-en dash" => "8",
1220             );
1221            
1222 0           $linestyle = lc($linestyle);
1223 0           my $retval = $linestyletab{$linestyle};
1224            
1225 0 0         if ($retval) {
1226 0           return $retval;
1227             } else {
1228 0           return 0;
1229             }
1230             }
1231              
1232             #
1233             # Subroutine: _get_baselinetype()
1234             #
1235             # Description: simply looks up a hash table and returns
1236             # a number that xmgrace understands that
1237             # corresponds to the baselinetype
1238             #
1239              
1240             sub _get_baselinetype ($ ) {
1241 0     0     my $baselinetype = shift;
1242 0           my %baselinetypetab = (
1243             "zero" => "0",
1244             "set min" => "1",
1245             "set max" => "2",
1246             "graph min" => "3",
1247             "graph max" => "4",
1248             );
1249            
1250 0           $baselinetype = lc($baselinetype);
1251 0           my $retval = $baselinetypetab{$baselinetype};
1252            
1253 0 0         if ($retval) {
1254 0           return $retval;
1255             } else {
1256 0           return 0;
1257             }
1258             }
1259              
1260              
1261             #
1262             # Subroutine: _get_filltype()
1263             #
1264             # Description: simply looks up a hash table and returns
1265             # a number that xmgrace understands that
1266             # corresponds to the filltype
1267             #
1268              
1269             sub _get_filltype ($ ) {
1270 0     0     my $filltype = shift;
1271 0           my %filltypetab = (
1272             "none" => "0",
1273             "as polygon" => "1",
1274             "to baseline" => "2",
1275             );
1276            
1277 0           $filltype = lc($filltype);
1278 0           my $retval = $filltypetab{$filltype};
1279            
1280 0 0         if ($retval) {
1281 0           return $retval;
1282             } else {
1283 0           return 0;
1284             }
1285             }
1286              
1287             #
1288             # Subroutine: _get_avaluetype()
1289             #
1290             # Description: simply looks up a hash table and returns
1291             # a number that xmgrace understands that
1292             # corresponds to the avaluetype
1293             #
1294              
1295             sub _get_avaluetype ($ ) {
1296 0     0     my $avaluetype = shift;
1297 0           my %avaluetypetab = (
1298             "none" => "0",
1299             "x" => "1",
1300             "y" => "2",
1301             "xy" => "3",
1302             "string" => "4",
1303             "z" => "5",
1304             );
1305            
1306 0           $avaluetype = lc($avaluetype);
1307 0           my $retval = $avaluetypetab{$avaluetype};
1308            
1309 0 0         if ($retval) {
1310 0           return $retval;
1311             } else {
1312 0           return 0;
1313             }
1314             }
1315              
1316              
1317             1;
1318              
1319             __END__