1## Xrt3d.pm is a sub-module of Graph.pm. It has all the subroutines
2## needed for the Xrt3d part of the package.
3##
4## $Id: Xrt3d.pm,v 1.30 2006/06/07 21:09:33 emile Exp $ $Name:  $
5##
6## This software product is developed by Michael Young 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##
40package Chart::Graph::Xrt3d;
41use Exporter ();
42
43@ISA = qw(Exporter);
44@EXPORT = qw();
45@EXPORT_OK = qw(&xrt3d);
46
47use FileHandle;			# to create generic filehandles
48use Carp;			# for carp() and croak()
49use Chart::Graph::Utils qw(:UTILS);	# get global subs and variables
50use Chart::Graph::XrtUtils qw(:UTILS);
51
52$cvs_Id = '$Id: Xrt3d.pm,v 1.30 2006/06/07 21:09:33 emile Exp $';
53$cvs_Author = '$Author: emile $';
54$cvs_Name = '$Name:  $';
55$cvs_Revision = '$Revision: 1.30 $';
56
57$VERSION = 3.2;
58
59use strict;
60
61#
62# xrt graphing package
63#
64
65
66my %def_xrt_global_opts;		# xrt specific globals
67
68%def_xrt_global_opts = (
69			"output file" =>  "untitled-xrt3d.gif",
70			"output type" => "gif",
71			"x-axis title" => "x-axis",
72			"y-axis title" => "y-axis",
73			"z-axis title" => "z-axis",
74			"x-min" => "0",
75			"y-min" => "0",
76			"x-step" => "1",
77			"y-step" => "1",
78			"x-ticks" => undef,
79			"y-ticks" => undef,
80			"header" => ["header"],
81			"footer" => ["footer"],
82		       );
83#
84#
85# Subroutine: xrt()
86#
87# Description: this is the main function you will be calling from
88#              our scripts. please see
89#              www.caida.org/Tools/Graph/ for a full description
90#              and how-to of this subroutine
91#
92
93sub xrt3d {
94    my $user_global_opts_ref = shift;
95    my $data_set_ref = shift;
96	my $matrix_data_ref;
97	my $data_filename;
98    my (%global_opts);
99
100    # variables to be written to the command file
101    my ($plot_file, $x_axis, $y_axis, $z_axis, $x_step, $y_step);
102    my ($x_min, $y_min, $x_ticks, $y_ticks, $header, $footer);
103    my ($x_cnt, $y_cnt, $hdr_cnt, $ftr_cnt);
104    my ($output_file);
105
106    if (@_) {
107	carp 'Too many arguments. Usage: xrt3d(\%options, \@data_set)';
108	return 0;
109    }
110
111    _make_tmpdir("_Xrt3d_");
112
113    # set paths for external programs
114    if (not _set_xrtpaths("xrt3d")) {
115	_cleanup_tmpdir();
116	return 0;
117    }
118
119    # check first arg for hash
120    if (ref($user_global_opts_ref) ne "HASH") {
121	carp "Global options must be a hash.";
122	_cleanup_tmpdir();
123	return 0;
124    }
125
126    # call to combine user options with default options
127    %global_opts = _mesh_opts($user_global_opts_ref, \%def_xrt_global_opts);
128
129    # check for values in command file
130    while (my ($key, $value) = each %global_opts) {
131
132	  if ($key eq "output file") {
133		$output_file = $value;
134		unless (defined $global_opts{"output type"}) {
135		    carp "Must have an output type defined";
136		    _cleanup_tmpdir();
137		    return 0;
138		  }
139	  }
140
141	  # If the file is PostScript ... what XRT makes is PostScript
142	  if ($global_opts{"output type"} eq "ps") {
143		$plot_file = _make_tmpfile("plot", "ps");
144	  }
145	  # For all raster formats XRT starts out with
146	  # X-Windows XWD format.
147	  elsif (($global_opts{"output type"} eq "gif") or
148			 ($global_opts{"output type"} eq "xwd") or
149			 ($global_opts{"output type"} eq "png") or
150			 ($global_opts{"output type"} eq "jpg")
151			) {
152		$plot_file = _make_tmpfile("plot", "xwd");
153	  } else {
154		# Default is XWD
155		carp "Unknown output type, defaulting to xwd";
156		$plot_file = _make_tmpfile("plot", "xwd");
157	  }
158
159
160		if ($key eq "x-axis title") {
161		    if(defined($value)) {
162			$x_axis = $value;
163		    }
164		}
165
166		if ($key eq "y-axis title") {
167		    if(defined($value)) {
168			$y_axis = $value;
169		    }
170		}
171
172		if ($key eq "z-axis title") {
173		    if(defined($value)) {
174			$z_axis = $value;
175		    }
176		}
177
178		if ($key eq "x-min") {
179		    if(defined($value)) {
180			$x_min = $value;
181		    }
182		}
183
184		if ($key eq "y-min") {
185		    if(defined($value)) {
186			$y_min = $value;
187		    }
188		}
189
190		if ($key eq "x-step") {
191		    if(defined($value)) {
192			$x_step = $value;
193		    }
194		}
195
196		if ($key eq "y-step") {
197		    if(defined($value)) {
198			$y_step = $value;
199	    }
200		}
201
202		if ($key eq "x-ticks") {
203		    if(defined($value)) {
204			$x_ticks = $value;
205		    }
206		}
207
208		if ($key eq "y-ticks") {
209		    if(defined($value)) {
210			$y_ticks = $value;
211		    }
212		}
213
214		if ($key eq "header") {
215		    if(defined($value)) {
216			$header = $value;
217		    }
218		}
219
220		if ($key eq "footer") {
221		    if(defined($value)) {
222			$footer = $value;
223		    }
224		}
225	}
226
227
228	# Extract options for data.
229	my $data_opts = shift @{$data_set_ref};
230	while (my ($key, $value) = each %{$data_opts}) {
231	  if ($key eq "type") {
232		if ($value eq "matrix") {
233		  $matrix_data_ref = $data_set_ref;
234		}
235		elsif ($value eq "file") {
236		  $data_filename = pop @{$data_set_ref};
237		} else {
238		  carp "Unsupported or unknown format for data";
239		}
240	  }
241	}
242
243    # because xrt allows multiline headers
244    # get the length of the header array
245    # each line of the header is one index
246    # in the array
247
248    $hdr_cnt = $#{$global_opts{"header"}} + 1;
249    $ftr_cnt = $#{$global_opts{"footer"}} + 1;
250
251
252	if (defined($matrix_data_ref)) {
253	  # get the number of columns and number of rows
254	  # and verify that each row has same number of
255	  # columns
256
257	  $x_cnt = $#{$matrix_data_ref} + 1;
258	  my $tmp = $#{$matrix_data_ref->[0]} + 1;
259
260	  foreach my $i (@{$matrix_data_ref}) {
261		if ($tmp != $#{$i} + 1) {
262		  carp "each row must have the same number of columns";
263		  _cleanup_tmpdir();
264		  return 0;
265		}
266	  }
267
268	  $y_cnt = $tmp;
269
270
271	  # verify that number of tick marks == corresponds
272	  # to that of xy matrix. One tick mark for every x
273	  # y.
274
275	  if (not _verify_ticks($x_cnt, $global_opts{"x-ticks"})) {
276		_cleanup_tmpdir();
277		return 0;
278	  }
279
280	  if (not _verify_ticks($y_cnt, $global_opts{"y-ticks"})) {
281		_cleanup_tmpdir();
282		return 0;
283	  }
284    } else {
285	  # XXX
286	  # Poor man's hack to compute rows and columns in data file.  This will
287	  # make a second pass through file, but is probably faster than doing it
288	  # in Perl.
289	  my ($lead, $words, $bytes);
290	  ($lead, $x_cnt, $words, $bytes)  = split(/\D+/, `wc $data_filename`);
291	  if (($x_cnt > 0) and ($words > 0)) {
292		$x_cnt++;
293		$y_cnt = $words/$x_cnt;
294	  } else {
295		$x_cnt = 0;
296		$y_cnt = 0;
297		carp "Cannot compute number of rows and/or columns in file data";
298	  }
299	}
300
301    ##
302    ## print command file using this format
303    ##
304
305    # output.file
306    # x_min (normally 0)
307    # y_min (normally 0)
308    # x_step (normally 1)
309    # y_step (normally 1)
310    # x_cnt (number of rows of input)
311    # y_cnt (number of columns of input)
312    # data11 data12 data13 data14 .... (x by y matrix of doubles)
313    # data21 data22 data23 ....
314    # .
315    # .
316    # .
317    # datax1 datax2 ... dataxy
318    # Number of header lines (multiple header lines available)
319    # header1
320    # header2
321    # ...
322    # Number of header lines (multiple header lines available)
323    # foot1
324    # foot2
325    # ...
326    # Title of x-axis
327    # Title of y-axis
328    # Title of z-axis
329    # xlabel0 (x_cnt number of labels for ticks along x-axis)
330    # ...
331    # xlabelx
332    # ylabel0 (y_cnt number of labels for ticks along y-axis)
333    # ...
334    # ylabely
335
336    # create command file and open file handle
337    my $command_file = _make_tmpfile("command");
338    my $handle = new FileHandle;
339    if (not $handle->open(">$command_file")) {
340	carp "could not open $command_file";
341	_cleanup_tmpdir();
342	return 0;
343    }
344
345    print $handle "$plot_file\n";
346    print $handle "$x_min\n";
347    print $handle "$y_min\n";
348    print $handle "$x_step\n";
349    print $handle "$y_step\n";
350    print $handle "$x_cnt\n";
351    print $handle "$y_cnt\n";
352	if (defined($matrix_data_ref)) {
353	  _print_matrix($handle, @{$matrix_data_ref});
354	} else {
355	   _transfer_file($handle, $data_filename)
356	}
357    print $handle "$hdr_cnt\n";
358    _print_array($handle, @{$header});
359    print $handle "$ftr_cnt\n";
360    _print_array($handle, @{$footer});
361    print $handle "$x_axis\n";
362    print $handle "$y_axis\n";
363    print $handle "$z_axis\n";
364    _print_array($handle, @{$x_ticks});
365    _print_array($handle, @{$y_ticks});
366    $handle->close();
367
368    # call xrt and convert file to gif
369    if (not _exec_xrt3d($command_file)) {
370	_cleanup_tmpdir();
371	return 0;
372    }
373    my $graph_format = $global_opts{"output type"};
374    if ($graph_format eq "ps") {
375	  if (not _chk_status(system("cp $plot_file $output_file"))) {
376	    _cleanup_tmpdir();
377	    return 0;
378	  }
379    } elsif ($graph_format eq "xwd") {
380	  if (not _chk_status(system("cp $plot_file $output_file"))) {
381	    _cleanup_tmpdir();
382	    return 0;
383	  }
384	} else {
385	  if(not _convert_raster($graph_format, $plot_file, $output_file)) {
386	    _cleanup_tmpdir();
387	    return 0;
388	  }
389    }
390
391    _cleanup_tmpdir();
392    return 1;
393}
394
3951;
396
397
398__END__
399
400=head1 NAME
401
402Chart::Graph::Xrt3d
403
404=head1 SYNOPSIS
405
406 #Include module
407 use Chart::Graph::Xrt3d qw(xrt3d);
408
409 # Function call
410 xrt3d(\%options,
411       \@data_set
412      );
413
414
415=head1 DESCRIPTION
416
417This module is unmaintained, it worked with Sitraka's XRT, and hasn't been
418tested against newer versions.
419
420Sitraka (now Quest) makes a number of graphics packages for UNIX systems.  XRT is
421a Motif-based commercial software product that has been adapted by
422CAIDA using a combination of C drivers and Perl function I<xrt3d()>.
423The Perl function I<xrt3d()> provides access to the three dimensional
424graphing capabilities of XRT from Perl.  To access the two dimensional
425graphing using XRT, use I<xrt2d()> also supplied in the
426I<Chart::Graph> package.
427
428=head1 ARGUMENTS
429
430The options to I<xrt3d()> are listed below.  Additional control over the
431resulting graph is possible by using the XRT application itself once
432the graph has been created.
433
434 +--------------------------------------------------------------------------+
435 |                                OPTIONS                                   |
436 +----------------+--------------------------+------------------------------+
437 | Name           |  Options                 | Default                      |
438 |"output file"   |  (set your own)          | "untitled-xrt3d.gif"         |
439 |"output type"   |  "ps","xwd", "png", "jpg"| "xwd"                        |
440 |"x-axis title"  |  (set your own)          | "x-axis"                     |
441 |"y-axis title"  |  (set your own)          | "y-axis"                     |
442 |"z-axis title"  |  (set your own)          | "z-axis"                     |
443 |"x-min"         |  "0" or "1"(normally 0)  | "0"                          |
444 |"y-min"         |  "0" or "1"(normally 0)  | "0"                          |
445 |"x-step"        |  "0" or "1"(normally 1)  | "1"                          |
446 |"y-step"        |  "0" or "1"(normally 1)  | "1"                          |
447 |"x-ticks"       |  (set your own)          | none                         |
448 |"y-ticks"       |  (set your own)          | none                         |
449 |"header"        |  (set your own)          | Array ref of "header" text   |
450 |"footer"        |  (set your own)          | Array ref of "footer" text   |
451 +----------------+--------------------------+------------------------------+
452
453The I<xrt3d> function only accepts data in one of two forms.  The
454choices are: either C<[\%data1_opts, \@data_matrix]> or
455C<[\%data1_opts, "filename"]> The data options are listed below.
456
457 +--------------------------------------------------------------------------+
458 |                             DATA OPTIONS                                 |
459 +----------------+--------------------------+------------------------------+
460 | Name           |  Options                 | Default                      |
461 +----------------+--------------------------+------------------------------+
462 | "type"         | Data format: "matrix" or | none                         |
463 |                | "file"                   |                              |
464 +----------------+--------------------------+------------------------------+
465
466=head2 DETAILS ON GRAPHICS CONVERTER OPTIONS
467
468The xrt package supports only two graphics formats internally:
469Postscript and the X windows format XWD.  Additional raster graphics
470formats are supported with Chart::Graph by using one of two graphics
471converter packages: I<Imagemagick> and I<Netpbm>.
472
473If you need to install a converter package,I<Imagemagick>
474I<http://www.imagemagick.org/> is probably preferable
475simply for its comparatively simplicity.  It uses one program
476I<convert> for all of it's conversion needs, so it is easy to manage
477and simple for Chart::Graph to use.  Many UNIX systems come with some
478collection of the I<Netpbm> utilities already installed, thus users
479may be able to start using Chart::Graph without adding any additional
480converters.  Alas, it is unlikely any distributions would include all
481the converters for the newest graphics formats used by Chart::Graph.
482In that case it may still preferable to use I<Imagemagick> simply for
483the sake of avoiding installing over 80 utilities that come with
484current distributions of I<Netpbm>.  For more information on the
485current distribution of I<Netpbm> go to the current website at:
486I<http://netpbm.sourceforge.net/>
487
488The xrt package also allows for multiple header and footers with each
489graph.  As a result, instead of just the usual string, an array
490reference containing the multiple strings for the header and footer
491text.
492
493=head1 EXAMPLES
494
495The following four examples show Chart::Graph::Xrt3d in different roles
496and producing different styles of output.
497
498=head2 EXAMPLE: STOCK PRICES FOR JOE'S RESTAURANT
499
500The first example creates a three dimensional bar chart of
501fictitious stock data that is displayed in the graphic file
502F<xrt3d-1.gif>.  Note that I<xrt3d()> uses the older gif file format,
503but can use others as noted above if you have the available converters
504provided.
505
506 #make sure to include Chart::Graph
507 use Chart::Graph::Xrt3d qw(xrt3d);
508
509 #using a 3 by 6 matrix for the data set
510 xrt3d({"output file" => "xrt3d-1.gif",
511			   "output type" => "gif",
512				   "header" =>
513			   ["Stock prices for Joe's restaurant chain",
514				"Compiled from local records"
515				],
516			   "footer" =>
517			   ["Joe's Restaurant"],
518			   "y-ticks"=>["Jan/Feb", "Mar/Apr", "May/Jun", "Jul/Aug",
519						   "Sep/Oct", "Nov/Dec"],
520			   "x-axis title" => "Years monitored",
521			   "y-axis title" => "Month's tracked",
522			   "z-axis title" => "Stock prices",
523			  },
524			  [{"type" => "matrix"},
525               ["4", "5", "3", "6", "6", "5"],
526			   ["8", "13", "20", "45", "100", "110" ],
527			   ["70", "45", "10", "5", "4", "3"]])
528
529=for html
530<p><center><img src="http://www.caida.org/tools/utilities/graphing/xrt3d-1.jpg"></center></p>
531<p><center><em>xrt3d-1.jpg</em></center></p>
532
533=head2 EXAMPLE: EARLY GROWTH OF THE INTERNET
534
535The following example creates a three dimensional bar chart of data
536collected on the early growth of the Internet (URL and corporate
537source included on graph.)  The result in this case is display in one
538of the newest graphics formats the PNG format: F<xrt3d-2.png>.
539
540 #make sure to include Chart::Graph
541 use Chart::Graph::Xrt3d qw(xrt3d);
542
543  xrt3d({"output file" => "xrt3d-2.png",
544         "output type" => "png",
545	 "header" =>
546	 ["Growth of Early Internet",
547	 "(according to Internet Wizards - http://www.nw.com/)",
548	 ],
549	 "footer" =>
550	 ["http://www.mit.edu/people/mkgray/net/internet-growth-raw-data.html"],
551	 "y-ticks"=>["Jan 93", "Apr 93", "Jul 93",
552		     "Oct 93", "Jan 94", "Jul 94",
553		     "Oct 94", "Jan 95", "Jul 95",
554		     "Jan 96"
555		    ],
556	 "x-ticks"=>["Hosts", "Domains", "Replied to Ping"],},
557	 [{"type" => "matrix"},
558      ["1.3e6", "1.5e6", "1.8e6", "2.1e6", "2.2e6", "3.2e6",
559	   "3.9e6","4.9e6", "6.6e6", "9.5e6"
560	  ],
561	  ["21000","22000", "26000", "28000", "30000", "46000",
562	   "56000", "71000", "120000", "240000"
563	  ],
564	  ["NA", "0.4e6", "NA", "0.5e6", "0.6e6", "0.7e6",
565	   "1.0e6", "1.0e6", "1.1e6", "1.7e6"
566	  ]
567	 ]
568        );
569
570=for html
571<p><center><img src="http://www.caida.org/tools/utilities/graphing/xrt3d-2.png"></center></p>
572<p><center><em>xrt3d-2.png</em></center></p>
573
574=head2 EXAMPLE: USING A DATA FILE FOR INPUT
575
576The next example uses a file instead of a array for it's data source.
577The file is listed below the Perl code.
578
579 #make sure to include Chart::Graph
580 use Chart::Graph::Xrt3d qw(xrt3d);
581
582    if (xrt3d({"output file" => "xrt3d-3.gif",
583               "output type" => "gif",
584               "x-ticks"=>["a", "b", "c"],
585	       "y-ticks"=>["w", "x", "y", "z"],},
586	    [{"type" => "file"},
587		 "xrt3d_data.txt"])) {
588	print "ok\n";
589    } else {
590	print "not ok\n";
591    }
592
593The data file used in the above example is as follows.
594
595 10 15 23  10
596 4 13 35 45
597 29 15 64 24
598
599=for html
600<p><center><img src="http://www.caida.org/tools/utilities/graphing/xrt3d-3.gif"></center></p>
601<p><center><em>xrt3d-3.gif</em></center></p>
602
603
604=head1 MORE INFO
605
606For more information on XRT:
607
608 http://www.quest.com/xrt_pds/
609
610=head1 CONTACT
611
612Send email to graph-dev@caida.org is you have problems, questions,
613or comments. To subscribe to the mailing list send mail to
614graph-dev-request@caida.org with a body of "subscribe your@email.com"
615
616=head1 AUTHOR
617
618 CAIDA Perl development team (cpan@caida.org)
619
620=cut
621