File Coverage

blib/lib/PDL/Graphics/Simple/PLplot.pm
Criterion Covered Total %
statement 27 158 17.0
branch 3 68 4.4
condition 1 20 5.0
subroutine 7 10 70.0
pod 0 3 0.0
total 38 259 14.6


line stmt bran cond sub pod time code
1              
2             ######################################################################
3             ######################################################################
4             ######################################################################
5             ###
6             ###
7             ### PLplot interface to PDL::Graphics::Simple
8             ###
9             ### See the PDL::Graphics::Simple docs for details
10             ###
11             ##
12             #
13              
14             package PDL::Graphics::Simple::PLplot;
15              
16 1     1   8 use strict;
  1         2  
  1         50  
17 1     1   7 use warnings;
  1         2  
  1         85  
18 1     1   7 use File::Temp qw/tempfile/;
  1         2  
  1         86  
19 1     1   777 use Time::HiRes qw/usleep/;
  1         1862  
  1         6  
20 1     1   203 use PDL::Options q/iparse/;
  1         2  
  1         62  
21 1     1   6 use PDL;
  1         2  
  1         7  
22              
23             our $mod = {
24             shortname => 'plplot',
25             module=>'PDL::Graphics::Simple::PLplot',
26             engine => 'PDL::Graphics::PLplot',
27             synopsis=> 'PLplot (nice plotting, sloooow images)',
28             pgs_api_version=> '1.012',
29             };
30             PDL::Graphics::Simple::register( $mod );
31              
32             my @DEVICES = qw(
33             qtwidget wxwidgets xcairo xwin wingcc
34             );
35             our $guess_filetypes = {
36             ps => ['pscairo','psc', 'psttfc', 'ps'],
37             svg => ['svgcairo','svg','svgqt'],
38             pdf => ['pdfcairo','pdfqt'],
39             png => ['pngcairo','pngqt']
40             };
41             our $filetypes;
42              
43             ##########
44             # PDL::Graphics::Simple::PLplot::check
45             # Checker
46              
47             sub check {
48 1     1 0 3 my $force = shift;
49 1 50       6 $force = 0 unless(defined($force));
50              
51 1 50 33     9 return $mod->{ok} unless( $force or !defined($mod->{ok}) );
52              
53 1         3 eval { require PDL::Graphics::PLplot; PDL::Graphics::PLplot->import };
  1         229  
  0         0  
54 1 50       6 if ($@) {
55 1         5 $mod->{ok} = 0;
56 1         4 $mod->{msg} = $@;
57 1         5 return 0;
58             }
59              
60             # Module loaded OK, now try to extract valid devices from it.
61 0           my $plgDevs = plgDevs();
62 0           $mod->{devices} = {map +($_=>1), keys %$plgDevs};
63              
64 0 0 0       if ( my ($good_dev) = $ENV{PDL_SIMPLE_DEVICE} || grep $mod->{devices}{$_}, @DEVICES ) {
65 0           $mod->{disp_dev} = $good_dev;
66             } else {
67 0           $mod->{ok} = 0;
68             $mod->{msg} = join("\n\t", "No suitable display device found among:",
69 0           sort keys %{ $mod->{devices} }) . "\n";
  0            
70 0           return 0;
71             }
72              
73 0           $filetypes = {};
74 0           for my $k (keys %{$guess_filetypes}) {
  0            
75 0           VAL:for my $v ( @{$guess_filetypes->{$k}} ) {
  0            
76 0 0         if ($mod->{devices}->{$v}) {
77 0           $filetypes->{$k} = $v;
78 0           last VAL;
79             }
80             }
81             }
82              
83 0 0         unless ($filetypes->{ps}) {
84 0           $mod->{ok} = 0;
85 0           $mod->{msg} = "No PostScript found";
86 0           return 0;
87             }
88 0           $mod->{plplot_version} = PDL::Graphics::PLplot::plgver();
89 0           $mod->{ok} = 1;
90 0           return 1;
91             }
92              
93              
94             ##########
95             # PDL::Graphics::Simple::PLplot::new
96             our $new_defaults ={
97             size => [8,6,'in'],
98             type => '',
99             output=>'',
100             multi=>undef
101             };
102              
103             sub new {
104 0     0 0   my $pkg = shift;
105 0           my $opt_in = shift;
106 0           my $opt = { iparse( $new_defaults, $opt_in ) };
107              
108             # Force a recheck on failure, in case the user fixed PLplot.
109 0 0         unless(check()) {
110 0 0         die "$mod->{shortname} appears nonfunctional: $mod->{msg}\n" unless(check(1));
111             }
112              
113             # Figure the device name and size to feed to PLplot.
114 0           my $conv_tempfile;
115             my $dev;
116 0           my @params;
117 0 0         if ( $opt->{type} =~ m/^i/i) {
118             ## Interactive devices
119 0           $dev = $mod->{disp_dev};
120 0 0         if ($opt->{output}) {
121 0           push(@params, FILE=>$opt->{output});
122             }
123             } else {
124 0           my $ext;
125             ## File devices
126 0 0         if ( $opt->{output} =~ m/\.(\w{2,4})$/ ) {
127 0           $ext = $1;
128             } else {
129 0           $ext = 'png';
130 0           $opt->{output} .= ".png";
131             }
132 0 0 0       unless( $filetypes->{$ext} and $mod->{devices}->{$filetypes->{$ext}} ) {
133             ## Have to set up file conversion
134 0           my($fh);
135 0           ($fh, $conv_tempfile) = tempfile('pgs_plplot_XXXX');
136 0           close $fh;
137 0           unlink $conv_tempfile; # just to be sure...
138 0           $conv_tempfile .= ".ps";
139 0           $dev = $filetypes->{ps};
140 0           push(@params, FILE=>$conv_tempfile);
141             } else {
142 0           $dev = "$filetypes->{$ext}";
143 0           push(@params, FILE=>$opt->{output});
144             }
145             }
146 0           push @params, DEV=>$dev;
147              
148 0           my $size = PDL::Graphics::Simple::_regularize_size($opt->{size},'px');
149 0           push(@params, PAGESIZE => [ @$size[0,1] ]);
150              
151 0           my $me = { opt=>$opt, conv_fn=>$conv_tempfile };
152              
153 0 0         if ( defined($opt->{multi}) ) {
154 0           push @params, SUBPAGES => [@{$opt->{multi}}[0,1]];
  0            
155 0           $me->{multi_cur} = 0;
156 0           $me->{multi_n} = $opt->{multi}[0] * $opt->{multi}[1];
157             }
158              
159 0           $me->{obj} = my $w = PDL::Graphics::PLplot->new( @params );
160 0           plsstrm($w->{STREAMNUMBER});
161 0           plspause(0);
162 0           return bless $me;
163             }
164              
165             sub DESTROY {
166             # Make sure X11 windows disappear when destroyed...
167 0     0     my $me = shift;
168 0 0 0       if( $me->{opt}->{type} =~ m/^i/i and defined($me->{obj}) ) {
169 0           $me->{obj}->close;
170 0           delete $me->{obj};
171             }
172             }
173              
174             # if the value is a string, it's a PLOTTYPE parameter sent to xplot. Otherwise
175             # it's a plotting sub...
176             our $plplot_methods = {
177             lines => 'LINE',
178             bins => sub {
179             my ($me, $ipo, $data, $ppo) = @_;
180             my $x = $data->[0];
181             my $x1 = $x->range( [[0],[-1]], [$x->dim(0)], 'e' )->average;
182             my $x2 = $x->range( [[1],[0]], [$x->dim(0)], 'e' )->average;
183             my $newx = pdl($x1,$x2)->mv(-1,0)->clump(2)->sever;
184             my $y = $data->[1];
185             my $newy = $y->dummy(0,2)->clump(2)->sever;
186             $me->{obj}->xyplot($newx, $newy, PLOTTYPE=>'LINE', %{$ppo});
187             },
188             points => 'POINTS',
189             errorbars => sub {
190             my ($me, $ipo, $data, $ppo) = @_;
191             $me->{obj}->xyplot(@$data[0,1], %$ppo, YERRORBAR=>$data->[2]*2);
192             },
193             limitbars => sub {
194             my ($me, $ipo, $data, $ppo) = @_;
195             $me->{obj}->xyplot($data->[0], 0.5*($data->[2]+$data->[3]), %$ppo,
196             YERRORBAR=>($data->[3]-$data->[2])->abs,
197             PLOTTYPE=>'POINTS', SYMBOLSIZE=>0.0001, %$ppo);
198             $me->{obj}->xyplot($data->[0], $data->[1], PLOTTYPE=>'LINE', %$ppo);
199             },
200             contours => sub {
201             my ($me,$ipo,$data,$ppo) = @_;
202             my ($vals, $cvals) = @$data;
203             my $obj = $me->{obj};
204             plsstrm($obj->{STREAMNUMBER});
205             $obj->setparm(%$ppo);
206             pllsty($ppo->{LINESTYLE});
207             plwidth($ppo->{LINEWIDTH}) if $ppo->{LINEWIDTH};
208             my ($nx,$ny) = $vals->dims;
209             $obj->_setwindow;
210             $obj->_drawlabels;
211             my $grid = plAlloc2dGrid($vals->xvals, $vals->yvals);
212             plcont($vals, 1, $nx, 1, $ny, $cvals, \&pltr2, $grid);
213             plFree2dGrid($grid);
214             },
215             image => sub {
216             my ($me,$ipo,$data,$ppo) = @_;
217              
218             # Hammer RGB into greyscale
219             if($data->[2]->dims>2) {
220             $data->[2] = $data->[2]->mv(2,0)->average;
221             }
222              
223             my ($immin,$immax) = $data->[2]->minmax;
224             $ppo->{ZRANGE} = [] unless defined($ppo->{ZRANGE});
225             $ppo->{ZRANGE}->[0] = $immin unless defined($ppo->{ZRANGE}->[0]);
226             $ppo->{ZRANGE}->[1] = $immax unless defined($ppo->{ZRANGE}->[1]);
227              
228             my $xmin = $data->[0]->min - 0.5 * ($data->[0]->max - $data->[0]->min) / $data->[0]->dim(0);
229             my $xmax = $data->[0]->max + 0.5 * ($data->[0]->max - $data->[0]->min) / $data->[0]->dim(0);
230             my $ymin = $data->[1]->min - 0.5 * ($data->[1]->max - $data->[1]->min) / $data->[1]->dim(1);
231             my $ymax = $data->[1]->max + 0.5 * ($data->[1]->max - $data->[1]->min) / $data->[1]->dim(1);
232             my $min = ($ipo->{crange} and defined($ipo->{crange}->[0])) ? $ipo->{crange}->[0] : $data->[2]->min;
233             my $max = ($ipo->{crange} and defined($ipo->{crange}->[1])) ? $ipo->{crange}->[1] : $data->[2]->max;
234              
235             my $nsteps = 128;
236              
237             my $obj = $me->{obj};
238              
239             plsstrm($obj->{STREAMNUMBER});
240             $obj->setparm(%$ppo);
241             my($nx,$ny) = $data->[0]->dims;
242              
243             $obj->_setwindow;
244             $obj->_drawlabels;
245              
246             plcol0(1);
247             plbox ($obj->{XTICK}, $obj->{NXSUB}, $obj->{YTICK}, $obj->{NYSUB},
248             $obj->{XBOX}, $obj->{YBOX}); # !!! note out of order call
249              
250             # Set color map
251             my $r = (xvals(128)/127)->sqrt;
252             my $g = (xvals(128)/127);
253             my $b = (xvals(128)/127)**2;
254             plscmap1l( 1, xvals(128)/127, $r, $g, $b, ones(128));
255              
256             my ($fill_width, $cont_color, $cont_width) = (2, 0, 0);
257             my $clevel = ((PDL->sequence($nsteps)*(($max - $min)/($nsteps-1))) + $min);
258             my $grid = plAlloc2dGrid($data->[0], $data->[1]);
259             plshades( $data->[2], $xmin, $xmax, $ymin, $ymax, $clevel, $fill_width, $cont_color, $cont_width, 0, 0, \&pltr2, $grid );
260             plFree2dGrid($grid);
261              
262             if($ipo->{wedge}) {
263             # Work around PLplot justify bug
264             local($obj->{JUST}) = 0;
265             $obj->colorkey($data->[2], 'v', VIEWPORT=>[0.93,0.96,0.15,0.85], TITLE=>"");
266             }
267             },
268             circles => sub {
269             my ($me,$ipo,$data,$ppo) = @_;
270             my $ang = PDL->xvals(362)*3.14159/180;
271             my $c = $ang->cos;
272             my $s = $ang->sin;
273             $s->slice("361") .= $c->slice("361") .= PDL->pdl(1.1)->acos; # NaN
274             my $dr = $data->[2]->flat;
275             my $dx = ($data->[0]->flat->slice("*1") + $dr->slice("*1") * $c)->flat;
276             my $dy = ($data->[1]->flat->slice("*1") + $dr->slice("*1") * $s)->flat;
277             $me->{obj}->xyplot( $dx, $dy, PLOTTYPE=>'LINE',%{$ppo});
278             },
279             polylines => sub {
280             require PDL::ImageND;
281             my ($me,$ipo,$data,$ppo) = @_;
282             my ($xy, $pen) = @$data;
283             my $pi = $pen->eq(0)->which;
284             $me->{obj}->xyplot($_->dog, PLOTTYPE=>'LINE', %$ppo)
285             for PDL::ImageND::path_segs($pi, $xy->mv(0,-1));
286             },
287             labels => sub {
288             my ($me, $ipo, $data, $ppo) = @_;
289              
290             # Call xyplot to make sure the axes get set up.
291             $me->{obj}->xyplot( pdl(1.1)->asin, pdl(1.1)->asin, %{$ppo} );
292              
293             for my $i (0..$data->[0]->dim(0)-1) {
294             my $j = 0;
295             my $s = $data->[2]->[$i];
296             if ($s =~ s/^([\<\|\> ])//) {
297             $j = 1 if($1 eq '>');
298             $j = 0.5 if($1 eq '|');
299             }
300             $me->{obj}->text($s, TEXTPOSITION=>[ $data->[0]->at($i), $data->[1]->at($i),
301             1,0,
302             $j
303             ],
304             );
305             }
306             }
307             };
308              
309             our @colors = qw/BLACK RED GREEN BLUE MAGENTA CYAN YELLOW TURQUOISE PINK AQUAMARINE LIGHTSEAGREEN GOLD2 BROWN/;
310              
311             ##############################
312             # PDL::Graphics::Simple::PLplot::plot
313              
314             sub plot {
315 0     0 0   my $me = shift;
316 0           my $ipo = shift;
317 0           my $ppo = {};
318              
319 0 0         $ppo->{TITLE} = $ipo->{title} if(defined($ipo->{title}));
320 0 0         $ppo->{XLAB} = $ipo->{xlabel} if(defined($ipo->{xlabel}));
321 0 0         $ppo->{YLAB} = $ipo->{ylabel} if(defined($ipo->{ylabel}));
322 0 0         $ppo->{ZRANGE} = $ipo->{crange} if(defined($ipo->{crange}));
323              
324 0 0         unless( $ipo->{oplot} ) {
325 0           $me->{style} = 0;
326 0           $me->{logaxis} = $ipo->{logaxis};
327 0           plsstrm($me->{obj}{STREAMNUMBER});
328             $me->{multi_cur} %= $me->{multi_n}, $me->{multi_cur}++
329 0 0         if $me->{opt}{multi};
330 0   0       pladv($me->{multi_cur} || 1);
331 0 0 0       if (!$me->{multi_n} or $me->{multi_cur}==1) {
332 0 0         if ($me->{opt}->{type}=~ m/^i/) {
333 0           pleop();
334 0           plclear();
335 0           plbop();
336             }
337             }
338 0 0         if($ipo->{logaxis} =~ m/x/i) {
339 0           $me->{obj}{XBOX} = 'bcnstl';
340 0           $ipo->{xrange} = [ map log10($_), @{$ipo->{xrange}}[0,1] ];
  0            
341             }
342 0 0         if($ipo->{logaxis} =~ m/y/i) {
343 0           $me->{obj}{YBOX} = 'bcnstl';
344 0           $ipo->{yrange} = [ map log10($_), @{$ipo->{yrange}}[0,1] ];
  0            
345             }
346 0           $me->{obj}{BOX} = [ @{$ipo->{xrange}}[0,1], @{$ipo->{yrange}}[0,1] ];
  0            
  0            
347 0           $me->{obj}{VIEWPORT} = [0.1,0.87,0.13,0.82]; # copied from defaults in PLplot.pm. Blech.
348 0           $me->{obj}{JUST} = !!$ipo->{justify};
349             }
350              
351 0 0         warn "P::G::S::PLplot: legends not implemented yet for PLplot" if($ipo->{legend});
352              
353 0           while (@_) {
354 0           my ($co, @data) = @{shift()};
  0            
355 0           my @extra_opts = ();
356 0 0         if (defined $co->{style}) {
357 0           $me->{style} = $co->{style};
358             } else {
359 0           $me->{style}++;
360             }
361 0           $ppo->{COLOR} = $colors[$me->{style}%(@colors)];
362 0           $ppo->{LINESTYLE} = (($me->{style}-1) % 8) + 1;
363 0 0         $ppo->{LINEWIDTH} = $co->{width} if $co->{width};
364 0           my $with = $co->{with};
365 0 0         if ($with eq 'fits') {
366 0           ($with, my $new_opts, my $new_img, my @coords) = PDL::Graphics::Simple::_fits_convert($data[0], $ipo);
367 0           $data[-1] = $new_img;
368 0           unshift @data, @coords;
369 0           $ppo->{XLAB} = delete $new_opts->{xlabel};
370 0           $ppo->{YLAB} = delete $new_opts->{ylabel};
371 0           $me->{obj}{BOX} = [ @{$new_opts->{xrange}}[0,1], @{$new_opts->{yrange}}[0,1] ];
  0            
  0            
372             }
373             die "Unknown curve option 'with $with'!"
374 0 0         unless my $plpm = $plplot_methods->{$with};
375 0 0         $data[0] = $data[0]->log10 if $me->{logaxis} =~ m/x/i;
376 0 0         $data[1] = $data[1]->log10 if $me->{logaxis} =~ m/y/i;
377 0 0         if (ref($plpm) eq 'CODE') {
378 0           $plpm->($me, $ipo, \@data, $ppo);
379             } else {
380 0           $me->{obj}->xyplot(@data,PLOTTYPE=>$plpm,%$ppo);
381             }
382 0           plflush();
383             }
384              
385 0 0 0       $me->{obj}->close if $me->{opt}{type} =~ m/^f/i and !defined $me->{opt}{multi};
386              
387 0 0         if ($me->{conv_fn}) {
388 0           my $im = rim($me->{conv_fn});
389 0           wim($im->mv(1,0)->slice(':,-1:0:-1'), $me->{opt}{output});
390 0           unlink($me->{conv_fn});
391             }
392             }
393              
394             1;