Skip to content

Commit 7f0d265

Browse files
committed
Add pie chart.
Add beginnings of some color palettes. Fix a typo in Data.pm
1 parent d75c2c1 commit 7f0d265

3 files changed

Lines changed: 233 additions & 16 deletions

File tree

htdocs/js/Plots/plots.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -375,13 +375,13 @@ const PGplots = {
375375
options.xAxis.overrideOptions ?? {}
376376
)
377377
));
378-
378+
379379
xAxis.defaultTicks.formatLabelText = plot.formatLabelText;
380380

381381
if (options.xAxis.ticks?.customLabels) {
382382
xAxis.defaultTicks.generateLabelText = function (tick) {
383-
return options.xAxis.ticks.customLabels[tick.usrCoords[1]/options.xAxis.ticks.distance-1];
384-
}
383+
return options.xAxis.ticks.customLabels[tick.usrCoords[1] / options.xAxis.ticks.distance - 1];
384+
};
385385
} else {
386386
xAxis.defaultTicks.generateLabelText = plot.generateLabelText;
387387
}

lib/Plots/Data.pm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ stored in the C<< $data->{function} >> hash, though other data is stored as a st
6666
);
6767
6868
Note, the first argument must be $self->context when called from C<Plots::Plot>
69-
to use a single context for all C<Plost::Data> objects.
69+
to use a single context for all C<Plots::Data> objects.
7070
7171
This is also used to set a two variable function (used for slope or vector fields):
7272
@@ -116,7 +116,7 @@ Takes a MathObject C<$formula> and replaces the function with either
116116
a JavaScript or PGF function string. If the function contains any function
117117
tokens not supported, a warning and empty string is returned.
118118
119-
$formula The mathobject formula object, either $self->{function}{Fx} or $self->{function}{Fy}.
119+
$formula The MathObject formula object, either $self->{function}{Fx} or $self->{function}{Fy}.
120120
$type 'js' or 'PGF' (falls back to js for any input except 'PGF').
121121
$xvar The x-variable name, $self->{function}{xvar}.
122122
$yvar The y-variable name, $self->{function}{yvar}, for vector fields.

macros/graph/StatisticalPlots.pl

Lines changed: 228 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ =head1 DESCRIPTION
2121
2222
=item Scatter Plots
2323
24+
=item Pie Charts
25+
2426
=back
2527
2628
=head2 USAGE
@@ -69,7 +71,7 @@ =head2 BAR PLOTS
6971
7072
$stat_plot->add_barplot($xdata, $ydata, %opts);
7173
72-
where C<$xdata> is an ARRAYREF of x-values where the bars will be centered and C<$ydata> is an
74+
where C<$xdata> is an array reference of x-values where the bars will be centered and C<$ydata> is an
7375
ARRAY of heights of the bars. Note: if the option C<< orientation => 'horizontal' >> is included
7476
then the bar lengths are the values in C<$xdata> and locations in C<$ydata>.
7577
@@ -169,9 +171,19 @@ =head3 Options
169171
of points. If the value is 1, then the heights are scaled so the total height of the
170172
bars is 1.
171173
174+
=item stroke_color
175+
176+
This sets the color of the boundary of the rectangle and the whiskers. It is an alias for
177+
the C<color> option of L<add_dataset|plots.pl/DATASET OPTIONS>. See L<COLORS|plots.pl> for options to change the color.
178+
179+
=item stroke_width
180+
181+
This sets the width of the boundary of the rectangle and the whiskers. This is an alias for
182+
the C<width> option of L<add_dataset|plots.pl>.
183+
172184
=back
173185
174-
The rest of the options are passed through to the C<add_barplot> method in which the
186+
The rest of the options are passed through to the L<add_barplot|BAR PLOTS> method in which the
175187
fill color and opacity as well as the stroke color and width. See both L<add_barplot>
176188
and L<add_dataset options|plots.pl/DATASET OPTIONS> for more details.
177189
@@ -189,8 +201,8 @@ =head2 BOX PLOTS
189201
where C<$data> is an array ref of univariate data or a hash ref of the boxplot characteristics,
190202
then a box plot is created using the five number summary (minimum, first quartile, median,
191203
third quartile, maximum) of the data. These values are calculated using the C<five_point_summary>
192-
function from C<PGstatisticsmacros.pl>. An example of creating a boxplot with an arrayref of
193-
univariate data is
204+
function from C<PGstatisticsmacros.pl>. An example of creating a boxplot with an array reference
205+
of univariate data is
194206
195207
@data = urand(100,25,75,6);
196208
@@ -211,7 +223,7 @@ =head2 BOX PLOTS
211223
and as with other methods in this macro, one can pass options to the characteristic of the
212224
box plot (like fill color or stroke color and width) within the C<add_boxplot> method.
213225
214-
If C<$data> is a hashref, it must contains the fields C<min, q1, median, q3, max> that are used to
226+
If C<$data> is a hash reference, it must contains the fields C<min, q1, median, q3, max> that are used to
215227
define the boxplot. Optionally, one may also include the field C<outliers> which is an array
216228
ref of values which will be plotted beyond the whiskers.
217229
@@ -257,7 +269,7 @@ =head3 Options
257269
258270
If multiple box plots are included, this option will be created to equally space the
259271
box plots between the axis and the edge of the plot. If included, this option must be an
260-
arrayref of values (in the x-direction for vertical plots and y-direction for horizontal).
272+
array reference of values (in the x-direction for vertical plots and y-direction for horizontal).
261273
262274
box_center => [3,6,9]
263275
@@ -285,9 +297,19 @@ =head3 Options
285297
The shape of the mark to use for outliers. Default is 'plus'. See L<Options for add_dataset|plots.pl/DATASET OPTIONS>
286298
for other mark options.
287299
300+
=item stroke_color
301+
302+
This sets the color of the boundary of the rectangle and the whiskers. It is an alias for
303+
the C<color> option of L<add_dataset|plots.pl/DATASET OPTIONS>. See L<COLORS|plots.pl> for options to change the color.
304+
305+
=item stroke_width
306+
307+
This sets the width of the boundary of the rectangle and the whiskers. This is an alias for
308+
the C<width> option of L<add_dataset|plots.pl>.
309+
288310
=back
289311
290-
As with other methods in the macro, other options can be passed along to C<add_rectangle>
312+
As with other methods in the macro, other options can be passed along to L<add_rectangle|plots.pl/PLOT RECTANGLES>
291313
and C<add_dataset> which are used in the macro.
292314
293315
Also, if C<fill_color> is included, then C<< fill => 'self' >> is automatically added on the
@@ -332,12 +354,108 @@ =head2 SCATTER PLOTS
332354
333355
The C<mark_size> is default to 3.
334356
357+
=item mark_color
358+
359+
This changes the mark color and is an alias for the C<color> option. See L<COLORS/plots.pl>
360+
for options to change the color.
361+
335362
=back
336363
337364
If more that one dataset is to be plotted, simply call the C<add_scatterplot> method multiple
338365
times. This can be done with a single C<add_dataset> method call, but this wrapper makes it
339366
easier to set different options
340367
368+
=head2 PIE CHARTS
369+
370+
A pie chart is a circle that divided in to sectors whose size is proportional to an input array.
371+
The sectors are generally given each a color and a label. This method will also produce
372+
donut charts (or ring charts), which is a pie chart with a hole.
373+
374+
The general form is
375+
376+
$stat_plot->add_piechart($data, %options);
377+
378+
where $data is an array reference of values.
379+
380+
The following are the options:
381+
382+
=over
383+
384+
=item center
385+
386+
The center of the circle as an array reference. The default value is C<[0,0]>.
387+
388+
=item radius
389+
390+
The radius of the circle. The default value of C<4> is chosen to fit nicely with the
391+
default values of the bounding box of the C<StatPlot> which ranges from -5 to 5
392+
in both the x- and y-directions.
393+
394+
=item inner_radius
395+
396+
If you desire a donut chart or ring chart, set this to a value less than the radius.
397+
The default value is 0.
398+
399+
=item angle_offset
400+
401+
The first sector by default starts at angle 0 (from the positive horizontal axis) in degrees. Use
402+
this to change this.
403+
404+
=item color_palette
405+
406+
This is either the name of a color palette or an array reference of colors for each of the
407+
sectors in the pie chart. If the length of this array reference is smaller than
408+
the C<$data> array reference, then the colors will be cycled. The default is to
409+
use the 'default' color palette. See L<COLOR PALETTES> for more information.
410+
411+
=item color_sectors
412+
413+
If this is 1 (default), then colors are used for the pie chart. If 0, then the
414+
sectors are not filled. See C<color_palette> for selecting colors.
415+
416+
=item sector_labels
417+
418+
The labels for the sector as a array reference of strings or values. The default is for
419+
no labels. If this is used, the length of this must be the same as the C<$data> array
420+
reference.
421+
422+
=back
423+
424+
=head2 COLOR PALETTES
425+
426+
The color palettes for the bar plots and pie charts can be select from the C<color_palette>
427+
function. This allows a number of built-in/generated color palettes. To get an
428+
array reference of either named or generated colors:
429+
430+
color_palette($name, num_colors => $n);
431+
432+
For example,
433+
434+
color_palette('rainbow');
435+
436+
returns the 6 colors of the rainbow. Some of the palettes have fixed numbers of colors,
437+
whereas others have variable numbers. If C<num_colors> is not defined, then some palettes
438+
return a fixed number (like 'rainbow') and if the C<num_colors> is needed, then the
439+
default of 10 is assumed.
440+
441+
=head3 PALETTE NAMES
442+
443+
=over
444+
445+
=item rainbow
446+
447+
The colors of the rainbow from violet to red. The C<num_colors> options is ignored.
448+
449+
=item random
450+
451+
This will return C<num_colors> random colors from the defined SVG colors.
452+
453+
=back
454+
455+
=head2 LEGENDS
456+
457+
A legend is helpful for some plots.
458+
341459
=cut
342460

343461
BEGIN { strict->import; }
@@ -399,10 +517,10 @@ sub add_barplot {
399517
my %options = (
400518
bar_width => 1,
401519
orientation => 'vertical',
402-
%opts
520+
plot_option_aliases(%opts)
403521
);
404522

405-
Value::Error('The lengths of the data in the first two arguments must be arrayrefs of the same length')
523+
Value::Error('The lengths of the data in the first two arguments must be array references of the same length')
406524
unless ref $xdata eq 'ARRAY' && ref $xdata eq 'ARRAY' && scalar(@$xdata) == scalar(@$ydata);
407525

408526
# assume that the $xdata is equally spaced. TODO: should we handle arbitrary spaced bars?
@@ -430,7 +548,7 @@ sub add_boxplot {
430548
whisker_cap => 0,
431549
cap_width => 0.2,
432550
outlier_mark => 'plus',
433-
%opts
551+
plot_option_aliases(%opts)
434552
);
435553

436554
# Placeholder for boxplot implementation.
@@ -546,11 +664,110 @@ sub add_scatterplot {
546664
linestyle => 'none',
547665
marks => 'circle',
548666
mark_size => 3,
549-
%opts
667+
plot_option_aliases(%opts)
550668
);
551669

552670
$self->add_dataset(@$data, %options);
553671

554672
}
555673

674+
sub add_piechart {
675+
my ($self, $data, %opts) = @_;
676+
677+
my %options = (
678+
center => [ 0, 0 ],
679+
radius => 4,
680+
angle_offset => 0,
681+
inner_radius => 0,
682+
plot_option_aliases(%opts)
683+
);
684+
685+
Value::Error('The number of labels must equal the number of sectors in the pie chart')
686+
unless defined($options{labels}) && scalar(@$data) == scalar(@{ $options{labels} });
687+
688+
my $fill_colors =
689+
(!defined $options{fill_colors} || ref $options{fill_colors} ne 'ARRAY')
690+
? color_palette($options{fill_colors})
691+
: $options{fill_colors};
692+
693+
my $pi = 4 * atan2(1, 1);
694+
my $total = 0;
695+
$total += $_ for (@$data);
696+
697+
my $theta = $options{angle_offset} * $pi / 180; # first angle of the sector
698+
for (0 .. $#$data) {
699+
my $delta_theta = 2 * $pi * $data->[$_] / $total;
700+
$self->add_multipath(
701+
[
702+
[
703+
"$options{center}->[0] + $options{radius} * cos(t)",
704+
"$options{center}->[1] + $options{radius} * sin(t)",
705+
$theta,
706+
$theta + $delta_theta
707+
],
708+
[
709+
"$options{center}->[0] + $options{inner_radius} * cos(t)",
710+
"$options{center}->[1] + $options{inner_radius} * sin(t)",
711+
$theta + $delta_theta,
712+
$theta
713+
],
714+
],
715+
't',
716+
cycle => 1,
717+
fill => 'self',
718+
fill_color => $fill_colors->[ $_ % scalar(@$fill_colors) ],
719+
%options
720+
);
721+
# add the labels if defined
722+
if ($options{labels}) {
723+
my $alpha = $theta + 0.5 * $delta_theta;
724+
# take $alpha mod 2pi
725+
$alpha = $alpha - (2 * $pi * int($alpha / (2 * $pi)));
726+
727+
$self->add_label(
728+
1.1 * $options{radius} * cos($alpha),
729+
1.1 * $options{radius} * sin($alpha),
730+
$options{labels}->[$_],
731+
(0 <= $alpha && $alpha < $pi / 4)
732+
|| (7 * $pi / 4 < $alpha && $alpha < 2 * $pi) ? (h_align => 'left')
733+
: $pi / 4 <= $alpha < 3 * $pi / 4 ? (v_align => 'bottom')
734+
: 3 * $pi / 4 <= $alpha < 5 * $pi / 4 ? (h_align => 'right')
735+
: (v_align => 'top')
736+
);
737+
}
738+
$theta += $delta_theta;
739+
}
740+
741+
}
742+
743+
# This provides some alias for options.
744+
# For additional aliases, add to the %aliases hash below.
745+
746+
sub plot_option_aliases {
747+
my (%options) = @_;
748+
749+
my %aliases = (
750+
width => 'stroke_width',
751+
color => 'stroke_color',
752+
color => 'mark_color'
753+
);
754+
755+
for (keys %aliases) {
756+
$options{$_} = $options{ $aliases{$_} } if $options{ $aliases{$_} };
757+
delete $options{ $aliases{$_} };
758+
}
759+
return %options;
760+
}
761+
762+
sub color_palette {
763+
my ($palette_name, $num_colors) = @_;
764+
765+
$palette_name = 'rainbow' unless defined($palette_name);
766+
767+
if ($palette_name eq 'rainbow') {
768+
return [ 'violet', 'blue', 'green', 'yellow', 'orange', 'red' ];
769+
}
770+
771+
}
772+
556773
1;

0 commit comments

Comments
 (0)