NAME

PDL::Drawing::Prima - PDL-aware drawing functions for Prima widgets

SYNOPSIS

Each of the methods comes with a small sample snippet. To see how it looks, copy this synopsis and replace the code in the Example code goes here block with the example code.

use strict;
use warnings;
use PDL;
use PDL::Drawing::Prima;
use Prima qw(Application);

my $window = Prima::MainWindow->create(
    text      => 'PDL::Drawing::Prima Test',
    antialias => 1,
    onPaint   => sub {
        my ( $self, $canvas) = @_;

        # wipe the canvas:
        $canvas->clear;

        ### Example code goes here ###

        # Draw a sine curve on the widget:
        my ($width, $height) = $canvas->size;
        my $x = sequence($width);
        my $y = ( sin($x / 20) + 1 ) * $height/2;
        $canvas->pdl_polylines($x, $y, lineWidths => 2);

        ### Example code ends here ###
    },
    backColor => cl::White,
);

run Prima;

DESCRIPTION

This module provides a number of PDL-threaded functions and bindings for use with the Prima toolkit. Many of the functions are PDL bindings for the standard Prima drawing functions. Others are useful functions for color manipulation, or getting data into a form that PDL knows how to handle. I generally divide the subroutines of this module into two categories: methods and functions. The methods are subroutines that operate on a Prima widget; the functions are subroutines that act on or return piddles.

Most of the methods given here are PDLified versions of the Prima drawing API functions, which are documented under Prima::Drawable. In general, where the Prima API uses singular nouns, I here use plural nouns. A few of the methods are only available in this module, mostly added to accomodate the needs of PDL::Graphics::Prima, the plotting library built on these bindings.

This bindings can be applied to any object whose class is derived from Prima::Drawable, including displayed widgets and abstract canvases such as Prima::PS::Printer. If you create your own derived canvas, these methods should Just Work. (I wish I could take credit for this, but it's really due to the fact that Prima's internals are very well engineered.)

COORDINATE ORIGIN

The Prima image coordinate origin is located in lower left corner, which is where you would expect to find it when creating plots. However, it is different from the way that many graphics libraries do their coordinates.

FUNCTIONS

piddle_of_patterns_for

If you want PDL to thread over line patterns, but you want to use the standard Prima line patterns, you'll need to convert them line patterns to a piddle. This works very simply like this:

my $patterns = piddle_of_patterns_for(lp::Solid, lp::Dash);

This creates a piddle with the two patterns so that you could have PDL thread over them.

You can also create your own line pattern piddles by hand. I recommend you use byte array, since otherwise it will be converted to byte arrays for you. The first element of a row in your byte array specifies the number of pixels to be "on", the second specifies the number to be "off", the third specifies the number to be "on" again, the fourth "off", the fifth "on", etc. If that doesn't make sense, hopefully a couple of examples will help clarify.

This example creates the equivalent of lp::Dash:

my $dash_pattern = byte (9, 3);

This example creates a piddle with four line types: lp::Solid, lp::Dash, lp::ShortDash, and lp::DashDot:

my $patterns = byte q[ 1; 9 3; 3 3; 9 3 1 3];

and should be identical to

my $patterns = piddle_of_patterns_for(
    lp::Solid, lp::Dash, lp::ShortDash, lp::DashDot);

When you create a byte piddle, all of the patterns must have the same number of bytes in their specification. Of course, different patterns have different lengths, so in that case simply pad the shorter specifications with zeroes.

METHODS

The methods described below are a bit unusual for PDL functions. First, they are not actually PDL functions at all but are methods for Prima::Drawable objects. Second, their signatures will look a bit funny. Don't worry too much about that, though, because they will resemble normal signatures close enough that you should be able to understand them, I hope.

pdl_arcs

Prima Signature: (widget; x(); y(); x_diameter(); y_diameter();
                   start_angle(); end_angle(); properties)

Draws arcs, i.e. incomplete ellipses.

Applicable properties include colors, backColors, linePatterns, lineWidths, rops, rop2s.

The arcs go from the start_angles to the end_angles along the ellipses centered at the xs and ys, with the specified x- and y-diameters. The angles are measured in degrees, not radians. The difference between this command and "pdl_chords" or "pdl_sectors" is that pdl_arcs does not connect the dangling ends.

Here's a simple example:

# Draw a bunch of random arcs on $canvas:
my $N_arcs = 20;
my ($x_max, $y_max) = $canvas->size;
my $xs = zeroes($N_arcs)->random * $x_max;
my $ys = $xs->random * $y_max;
my $dxs = $xs->random * $x_max / 4;
my $dys = $xs->random * $y_max / 4;
my $th_starts = $xs->random * 360;
my $th_stops = $xs->random * 360;

# Now that we've generated the data, call the command:
$canvas->pdl_arcs($xs, $ys, $dxs
               , $dys, $th_starts, $th_stops);

If you put that snippet of code in the onPaint method, as suggested in the synopsis, a completely new set of arcs will get redrawn whenever you resize your window.

Compare to the Prima method "arc" in Prima::Drawable. Closely related routines include "pdl_chords" and "pdl_sectors". See also "pdl_fill_chords", and "pdl_fill_sectors", "pdl_ellipses", and "pdl_fill_ellipses".

Spline drawing provides a similar functionality, though more complex and more powerful. There are no PDL bindings for the spline functions yet. See "spline" in Prima::Drawable for more information.

pdl_arcs does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_bars

Prima Signature: (widget; x1(); y1(); x2(); y2(); properties)

Draws filled rectangles from corners (x1, y1) to (x2, y2).

Applicable properties include colors, backColors, clipRects, fillPatterns, rops, rop2s.

# Draw 20 random filled rectangles on $canvas:
my $N_bars = 20;
my ($x_max, $y_max) = $canvas->size;
my $x1s = zeroes($N_bars)->random * $x_max;
my $y1s = $x1s->random * $y_max;
my $x2s = $x1s + $x1s->random * ($x_max - $x1s);
my $y2s = $y1s + $x1s->random * ($y_max - $y1s);
my $colors = $x1s->random * 2**24;

# Now that we've generated the data, call the command:
$canvas->pdl_bars($x1s, $y1s, $x2s, $y2s
        , colors => $colors);

If you put that snippet of code in the onPaint method, as suggested in the synopsis, a completely new set of filled rectangles will get redrawn whenever you resize your window.

Compare to the Prima method "bar" in Prima::Drawable. See also "pdl_rectangles", which is the unfilled equivalent, and "pdl_clears", which is sorta the opposite of this.

pdl_bars does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_chords

Prima Signature: (widget; x(); y(); x_diameter(); y_diameter();
                         start_angle(); end_angle(); properties)

Draws arcs (i.e. incomplete ellipses) whose ends are connected by a line.

The chord starts at start_angle and runs to end_angle along the ellipse centered at x, y, with their specified diameters x_diameter, y_diameter. Unlike "pdl_arcs" or "pdl_sectors", it connects the ends of the arc with a straight line. The angles are measured in degrees, not radians.

Applicable properties include colors, backColors, clipRects, linePatterns, lineWidths, rops, rop2s.

# For this example, you will need:
use PDL::Char;

# Draw a bunch of random arcs on $canvas:
my $N_chords = 20;
my ($x_max, $y_max) = $canvas->size;
my $xs = zeroes($N_chords)->random * $x_max;
my $ys = $xs->random * $y_max;
my $dxs = $xs->random * $x_max / 4;
my $dys = $xs->random * $y_max / 4;
my $th_starts = $xs->random * 360;
my $th_stops = $xs->random * 360;

# make a small list of patterns:
my $patterns_list = PDL::Char->new(
         [lp::Solid, lp::Dash, lp::DashDot]);

# Randomly select 20 of those patterns:
my $rand_selections = ($xs->random * 3)->byte;
use PDL::NiceSlice;
my $patterns = $patterns_list($rand_selections)->transpose;

# Now that we've generated the data, call the command:
$canvas->pdl_chords($xs, $ys, $dxs
               , $dys, $th_starts, $th_stops
               , linePatterns => $patterns);

If you put that snippet of code in the onPaint method, as suggested in the synopsis, a completely new set of chords will get redrawn whenever you resize your window.

Compare to the Prima method "chord" in Prima::Drawable. The filled equivalent is "pdl_fill_chords". Closely related routines are "pdl_arcs" and "pdl_sectors". See also "pdl_fill_sectors", "pdl_ellipses", and "pdl_fill_ellipses", as well as "spline" in Prima::Drawable.

pdl_chords does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_clears

Prima Signature: (widget; x1(); y1(); x2(); y2(); properties)

Clears the specified rectangle(s).

Applicable properties include backColors, rop2s.

my ($width, $height) = $canvas->size;
# Begin by drawing a filled rectangle:
$canvas->color(cl::Blue);
$canvas->bar(0, 0, $width, $height);

# Now cut random rectangles out of it:
my $N_chunks = 20;
my $x1 = random($N_chunks) * $width;
my $x2 = random($N_chunks) * $width;
my $y1 = random($N_chunks) * $width;
my $y2 = random($N_chunks) * $width;
$canvas->pdl_clears($x1, $y1, $x2, $y2);

Like the other examples, this will give you something new whenever you resize the window if you put the code in the onPaint method, as the Synopsis suggests.

Compare to the Prima method "clear" in Prima::Drawable. In practice I suppose this might be considered the opposite of "pdl_bars", though technically this is meant for erasing, not drawing.

pdl_clears does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_ellipses

Prima Signature: (widget; x(); y(); x_diameter();
                        y_diameter(); properties)

Draws ellipses centered at x, y with diameters x_diameter and y_diameter.

Applicable properties include colors, backColors, clipRects, linePatterns, lineWidths, rops, rop2s.

To draw circles, just use the same x- and y-diameter.

# Draw increasingly taller ellipses along the center line
my $N_ellipses = 10;
my ($width, $height) = $canvas->size;
# horizontal positions evenly spaced
my $x = (sequence($N_ellipses) + 0.5) * $width / $N_ellipses;
# Vertically, right in the middle of the window
my $y = $height/2;
# Use the same x-diameter
my $x_diameter = 15;
# Increase the y-diameter
my $y_diameter = $x->xlinvals(10, $height/1.3);

# Use the pdl_ellipses method to draw!
$canvas->pdl_ellipses($x, $y, $x_diameter, $y_diameter, lineWidths => 2);

For this example, if you resize the window, the distance between the ellipses and the ellipse heights will adjust automatically.

Compare to the Prima method "ellipse" in Prima::Drawable. The filled equivalent is "pdl_fill_ellipses". See also "pdl_arcs", "pdl_chords", and "pdl_sectors" as well as "pdl_fill_chords" and "pdl_fill_sectors". You may also be interested in "spline" in Prima::Drawable, which does not yet have a PDL interface.

pdl_ellipses does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_fill_chords

Prima Signature: (widget; x(); y(); x_diameter(); y_diameter();
                        start_angle(); end_angle(); properties)

Draws filled chords (see "pdl_chords").

Applicable properties include colors, backColors, clipRects, fillPatterns, rops, rop2s.

Chords are partial elipses that run from start_angle to end_angle along the ellipse centered at x, y, each with their specified diameters. The ends are connected with a line and the interior is filled. Use this to draw the open-mouth part of a smiley face.

# working here:
$canvas->pdl_fill_chords($x, $y, $xd, $yd, $ti, $tf);

Compare to the Prima method "fill_chord" in Prima::Drawable. The unfilled equivalent is "pdl_chords". Closely related to "pdl_fill_ellipses" and "pdl_fill_sectors". See also "pdl_arcs", "pdl_ellipses", and "pdl_sectors".

pdl_fill_chords does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_fill_ellipses

Prima Signature: (widget; x(); y(); x_diameter();
                        y_diameter(); properties)

Draws filled ellipses (see "pdl_ellipses").

Applicable properties include colors, backColors, clipRects, fillPatterns, rops, rop2s.

# Draw increasingly taller ellipses along the center line
my $N_ellipses = 10;
my ($width, $height) = $canvas->size;
# horizontal positions evenly spaced
my $x = (sequence($N_ellipses) + 0.5) * $width / $N_ellipses;
# Vertically, right in the middle of the window
my $y = $height/2;
# Use the same x-diameter
my $x_diameter = 15;
# Increase the y-diameter
my $y_diameter = $x->xlinvals(10, $height/1.3);

# Use the pdl_ellipses method to draw!
$canvas->pdl_fill_ellipses($x, $y, $x_diameter, $y_diameter);

If you resize the window the distance between the ellipses and the ellipse heights will adjust automatically.

Compare to the Prima method "fill_ellipse" in Prima::Drawable. The unfilled equivalent is "pdl_ellipses". Closely related to "pdl_fill_chords" and "pdl_fill_ellipses", and "pdl_fill_sectors". See also "pdl_arcs", "pdl_ellipses", and "pdl_sectors". Also, check out "fill_spline" in Prima::Drawable, which does not yet have PDL bindings.

pdl_fill_ellipses does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_fillpolys

Prima Signature: (widget; x(n); y(n); properties)

Draws and fills a polygon with (mostly) arbitrary edge vertices.

Applicable properties include colors, backColors, clipRects, fillPatterns, fillModes, rops, rop2s.

NOTE: there is no underscore between fill and poly, which is different from the other fill methods!

This is useful for drawing arbitrary filled shapes and for visualizing integrals. Splines would be the better choice if you want to draw curves, but a PDL interface to splines is not (yet) implemented.

Unlike most of the other methods, this one actually makes a half-hearted effort to process bad values. In addition to the IEEE bad values of nan and inf, PDL has support for bad values. Unlike in pdl_polys, pdl_fillpolys will simply skip any point that is marked as bad, but drawing the rest of the polygon. In other words, it reduces the degree of your polygon by one. If you sent it four points and one of them was bad, you would get a triangle instead of a quadralaters.

Infinities are also handled, though not perfectly. There are a few situations where pdl_polys will correctly draw what you mean but pdl_fillpolys will not.

Because this skips bad data altogether, if you have too much bad data (i.e. fewer than three good points), the routine will simply not draw anything. I'm debating if this should croak, or at least give a warning. (Of course, a warning to STDOUT is rather silly for a GUI toolkit.)

For example:

# Create a poorly sampled sine-wave
my ($width, $height) = $canvas->size;
my $x = sequence(23)/4;
my $y = $x->sin;

# Draw it in such a way that it fits the canvas nicely
$canvas->pdl_fillpolys($x * $width / $x->max,
    ($y + 1) * $height / 2, fillModes => fm::Winding,
);

Resizing the window will result in a similar rendering that fits the aspect ratio of your (resized) window.

Compare to the Prima method "fillpoly" in Prima::Drawable. See also "pdl_bars" and "pdl_polylines".

pdl_fillpolys processes bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_fill_sectors

Prima Signature: (widget; x(); y(); x_diameter(); y_diameter();
                        start_angle(); end_angle(); properties)

Draws filled sectors, i.e. a pie-slices or Pac-Mans.

Applicable properties include colors, backColors, clipRects, fillPatterns, rops, rop2s.

More specifically, this draws an arc from start_angle to end_angle along the ellipse centered at x, y, with specified x- and y-diameters. Like "pdl_fill_chords", this command connects the end points of the arc, but unlike "pdl_fill_chords", it does so by drawing two lines, both of which also connect to the ellipse's center. This results in shapes that look like pie pieces or pie remnants, depending of whether you're a glass-half-full or glass-half-empty sort of person.

# Draw a bunch of random arcs on $canvas:
my $N_chords = 20;
my ($x_max, $y_max) = $canvas->size;
my $xs = zeroes($N_chords)->random * $x_max;
my $ys = $xs->random * $y_max;
my $dxs = $xs->random * $x_max / 4;
my $dys = $xs->random * $y_max / 4;
my $th_starts = $xs->random * 360;
my $th_stops = $xs->random * 360;

# Now that we've generated the data, call the command:
$canvas->pdl_fill_sectors($xs, $ys, $dxs
               , $dys, $th_starts, $th_stops);

Compare to the Prima method "fill_sector" in Prima::Drawable. The unfilled equivalent is "pdl_sectors". This is closely related to /pdl_fill_chords and /pdl_fill_ellipses. See also "pdl_arcs", "pdl_chords", and "pdl_ellipses".

pdl_fill_sectors does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_flood_fills

Prima Signature: (widget; x(); y(); fill_color();
                 singleborder(); properties)

Fill a contiguous region.

NOTE THIS MAY NOT WORK ON MACS! There seems to be a bug in either Prima or in Mac's X-windows library that prevents this function from doing its job as described. That means that flood filling is not cross-platform, at least not at the moment. This notice will be removed from the latest version of this documentation as soon as the item is addressed, and it may be that your version of Prima has a work-around for this problem. At any rate, it only effects Mac users.

Applicable properties include colors, backColors, clipRects, fillPatterns, rops, rop2s.

Note that fill_color is probably not what you think it is. The color of the final fill is determined by your colors property. What, then, does fill_color specify? It indicates how Prima is supposed to perform the fill. If singleborder is zero, then fill_color is the color of the boundary to which Prima is to fill. In other words, if you had a bunch of intersecting lines that were all red and you wanted the interior of those intersecting lines to be blue, you would say something like

$widget->pdl_flood_fills($x, $y, cl::Red, 0, colors => cl::Blue);

On the other hand, if singleborder is 1, then the value of fill_color tells Prima to replace every contiguous pixel of color fill_color with the new color specified by colors (or the current color, if no colors piddle is given).

# Generate a collection of intersecting
# circles and triangles
my ($width, $height) = $canvas->size;
my $N_items = 20;
my $x = random($N_items) * $width;
my $y = random($N_items) * $width;
$canvas->pdl_ellipses($x, $y, 20, 20, lineWidths => 3);
$canvas->pdl_symbols($x, $y, 3, 0, 0, 10, 1, lineWidths => 3);

# Fill the interior of those circle/triangle intersections
$canvas->pdl_flood_fills($x, $y, cl::Black, 0);

If you put that snippet of code in the example from the synopsis, you should see a number of narrow rectangles intersecting circles, with the interior of both shapes filled. Resizing the window will lead to randomly changed positions for those space-ship looking things.

Compare to the Prima method "flood_fill" in Prima::Drawable. See also "pdl_clears" and the various fill-based drawing methods.

pdl_flood_fills does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_lines

Prima Signature: (widget; x1(); y1(); x2(); y2(); properties)

Draws a line from (x1, y1) to (x2, y2).

Applicable properties include colors, backColors, clipRects, lineJoins, linePatterns, lineWidths, rops, rop2s.

In contrast to polylines, which are supposed to be connected, these lines are meant to be independent. Also note that this method does make an effort to handle bad values, both the IEEE sort (nan, inf) and the official PDL bad values. See "pdl_polylines" for a discussion of what might constitute proper bad value handling.

working here

Compare to the Prima methods "lines" in Prima::Drawable and "lines" in Prima::Drawable. See also "pdl_polylines".

pdl_lines processes bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_polylines

Prima Signature: (widget; x(n); y(n); properties)

Draws a multi-segment line with the given x- and y-coordinates.

Applicable properties include colors, backColors, clipRects, lineJoins, linePatterns, lineWidths, rops, rop2s.

This method goes to great lengths to Do What You Mean, which is actually harder than you might have expected. This is the backbone for the Lines plot type of PDL::Graphics::Prima, so it needs to be able to handle all manner of strange input. Here is what happens when you specify strange values:

IEEE nan or PDL Bad Value

If either of these values are specified in the middle of a line drawing, the polyline will completely skip this point and begin drawing a new polyline at the next point.

both x and y are inf and/or -inf

There is no sensible way of interpreting what it means for both x and y to be infinite, so any such point is skipped, just like nan and Bad.

either x or y is inf or -inf

If an x value is infinite (but the paired y value is not), a horizontal line is drawn from the previous x/y pair out to the edge of a widget; another line is drawn from the edge to the next x/y pair. The behavior for an infinite y value is similar, except that the line is drawn vertically.

For example, the three points (0, 1), (1, 1), (2, inf), (3, 1), (4, 1) would be rendered as a line from (0, 1) to (1, 1), then a vertical line straight up from (1, 1) to the upper edge of the widget or clipping rectangle, then a vertical line straight down to (3, 1) from the upper edge of the widget or clipping rectangle, then a horizontal line from (3, 1) to (4, 1).

x and/or y is a large value

If x or y is a large value (say, both x and y are 5e27 when the rest of your numbers are of the order of 100), it will not be possible to actually show a renderin of a line to that point. However, it is possible to correctly render the slope of that point out to the edge of the clipping rectangle. Thus the slope of the line from within-clip points to large values is faithfully rendered.

Here's an example of how to plot data using pdl_polylines (though you'd do better to use PDL::Graphics::Prima to create plots):

# Draw a sine curve on the widget:
my $x = sequence(200);
my $y = ( sin($x / 20) + 1 ) * 50;
$canvas->pdl_polylines($x, $y);

Compare to the Prima method "polyline" in Prima::Drawable. See also "pdl_lines" and "pdl_fillpolys".

pdl_polylines processes bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_rectangles

Prima Signature: (widget; x1(); y1(); x2(); y2(); properties)

Draws a rectangle from corner (x1, y1) to corner (x2, y2).

Applicable properties include colors, backColors, clipRects, linePatterns, lineWidths, rops, rop2s.

# Draw 20 random rectangles on $canvas:
my $N_bars = 20;
my ($x_max, $y_max) = $canvas->size;
my $x1s = zeroes($N_bars)->random * $x_max;
my $y1s = $x1s->random * $y_max;
my $x2s = $x1s + $x1s->random * ($x_max - $x1s);
my $y2s = $y1s + $x1s->random * ($y_max - $y1s);
my $colors = $x1s->random * 2**24;

# Now that we've generated the data, call the command:
$canvas->pdl_rectangles($x1s, $y1s, $x2s, $y2s
        , colors => $colors);

If you put that snippet of code in the onPaint method, as suggested in the synopsis, a completely new set of rectangles will get redrawn whenever you resize your window.

Compare to the Prima method "rectangle" in Prima::Drawable. See also "pdl_bars", which is the filled equivalent, and "pdl_lines", which draws a line from (x1, y1) to (x2, y2) instead. Also, there is a Prima method that does not (yet) have a pdl-based equivalent known as "rects3d" in Prima::Drawable, which draws beveled edges around a rectangle.

pdl_rectangles does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

pdl_sectors

Prima Signature: (widget; x(); y(); x_diameter(); y_diameter(); start_angle(); end_angle(); properties)

Draws the outlines of sectors, i.e. a pie-slices or Pac-Mans.

Applicable properties include colors, backColors, clipRects, linePatterns, lineWidths, rops, rop2s.

More specifically, this draws an arc from start_angle to end_angle along the ellipse centered at x, y, with specified x- and y-diameters. Like "pdl_fill_chords", this command connects the end points of the arc, but unlike "pdl_fill_chords", it does so by drawing two lines, both of which also connect to the ellipse's center. This results in shapes that look like pie pieces or pie remnants, depending of whether you're a glass-half-full or glass-half-empty sort of person.

# For this example, you will need:
use PDL::Char;

# Draw a bunch of random sectors on $canvas:
my $N_chords = 20;
my ($x_max, $y_max) = $canvas->size;
my $xs = zeroes($N_chords)->random * $x_max;
my $ys = $xs->random * $y_max;
my $dxs = $xs->random * $x_max / 4;
my $dys = $xs->random * $y_max / 4;
my $th_starts = $xs->random * 360;
my $th_stops = $xs->random * 360;

# make a small list of patterns:
my $patterns_list = PDL::Char->new(
         [lp::Solid, lp::Dash, lp::DashDot]);

# Randomly select 20 of those patterns:
my $rand_selections = ($xs->random * 3)->byte;
use PDL::NiceSlice;
my $patterns = $patterns_list($rand_selections)->transpose;

# Now that we've generated the data, call the command:
$canvas->pdl_sectors($xs, $ys, $dxs
               , $dys, $th_starts, $th_stops
               , linePatterns => $patterns);

Compare to the Prima method "sector" in Prima::Drawable. The filled equivalent is "pdl_fill_sectors". There is a whole slew of arc-based drawing methods including "pdl_arcs", "pdl_chords", and "pdl_ellipses" along with their filled equivalents. You may also be interested in "spline" in Prima::Drawable, which does not yet have a PDL interface.

pdl_sectors does not process bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

PDL-ONLY METHODS

These are drawing methods that have no analogous Prima::Drawable function.

pdl_symbols

Signature: (widget; x(); y(); N_points(); orientation(); filled(); size(); skip(); properties)

Draws a wide variety of symbols centered at (x, y).

Applicable properties include colors, backColors, clipRects, fillPatterns, fillModes, linePatterns, lineWidths, rops, rop2s.

Through various combinations of N_points, filled, and skip, you can generate many different regular symbols, including dashes, stars, asterisks, triangles, and diamonds. You can also specify each symbol's size and orientation. The size is the radius of a circle that would circumscribe the shape. The orientation is... well... just keep reading.

The shape drawn depends on N_points. If N_points is:

zero or one

This will draw a circle with a radius of the given size. The circle will be filled or not based on the value passed for filled, but the orientation and skip parameters are ignored. This is almost redundant compared with the ellipse functions, except that this arrangement makes it very easy to thead over filled/not-filled, and you cannot specify an eccentricity for your points using pdl_symbols.

two

This will draw a line centered at (x, y) and with a length of 2*size. The orientation is measured in degrees, starting from horizontal, with increasing angles rotating the line counter-clockwise. The value for skip is ignored.

This is particulary useful for visualizing slope-fields (although calculating the angles for the slope field is surprisingly tricky).

three or more

This will draw a shape related to a regular polygon with the specified number of sides. Precisely what kind of polygon it draws is based on the value of skip. For example, a five-sided polygon with a skip of one would give you a pentagon:

skip = 1

In contrast, a five-sided polygon with a skip of 2 will give you a star:

skip = 2

A skip of three would give visually identical results but the actual order in which the vertices are drawn is different:

skip = 3

A skip of zero is a special case, and means draw lines to each point from the center. In other words, create an asterisk:

skip = 0

In summary, a skip of zero gives you an N-asterisk. A skip of one gives you a regular polygon. A skip of two gives you a star. And so forth. Higher values of skip are allowed; they simply add to the winding behavior.

Specifying the orientation changes the position of the first point and, therefore, all subsequent points. A positive orientation rotates the first point counter-clockwise by the specified number of degrees. Obviously, due to the symmetry of the shapes, rotations of 360 / N_points look identical to not performing any rotation.

For all nonzero values of skip, specifying a fill will end up with a filled shape instead of a line drawing.

By default, filled stars and other symbols with odd numbers of points have a hole in their middle. However, Prima provides a means for indicating that you want such shapes filled; that is the fillMode property. As with almost all graphical properties, you can specify the fillMode property for each symbol by specifying the fillMode piddle to one of fm:: constants.

This example creates a table of shapes. It uses an argument from the command line to determine the line width.

use PDL::NiceSlice;

# Generate a table of shapes:
my @dims = (40, 1, 30);
my $N_points = xvals(@dims)->clump(2) + 1;
my $orientation = 0;
my $filled = yvals(@dims)->clump(2) + 1;
my $size = 10;
my $skip = zvals(@dims)->clump(2);
my $x = $N_points->xvals * 25 + 25;
my $y = $N_points->yvals * 25 + 25;
my $lineWidths = $ARGV[0] || 1;

# Draw them:
$canvas->pdl_symbols($x, $y, $N_points, 0, $filled, 10, $skip

Bad values are handled by pdl_symbols. If any of the values you pass in are bad, the symbol is not drawn at that x/y coordinate.

pdl_symbols processes bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays.

ERROR MESSAGE

These functions may throw the following exception:

Your widget must be derived from Prima::Drawable

This means that you tried to draw on something that is not a Prima::Drawable object, or a class derived from it. I don't know enough about the Prima internals to know if that has any hope of working, but why do it in the first place?

PDL::PP DETAILS

Those well versed in PDL::PP might ask how I manage to produce pdlified methods that take variable numbers of arguments. That is a long story, and it is told in the volumes of comments in pdlprima.pd. Give it a read if you want to know what goes on behind the scenes.

TODO

These are all the things I wish to do:

Full Drawabel API

I would like a PDL function for every drawable function in the API. Prima Drawable functions that currently do not have an equivalent PDL implementation include "draw_text" in Prima::Drawable, "fill_spline" in Prima::Drawable, "put_image" in Prima::Drawable, "put_image_indirect" in Prima::Drawable, "rect3d" in Prima::Drawable, "rect_focus" in Prima::Drawable, "spline" in Prima::Drawable, "stretch_image" in Prima::Drawable, and "text_out" in Prima::Drawable

Bad Value Support

Bad values are handled decently in "pdl_polylines" and "pdl_fillpolys", but not for the other functions. Bad x/y values should be skipped for almost all the drawing primitives, but what about bad colors for valid coordinates? I could not draw the primitive, defer to the widget's default color, or use the value associated with the singular key (i.e. color). But I haven't decided which of these is best.

AUTHOR

David Mertens, <dcmertens.perl@gmail.com>.

SEE ALSO

Some useful PDL/Prima functions are defined in PDL::Drawing::Prima::Utils, especially for converting among simple color formats.

This is built as an extension for the Prima toolkit, http://www.prima.eu.org/, Prima.

This is built using (and targeted at users of) the Perl Data Language, PDL.

This is the bedrock for the plotting package PDL::Graphics::Prima.

Another interface between PDL and Prima is <PDL::PrimaImage>. I am indebted to Dmitry for that module because it gave me a working template for this module, including a working Makefile.PL. Thanks Dmitry!