NAME
PDL::Drawing::Prima - PDL-aware drawing functions for Prima widgets
CPAN
You, gentle reader, are reading this documentation off of CPAN. I know this because this paragraph does not show up in the final .pm files that are installed on a user's computer. The documentation that you see may have a few subtle differences from the documentation that you would view on your computer using podview
or perldoc
. If you have any concerns regarding documentation skew, be sure to check the documentation on your local machine.
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',
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::Drawable. 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, lineEnds, linePatterns, lineWidths, rops, rop2s, and translates.
The arcs go from the start_angle
s to the end_angle
s along the ellipses centered at the x
s and y
s, with the specified x- and y-diameters. The angles are measured in degrees, not radians. The difference between this command and "chords" or "sectors" is that 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_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, and translates.
# 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_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 "arcs" or "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, lineEnds, linePatterns, lineWidths, rops, rop2s, and translates.
# 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_clears
Prima Signature: (widget; x1(); y1(); x2(); y2(); properties)
Clears the specified rectangle(s).
Applicable properties include backColors, rop2s, and translates.
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_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, and translates.
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_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, and translates.
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_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, and translates.
# 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_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, and translates.
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_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, and translates.
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 "fill_chords", this command connects the end points of the arc, but unlike "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_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, and translates.
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_lines
Prima Signature: (widget; x1(); y1(); x2(); y2(); properties)
Draws a line from (x1, y1) to (x2, y2).
Applicable properties include colors, backColors, clipRects, lineEnds, lineJoins, linePatterns, lineWidths, rops, rop2s, and translates.
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_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, lineEnds, lineJoins, linePatterns, lineWidths, rops, rop2s, and translates.
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_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, and translates.
# 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_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, lineEnds, linePatterns, lineWidths, rops, rop2s, and translates.
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 "fill_chords", this command connects the end points of the arc, but unlike "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-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, lineEnds, linePatterns, lineWidths, rops, rop2s, and translates.
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 theorientation
andskip
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 usingpdl_symbols
. - two
-
This will draw a line centered at (x, y) and with a length of 2*
size
. Theorientation
is measured in degrees, starting from horizontal, with increasing angles rotating the line counter-clockwise. The value forskip
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 askip
of one would give you a pentagon:second point _ third __..--'' \ point | \ | \ | \ first | / point | / |__ / fourth ``--.._/ point fifth point skip = 1
In contrast, a five-sided polygon with a skip of 2 will give you a star:
fourth point second /| point \`~.._/ | `\ / `--|.__ X | __> first point ,/ \_,--|' fifth /_~'' \ | point \| third point skip = 2
A skip of three would give visually identical results but the actual order in which the vertices are drawn is different:
third point fifth /| point \`~.._/ | `\ / `--|.__ X | __> first point ,/ \_,--|' second /_~'' \ | point \| fourth point 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:
second point third / point `. / `. / `./_______ first .'\ point .' \ .' \ fourth \ point fifth point skip = 0
In summary, a
skip
of zero gives you an N-asterisk. Askip
of one gives you a regular polygon. Askip
of two gives you a star. And so forth. Higher values ofskip
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.
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 "polylines" and "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!