# Always load Prima first: use Prima; # # GENERATED WITH PDL::PP! Don't modify! # package PDL::Drawing::Prima; @EXPORT_OK = qw( ); %EXPORT_TAGS = (Func=>[@EXPORT_OK]); use PDL::Core; use PDL::Exporter; use DynaLoader; $PDL::Drawing::Prima::VERSION = "0.12"; @ISA = ( 'PDL::Exporter','DynaLoader' ); push @PDL::Core::PP, __PACKAGE__; bootstrap PDL::Drawing::Prima $VERSION; use strict; use warnings; use PDL; use PDL::Char; use Scalar::Util 'blessed'; use Carp 'croak'; use PDL::Drawing::Prima::Utils; use Prima::noX11; # working here - use Exporter and set as 'EXPORT_OK' the functions # pdl_of_patterns_for. Its usage looks like this: # my $patterns = pdl_of_patterns_for($pat1, $pat2, ...); =head1 NAME PDL::Drawing::Prima - PDL-aware drawing functions for Prima widgets =cut =head1 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 C 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; =head1 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: B and B. 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 L. 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 L, the plotting library built on these bindings. This bindings can be applied to B object whose class is derived from L, including displayed widgets and abstract canvases such as L. 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.) =head1 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. =head1 FUNCTIONS =head2 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. =cut # Builds a piddle of patterns with the appropriate sizes, etc. sub piddle_of_patterns_for { # Make sure they're not being stupid: croak("You must supply at least one pattern to make a pattern piddle") if @_ == 0; # First get the longest pattern: my $length = 0; foreach(@_) { $length = length($_) if $length < length($_); } use PDL::NiceSlice; # Create the new byte array with the appropriate dimensions: my $to_return = zeroes(byte, $length, scalar(@_)); $to_return .= $to_return->sequence; for (my $i = 0; $i < @_; $i++) { # Get a slice and update it: my $slice = $to_return(:,$i); substr ${$slice->get_dataref}, 0, length($_[$i]), $_[$i]; # Make sure the modifications propogate back to the original: $slice->upd_data; } no PDL::NiceSlice; return $to_return; } =head2 piddle_of_handles_for THIS FUNCTION IS DEPRECATED AND NOW DIES IF YOU TRY TO USE IT. Performing the same PDL drawing operation on different widgets via PDL threading makes atomic behavior very difficult, so this functionality has been removed. If you think you'd like this behavior back, please let me know. =cut sub piddle_of_handles_for { croak("piddle_of_handles_for has been deprecated"); } =head1 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 C 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. =cut # This is a list of the number of arguments for each property. It is based on the # pars_args_for hash which is built in the .pd file associated with this module my %N_args_for = qw( rops 1 rop2s 1 clipRects 4 fillWindings 1 splinePrecisions 1 backColors 1 lineJoins 1 translates 2 lineWidths 1 fillPatterns 1 colors 1 lineEnds 1 linePatterns 1 ); sub get_sorted_args_with_defaults { my ($self, $arg_names, $given) = @_; # Default to an empty list: $given = {} unless ref($given) eq 'HASH'; # Check that they supplied only allowed parameters (allowing both # singular and plural forms) for my $parameter (keys %$given) { croak("Unknown parameter $parameter") unless grep { # check singular and plural parameter names $_ eq $parameter or $_ eq $parameter . 's' } @$arg_names } # Return the sorted list of supplied or default values my @to_return = (); for my $arg_name (@$arg_names) { # If a plural property is not specified, return a default property of # a zeroed-out, one-element piddle. Set the value to zero since it is # never used. if (not exists $given->{$arg_name}) { push @to_return, (0) for (1..$N_args_for{$arg_name}); } elsif (ref ($given->{$arg_name}) eq 'ARRAY') { # If an array ref, dereference it and make sure the number # of arguments agrees with what we expect: if (@{$given->{$arg_name}} != $N_args_for{$arg_name}) { croak("Expected 1 argument for $arg_name") if $N_args_for{$arg_name} == 1; croak("Expected $N_args_for{$arg_name} arguments for $arg_name"); } push @to_return, @{$given->{$arg_name}}; } else { # Otherwise, return it outright, if we only expected one # argument: $N_args_for{$arg_name} == 1 or croak("Expected $N_args_for{$arg_name} arguments for $arg_name"); push @to_return, $given->{$arg_name}; } } return @to_return; } =head2 pdl_arcs =for sig Prima Signature: (widget; x(); y(); x_diameter(); y_diameter(); start_angle(); end_angle(); properties) =for ref Draws arcs, i.e. incomplete ellipses. Applicable properties include colors, backColors, lineEnds, linePatterns, lineWidths, rops, rop2s, and translates. The arcs go from the Cs to the Cs along the ellipses centered at the Cs and Cs, with the specified x- and y-diameters. The angles are measured in degrees, not radians. The difference between this command and L or L is that C does not connect the dangling ends. Here's a simple example: =for 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 C 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 L. Closely related routines include L and L. See also L, and L, L, and L. Spline drawing provides a similar functionality, though more complex and more powerful. There are no PDL bindings for the spline functions yet. See L for more information. =for bad pdl_arcs does not process bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @arcs_props = qw(colors backColors lineEnds linePatterns lineWidths rops rop2s translates); our @arcs_args = qw(x y x_diameter y_diameter start_angle end_angle); sub Prima::Drawable::pdl_arcs { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_arcs is a widget method that expectes '. scalar(@arcs_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @arcs_args)) unless (@_ > @arcs_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x = shift; my $y = shift; my $x_diameter = shift; my $y_diameter = shift; my $start_angle = shift; my $end_angle = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_arcs expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@arcs_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_arcs_int($x, $y, $x_diameter, $y_diameter, $start_angle, $end_angle, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_arcs: $@"; } } =head2 pdl_bars Prima Signature: (widget; x1(); y1(); x2(); y2(); properties) =for ref Draws filled rectangles from corners (x1, y1) to (x2, y2). Applicable properties include colors, backColors, clipRects, fillPatterns, rops, rop2s, and translates. =for example # 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 C 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 L. See also L, which is the unfilled equivalent, and L, which is sorta the opposite of this. =for bad pdl_bars does not process bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @bars_props = qw(colors backColors clipRects fillPatterns rops rop2s translates); our @bars_args = qw(x1 y1 x2 y2); sub Prima::Drawable::pdl_bars { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_bars is a widget method that expectes '. scalar(@bars_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @bars_args)) unless (@_ > @bars_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x1 = shift; my $y1 = shift; my $x2 = shift; my $y2 = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_bars expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@bars_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_bars_int($x1, $y1, $x2, $y2, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_bars: $@"; } } =head2 pdl_chords Prima Signature: (widget; x(); y(); x_diameter(); y_diameter(); start_angle(); end_angle(); properties) =for ref Draws arcs (i.e. incomplete ellipses) whose ends are connected by a line. The chord starts at C and runs to C along the ellipse centered at C, C, with their specified diameters C, C. Unlike L or L, 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 example # 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 L. The filled equivalent is L. Closely related routines are L and L. See also L, L, and L, as well as L. =for bad pdl_chords does not process bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @chords_props = qw(colors backColors clipRects lineEnds linePatterns lineWidths rops rop2s translates); our @chords_args = qw(x y x_diameter y_diameter start_angle end_angle); sub Prima::Drawable::pdl_chords { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_chords is a widget method that expectes '. scalar(@chords_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @chords_args)) unless (@_ > @chords_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x = shift; my $y = shift; my $x_diameter = shift; my $y_diameter = shift; my $start_angle = shift; my $end_angle = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_chords expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@chords_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_chords_int($x, $y, $x_diameter, $y_diameter, $start_angle, $end_angle, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_chords: $@"; } } =head2 pdl_clears Prima Signature: (widget; x1(); y1(); x2(); y2(); properties) =for ref Clears the specified rectangle(s). Applicable properties include backColors, rop2s, and translates. =for example 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 L. In practice I suppose this might be considered the opposite of L, though technically this is meant for erasing, not drawing. =for bad pdl_clears does not process bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @clears_props = qw(colors backColors clipRects lineEnds linePatterns lineWidths rops rop2s translates); our @clears_args = qw(x1 y1 x2 y2); sub Prima::Drawable::pdl_clears { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_clears is a widget method that expectes '. scalar(@clears_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @clears_args)) unless (@_ > @clears_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x1 = shift; my $y1 = shift; my $x2 = shift; my $y2 = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_clears expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@clears_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_clears_int($x1, $y1, $x2, $y2, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_clears: $@"; } } =head2 pdl_ellipses Prima Signature: (widget; x(); y(); x_diameter(); y_diameter(); properties) =for ref Draws ellipses centered at C, C with diameters C and C. Applicable properties include colors, backColors, clipRects, linePatterns, lineWidths, rops, rop2s, and translates. To draw circles, just use the same x- and y-diameter. =for example # 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 L. The filled equivalent is L. See also L, L, and L as well as L and L. You may also be interested in L, which does not yet have a PDL interface. =for bad pdl_ellipses does not process bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @ellipses_props = qw(colors backColors clipRects linePatterns lineWidths rops rop2s translates); our @ellipses_args = qw(x y x_diameter y_diameter); sub Prima::Drawable::pdl_ellipses { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_ellipses is a widget method that expectes '. scalar(@ellipses_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @ellipses_args)) unless (@_ > @ellipses_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x = shift; my $y = shift; my $x_diameter = shift; my $y_diameter = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_ellipses expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@ellipses_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_ellipses_int($x, $y, $x_diameter, $y_diameter, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_ellipses: $@"; } } =head2 pdl_fill_chords Prima Signature: (widget; x(); y(); x_diameter(); y_diameter(); start_angle(); end_angle(); properties) =for ref Draws filled chords (see L). Applicable properties include colors, backColors, clipRects, fillPatterns, rops, rop2s, and translates. Chords are partial elipses that run from C to C along the ellipse centered at C, C, 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. =for example # working here: $canvas->pdl_fill_chords($x, $y, $xd, $yd, $ti, $tf); Compare to the Prima method L. The unfilled equivalent is L. Closely related to L and L. See also L, L, and L. =for bad pdl_fill_chords does not process bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @fill_chords_props = qw(colors backColors clipRects fillPatterns rops rop2s translates); our @fill_chords_args = qw(x y x_diameter y_diameter start_angle end_angle); sub Prima::Drawable::pdl_fill_chords { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_fill_chords is a widget method that expectes '. scalar(@fill_chords_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @fill_chords_args)) unless (@_ > @fill_chords_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x = shift; my $y = shift; my $x_diameter = shift; my $y_diameter = shift; my $start_angle = shift; my $end_angle = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_fill_chords expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@fill_chords_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_fill_chords_int($x, $y, $x_diameter, $y_diameter, $start_angle, $end_angle, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_fill_chords: $@"; } } =head2 pdl_fill_ellipses Prima Signature: (widget; x(); y(); x_diameter(); y_diameter(); properties) =for ref Draws filled ellipses (see L). Applicable properties include colors, backColors, clipRects, fillPatterns, rops, rop2s, and translates. =for example # 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 L. The unfilled equivalent is L. Closely related to L and L, and L. See also L, L, and L. Also, check out L, which does not yet have PDL bindings. =for bad pdl_fill_ellipses does not process bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @fill_ellipses_props = qw(colors backColors clipRects fillPatterns rops rop2s translates); our @fill_ellipses_args = qw(x y x_diameter y_diameter); sub Prima::Drawable::pdl_fill_ellipses { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_fill_ellipses is a widget method that expectes '. scalar(@fill_ellipses_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @fill_ellipses_args)) unless (@_ > @fill_ellipses_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x = shift; my $y = shift; my $x_diameter = shift; my $y_diameter = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_fill_ellipses expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@fill_ellipses_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_fill_ellipses_int($x, $y, $x_diameter, $y_diameter, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_fill_ellipses: $@"; } } =head2 pdl_fillpolys Prima Signature: (widget; x(n); y(n); properties) =for ref Draws and fills a polygon with (mostly) arbitrary edge vertices. Applicable properties include colors, backColors, clipRects, fillPatterns, fillWindings, rops, rop2s, and translates. NOTE: there is B underscore between C and C, which is different from the other C 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 C and C, PDL has support for bad values. Unlike in C, C 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 C will correctly draw what you mean but C 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: =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, fillWindings => 1 ); Resizing the window will result in a similar rendering that fits the aspect ratio of your (resized) window. Compare to the Prima method L. See also L and L. =for bad pdl_fillpolys processes bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @fillpolys_props = qw(colors backColors clipRects fillPatterns fillWindings rops rop2s translates); our @fillpolys_args = qw(x y); sub Prima::Drawable::pdl_fillpolys { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_fillpolys is a widget method that expectes '. scalar(@fillpolys_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @fillpolys_args)) unless (@_ > @fillpolys_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x = shift; my $y = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_fillpolys expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@fillpolys_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_fillpolys_int($x, $y, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_fillpolys: $@"; } } =head2 pdl_fill_sectors Prima Signature: (widget; x(); y(); x_diameter(); y_diameter(); start_angle(); end_angle(); properties) =for ref 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 C to C along the ellipse centered at C, C, with specified x- and y-diameters. Like L, this command connects the end points of the arc, but unlike L, 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 example # 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 L. The unfilled equivalent is L. This is closely related to C and C. See also L, L, and L. =for bad pdl_fill_sectors does not process bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @fill_sectors_props = qw(colors backColors clipRects fillPatterns rops rop2s translates); our @fill_sectors_args = qw(x y x_diameter y_diameter start_angle end_angle); sub Prima::Drawable::pdl_fill_sectors { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_fill_sectors is a widget method that expectes '. scalar(@fill_sectors_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @fill_sectors_args)) unless (@_ > @fill_sectors_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x = shift; my $y = shift; my $x_diameter = shift; my $y_diameter = shift; my $start_angle = shift; my $end_angle = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_fill_sectors expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@fill_sectors_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_fill_sectors_int($x, $y, $x_diameter, $y_diameter, $start_angle, $end_angle, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_fill_sectors: $@"; } } =head2 pdl_flood_fills Prima Signature: (widget; x(); y(); fill_color(); singleborder(); properties) =for ref 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 C is probably B what you think it is. The color of the final fill is determined by your C property. What, then, does C specify? It indicates how Prima is supposed to perform the fill. If C is zero, then C is the color of the B 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 C is 1, then the value of C tells Prima to replace every contiguous pixel B C with the new color specified by C (or the current color, if no C piddle is given). =for example # 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 L. See also L and the various fill-based drawing methods. =for bad pdl_flood_fills does not process bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @flood_fills_props = qw(colors backColors clipRects fillPatterns rops rop2s translates); our @flood_fills_args = qw(x y color singleborder); sub Prima::Drawable::pdl_flood_fills { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_flood_fills is a widget method that expectes '. scalar(@flood_fills_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @flood_fills_args)) unless (@_ > @flood_fills_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x = shift; my $y = shift; my $color = shift; my $singleborder = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_flood_fills expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@flood_fills_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_flood_fills_int($x, $y, $color, $singleborder, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_flood_fills: $@"; } } =head2 pdl_lines Prima Signature: (widget; x1(); y1(); x2(); y2(); properties) =for ref 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 L for a discussion of what might constitute proper bad value handling. =for example working here Compare to the Prima methods L and L. See also L. =for bad pdl_lines processes bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @lines_props = qw(colors backColors clipRects lineEnds lineJoins linePatterns lineWidths rops rop2s translates); our @lines_args = qw(x1 y1 x2 y2); sub Prima::Drawable::pdl_lines { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_lines is a widget method that expectes '. scalar(@lines_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @lines_args)) unless (@_ > @lines_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x1 = shift; my $y1 = shift; my $x2 = shift; my $y2 = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_lines expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@lines_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_lines_int($x1, $y1, $x2, $y2, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_lines: $@"; } } =head2 pdl_polylines Prima Signature: (widget; x(n); y(n); properties) =for ref 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 L, so it needs to be able to handle all manner of strange input. Here is what happens when you specify strange values: =over =item 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. =item 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. =item 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). =item 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. =back Here's an example of how to plot data using C (though you'd do better to use L to create plots): =for example # 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 L. See also L and L. =for bad pdl_polylines processes bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @polylines_props = qw(colors backColors clipRects lineEnds lineJoins linePatterns lineWidths rops rop2s translates); our @polylines_args = qw(x y); sub Prima::Drawable::pdl_polylines { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_polylines is a widget method that expectes '. scalar(@polylines_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @polylines_args)) unless (@_ > @polylines_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x = shift; my $y = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_polylines expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@polylines_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_polylines_int($x, $y, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_polylines: $@"; } } =head2 pdl_rectangles Prima Signature: (widget; x1(); y1(); x2(); y2(); properties) =for ref Draws a rectangle from corner (x1, y1) to corner (x2, y2). Applicable properties include colors, backColors, clipRects, linePatterns, lineWidths, rops, rop2s, and translates. =for example # 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 C 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 L. See also L, which is the filled equivalent, and L, 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 L, which draws beveled edges around a rectangle. =for bad pdl_rectangles does not process bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @rectangles_props = qw(colors backColors clipRects linePatterns lineWidths rops rop2s translates); our @rectangles_args = qw(x1 y1 x2 y2); sub Prima::Drawable::pdl_rectangles { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_rectangles is a widget method that expectes '. scalar(@rectangles_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @rectangles_args)) unless (@_ > @rectangles_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x1 = shift; my $y1 = shift; my $x2 = shift; my $y2 = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_rectangles expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@rectangles_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_rectangles_int($x1, $y1, $x2, $y2, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_rectangles: $@"; } } =head2 pdl_sectors Prima Signature: (widget; x(); y(); x_diameter(); y_diameter(); start_angle(); end_angle(); properties) =for ref 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 C to C along the ellipse centered at C, C, with specified x- and y-diameters. Like L, this command connects the end points of the arc, but unlike L, 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 example # 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 L. The filled equivalent is L. There is a whole slew of arc-based drawing methods including L, L, and L along with their filled equivalents. You may also be interested in L, which does not yet have a PDL interface. =for bad pdl_sectors does not process bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @sectors_props = qw(colors backColors clipRects lineEnds linePatterns lineWidths rops rop2s translates); our @sectors_args = qw(x y x_diameter y_diameter start_angle end_angle); sub Prima::Drawable::pdl_sectors { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_sectors is a widget method that expectes '. scalar(@sectors_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @sectors_args)) unless (@_ > @sectors_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x = shift; my $y = shift; my $x_diameter = shift; my $y_diameter = shift; my $start_angle = shift; my $end_angle = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_sectors expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@sectors_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_sectors_int($x, $y, $x_diameter, $y_diameter, $start_angle, $end_angle, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_sectors: $@"; } } =head2 PDL-ONLY METHODS These are drawing methods that have no analogous Prima::Drawable function. =cut =head2 pdl_symbols Signature: (widget; x(); y(); N_points(); orientation(); filled(); size(); skip(); properties) =for ref Draws a wide variety of symbols centered at (x, y). Applicable properties include colors, backColors, clipRects, fillPatterns, fillWindings, lineEnds, linePatterns, lineWidths, rops, rop2s, and translates. Through various combinations of C, C, and C, you can generate many different regular symbols, including dashes, stars, asterisks, triangles, and diamonds. You can also specify each symbol's C and C. 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 C. If C is: =over =item 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 C, but the C and C 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 C. =item two This will draw a line centered at (x, y) and with a length of 2*C. The C is measured in degrees, starting from horizontal, with increasing angles rotating the line counter-clockwise. The value for C is ignored. This is particulary useful for visualizing slope-fields (although calculating the angles for the slope field is surprisingly tricky). =item 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 C. For example, a five-sided polygon with a C 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 I In other words, create an asterisk: second point third / point `. / `. / `./_______ first .'\ point .' \ .' \ fourth \ point fifth point skip = 0 In summary, a C of zero gives you an N-asterisk. A C of one gives you a regular polygon. A C of two gives you a star. And so forth. Higher values of C 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 C, specifying a fill will end up with a filled shape instead of a line drawing. =back 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 C property. As with almost all graphical properties, you can specify the C property for each symbol by specifying the C piddle. This example creates a table of shapes. It uses an argument from the command line to determine the line width. =for example 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 =for bad Bad values are handled by C. If any of the values you pass in are bad, the symbol is not drawn at that x/y coordinate. =for bad pdl_symbols processes bad values. It will set the bad-value flag of all output piddles if the flag is set for any of the input piddles. =cut our @symbols_props = qw(colors backColors clipRects fillPatterns fillWindings lineEnds linePatterns lineWidths rops rop2s translates); our @symbols_args = qw(x y N_points orientation filled size skip); sub Prima::Drawable::pdl_symbols { # Before anything else, make sure they supplied at least the # required number of arguments: croak('pdl_symbols is a widget method that expectes '. scalar(@symbols_args) . ' arguments (besides the widget): ' . join(', ', 'widget', @symbols_args)) unless (@_ > @symbols_args); # unpack the widget and the required arguments for this function: my $self = shift; my $x = shift; my $y = shift; my $N_points = shift; my $orientation = shift; my $filled = shift; my $size = shift; my $skip = shift; # Check for an even number of remaining arguments (key-value pairs): croak('pdl_symbols expects optional parameters as key => value pairs') unless @_ % 2 == 0; my %args = @_; # Check for piddles as values for singular properties for (keys %args) { if ($_ !~ /s$/ && eval { $args{$_}->isa('PDL') }) { croak "A piddle passed as singular $_ property"; } } # Get the a full list of arguments suitable for the internal pp code # in the correct order: my @args_with_defs = get_sorted_args_with_defaults($self, \@symbols_props, \%args); # Add the widget to the set of args $args{widget} = $self; # Call the PP'd internal code. Always put the args hash last. eval { PDL::_prima_symbols_int($x, $y, $N_points, $orientation, $filled, $size, $skip, @args_with_defs, \%args); }; if ($@) { # die $@; $@ =~ s/at (.*?) line \d+\.\n$//; croak "Issues calling pdl_symbols: $@"; } } ; =head1 ERROR MESSAGE These functions may throw the following exception: =head2 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? =head1 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. =head1 TODO These are all the things I wish to do: =over =item 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 L, L, L, L, L, L, L, L, and L =item Bad Value Support Bad values are handled decently in L and L, 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. C). But I haven't decided which of these is best. =back =head1 AUTHOR David Mertens, Edcmertens.perl@gmail.comE. =head1 SEE ALSO Some useful PDL/Prima functions are defined in L, especially for converting among simple color formats. This is built as an extension for the Prima toolkit, http://www.prima.eu.org/, L. This is built using (and targeted at users of) the Perl Data Language, L. This is the bedrock for the plotting package L. Another interface between PDL and Prima is . 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! =cut # Exit with OK status 1;