PDL::G...:Simple(3pm)
π PDL::Graphics::Simple(3pm) β User Contributed Perl Documentation π
okay bestie, sit down, hydrate, and let me tell you about PDL::Graphics::Simple β the plotting module that finally lets you just throw a plot on the screen without having a whole existential crisis about which backend you installed. π
β¨ NAME β¨
PDL::Graphics::Simple β simple backend-independent plotting for PDL π
π SYNOPSIS π
# Simple interface - throw plots up on-screen, ASAP
use PDL::Graphics::Simple;
imag $a; # Display an image PDL
imag $a, 0, 300; # Display with color range
line $rrr, $fit; # Plot a line
points $rr, $sec; # Plot points
hold; # Hold graphics so subsequent calls overplot
line $rrr, $fit; # Overplot a line in a contrasting color
release; # Release graphics
# Object interface - simple plotting, to file or screen
$w = pgswin( size=>[8,4], multi=>[2,2] ); # 2x2 plot grid on an 8"x4" window
$w = pgswin( size=>[1000,1000,'px'], output=>'plot.png' ); # output to a PNG
$w->plot( with=>'points', $rr, $sec, with=>'line', $rrr, $fit,
{title=>"Points and fit", xlabel=>"Abscissa", ylabel=>"Ordinate"});
likeβ¦ look at that. no if ($have_gnuplot) branches, no tears, no cap. β¨
π¦ DESCRIPTION π¦
So here's the tea, bestie: PDL can plot through a whole crowd of external plotting modules. and the problem is... every single one of them is less widely available than Perl itself, and each one needs like an extra install step or two before it'll even talk to you. π
For simple applications β think "throw up an image on the screen", or "plot a curve" β it's genuinely useful to have a subset of all plotting capability sitting there in a backend-independent layer. and that's literally the whole job of PDL::Graphics::Simple. it provides that capability, periodt. π
PDL::Graphics::Simple implements all the functionality used in the PDL::Book examples, with identical syntax. it also generalizes that syntax β you can use ::Simple graphics, with slight syntactical differences, in exactly the same manner you'd use any of the engine modules. examples are down below, go look π
and here's the iconic part: the plot you get will always be what you asked for, regardless of which plotting engine you have installed on your system. main character energy from a plotting module, honestly. π
but β gentle reality check incoming π β only a small subset of PDL's complete graphics functionality is supported. each individual plotting module has unique advantages and functionality that are beyond what PDL::Graphics::Simple can do. also: only 2-D plotting is supported. for 3-D plotting, go use PDL::Graphics::Gnuplot or PDL::Graphics::TriD directly. iykyk. π
β οΈ also important, bestie: when you're plotting to a file, the file output is not guaranteed to be present until the plot object is destroyed (e.g. by beingundefed or going out of scope). so don't go peeking atplot.pngtwo lines later and then act surprised. π
πΈ STATE OF DEVELOPMENT πΈ
PDL::Graphics::Simple currently supports most of the planned functionality. it's being released as a beta test to determine if it meets users' needs and to gain feedback on the API β so please give feedback! π«Ά they literally asked. nicely.
π₯ SUPPORTED GRAPHICS ENGINES π₯
PDL::Graphics::Simple includes support for these graphics engines (yes, you can load additional driver modules dynamically β see "register", below π). every one of these engines has unique capabilities and flavor that are not captured in PDL::Graphics::Simple β you are encouraged to go look at the individual modules for more capability! π§
- Gnuplot (via
PDL::Graphics::Gnuplot) β Gnuplot is an extremely richly featured plotting package that offers markup, rich text control, RGB color, and 2-D and 3-D plotting. its output is publication quality. it's supported on POSIX systems, MacOS, and Microsoft Windows, and it's available from most package managers. the people's champion fr fr. β - PGPLOT (via
PDL::Graphics::PGPLOT::Window) β PGPLOT is venerable and nearly as fully featured as Gnuplot for 2-D plotting. it lacks RGB color output. it does have rich text control, but uses simple plotter fonts that are generated internally. supported on MacOS and POSIX, but it's not as widely available as Gnuplot. still slaying in its own retro way π - PLplot (via
PDL::Graphics::PLplot) β PLplot is a moderately full featured plotting package that generates publication quality output with a simple high-level interface. supported on MacOS and POSIX. π¦ - Prima (via
PDL::Graphics::Prima) β Prima is based around a widget paradigm that enables complex interaction with data in real-time, and it's highly optimized for that application. it's not as mature as the other platforms, particularly for static plot generation to files. which means PDL::Graphics::Simple does not play to its considerable strengths, although Prima is serviceable and fast in this application. please run the Prima demo in theperldlshell for a better sample of Prima's capabilities. π
π EXAMPLES π
PDL::Graphics::Simple can be called using plot-atomic or curve-atomic plotting styles, using a pidgin form of calls to any of the main modules. the examples are divided into Book-like (very simple), PGPLOT-like (curve-atomic), and Gnuplot-like (plot-atomic) cases.
there are three main styles of interaction with plot objects that PDL::Graphics::Simple supports, reflective of the pre-existing modules' styles of interaction. you can mix-and-match them to match your particular needs and coding style. π here are examples showing convenient ways to call the code.
π First steps (non-object-oriented) π
For the very simplest actions there are non-object-oriented shortcuts. Here are some examples of simple tasks, including axis labels and plot titles. These non-object-oriented shortcuts are useful for display with the default window size. They make use of a package-global plot object.
The non-object interface will keep using the last plot engine you used successfully. On first start, you can specify an engine with the environment variable "PDL_SIMPLE_ENGINE". As of 1.011, only that will be tried, but if you didn't specify one, all known engines are tried in alphabetical order until one works.
The value of "PDL_SIMPLE_ENGINE" should be the "shortname" of the engine, currently:
"gnuplot""plplot""pgplot""prima"
okay so now the actual examples, bestie π
- Load module and create line plots π
use PDL::Graphics::Simple;
$x = xvals(51)/5;
$y = $x**3;
$y->line;
line( $x, $y );
line( $x, $y, {title=>"My plot", ylabel=> "Ordinate", xlabel=>"Abscissa"} );
- Bin plots ποΈ
$y->bins;
bins($y, {title=>"Bin plot", xl=>"Bin number", yl=>"Count"} );
- Point plots β¨
$y->points;
points($y, {title=>"Points plot"});
- Logarithmic scaling π
line( $y, { log=>'y' } ); # semilog
line( $y, { log=>'xy' } ); # log-log
- Image display πΌοΈ
$im = 10 * sin(rvals(101,101)) / (10 + rvals(101,101));
imag $im; # Display image
imag $im, 0, 1; # Set lower/upper color range
- Overlays π«Ά
points($x, $y, {logx=>1});
hold;
line($x, sqrt($y)*10);
release;
- Justify aspect ratio βοΈ
imag $im, {justify=>1}
points($x, $y, {justify=>1});
- Erase/delete the plot window π§Ή
erase();
π¦ Simple object-oriented plotting π¦
More functionality is accessible through direct use of the PDL::Graphics::Simple object. You can set plot size, direct plots to files, and set up multi-panel plots.
The constructor accepts window configuration options that set the plotting environment, including size, driving plot engine, output, and multiple panels in a single window.
For interactive/display plots, the plot is rendered immediately, and lasts until the object is destroyed. For file plots, the file is not guaranteed to exist and be correct until the object is destroyed. (yes, again. it really means it. π)
The basic plotting method is "plot". "plot" accepts a collection of arguments that describe one or more "curves" (or datasets) to plot, followed by an optional plot option hash that affects the entire plot. Overplotting is implemented via plot option, via a held/released state (as in PGPLOT), and via a convenience method "oplot" that causes the current plot to be overplotted on the previous one.
Plot style (line/points/bins/etc.) is selected via the "with" curve option. Several convenience methods exist to create plots in the various styles.
- Load module and create basic objects π
use PDL::Graphics::Simple;
$x = xvals(51)/5;
$y = $x**3;
$win = pgswin(); # plot to a default-shape window
$win = pgswin( size=>[4,3] ); # size is given in inches by default
$win = pgswin( size=>[10,5,'cm'] ); # You can feed in other units too
$win = pgswin( out=>'plot.ps' ); # Plot to a file (type is via suffix)
$win = pgswin( engine=>'gnuplot' ); # Pick a particular plotting engine
$win = pgswin( multi=>[2,2] ); # Set up for a 2x2 4-panel plot
- Simple plots with
"plot"π₯
$win->plot( with=>'line', $x, $y, {title=>"Simple line plot"} );
$win->plot( with=>'errorbars', $x, $y, sqrt($y), {title=>"Error bars"} );
$win->plot( with=>'circles', $x, $y, sin($x)**2 );
- Plot overlays π
# All at once
$win->plot( with=>'line', $x, $y, with=>'circles', $x, $y/2, sqrt($y) );
# Using oplot (IDL-style; PLplot-style)
$win->plot( with=>'line', $x, $y );
$win->oplot( with=>'circles', $x, $y/2, sqrt($y) );
# Using object state (PGPLOT-style)
$win->line( $x, $y );
$win->hold;
$win->circles( $x, $y/2, sqrt($y) );
$win->release;
π FUNCTIONS π
π show π
PDL::Graphics::Simple::show
"show" lists the supported engines and a one-line synopsis of each. that's it, that's the function. a little menu of your options, no drama. β¨
π pgswin - exported constructor π
$w = pgswin( %opts );
"pgswin" is a constructor that is exported by default into the using package. Calling pgswin(%opts) is exactly the same as calling "PDL::Graphics::Simple->new(%opts)". same plot, shorter name, no cap. π
π new π
$w = PDL::Graphics::Simple->new( %opts );
"new" is the main constructor for PDL::Graphics::Simple. it accepts a list of options about the type of window you want:
engineβ If specified, this must be one of the supported plotting engines. You can use a module name or the shortened name. If you don't give one, the constructor will try the last one you used, or else scan through existing modules and pick one that seems to work. It will first check the environment variable"PDL_SIMPLE_ENGINE", and as of 1.011, only that will be tried, but if you didn't specify one, all known engines are tried in alphabetical order until one works. π§sizeβ This is a window size as an ARRAY ref containing[width, height, units]. If no units are specified, the default is"inches". Accepted units are"in","pt","px","mm", and"cm". The conversion used for pixels is 100 px/inch. πtypeβ This describes the kind of plot to create, and should be either"file"or"interactive"β though only the leading character is checked. If you don't specify either"type"or"output"(below), the default is"interactive". If you specify only"output", the default is"file". ποΈoutputβ πΎ where your plot actually goes. This should be a window number or name for interactive plots, or a file name for file plots. The default file name isplot.pngin the current working directory. Individual plotting modules all support at least.png,.pdf, and.psβ via format conversion if necessary. Most other standard file types are supported, but bestie, they're not guaranteed to work. πmultiβ πΌοΈ this is the plot-multiple-things-on-one-screen switch. You feed in a single array ref containing(nx, ny). Subsequent calls toplotsend graphics to subsequent locations on the window. The ordering is always horizontal first, and left-to-right, top-to-bottom. periodt.
NOTE for multiplotting: oplot does not work and will cause an exception. This is a limitation imposed by Gnuplot. π
β¨ plot β¨
$w = PDL::Graphics::Simple->new( %opts );
$w->plot($data);
plot plots zero or more traces of data on a graph. It accepts two kinds of options: plot options that affect the whole plot, and curve options that affect each curve. The arguments get divided into "curve blocks", and each block is a curve options hash followed by data. π
So here's the hash-reading tea: if the last argument is a hash ref, it is always treated as plot options. And if the first and second arguments are both hash refs, then the first argument is treated as plot options and the second as curve options for the first curve block. π
Plot options:
oplotβ if this is set, then the plot overplots a previous plot.titleβ if this is set, it's a title for the plot as a whole.xlabelβ if this is set, it's a title for the X axis.ylabelβ if this is set, it's a title for the Y axis.xrangeβ if this is set, it's a two-element ARRAY ref containing a range for the X axis. If it's clear, the axis is autoscaled. β¨yrangeβ if this is set, it's a two-element ARRAY ref containing a range for the Y axis. If it's clear, the axis is autoscaled.logaxisβ this should be empty,"x","y", or"xy"(case and order insensitive). Named axes are scaled logarithmically.crangeβ if this is set, it's a two-element ARRAY ref containing a range for color values, full black to full white. If it's clear, the engine or plot module is responsible for setting the range.wedgeβ if this is set, then image plots get a scientific colorbar on the right side of the plot. (You can also say"colorbar","colorbox", or"cb"if you're more familiar with Gnuplot. no judgement π)justifyβ if this is set to a true value, then the screen aspect ratio is adjusted to keep the Y axis and X axis scales equal β so circles appear circular, and squares appear square. it's giving geometry slay π₯legend(EXPERIMENTAL) β π¨ thelegendplot option is intended for full support but it is currently experimental: it is not fully implemented in all the engines, and implementation is more variable than one would like in the engines that do support it.
This controls whether and where a plot legend should be placed. If you set it, you supply a combination of 't', 'b', 'c', 'l', and 'r': indicating top, bottom, center, left, right position for the plot legend. For example, 'tl' for top left, 'tc' for center top, 'c' or 'cc' for dead center. If left unset, no legend will be plotted. If you set it but don't specify a position (or part of one), it defaults to top and left.
If you supply even one 'key' curve option in the curves, legend defaults to the value 'tl' if it isn't specified.
Curve options:
withβ this names the type of curve to be plotted. See below for supported curve types. πkeyβ this gives a name for the following curve, to be placed in a master plot legend. If you don't specify a name but do call for a legend, the curve will be named with the plot type and number (e.g."line 3"or"points 4").widthβ this lets you specify the width of the line, as a multiplier on the standard width the engine uses. That lets you pick normal-width or extra-bold lines for any given curve. The option takes a single positive natural number.styleβ you can specify the line style in a very limited way β as a style number supported by the backend. The styles are generally defined by a mix of color and dash pattern, but the particular color and dash pattern depend on the engine in use. The first 30 styles are guaranteed to be distinguishable. This is useful to produce, e.g., multiple traces with the same style.0is a valid value.
π Curve types supported π
pointsβ this is a simple point plot. It takes 1 or 2 columns of data.linesβ this is a simple line plot. It takes 1 or 2 columns of data.binsβ stepwise line plot, with the steps centered on each X value. 1 or 2 columns.errorbarsβ simple points-with-errorbar plot, with centered errorbars. It takes 2 or 3 columns, and the last column is the absolute size of the errorbar (which is centered on the data point).limitbarsβ simple points-with-errorbar plot, with asymmetric errorbars. It takes 3 or 4 columns, and the last two columns are the absolute low and high values of the errorbar around each point (specified relative to the origin, not relative to the data point value).circlesβ plot unfilled circles. Requires 2 or 3 columns of data; the last column is the radius of each circle. The circles are circular in scientific coordinates, not necessarily in screen coordinates (unless you specify thejustifyplot option). π«imageβ this is a monochrome or RGB image. It takes a 2-D or 3-D array of values, as (width x height x color-index). Images are displayed in a sepiatone color scale that enhances contrast and preserves intensity when converted to grayscale. If you use the convenience routines (imageorimag), thejustifyplot option defaults to 1 β so the image will be displayed with square pixel aspect. If you useplot(with=>'image' ...),justifydefaults to 0 and you will have to set it if you want square pixels.
For RGB images, the numerical values need to be in the range 0-255, as they are interpreted as 8 bits per plane colour values. E.g.:
$w = pgswin(); # plot to a default-shape window
$w->image( pdl(xvals(9,9),yvals(9,9),rvals(9,9))*20 );
# or, from an image on disk:
$image_data = rpic( 'my-image.png' )->mv(0,-1); # need RGB 3-dim last
$w->image( $image_data );
If you have a 2-D field of values that you would like to see with a heatmap:
use PDL::Graphics::ColorSpace;
sub as_heatmap {
my ($d) = @_;
my $max = $d->max;
die "as_heatmap: can't work if max == 0" if $max == 0;
$d /= $max; # negative OK
my $hue = (1 - $d)*240;
$d = cat($hue, pdl(1), pdl(1));
(hsv_to_rgb($d->mv(-1,0)) * 255)->byte->mv(0,-1);
}
$w->image( as_heatmap(rvals 300,300) );
contoursβ As of 1.012. Draws contours. Takes a 2-D array of values, as (width x height), and optionally a 1-D vector of contour values.fitsβ As of 1.012. Displays an image from an ndarray with a FITS header. UsesCUNIT[12]etc to make X & Y axes including labels.polylinesβ As of 1.012. Draws polylines, with 2 arguments ($xy,$pen). The"pen"has value 0 for the last point in that polyline.
use PDL::Transform::Cartography;
use PDL::Graphics::Simple qw(pgswin);
$coast = earth_coast()->glue( 1, scalar graticule(15,1) );
$w = pgswin();
$w->plot(with => 'polylines', $coast->clean_lines);
labelsβ this places text annotations on the plot. It requires three input arguments: the X and Y location(s) as PDLs, and the label(s) as a list ref. The labels are normally left-justified, but you can explicitly set the alignment for each one by beginning the label with"<"for left,"|"for center, and">"for right justification, or a single" "to denote default justification (left). π·οΈ
π oplot π
$w = PDL::Graphics::Simple->new( %opts );
$w->plot($data);
$w->oplot($more_data);
oplot is a convenience interface. It is exactly equivalent to plot except it sets the plot option oplot, so that the plot will be overlain on the previous one. stacking era fr fr π«Ά
π line, points, bins, image, imag, cont π
# Object-oriented convenience
$w = PDL::Graphics::Simple->new( % opts );
$w->line($data);
# Very Lazy Convenience
$a = xvals(50);
lines $a;
$im = sin(rvals(100,100)/3);
imag $im;
imag $im, 0, 1, {title=>"Bullseye?", j=>1};
line, points, and image are convenience interfaces. They are exactly equivalent to plot except that they set the default with curve option to the appropriate plot type. π
imag is even more DWIMMy for PGPLOT users or PDL Book readers: it accepts up to three non-hash arguments at the start of the argument list. The second and third are taken to be values for the crange plot option.
cont resembles the PGPLOT function.
π§Ή erase π§Ή
use PDL::Graphics::Simple qw/erase hold release/;
line xvals(10), xvals(10)**2 ;
sleep 5;
erase;
erase removes a global plot window. It should not be called as a method. To remove a plot window contained in a variable, undefine it. bye bestie π
β hold β
use PDL::Graphics::Simple;
line xvals(10);
hold;
line xvals(10)**0.5;
Causes subsequent plots to be overplotted on any existing one. Called as a function with no arguments, hold applies to the global object. Called as an object method, it applies to the object.
ποΈ release ποΈ
use PDL::Graphics::Simple;
line xvals(10);
hold;
line xvals(10)**0.5;
release;
line xvals(10)**0.5;
Releases a hold placed by hold.
π register π
PDL::Graphics::Simple::register( \%description );
This is the registration mechanism for new driver methods for PDL::Graphics::Simple. Compliant drivers should announce themselves at compile time by calling register, passing a hash ref containing the following keys: π
shortnameβ this is the short name of the engine, by which users refer to it colloquially.moduleβ this is the fully qualified package name of the module itself.engineβ this is the fully qualified package name of the Perl API for the graphics engine.synopsisβ this is a brief string describing the backend.pgs_api_versionβ this is a one-period version number of PDL::Graphics::Simple against which the module has been tested. A warning will be thrown if the version isn't the same as$PDL::Graphics::Simple::API_VERSION.
That value only shifts when the API itself shifts, which means the modules get to be released on their own cute little schedule instead of being chained to every single version of PDL::Graphics::Simple like they were up to 1.010. Growth! Independence! We love to see it π¦
π IMPLEMENTATION π
So here's the whole deal, bestie. PDL::Graphics::Simple builds an object that is the plotting window / interface. When you make that object, you either name a backend yourself or you let PDL::Graphics::Simple go find one that seems to actually work on your system. After that, every plotting command gets translated and handed off to whichever module is doing the working. One brain, many hands π
PDL::Graphics::Simple dispatches in two steps, and honestly it's giving relay race:
- The main module curries the arguments β parsing them into a regularized form and doing its DWIM optimizations.
- Those regularized arguments get passed to the implementation classes, which translate them into the APIs of their own plot engines.
The classes are very simple and implement only a few methods, which we're about to list. They're only meant to be called by the PDL::Graphics::Simple driver, and that's on purpose β it means they barely have to bother with argument processing, currying, or parsing. So a class's only real job is turning regularized parameters into plot calls in whatever form its plot module expects. No overthinking, just vibes β¨
Also, lowkey important: PDL::Graphics::Simple works through a call-and-dispatch system rather than leaning all the way into inheritance. Why? Two reasons, and it says so right there:
- It makes central control mildly easier going forward, since calls are dispatched through the main module.
- It makes the non-object-oriented interface easier to implement, since the main interface modules are in one place and can access the global object easily.
π Interface class methods π
Every interface module supports the following methods. That's the whole list. Learn it, love it.
check
check tries to load the relevant engine module and tests that it actually works. Besides returning a boolean for success when it's true, it also registers that success or failure in the main $mods hash under the "ok" flag. If it fails in a way that spits out an error message, that error gets logged under the "msg" flag. Receipts kept, always π§Ύ
check takes one parameter, force. If it's missing or false and "ok" is already defined, check just echoes the previous result. If force is true, then and only then does check really go check the status, regardless of what "ok" says. β¨
new
new creates and returns an appropriate plot object, or dies on failure. No gentle let-down, just death. π
Every new method should accept the following options, defined exactly as in the description for PDL::Graphics::Simple::new (above). And no, you don't need to set default values β every argument should already be set to something reasonable by the superclass. She's already handled it, you're welcome π
For file output, the method should autodetect the file type by dot-suffix. At minimum, .png and .ps should be supported. Those are the two. Not negotiable.
Required options: size, type, output, multi.
plot
plot generates a plot. It should accept a standardized collection of options as generated by the PDL::Graphics::Simple plot method: the standard plot options as a hash ref, followed by a list of curve blocks.
It should render either a full-sized plot that fills the plot window, or β if the object's multi option was set at construction β the current subwindow. For interactive plot types it should act as an atomic plot operation, displaying the complete plot. For file plot types the atomicity isn't well defined, because multiplot grids may be problematic, but the plot should be closed as soon as is practical. Best effort, bestie π«Ά
The plot options hash contains the plot options listed under "plot" above, plus one extra flag: oplot, which says the new data is meant to be overplotted on top of whatever's already chilling in the plotting window. All options are present in that hash β all of them, no sneaky gaps.
title,xlabel,ylabel, andlegenddefault to undef, and undef means that plot feature should not be rendered at all. It's not invited.oplot,xrange,yrange,crange,wedge, andjustifyare always both present and defined. Always. β¨
If the oplot plot option is set, the plot should be overlain on a previous plot β not losing any range settings, and not obeying any given ones either.
β οΈ BIG NOTE, PAY ATTENTION: if any data given to the original plot, or any overplots, might get changed before plot updates happen, it is the user's responsibility to pass in copies, because some engines β Prima and Gnuplot β only store data by reference for performance reasons. That's the tradeoff, that's the deal. Otherwise the module should just display a fresh plot.
Now, curve blocks. Each curve block is an ARRAY ref with a hash in element 0 and all the required data in the following elements β one PDL per ordinate/abscissa pair.
- For 1-D plot types (like points and lines) the PDLs must be 1D.
- For image plot types the lone PDL must be 2D (monochrome) or 3D (RGB). π¨
The hash inside a curve block holds the curve options for that particular curve, and they're all set to reasonable default values already. The values actually passed in are with and key. And if the legend option is undefined, then the curve should not be placed into a plot legend (if there is one). Stay out of the legend, girl π
πΈ ENVIRONMENT πΈ
Setting some environment variables affects how the module behaves. Here's the trio:
PDL_SIMPLE_ENGINE
See "new". That's the whole note, go read it π
PDL_SIMPLE_DEVICE
If a device is a meaningful thing for whatever engine you've got, this value gets used instead of letting the driver module guess. You decide, not the driver β¨
PDL_SIMPLE_OUTPUT
This one overrides passed-in arguments, creating the given file as output. And if it contains %d, then with Gnuplot that %d gets replaced with an increasing number β which the page itself calls "an amazing PDL::Graphics::Gnuplot feature" and honestly? Correct. Iconic behavior π
π TO-DO π
Deal with legend generation. In particular: adding legends with multi-call protocols is awkward and leads to many edge cases in the internal protocol. This needs more thought. (Respect for admitting it, honestly.)
π REPOSITORY π
<https://github.com/PDLPorters/PDL-Graphics-Simple>
π AUTHOR π
Craig DeForest, "<craig@deforest.org>"
π LICENSE AND COPYRIGHT π
Copyright 2013 Craig DeForest
This program is free software; you can redistribute it and/or modify it under the terms of either: the Gnu General Public License v1 as published by the Free Software Foundation; or the Perl Artistic License included with the Perl language.
see <http://dev.perl.org/licenses/> for more information.
okay and that's the ENTIRE page, bestie, you made it to the end π this part is from perl v5.42.3, dated 2026-08-19, and it's all part of the PDL::G...:Simple(3pm) page in the User Contributed Perl Documentation family. now go make something pretty and plot it ππβ¨
π¦ where this page comes from
- ubuntu stonking: libpdl-graphics-simple-perl 1.017-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms