From Patrick Michaud:
[imager-graph.git] / Graph.pm
CommitLineData
35574351 1package Imager::Graph;
54ada35d 2require 5.005;
35574351
TC
3
4=head1 NAME
5
6Imager::Graph - Perl extension for producing Graphs using the Imager library.
7
8=head1 SYNOPSIS
9
2eac77fc 10 use Imager::Graph::Sub_class;
11 my $chart = Imager::Graph::Sub_class->new;
81453d28 12 my $img = $chart->draw(data=> \@data, ...)
35574351
TC
13 or die $chart->error;
14
15=head1 DESCRIPTION
16
17Imager::Graph provides style information to its base classes. It
18defines the colors, text display information and fills based on both
19built-in styles and modifications supplied by the user to the draw()
20method.
21
35574351
TC
22=over
23
24=cut
25
26use strict;
27use vars qw($VERSION);
28use Imager qw(:handy);
bb0de914 29use Imager::Fountain;
35574351 30
68a6c9c6 31$VERSION = '0.06';
35574351 32
35574351
TC
33# the maximum recursion depth in determining a color, fill or number
34use constant MAX_DEPTH => 10;
35
36my $NUM_RE = '(?:[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]\d+?)?)';
37
38=item new
39
40This is a simple constructor. No parameters required.
41
42=cut
43
44sub new {
45 bless {}, $_[0];
46}
47
2eac77fc 48=item set_graph_size($size)
dfd889da 49
50Sets the size of the graph (in pixels) within the image. The size of the image defaults to 1.5 * $graph_size.
51
52=cut
53
2eac77fc 54sub set_graph_size {
55 $_[0]->{'custom_style'}->{'size'} = $_[1];
dfd889da 56}
57
2eac77fc 58=item set_image_width($width)
dfd889da 59
60Sets the width of the image in pixels.
61
62=cut
63
2eac77fc 64sub set_image_width {
65 $_[0]->{'custom_style'}->{'width'} = $_[1];
dfd889da 66}
67
2eac77fc 68=item set_image_height($height)
dfd889da 69
70Sets the height of the image in pixels.
71
72=cut
73
2eac77fc 74sub set_image_height {
75 $_[0]->{'custom_style'}->{'height'} = $_[1];
dfd889da 76}
77
2eac77fc 78=item add_data_series([8, 6, 7, 5, 3, 0, 9], 'Series Name');
dfd889da 79
80Adds a data series to the graph. For L<Imager::Graph::Pie>, only one data series can be added.
81
82=cut
83
2eac77fc 84sub add_data_series {
dfd889da 85 my $self = shift;
86 my $data_ref = shift;
87 my $series_name = shift;
88
89 my $graph_data = $self->{'graph_data'} || [];
90
91 push @$graph_data, { data => $data_ref, series_name => $series_name };
92
93 $self->{'graph_data'} = $graph_data;
94 return;
95}
96
2eac77fc 97sub _get_data_series {
a17e870a
TC
98 my ($self, $opts) = @_;
99
100 # return the data supplied to draw() if any.
101 if ($opts->{data}) {
102 # one or multiple series?
103 my $data = $opts->{data};
104 if (@$data && ref $data->[0] && ref $data->[0] =~ /ARRAY/) {
105 return $data;
106 }
107 else {
108 return [ { data => $data } ];
109 }
110 }
111
112 return $self->{'graph_data'};
dfd889da 113}
114
2eac77fc 115=item set_labels(['label1', 'label2' ... ])
dfd889da 116
117Labels the specific data points. For line/bar graphs, this is the x-axis. For pie graphs, it is the label for the wedges.
118
119=cut
120
2eac77fc 121sub set_labels {
dfd889da 122 $_[0]->{'labels'} = $_[1];
123}
124
2eac77fc 125sub _get_labels {
a17e870a
TC
126 my ($self, $opts) = @_;
127
128 $opts->{labels}
129 and return $opts->{labels};
130
dfd889da 131 return $_[0]->{'labels'}
132}
133
2eac77fc 134=item set_title($title)
dfd889da 135
136Sets the title of the graph. Requires setting a font.
137
138=cut
139
2eac77fc 140sub set_title {
141 $_[0]->{'custom_style'}->{'title'}->{'text'} = $_[1];
dfd889da 142}
143
2eac77fc 144=item set_font($font)
dfd889da 145
146Sets the font to use for text. Takes an L<Imager::Font> object.
147
148=cut
149
2eac77fc 150sub set_font {
151 $_[0]->{'custom_style'}->{'font'} = $_[1];
dfd889da 152}
153
2eac77fc 154=item set_style($style_name)
dfd889da 155
156Sets the style to be used for the graph. Imager::Graph comes with several pre-defined styles: fount_lin (default), fount_rad, mono, primary_red, and primary.
157
158=cut
159
2eac77fc 160sub set_style {
dfd889da 161 $_[0]->{'style'} = $_[1];
162}
163
2eac77fc 164sub _get_style {
a17e870a
TC
165 my ($self, $opts) = @_;
166
167 $opts->{style}
168 and return $opts->{style};
169
170 return $self->{'style'};
dfd889da 171}
172
35574351
TC
173=item error
174
a22672ea 175Returns an error message. Only valid if the draw() method returns false.
35574351
TC
176
177=cut
178
179sub error {
180 $_[0]->{_errstr};
181}
182
183=item draw
184
185Creates a new image, draws the chart onto that image and returns it.
186
2eac77fc 187Optionally, instead of using the api methods to configure your chart,
188you can supply a C<data> parameter in the format
35574351
TC
189required by that particular graph, and if your graph will use any
190text, a C<font> parameter
191
192You can also supply many different parameters which control the way
193the graph looks. These are supplied as keyword, value pairs, where
194the value can be a hashref containing sub values.
195
196The C<style> parameter will selects a basic color set, and possibly
197sets other related parameters. See L</"STYLES">.
198
aaaa03e1 199 my $font = Imager::Font->new(file => 'ImUgly.ttf');
81453d28 200 my $img = $chart->draw(
201 data => \@data,
202 font => $font,
203 title => {
204 text => "Hello, World!",
205 size => 36,
206 color => 'FF0000'
207 }
208 );
35574351
TC
209
210When referring to a single sub-value this documentation will refer to
211'title.color' rather than 'the color element of title'.
212
213Returns the graph image on success, or false on failure.
214
215=back
216
217=head1 STYLES
218
219The currently defined styles are:
220
221=over
222
7b94e723
TC
223=item primary
224
225a light grey background with no outlines. Uses primary colors for the
226data fills.
227
35574351
TC
228=item primary_red
229
230a light red background with no outlines. Uses primary colors for the
7b94e723 231data fills.
35574351
TC
232
233Graphs drawn using this style should save well as a gif, even though
234some graphs may perform a slight blur.
235
7b94e723 236This was the default style, but the red was too loud.
35574351
TC
237
238=item mono
239
240designed for monochrome output, such as most laser printers, this uses
241hatched fills for the data, and no colors. The returned image is a
242one channel image (which can be overridden with the C<channels>
243parameter.)
244
245You can also override the colors used by all components for background
246or drawing by supplying C<fg> and/or C<bg> parameters. ie. if you
247supply C<<fg=>'FF0000', channels=>3>> then the hash fills and anything
248else will be drawn in red. Another use might be to set a transparent
249background, by supplying C<<bg=>'00000000', channels=>4>>.
250
251This style outlines the legend if present and outlines the hashed fills.
252
35574351
TC
253=item fount_lin
254
255designed as a "pretty" style this uses linear fountain fills for the
256background and data fills, and adds a drop shadow.
257
258You can override the value used for text and outlines by setting the
259C<fg> parameter.
260
81453d28 261This is the default style.
262
35574351
TC
263=item fount_rad
264
265also designed as a "pretty" style this uses radial fountain fills for
266the data and a linear blue to green fill for the background.
267
268=back
269
2eac77fc 270=head1 Style API
271
272To set or override styles, you can use the following methods:
273
274=over 4
275
276=item set_image_background
277
278=cut
279
280sub set_image_background {
281 $_[0]->{'custom_style'}->{'back'} = $_[1];
282}
283
284=item set_channels
285
286=cut
287
288sub set_channels {
289 $_[0]->{'custom_style'}->{'channels'} = $_[1];
290}
291
292=item set_line_color
293
294=cut
295
296sub set_line_color {
297 $_[0]->{'custom_style'}->{'line'} = $_[1];
298}
299
300=item set_title_font_size
301
302=cut
303
304sub set_title_font_size {
305 $_[0]->{'custom_style'}->{'title'}->{'size'} = $_[1];
306}
307
308=item set_title_font_color
309
310=cut
311
312sub set_title_font_color {
313 $_[0]->{'custom_style'}->{'title'}->{'color'} = $_[1];
314}
315
316=item set_title_horizontal_align
317
318=cut
319
320sub set_title_horizontal_align {
321 $_[0]->{'custom_style'}->{'title'}->{'halign'} = $_[1];
322}
323
324=item set_title_vertical_align
325
326=cut
327
328sub set_title_vertical_align {
329 $_[0]->{'custom_style'}->{'title'}->{'valign'} = $_[1];
330}
331
332=item set_text_font_color
333
334=cut
335
336sub set_text_font_color {
337 $_[0]->{'custom_style'}->{'text'}->{'color'} = $_[1];
338}
339
340=item set_text_font_size
341
342=cut
343
344sub set_text_font_size {
345 $_[0]->{'custom_style'}->{'text'}->{'size'} = $_[1];
346}
347
348=item set_graph_background_color
349
350=cut
351
352sub set_graph_background_color {
353 $_[0]->{'custom_style'}->{'bg'} = $_[1];
354}
355
356=item set_graph_foreground_color
357
358=cut
359
360sub set_graph_foreground_color {
361 $_[0]->{'custom_style'}->{'fg'} = $_[1];
362}
363
364=item set_legend_font_color
365
366=cut
367
368sub set_legend_font_color {
369 $_[0]->{'custom_style'}->{'legend'}->{'color'} = $_[1];
370}
371
372=item set_legend_font
373
374=cut
375
376sub set_legend_font {
377 $_[0]->{'custom_style'}->{'legend'}->{'font'} = $_[1];
378}
379
380=item set_legend_font_size
381
382=cut
383
384sub set_legend_font_size {
385 $_[0]->{'custom_style'}->{'legend'}->{'size'} = $_[1];
386}
387
388=item set_legend_patch_size
389
390=cut
391
392sub set_legend_patch_size {
393 $_[0]->{'custom_style'}->{'legend'}->{'patchsize'} = $_[1];
394}
395
396=item set_legend_patch_gap
397
398=cut
399
400sub set_legend_patch_gap {
401 $_[0]->{'custom_style'}->{'legend'}->{'patchgap'} = $_[1];
402}
403
404=item set_legend_horizontal_align
405
406=cut
407
408sub set_legend_horizontal_align {
409 $_[0]->{'custom_style'}->{'legend'}->{'halign'} = $_[1];
410}
411
412=item set_legend_vertical_align
413
414=cut
415
416sub set_legend_vertical_align {
417 $_[0]->{'custom_style'}->{'legend'}->{'valign'} = $_[1];
418}
419
420=item set_legend_padding
421
422=cut
423
424sub set_legend_padding {
425 $_[0]->{'custom_style'}->{'legend'}->{'padding'} = $_[1];
426}
427
428=item set_legend_outside_padding
429
430=cut
431
432sub set_legend_outside_padding {
433 $_[0]->{'custom_style'}->{'legend'}->{'outsidepadding'} = $_[1];
434}
435
436=item set_legend_fill
437
438=cut
439
440sub set_legend_fill {
441 $_[0]->{'custom_style'}->{'legend'}->{'fill'} = $_[1];
442}
443
444=item set_legend_border
445
446=cut
447
448sub set_legend_border {
449 $_[0]->{'custom_style'}->{'legend'}->{'border'} = $_[1];
450}
451
452=item set_legend_orientation
453
454=cut
455
456sub set_legend_orientation {
457 $_[0]->{'custom_style'}->{'legend'}->{'orientation'} = $_[1];
458}
459
460=item set_callout_font_color
461
462=cut
463
464sub set_callout_font_color {
465 $_[0]->{'custom_style'}->{'callout'}->{'color'} = $_[1];
466}
467
468=item set_callout_font
469
470=cut
471
472sub set_callout_font {
473 $_[0]->{'custom_style'}->{'callout'}->{'font'} = $_[1];
474}
475
476=item set_callout_font_size
477
478=cut
479
480sub set_callout_font_size {
481 $_[0]->{'custom_style'}->{'callout'}->{'size'} = $_[1];
482}
483
484=item set_callout_line_color
485
486=cut
487
488sub set_callout_line_color {
489 $_[0]->{'custom_style'}->{'callout'}->{'line'} = $_[1];
490}
491
492=item set_callout_leader_inside_length
493
494=cut
495
496sub set_callout_leader_inside_length {
497 $_[0]->{'custom_style'}->{'callout'}->{'inside'} = $_[1];
498}
499
500=item set_callout_leader_outside_length
501
502=cut
503
504sub set_callout_leader_outside_length {
505 $_[0]->{'custom_style'}->{'callout'}->{'outside'} = $_[1];
506}
507
508=item set_callout_leader_length
509
510=cut
511
512sub set_callout_leader_length {
513 $_[0]->{'custom_style'}->{'callout'}->{'leadlen'} = $_[1];
514}
515
516=item set_callout_gap
517
518=cut
519
520sub set_callout_gap {
521 $_[0]->{'custom_style'}->{'callout'}->{'gap'} = $_[1];
522}
523
524=item set_label_font_color
525
526=cut
527
528sub set_label_font_color {
529 $_[0]->{'custom_style'}->{'label'}->{'color'} = $_[1];
530}
531
532=item set_label_font
533
534=cut
535
536sub set_label_font {
537 $_[0]->{'custom_style'}->{'label'}->{'font'} = $_[1];
538}
539
540=item set_label_font_size
541
542=cut
543
544sub set_label_font_size {
545 $_[0]->{'custom_style'}->{'label'}->{'size'} = $_[1];
546}
547
548=item set_drop_shadow_fill_color
549
550=cut
551
552sub set_drop_shadow_fill_color {
553 $_[0]->{'custom_style'}->{'dropshadow'}->{'fill'} = $_[1];
554}
555
556=item set_drop_shadow_offset
557
558=cut
559
560sub set_drop_shadow_offset {
561 $_[0]->{'custom_style'}->{'dropshadow'}->{'off'} = $_[1];
562}
563
564=item set_drop_shadowXOffset
565
566=cut
567
568sub set_drop_shadowXOffset {
569 $_[0]->{'custom_style'}->{'dropshadow'}->{'offx'} = $_[1];
570}
571
572=item set_drop_shadowYOffset
573
574=cut
575
576sub set_drop_shadowYOffset {
577 $_[0]->{'custom_style'}->{'dropshadow'}->{'offy'} = $_[1];
578}
579
580=item set_drop_shadow_filter
581
582=cut
583
584sub set_drop_shadow_filter {
585 $_[0]->{'custom_style'}->{'dropshadow'}->{'filter'} = $_[1];
586}
587
588=item set_outline_color
589
590=cut
591
592sub set_outline_color {
593 $_[0]->{'custom_style'}->{'outline'}->{'line'} = $_[1];
594}
595
596=item set_data_area_fills
597
598=cut
599
600sub set_data_area_fills {
601 $_[0]->{'custom_style'}->{'fills'} = $_[1];
602}
603
604=item set_data_line_colors
605
606=cut
607
608sub set_data_line_colors {
609 $_[0]->{'custom_style'}->{'colors'} = $_[1];
610}
611
612=back
613
35574351
TC
614=head1 FEATURES
615
616Each graph type has a number of features. These are used to add
617various items that are displayed in the graph area. Some common
2eac77fc 618methods are:
619
620=over
621
622=item show_legend()
623
624adds a box containing boxes filled with the data filess, with
625the labels provided to the draw method. The legend will only be
626displayed if both the legend feature is enabled and labels are
627supplied.
628
629=cut
630
631sub show_legend {
632 $_[0]->{'custom_style'}->{'features'}->{'legend'} = 1;
633}
634
635=item show_outline()
636
637draws a border around the data areas.
638
639=cut
640
641sub show_outline {
642 $_[0]->{'custom_style'}->{'features'}->{'outline'} = 1;
643}
644
645=item show_labels()
646
647labels each data fill, usually by including text inside the data fill.
648If the text does not fit in the fill, they could be displayed in some
649other form, eg. as callouts in a pie graph. There usually isn't much
650point in including both labels and a legend.
651
652=cut
653
654sub show_labels {
655 $_[0]->{'custom_style'}->{'features'}->{'labels'} = 1;
656}
657
658=item show_drop_shadow()
659
660a simple drop shadow is shown behind some of the graph elements.
661
662=cut
663
664sub show_drop_shadow {
665 $_[0]->{'custom_style'}->{'features'}->{'dropshadow'} = 1;
666}
667
668=item reset_features()
669
670Unsets all of the features
671
672=cut
673
674sub reset_features {
675 $_[0]->{'custom_style'}->{'features'} = {};
676 $_[0]->{'custom_style'}->{'features'}->{'reset'} = 1;
677}
678
679=back
680
681Additionally, features can be set by passing them into the draw() method:
35574351
TC
682
683=over
684
685=item legend
686
687adds a box containing boxes filled with the data filess, with
688the labels provided to the draw method. The legend will only be
689displayed if both the legend feature is enabled and labels are
690supplied.
691
692=item labels
693
694labels each data fill, usually by including text inside the data fill.
695If the text does not fit in the fill, they could be displayed in some
696other form, eg. as callouts in a pie graph. There usually isn't much
697point in including both labels and a legend.
698
699=item dropshadow
700
701a simple drop shadow is shown behind some of the graph elements.
702
703=back
704
705Each graph also has features specific to that graph.
706
707=head1 COMMON PARAMETERS
708
709When referring to a single sub-value this documentation will refer to
710'title.color' rather than 'the color element of title'.
711
712Normally, except for the font parameter, these are controlled by
713styles, but these are the style parameters I'd mostly likely expect
714you want to use:
715
716=over
717
718=item font
719
720the Imager font object used to draw text on the chart.
721
722=item back
723
724the background fill for the graph. Default depends on the style.
725
726=item size
727
728the base size of the graph image. Default: 256
729
730=item width
731
732the width of the graph image. Default: 1.5 * size (384)
733
734=item height
735
736the height of the graph image. Default: size (256)
737
738=item channels
739
740the number of channels in the image. Default: 3 (the 'mono' style
741sets this to 1).
742
743=item line
744
745the color used for drawing lines, such as outlines or callouts.
746Default depends on the current style. Set to undef to remove the
747outline from a style.
748
749=item title
750
751the text used for a graph title. Default: no title. Note: this is
752the same as the title=>{ text => ... } field.
753
754=over
755
756=item halign
757
758horizontal alignment of the title in the graph, one of 'left',
759'center' or 'right'. Default: center
760
761=item valign
762
763vertical alignment of the title, one of 'top', 'center' or 'right'.
764Default: top. It's probably a bad idea to set this to 'center' unless
765you have a very short title.
766
767=back
768
769=item text
770
771This contains basic defaults used in drawing text.
772
773=over
774
775=item color
776
777the default color used for all text, defaults to the fg color.
778
779=item size
780
781the base size used for text, also used to scale many graph elements.
782Default: 14.
783
784=back
785
786=back
787
788=head1 BEYOND STYLES
789
790In most cases you will want to use just the styles, but you may want
791to exert more control over the way your chart looks. This section
792describes the options you can use to control the way your chart looks.
793
794Hopefully you don't need to read this.
795
796=over
797
798=item back
799
800The background of the graph.
801
802=item bg
803
804=item fg
805
806Used to define basic background and foreground colors for the graph.
807The bg color may be used for the background of the graph, and is used
808as a default for the background of hatcheed fills. The fg is used as
809the default for line and text colors.
810
811=item font
812
813The default font used by the graph. Normally you should supply this
814if your graph as any text.
815
816=item line
817
818The default line color.
819
820=item text
821
822defaults for drawing text. Other textual graph elements will inherit
823or modify these values.
824
825=over
826
827=item color
828
829default text color, defaults to the I<fg> color.
830
831=item size
832
833default text size. Default: 14. This is used to scale many graph
834elements, including padding and leader sizes. Other text elements
835will either use or scale this value.
836
837=item font
838
839default font object. Inherited from I<font>, which should have been
840supplied by the caller.
841
842=back
843
844=item title
845
846If you supply a scalar value for this element, it will be stored in
847the I<text> field.
848
849Defines the text, font and layout information for the title.
850
851=over
852
853=item color
854
855The color of the title, inherited from I<text.color>.
856
857=item font
858
859The font object used for the title, inherited from I<text.font>.
860
861=item size
862
863size of the title text. Default: double I<text.size>
864
865=item halign
866
867=item valign
868
869The horizontal and vertical alignment of the title.
870
871=back
872
873=item legend
874
875defines attributes of the graph legend, if present.
876
877=over
878
879=item color
880
881=item font
882
883=item size
884
885text attributes for the labels used in the legend.
886
887=item patchsize
888
889the width and height of the color patch in the legend. Defaults to
89090% of the legend text size.
891
892=item patchgap
893
894the minimum gap between patches in pixels. Defaults to 30% of the
895patchsize.
896
897=item patchborder
898
899the color of the border drawn around each patch. Inherited from I<line>.
900
901=item halign
902
903=item valign
904
905the horizontal and vertical alignment of the legend within the graph.
906Defaults to 'right' and 'top'.
907
908=item padding
909
910the gap between the legend patches and text and the outside of it's
911box, or to the legend border, if any.
912
913=item outsidepadding
914
915the gap between the border and the outside of the legend's box. This
916is only used if the I<legend.border> attribute is defined.
917
918=item fill
919
920the background fill for the legend. Default: none
921
922=item border
923
924the border color of the legend. Default: none (no border is drawn
925around the legend.)
926
33a928b7
TC
927=item orientation
928
929The orientation of the legend. If this is C<vertical> the the patches
930and labels are stacked on top of each other. If this is C<horizontal>
931the patchs and labels are word wrapped across the image. Default:
932vertical.
933
35574351
TC
934=back
935
33a928b7
TC
936For example to create a horizontal legend with borderless patches,
937darker than the background, you might do:
938
939 my $im = $chart->draw
940 (...,
941 legend =>
942 {
943 patchborder => undef,
944 orientation => 'horizontal',
945 fill => { solid => Imager::Color->new(0, 0, 0, 32), }
946 },
947 ...);
948
35574351
TC
949=item callout
950
951defines attributes for graph callouts, if any are present. eg. if the
952pie graph cannot fit the label into the pie graph segement it will
953present it as a callout.
954
955=over
956
957=item color
958
959=item font
960
961=item size
962
963the text attributes of the callout label. Inherited from I<text>.
964
965=item line
966
967the color of the callout lines. Inherited from I<line>
968
969=item inside
970
971=item outside
972
973the length of the leader on the inside and the outside of the fill,
974usually at some angle. Both default to the size of the callout text.
975
976=item leadlen
977
978the length of the horizontal portion of the leader. Default:
979I<callout.size>.
980
981=item gap
982
983the gap between the callout leader and the callout text. Defaults to
98430% of the text callout size.
985
986=back
987
988=item label
989
990defines attributes for labels drawn into the data areas of a graph.
991
992=over
993
994=item color
995
996=item font
997
998=item size
999
1000The text attributes of the labels. Inherited from I<text>.
1001
1002=back
1003
1004=item dropshadow
1005
1006the attributes of the graph's drop shadow
1007
1008=over
1009
1010=item fill
1011
1012the fill used for the drop shadow. Default: '404040' (dark gray)
1013
1014=item off
1015
1016the offset of the drop shadow. A convenience value inherited by offx
1017and offy. Default: 40% of I<text.size>.
1018
1019=item offx
1020
1021=item offy
1022
1023the horizontal and vertical offsets of the drop shadow. Both
1024inherited from I<dropshadow.off>.
1025
1026=item filter
1027
1028the filter description passed to Imager's filter method to blur the
1029drop shadow. Default: an 11 element convolution filter.
1030
1031=back
1032
1033=item outline
1034
1035describes the lines drawn around filled data areas, such as the
1036segments of a pie chart.
1037
1038=over
1039
1040=item line
1041
1042the line color of the outlines, inherited from I<line>.
1043
1044=back
1045
1046=item fills
1047
1048a reference to an array containing fills for each data item.
1049
1050You can mix fill types, ie. using a simple color for the first item, a
1051hatched fill for the second and a fountain fill for the next.
1052
1053=back
1054
1055=head1 HOW VALUES WORK
1056
1057Internally rather than specifying literal color, fill, or font objects
1058or literal sizes for each element, Imager::Graph uses a number of
1059special values to inherit or modify values taken from other graph
1060element names.
1061
1062=head2 Specifying colors
1063
1064You can specify colors by either supplying an Imager::Color object, by
1065supplying lookup of another color, or by supplying a single value that
1066Imager::Color::new can use as an initializer. The most obvious is
1067just a 6 or 8 digit hex value representing the red, green, blue and
1068optionally alpha channels of the image.
1069
1070You can lookup another color by using the lookup() "function", for
1071example if you give a color as "lookup(fg)" then Imager::Graph will
1072look for the fg element in the current style (or as overridden by
1073you.) This is used internally by Imager::Graph to set up the
1074relationships between the colors of various elements, for example the
1075default style information contains:
1076
1077 text=>{
d7fd5863 1078 color=>'lookup(fg)',
35574351
TC
1079 ...
1080 },
1081 legend =>{
d7fd5863 1082 color=>'lookup(text.color)',
35574351
TC
1083 ...
1084 },
1085
1086So by setting the I<fg> color, you also set the default text color,
1087since each text element uses lookup(text.color) as its value.
1088
1089=head2 Specifying fills
1090
1091Fills can be used for the graph background color, the background color
1092for the legend block and for the fills used for each data element.
1093
1094You can specify a fill as a L<color value|Specifying colors> or as a
33a928b7 1095general fill, see L<Imager::Fill> for details.
35574351
TC
1096
1097You don't need (or usually want) to call Imager::Fill::new yourself,
1098since the various fill functions will call it for you, and
1099Imager::Graph provides some hooks to make them more useful.
1100
1101=over
1102
1103=item *
1104
1105with hatched fills, if you don't supply a 'fg' or 'bg' parameter,
1106Imager::Graph will supply the current graph fg and bg colors.
1107
1108=item *
1109
1110with fountain fill, you can supply the xa_ratio, ya_ratio, xb_ratio
1111and yb_ratio parameters, and they will be scaled in the fill area to
1112define the fountain fills xa, ya, xb and yb parameters.
1113
1114=back
1115
1116As with colors, you can use lookup(name) or lookup(name1.name2) to
1117have one element to inherit the fill of another.
1118
33a928b7
TC
1119Imager::Graph defaults the fill combine value to C<'normal'>. This
1120doesn't apply to simple color fills.
1121
35574351
TC
1122=head2 Specifying numbers
1123
1124You can specify various numbers, usually representing the size of
1125something, commonly text, but sometimes the length of a line or the
1126size of a gap.
1127
1128You can use the same lookup mechanism as with colors and fills, but
1129you can also scale values. For example, 'scale(0.5,text.size)' will
1130return half the size of the normal text size.
1131
1132As with colors, this is used internally to scale graph elements based
1133on the base text size. If you change the base text size then other
1134graph elements will scale as well.
1135
1136=head2 Specifying other elements
1137
1138Other elements, such as fonts, or parameters for a filter, can also
1139use the lookup(name) mechanism.
1140
1141=head1 INTERNAL METHODS
1142
1143Only useful if you need to fix bugs, add features or create a new
1144graph class.
1145
1146=over
1147
1148=cut
1149
1150my %style_defs =
1151 (
1152 back=> 'lookup(bg)',
1153 line=> 'lookup(fg)',
49a35584 1154 aa => 1,
35574351 1155 text=>{
d7fd5863 1156 color => 'lookup(fg)',
35574351 1157 font => 'lookup(font)',
d7fd5863 1158 size => 14,
49a35584 1159 aa => 'lookup(aa)',
d7fd5863 1160 },
35574351 1161 title=>{
d7fd5863 1162 color => 'lookup(text.color)',
35574351 1163 font => 'lookup(text.font)',
d7fd5863
TC
1164 halign => 'center',
1165 valign => 'top',
1166 size => 'scale(text.size,2.0)',
49a35584 1167 aa => 'lookup(text.aa)',
d7fd5863 1168 },
35574351 1169 legend =>{
d7fd5863 1170 color => 'lookup(text.color)',
35574351 1171 font => 'lookup(text.font)',
49a35584 1172 aa => 'lookup(text.aa)',
d7fd5863
TC
1173 size => 'lookup(text.size)',
1174 patchsize => 'scale(legend.size,0.9)',
1175 patchgap => 'scale(legend.patchsize,0.3)',
1176 patchborder => 'lookup(line)',
1177 halign => 'right',
1178 valign => 'top',
1179 padding => 'scale(legend.size,0.3)',
1180 outsidepadding => 'scale(legend.padding,0.4)',
1181 },
35574351 1182 callout => {
d7fd5863 1183 color => 'lookup(text.color)',
35574351 1184 font => 'lookup(text.font)',
d7fd5863
TC
1185 size => 'lookup(text.size)',
1186 line => 'lookup(line)',
1187 inside => 'lookup(callout.size)',
1188 outside => 'lookup(callout.size)',
1189 leadlen => 'scale(0.8,callout.size)',
1190 gap => 'scale(callout.size,0.3)',
49a35584
TC
1191 aa => 'lookup(text.aa)',
1192 lineaa => 'lookup(lineaa)',
d7fd5863 1193 },
35574351
TC
1194 label => {
1195 font => 'lookup(text.font)',
d7fd5863
TC
1196 size => 'lookup(text.size)',
1197 color => 'lookup(text.color)',
35574351
TC
1198 hpad => 'lookup(label.pad)',
1199 vpad => 'lookup(label.pad)',
1200 pad => 'scale(label.size,0.2)',
1201 pcformat => sub { sprintf "%s (%.0f%%)", $_[0], $_[1] },
1202 pconlyformat => sub { sprintf "%.1f%%", $_[0] },
49a35584
TC
1203 aa => 'lookup(text.aa)',
1204 lineaa => 'lookup(lineaa)',
d7fd5863 1205 },
35574351 1206 dropshadow => {
320f5a49 1207 fill => { solid => Imager::Color->new(0, 0, 0, 96) },
35574351
TC
1208 off => 'scale(0.4,text.size)',
1209 offx => 'lookup(dropshadow.off)',
1210 offy => 'lookup(dropshadow.off)',
1211 filter => { type=>'conv',
1212 # this needs a fairly heavy blur
1213 coef=>[0.1, 0.2, 0.4, 0.6, 0.7, 0.9, 1.2,
1214 0.9, 0.7, 0.6, 0.4, 0.2, 0.1 ] },
1215 },
1216 outline => {
1217 line =>'lookup(line)',
49a35584 1218 lineaa => 'lookup(lineaa)',
35574351
TC
1219 },
1220 size=>256,
1221 width=>'scale(1.5,size)',
1222 height=>'lookup(size)',
49a35584
TC
1223
1224 # yes, the handling of fill and line AA is inconsistent, lack of
1225 # forethought, unfortunately
1226 fill => {
1227 aa => 'lookup(aa)',
1228 },
1229 lineaa => 'lookup(aa)',
35574351
TC
1230 );
1231
1232=item _error($message)
1233
1234Sets the error field of the object and returns an empty list or undef,
1235depending on context. Should be used for error handling, since it may
1236provide some user hooks at some point.
1237
f68db40f
TC
1238The intended usage is:
1239
1240 some action
1241 or return $self->_error("error description");
1242
1243You should almost always return the result of _error() or return
1244immediately afterwards.
1245
35574351
TC
1246=cut
1247
1248sub _error {
1249 my ($self, $error) = @_;
1250
1251 $self->{_errstr} = $error;
1252
1253 return;
1254}
1255
1256
1257=item _style_defs()
1258
1259Returns the style defaults, such as the relationships between line
1260color and text color.
1261
1262Intended to be over-ridden by base classes to provide graph specific
1263defaults.
1264
1265=cut
1266
1267sub _style_defs {
1268 \%style_defs;
1269}
1270
81453d28 1271# Let's make the default something that looks really good, so folks will be interested enough to customize the style.
1272my $def_style = 'fount_lin';
35574351
TC
1273
1274my %styles =
1275 (
7b94e723
TC
1276 primary =>
1277 {
1278 fills=>
1279 [
1280 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1281 ],
1282 fg=>'000000',
2eac77fc 1283 negative_bg=>'EEEEEE',
7b94e723
TC
1284 bg=>'E0E0E0',
1285 legend=>
1286 {
1287 #patchborder=>'000000'
1288 },
1289 },
35574351
TC
1290 primary_red =>
1291 {
1292 fills=>
1293 [
1294 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1295 ],
1296 fg=>'000000',
2eac77fc 1297 negative_bg=>'EEEEEE',
35574351
TC
1298 bg=>'C08080',
1299 legend=>
1300 {
1301 patchborder=>'000000'
1302 },
1303 },
1304 mono =>
1305 {
1306 fills=>
1307 [
1308 { hatch=>'slash2' },
1309 { hatch=>'slosh2' },
1310 { hatch=>'vline2' },
1311 { hatch=>'hline2' },
1312 { hatch=>'cross2' },
1313 { hatch=>'grid2' },
1314 { hatch=>'stipple3' },
1315 { hatch=>'stipple2' },
1316 ],
1317 channels=>1,
1318 bg=>'FFFFFF',
1319 fg=>'000000',
2eac77fc 1320 negative_bg=>'EEEEEE',
35574351
TC
1321 features=>{ outline=>1 },
1322 pie =>{
1323 blur=>undef,
1324 },
49a35584 1325 aa => 0,
35574351 1326 },
bb0de914 1327 fount_lin =>
35574351
TC
1328 {
1329 fills=>
1330 [
1331 { fountain=>'linear',
1332 xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
35574351 1333 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1334 colors=>[ NC('FFC0C0'), NC('FF0000') ]),
35574351
TC
1335 },
1336 { fountain=>'linear',
1337 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1338 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1339 colors=>[ NC('C0FFC0'), NC('00FF00') ]),
35574351
TC
1340 },
1341 { fountain=>'linear',
1342 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1343 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1344 colors=>[ NC('C0C0FF'), NC('0000FF') ]),
35574351
TC
1345 },
1346 { fountain=>'linear',
1347 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1348 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1349 colors=>[ NC('FFFFC0'), NC('FFFF00') ]),
35574351
TC
1350 },
1351 { fountain=>'linear',
1352 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1353 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1354 colors=>[ NC('C0FFFF'), NC('00FFFF') ]),
35574351
TC
1355 },
1356 { fountain=>'linear',
1357 xa_ratio=>0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1358 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1359 colors=>[ NC('FFC0FF'), NC('FF00FF') ]),
35574351
TC
1360 },
1361 ],
dfd889da 1362 colors => [
1363 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1364 ],
8e140388
TC
1365 line_markers =>[
1366 { shape => 'circle', radius => 4 },
1367 { shape => 'square', radius => 4 },
1368 { shape => 'diamond', radius => 4 },
1369 { shape => 'triangle', radius => 4 },
1370 { shape => 'x', radius => 4 },
1371 { shape => 'plus', radius => 4 },
1372 ],
35574351
TC
1373 back=>{ fountain=>'linear',
1374 xa_ratio=>0, ya_ratio=>0,
1375 xb_ratio=>1.0, yb_ratio=>1.0,
1376 segments=>Imager::Fountain->simple
1377 ( positions=>[0, 1],
1378 colors=>[ NC('6060FF'), NC('60FF60') ]) },
1379 fg=>'000000',
2eac77fc 1380 negative_bg=>'EEEEEE',
35574351
TC
1381 bg=>'FFFFFF',
1382 features=>{ dropshadow=>1 },
bb0de914
TC
1383 },
1384 fount_rad =>
1385 {
35574351
TC
1386 fills=>
1387 [
1388 { fountain=>'radial',
1389 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1390 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1391 colors=>[ NC('FF8080'), NC('FF0000') ]),
35574351
TC
1392 },
1393 { fountain=>'radial',
1394 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1395 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1396 colors=>[ NC('80FF80'), NC('00FF00') ]),
35574351
TC
1397 },
1398 { fountain=>'radial',
1399 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1400 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1401 colors=>[ NC('808080FF'), NC('0000FF') ]),
35574351
TC
1402 },
1403 { fountain=>'radial',
1404 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1405 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1406 colors=>[ NC('FFFF80'), NC('FFFF00') ]),
35574351
TC
1407 },
1408 { fountain=>'radial',
1409 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1410 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1411 colors=>[ NC('80FFFF'), NC('00FFFF') ]),
35574351
TC
1412 },
1413 { fountain=>'radial',
1414 xa_ratio=>0.5, ya_ratio=>0.5, xb_ratio=>1.0, yb_ratio=>0.5,
1415 segments => Imager::Fountain->simple(positions=>[0, 1],
d7fd5863 1416 colors=>[ NC('FF80FF'), NC('FF00FF') ]),
35574351
TC
1417 },
1418 ],
dfd889da 1419 colors => [
1420 qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
1421 ],
35574351
TC
1422 back=>{ fountain=>'linear',
1423 xa_ratio=>0, ya_ratio=>0,
1424 xb_ratio=>1.0, yb_ratio=>1.0,
1425 segments=>Imager::Fountain->simple
1426 ( positions=>[0, 1],
1427 colors=>[ NC('6060FF'), NC('60FF60') ]) },
1428 fg=>'000000',
2eac77fc 1429 negative_bg=>'EEEEEE',
35574351 1430 bg=>'FFFFFF',
bb0de914
TC
1431 }
1432 );
35574351 1433
2eac77fc 1434$styles{'ocean'} = {
1435 fills => [
1436 {
1437 fountain =>'linear',
1438 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1439 segments => Imager::Fountain->simple(
1440 positions=>[0, 1],
7422650e 1441 colors=>[ NC('EFEDCF'), NC('E6E2AF') ]),
2eac77fc 1442 },
1443 {
1444 fountain =>'linear',
1445 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1446 segments => Imager::Fountain->simple(
1447 positions=>[0, 1],
7422650e 1448 colors=>[ NC('DCD7AB'), NC('A7A37E') ]),
2eac77fc 1449 },
1450 {
1451 fountain =>'linear',
1452 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1453 segments => Imager::Fountain->simple(
1454 positions=>[0, 1],
7422650e 1455 colors=>[ NC('B2E5D4'), NC('80B4A2') ]),
2eac77fc 1456 },
1457 {
1458 fountain =>'linear',
1459 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1460 segments => Imager::Fountain->simple(
1461 positions=>[0, 1],
7422650e 1462 colors=>[ NC('7aaab9'), NC('046380') ]),
2eac77fc 1463 },
1464 {
1465 fountain =>'linear',
1466 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1467 segments => Imager::Fountain->simple(
1468 positions=>[0, 1],
7422650e 1469 colors=>[ NC('c3b8e9'), NC('877EA7') ]),
2eac77fc 1470 },
1471 {
1472 fountain =>'linear',
1473 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1474 segments => Imager::Fountain->simple(
1475 positions=>[0, 1],
7422650e 1476 colors=>[ NC('A3DF9A'), NC('67A35E') ]),
2eac77fc 1477 },
1478 {
1479 fountain =>'linear',
1480 xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
1481 segments => Imager::Fountain->simple(
1482 positions=>[0, 1],
7422650e 1483 colors=>[ NC('E19C98'), NC('B4726F') ]),
2eac77fc 1484 },
1485 ],
1486 colors => [
1487 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1488 ],
1489 fg=>'000000',
1490 negative_bg=>'EEEEEE',
1491 bg=>'FFFFFF',
1492 features=>{ dropshadow=>1 },
1493
1494};
1495
7422650e 1496$styles{'ocean_flat'} = {
1497 fills=>
1498 [
1499 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1500 ],
1501 colors => [
1502 qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
1503 ],
1504 fg=>'000000',
1505 negative_bg=>'EEEEEE',
1506 bg=>'FFFFFF',
1507 features=>{ dropshadow=>1 },
1508
1509};
1510
1511
35574351
TC
1512=item $self->_style_setup(\%opts)
1513
1514Uses the values from %opts to build a customized hash describing the
1515way the graph should be drawn.
1516
1517=cut
1518
1519sub _style_setup {
1520 my ($self, $opts) = @_;
1521 my $style_defs = $self->_style_defs;
1522 my $style;
dfd889da 1523
2eac77fc 1524 my $pre_def_style = $self->_get_style($opts);
1525 my $api_style = $self->{'custom_style'} || {};
dfd889da 1526 $style = $styles{$pre_def_style} if $pre_def_style;
1527
35574351
TC
1528 $style ||= $styles{$def_style};
1529
2eac77fc 1530 my @search_list = ( $style_defs, $style, $api_style, $opts);
35574351
TC
1531 my %work;
1532
1533 my @composite = $self->_composite();
1534 my %composite;
1535 @composite{@composite} = @composite;
1536
1537 for my $src (@search_list) {
1538 for my $key (keys %$src) {
1539 if ($composite{$key}) {
1540 $work{$key} = {} unless exists $work{$key};
1541 if (ref $src->{$key}) {
1542 # some keys have sub values, especially text
1543 @{$work{$key}}{keys %{$src->{$key}}} = values %{$src->{$key}};
1544 }
1545 else {
1546 # assume it's the text for a title or something
1547 $work{$key}{text} = $src->{$key};
1548 }
1549 }
1550 else {
a17e870a
TC
1551 $work{$key} = $src->{$key}
1552 if defined $src->{$key}; # $opts with pmichauds new accessor handling
35574351
TC
1553 }
1554 }
1555 }
1556
1557 # features are handled specially
1558 $work{features} = {};
1559 for my $src (@search_list) {
1560 if ($src->{features}) {
1561 if (ref $src->{features}) {
1562 if (ref($src->{features}) =~ /ARRAY/) {
1563 # just set those features
1564 for my $feature (@{$src->{features}}) {
1565 $work{features}{$feature} = 1;
1566 }
1567 }
1568 elsif (ref($src->{features}) =~ /HASH/) {
1569 if ($src->{features}{reset}) {
1570 $work{features} = {}; # only the ones the user specifies
1571 }
1572 @{$work{features}}{keys %{$src->{features}}} =
1573 values(%{$src->{features}});
1574 }
1575 }
1576 else {
1577 # just set that single feature
1578 $work{features}{$src->{features}} = 1;
1579 }
1580 }
1581 }
1582 #use Data::Dumper;
1583 #print Dumper(\%work);
1584
1585 $self->{_style} = \%work;
1586}
1587
1588=item $self->_get_thing($name)
1589
1590Retrieve some general 'thing'.
1591
1592Supports the 'lookup(foo)' mechanism.
1593
f68db40f
TC
1594Returns an empty list on failure.
1595
35574351
TC
1596=cut
1597
1598sub _get_thing {
1599 my ($self, $name, @depth) = @_;
1600
1601 push(@depth, $name);
1602 my $what;
1603 if ($name =~ /^(\w+)\.(\w+)$/) {
1604 $what = $self->{_style}{$1}{$2};
1605 }
1606 else {
1607 $what = $self->{_style}{$name};
1608 }
1609 defined $what or
1610 return;
1611 if (ref $what) {
1612 return $what;
1613 }
1614 elsif ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1615 @depth < MAX_DEPTH
1616 or return $self->_error("too many levels of recursion in lookup(@depth)");
1617 return $self->_get_thing($1, @depth);
1618 }
1619 else {
1620 return $what;
1621 }
1622}
1623
1624=item $self->_get_number($name)
1625
1626Retrieves a number from the style. The value in the style can be the
1627number, or one of two functions:
1628
1629=over
1630
1631=item lookup(newname)
1632
1633Recursively looks up I<newname> in the style.
1634
1635=item scale(value1,value2)
1636
33a928b7 1637Each value can be a number or a name. Names are recursively looked up
35574351
TC
1638in the style and the product is returned.
1639
1640=back
1641
1642=cut
bb0de914 1643
35574351
TC
1644sub _get_number {
1645 my ($self, $name, @depth) = @_;
1646
1647 push(@depth, $name);
1648 my $what;
1649 if ($name =~ /^(\w+)\.(\w+)$/) {
1650 $what = $self->{_style}{$1}{$2};
1651 }
1652 else {
1653 $what = $self->{_style}{$name};
1654 }
1655 defined $what or
1656 return $self->_error("$name is undef (@depth)");
1657
1658 if (ref $what) {
1659 if ($what =~ /CODE/) {
1660 $what = $what->($self, $name);
1661 }
1662 }
1663 else {
1664 if ($what =~ /^lookup\(([\w.]+)\)$/) {
1665 @depth < MAX_DEPTH
d7fd5863 1666 or return $self->_error("too many levels of recursion in lookup (@depth)");
35574351
TC
1667 return $self->_get_number($1, @depth);
1668 }
1669 elsif ($what =~ /^scale\(
d7fd5863 1670 ((?:[a-z][\w.]*)|$NUM_RE)
35574351 1671 ,
d7fd5863 1672 ((?:[a-z][\w.]*)|$NUM_RE)\)$/x) {
35574351
TC
1673 my ($left, $right) = ($1, $2);
1674 unless ($left =~ /^$NUM_RE$/) {
d7fd5863
TC
1675 @depth < MAX_DEPTH
1676 or return $self->_error("too many levels of recursion in scale (@depth)");
1677 $left = $self->_get_number($left, @depth);
35574351
TC
1678 }
1679 unless ($right =~ /^$NUM_RE$/) {
d7fd5863
TC
1680 @depth < MAX_DEPTH
1681 or return $self->_error("too many levels of recursion in scale (@depth)");
1682 $right = $self->_get_number($right, @depth);
35574351
TC
1683 }
1684 return $left * $right;
1685 }
1686 else {
1687 return $what+0;
1688 }
1689 }
1690}
1691
379c5b02
TC
1692=item $self->_get_integer($name)
1693
1694Retrieves an integer from the style. This is a simple wrapper around
1695_get_number() that rounds the result to an integer.
1696
f68db40f
TC
1697Returns an empty list on failure.
1698
379c5b02
TC
1699=cut
1700
1701sub _get_integer {
1702 my ($self, $name, @depth) = @_;
1703
1704 my $number = $self->_get_number($name, @depth)
1705 or return;
1706
1707 return sprintf("%.0f", $number);
1708}
1709
35574351
TC
1710=item _get_color($name)
1711
1712Returns a color object of the given name from the style hash.
1713
1714Uses Imager::Color->new to translate normal scalars into color objects.
1715
1716Allows the lookup(name) mechanism.
1717
f68db40f
TC
1718Returns an empty list on failure.
1719
35574351
TC
1720=cut
1721
1722sub _get_color {
1723 my ($self, $name, @depth) = @_;
1724
1725 push(@depth, $name);
1726 my $what;
1727 if ($name =~ /^(\w+)\.(\w+)$/) {
1728 $what = $self->{_style}{$1}{$2};
1729 }
1730 else {
1731 $what = $self->{_style}{$name};
1732 }
1733
1734 defined($what)
1735 or return $self->_error("$name was undefined (@depth)");
1736
1737 unless (ref $what) {
1738 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1739 @depth < MAX_DEPTH or
d7fd5863 1740 return $self->_error("too many levels of recursion in lookup (@depth)");
35574351
TC
1741
1742 return $self->_get_color($1, @depth);
1743 }
1744 $what = Imager::Color->new($what);
1745 }
1746
1747 $what;
1748}
1749
1750=item _translate_fill($what, $box)
1751
1752Given the value of a fill, either attempts to convert it into a fill
1753list (one of C<<color=>$color_value, filled=>1>> or C<<fill=>{ fill
1754parameters }>>), or to lookup another fill that is referred to with
1755the 'lookup(name)' mechanism.
1756
1757This function does the fg and bg initialization for hatched fills, and
1758translation of *_ratio for fountain fills (using the $box parameter).
1759
f68db40f
TC
1760Returns an empty list on failure.
1761
35574351
TC
1762=cut
1763
1764sub _translate_fill {
1765 my ($self, $what, $box, @depth) = @_;
1766
1767 if (ref $what) {
1768 if (UNIVERSAL::isa($what, "Imager::Color")) {
1769 return ( color=>Imager::Color->new($what), filled=>1 );
1770 }
1771 else {
1772 # a general fill
33a928b7
TC
1773 # default to normal combine mode
1774 my %work = ( combine => 'normal', %$what );
35574351 1775 if ($what->{hatch}) {
d7fd5863
TC
1776 if (!$work{fg}) {
1777 $work{fg} = $self->_get_color('fg')
1778 or return;
1779 }
1780 if (!$work{bg}) {
1781 $work{bg} = $self->_get_color('bg')
1782 or return;
1783 }
1784 return ( fill=>\%work );
35574351
TC
1785 }
1786 elsif ($what->{fountain}) {
d7fd5863
TC
1787 for my $key (qw(xa ya xb yb)) {
1788 if (exists $work{"${key}_ratio"}) {
1789 if ($key =~ /^x/) {
1790 $work{$key} = $box->[0] + $work{"${key}_ratio"}
1791 * ($box->[2] - $box->[0]);
1792 }
1793 else {
1794 $work{$key} = $box->[1] + $work{"${key}_ratio"}
1795 * ($box->[3] - $box->[1]);
1796 }
1797 }
1798 }
1799 return ( fill=>\%work );
35574351
TC
1800 }
1801 else {
d7fd5863 1802 return ( fill=> \%work );
35574351
TC
1803 }
1804 }
1805 }
1806 else {
1807 if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
1808 return $self->_get_fill($1, $box, @depth);
1809 }
1810 else {
1811 # assumed to be an Imager::Color single value
1812 return ( color=>Imager::Color->new($what), filled=>1 );
1813 }
1814 }
1815}
1816
1817=item _data_fill($index, $box)
1818
1819Retrieves the fill parameters for a data area fill.
1820
1821=cut
1822
1823sub _data_fill {
1824 my ($self, $index, $box) = @_;
1825
1826 my $fills = $self->{_style}{fills};
1827 return $self->_translate_fill($fills->[$index % @$fills], $box,
1828 "data.$index");
1829}
1830
dfd889da 1831sub _data_color {
1832 my ($self, $index) = @_;
1833
1834 my $colors = $self->{'_style'}{'colors'} || [];
1835 my $fills = $self->{'_style'}{'fills'} || [];
1836
1837 # Try to just use a fill, so non-fountain styles don't need
1838 # to have a duplicated set of fills and colors
1839 my $fill = $fills->[$index % @$fills];
1840 if (!ref $fill) {
1841 return $fill;
1842 }
1843
1844 if (@$colors) {
1845 return $colors->[$index % @$colors] || '000000';
1846 }
1847 return '000000';
1848}
1849
35574351
TC
1850=item _get_fill($index, $box)
1851
1852Retrieves fill parameters for a named fill.
1853
1854=cut
1855
1856sub _get_fill {
1857 my ($self, $name, $box, @depth) = @_;
1858
1859 push(@depth, $name);
1860 my $what;
1861 if ($name =~ /^(\w+)\.(\w+)$/) {
1862 $what = $self->{_style}{$1}{$2};
1863 }
1864 else {
1865 $what = $self->{_style}{$name};
1866 }
1867
1868 defined($what)
1869 or return $self->_error("no fill $name found");
1870
1871 return $self->_translate_fill($what, $box, @depth);
1872}
1873
1874=item _make_img()
1875
1876Builds the image object for the graph and fills it with the background
1877fill.
1878
1879=cut
1880
1881sub _make_img {
1882 my ($self) = @_;
35574351 1883
81453d28 1884 my $width = $self->_get_number('width') || 256;
1885 my $height = $self->_get_number('height') || 256;
35574351
TC
1886 my $channels = $self->{_style}{channels};
1887
1888 $channels ||= 3;
1889
1890 my $img = Imager->new(xsize=>$width, ysize=>$height, channels=>$channels);
1891
1892 $img->box($self->_get_fill('back', [ 0, 0, $width-1, $height-1]));
1893
1894 $img;
1895}
1896
2eac77fc 1897sub _get_image {
1898 my $self = shift;
1899
1900 if (!$self->{'_image'}) {
1901 $self->{'_image'} = $self->_make_img();
1902 }
1903 return $self->{'_image'};
1904}
1905
f68db40f
TC
1906=item _text_style($name)
1907
1908Returns parameters suitable for calls to Imager::Font's bounding_box()
1909and draw() methods intended for use in defining text styles.
1910
1911Returns an empty list on failure.
1912
49a35584
TC
1913Returns the following attributes: font, color, size, aa, sizew
1914(optionally)
1915
f68db40f
TC
1916=cut
1917
35574351
TC
1918sub _text_style {
1919 my ($self, $name) = @_;
1920
1921 my %work;
1922
1923 if ($self->{_style}{$name}) {
1924 %work = %{$self->{_style}{$name}};
1925 }
1926 else {
1927 %work = %{$self->{_style}{text}};
1928 }
1929 $work{font}
5d622bb8 1930 or return $self->_error("$name has no font parameter");
35574351
TC
1931
1932 $work{font} = $self->_get_thing("$name.font")
5d622bb8 1933 or return $self->_error("No $name.font defined, either set $name.font or font to a font");
35574351
TC
1934 UNIVERSAL::isa($work{font}, "Imager::Font")
1935 or return $self->_error("$name.font is not a font");
1936 if ($work{color} && !ref $work{color}) {
1937 $work{color} = $self->_get_color("$name.color")
1938 or return;
1939 }
1940 $work{size} = $self->_get_number("$name.size");
1941 $work{sizew} = $self->_get_number("$name.sizew")
1942 if $work{sizew};
49a35584 1943 $work{aa} = $self->_get_number("$name.aa");
35574351
TC
1944
1945 %work;
1946}
1947
f68db40f
TC
1948=item _text_bbox($text, $name)
1949
1950Returns a bounding box for the specified $text as styled by $name.
1951
1952Returns an empty list on failure.
1953
1954=cut
1955
35574351
TC
1956sub _text_bbox {
1957 my ($self, $text, $name) = @_;
1958
5d622bb8
TC
1959 my %text_info = $self->_text_style($name)
1960 or return;
35574351
TC
1961
1962 my @bbox = $text_info{font}->bounding_box(%text_info, string=>$text,
d7fd5863 1963 canon=>1);
35574351
TC
1964
1965 return @bbox[0..3];
1966}
1967
49a35584
TC
1968=item _line_style($name)
1969
1970Return parameters suitable for calls to Imager's line(), polyline(),
1971and box() methods.
1972
1973For now this returns only color and aa parameters, but future releases
1974of Imager may support extra parameters.
1975
1976=cut
1977
1978sub _line_style {
1979 my ($self, $name) = @_;
1980
1981 my %line;
1982 $line{color} = $self->_get_color("$name.line")
1983 or return;
1984 $line{aa} = $self->_get_number("$name.lineaa");
1985 defined $line{aa} or $line{aa} = $self->_get_number("aa");
1986
1987 return %line;
1988}
1989
35574351
TC
1990sub _align_box {
1991 my ($self, $box, $chart_box, $name) = @_;
1992
1993 my $halign = $self->{_style}{$name}{halign}
1994 or $self->_error("no halign for $name");
1995 my $valign = $self->{_style}{$name}{valign};
1996
1997 if ($halign eq 'right') {
1998 $box->[0] += $chart_box->[2] - $box->[2];
1999 }
2000 elsif ($halign eq 'left') {
2001 $box->[0] = $chart_box->[0];
2002 }
2003 elsif ($halign eq 'center' || $halign eq 'centre') {
2004 $box->[0] = ($chart_box->[0] + $chart_box->[2] - $box->[2])/2;
2005 }
2006 else {
2007 return $self->_error("invalid halign $halign for $name");
2008 }
2009
2010 if ($valign eq 'top') {
2011 $box->[1] = $chart_box->[1];
2012 }
2013 elsif ($valign eq 'bottom') {
2014 $box->[1] = $chart_box->[3] - $box->[3];
2015 }
2016 elsif ($valign eq 'center' || $valign eq 'centre') {
2017 $box->[1] = ($chart_box->[1] + $chart_box->[3] - $box->[3])/2;
2018 }
2019 else {
2020 return $self->_error("invalid valign $valign for $name");
2021 }
2022 $box->[2] += $box->[0];
2023 $box->[3] += $box->[1];
2024}
2025
2026sub _remove_box {
2027 my ($self, $chart_box, $object_box) = @_;
2028
2029 my $areax;
2030 my $areay;
2031 if ($object_box->[0] - $chart_box->[0]
2032 < $chart_box->[2] - $object_box->[2]) {
2033 $areax = ($object_box->[2] - $chart_box->[0])
2034 * ($chart_box->[3] - $chart_box->[1]);
2035 }
2036 else {
2037 $areax = ($chart_box->[2] - $object_box->[0])
2038 * ($chart_box->[3] - $chart_box->[1]);
2039 }
2040
2041 if ($object_box->[1] - $chart_box->[1]
2042 < $chart_box->[3] - $object_box->[3]) {
2043 $areay = ($object_box->[3] - $chart_box->[1])
2044 * ($chart_box->[2] - $chart_box->[0]);
2045 }
2046 else {
2047 $areay = ($chart_box->[3] - $object_box->[1])
2048 * ($chart_box->[2] - $chart_box->[0]);
2049 }
2050
2051 if ($areay < $areax) {
2052 if ($object_box->[1] - $chart_box->[1]
d7fd5863 2053 < $chart_box->[3] - $object_box->[3]) {
35574351
TC
2054 $chart_box->[1] = $object_box->[3];
2055 }
2056 else {
2057 $chart_box->[3] = $object_box->[1];
2058 }
2059 }
2060 else {
2061 if ($object_box->[0] - $chart_box->[0]
d7fd5863 2062 < $chart_box->[2] - $object_box->[2]) {
35574351
TC
2063 $chart_box->[0] = $object_box->[2];
2064 }
2065 else {
2066 $chart_box->[2] = $object_box->[0];
2067 }
2068 }
2069}
2070
2071sub _draw_legend {
2072 my ($self, $img, $labels, $chart_box) = @_;
2073
33a928b7
TC
2074 my $orient = $self->_get_thing('legend.orientation');
2075 defined $orient or $orient = 'vertical';
2076
2077 if ($orient eq 'vertical') {
2078 return $self->_draw_legend_vertical($img, $labels, $chart_box);
2079 }
2080 elsif ($orient eq 'horizontal') {
2081 return $self->_draw_legend_horizontal($img, $labels, $chart_box);
2082 }
2083 else {
2084 return $self->_error("Unknown legend.orientation $orient");
2085 }
2086}
2087
2088sub _draw_legend_horizontal {
2089 my ($self, $img, $labels, $chart_box) = @_;
2090
2091 defined(my $padding = $self->_get_integer('legend.padding'))
2092 or return;
2093 my $patchsize = $self->_get_integer('legend.patchsize')
2094 or return;
2095 defined(my $gap = $self->_get_integer('legend.patchgap'))
2096 or return;
2097
2098 my $minrowsize = $patchsize + $gap;
2099 my ($width, $height) = (0,0);
2100 my $row_height = $minrowsize;
2101 my $pos = 0;
2102 my @sizes;
2103 my @offsets;
2104 for my $label (@$labels) {
5d622bb8
TC
2105 my @text_box = $self->_text_bbox($label, 'legend')
2106 or return;
33a928b7
TC
2107 push(@sizes, \@text_box);
2108 my $entry_width = $patchsize + $gap + $text_box[2];
2109 if ($pos == 0) {
2110 # never re-wrap the first entry
2111 push @offsets, [ 0, $height ];
2112 }
2113 else {
2114 if ($pos + $gap + $entry_width > $chart_box->[2]) {
d7fd5863
TC
2115 $pos = 0;
2116 $height += $row_height;
33a928b7
TC
2117 }
2118 push @offsets, [ $pos, $height ];
2119 }
2120 my $entry_right = $pos + $entry_width;
2121 $pos += $gap + $entry_width;
2122 $entry_right > $width and $width = $entry_right;
2123 if ($text_box[3] > $row_height) {
2124 $row_height = $text_box[3];
2125 }
2126 }
2127 $height += $row_height;
2128 my @box = ( 0, 0, $width + $padding * 2, $height + $padding * 2 );
2129 my $outsidepadding = 0;
2130 if ($self->{_style}{legend}{border}) {
2131 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
2132 or return;
2133 $box[2] += 2 * $outsidepadding;
2134 $box[3] += 2 * $outsidepadding;
2135 }
2136 $self->_align_box(\@box, $chart_box, 'legend')
2137 or return;
2138 if ($self->{_style}{legend}{fill}) {
2139 $img->box(xmin=>$box[0]+$outsidepadding,
2140 ymin=>$box[1]+$outsidepadding,
2141 xmax=>$box[2]-$outsidepadding,
2142 ymax=>$box[3]-$outsidepadding,
d7fd5863 2143 $self->_get_fill('legend.fill', \@box));
33a928b7
TC
2144 }
2145 $box[0] += $outsidepadding;
2146 $box[1] += $outsidepadding;
2147 $box[2] -= $outsidepadding;
2148 $box[3] -= $outsidepadding;
2149 my %text_info = $self->_text_style('legend')
2150 or return;
2151 my $patchborder;
2152 if ($self->{_style}{legend}{patchborder}) {
2153 $patchborder = $self->_get_color('legend.patchborder')
2154 or return;
2155 }
2156
2157 my $dataindex = 0;
2158 for my $label (@$labels) {
2159 my ($left, $top) = @{$offsets[$dataindex]};
2160 $left += $box[0] + $padding;
2161 $top += $box[1] + $padding;
2162 my $textpos = $left + $patchsize + $gap;
2163 my @patchbox = ( $left, $top,
2164 $left + $patchsize, $top + $patchsize );
2165 my @fill = $self->_data_fill($dataindex, \@patchbox)
2166 or return;
2167 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
d7fd5863 2168 ymax=>$top + $patchsize, @fill);
33a928b7
TC
2169 if ($self->{_style}{legend}{patchborder}) {
2170 $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
d7fd5863
TC
2171 ymax=>$top + $patchsize,
2172 color=>$patchborder);
33a928b7
TC
2173 }
2174 $img->string(%text_info, x=>$textpos, 'y'=>$top + $patchsize,
2175 text=>$label);
2176
2177 ++$dataindex;
2178 }
2179 if ($self->{_style}{legend}{border}) {
2180 my $border_color = $self->_get_color('legend.border')
2181 or return;
2182 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
d7fd5863 2183 color=>$border_color);
33a928b7
TC
2184 }
2185 $self->_remove_box($chart_box, \@box);
2186 1;
2187}
2188
2189sub _draw_legend_vertical {
2190 my ($self, $img, $labels, $chart_box) = @_;
2191
379c5b02 2192 defined(my $padding = $self->_get_integer('legend.padding'))
35574351 2193 or return;
379c5b02 2194 my $patchsize = $self->_get_integer('legend.patchsize')
35574351 2195 or return;
379c5b02 2196 defined(my $gap = $self->_get_integer('legend.patchgap'))
35574351
TC
2197 or return;
2198 my $minrowsize = $patchsize + $gap;
2199 my ($width, $height) = (0,0);
2200 my @sizes;
2201 for my $label (@$labels) {
5d622bb8
TC
2202 my @box = $self->_text_bbox($label, 'legend')
2203 or return;
35574351
TC
2204 push(@sizes, \@box);
2205 $width = $box[2] if $box[2] > $width;
2206 if ($minrowsize > $box[3]) {
2207 $height += $minrowsize;
2208 }
2209 else {
2210 $height += $box[3];
2211 }
2212 }
2213 my @box = (0, 0,
d7fd5863
TC
2214 $width + $patchsize + $padding * 2 + $gap,
2215 $height + $padding * 2 - $gap);
35574351
TC
2216 my $outsidepadding = 0;
2217 if ($self->{_style}{legend}{border}) {
33a928b7 2218 defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
35574351
TC
2219 or return;
2220 $box[2] += 2 * $outsidepadding;
2221 $box[3] += 2 * $outsidepadding;
2222 }
2223 $self->_align_box(\@box, $chart_box, 'legend')
2224 or return;
2225 if ($self->{_style}{legend}{fill}) {
2226 $img->box(xmin=>$box[0]+$outsidepadding,
2227 ymin=>$box[1]+$outsidepadding,
2228 xmax=>$box[2]-$outsidepadding,
2229 ymax=>$box[3]-$outsidepadding,
d7fd5863 2230 $self->_get_fill('legend.fill', \@box));
35574351
TC
2231 }
2232 $box[0] += $outsidepadding;
2233 $box[1] += $outsidepadding;
2234 $box[2] -= $outsidepadding;
2235 $box[3] -= $outsidepadding;
2236 my $ypos = $box[1] + $padding;
2237 my $patchpos = $box[0]+$padding;
2238 my $textpos = $patchpos + $patchsize + $gap;
2239 my %text_info = $self->_text_style('legend')
2240 or return;
2241 my $patchborder;
2242 if ($self->{_style}{legend}{patchborder}) {
2243 $patchborder = $self->_get_color('legend.patchborder')
2244 or return;
2245 }
2246 my $dataindex = 0;
2247 for my $label (@$labels) {
2248 my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
2249 $patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
2eac77fc 2250
2251 my @fill;
2252 if ($self->_draw_flat_legend()) {
2253 @fill = (color => $self->_data_color($dataindex), filled => 1);
2254 }
2255 else {
2256 @fill = $self->_data_fill($dataindex, \@patchbox)
2257 or return;
2258 }
35574351 2259 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
d7fd5863 2260 ymax=>$ypos + $patchsize, @fill);
35574351
TC
2261 if ($self->{_style}{legend}{patchborder}) {
2262 $img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
d7fd5863
TC
2263 ymax=>$ypos + $patchsize,
2264 color=>$patchborder);
35574351
TC
2265 }
2266 $img->string(%text_info, x=>$textpos, 'y'=>$ypos + $patchsize,
2267 text=>$label);
2268
2269 my $step = $patchsize + $gap;
2270 if ($minrowsize < $sizes[$dataindex][3]) {
2271 $ypos += $sizes[$dataindex][3];
2272 }
2273 else {
2274 $ypos += $minrowsize;
2275 }
2276 ++$dataindex;
2277 }
2278 if ($self->{_style}{legend}{border}) {
2279 my $border_color = $self->_get_color('legend.border')
2280 or return;
2281 $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
d7fd5863 2282 color=>$border_color);
35574351
TC
2283 }
2284 $self->_remove_box($chart_box, \@box);
2285 1;
2286}
2287
2288sub _draw_title {
2289 my ($self, $img, $chart_box) = @_;
2290
2291 my $title = $self->{_style}{title}{text};
5d622bb8
TC
2292 my @box = $self->_text_bbox($title, 'title')
2293 or return;
35574351
TC
2294 my $yoff = $box[1];
2295 @box[0,1] = (0,0);
2296 $self->_align_box(\@box, $chart_box, 'title');
5d622bb8
TC
2297 my %text_info = $self->_text_style('title')
2298 or return;
35574351
TC
2299 $img->string(%text_info, x=>$box[0], 'y'=>$box[3] + $yoff, text=>$title);
2300 $self->_remove_box($chart_box, \@box);
2301 1;
2302}
2303
2304sub _small_extent {
2305 my ($self, $box) = @_;
2306
2307 if ($box->[2] - $box->[0] > $box->[3] - $box->[1]) {
2308 return $box->[3] - $box->[1];
2309 }
2310 else {
2311 return $box->[2] - $box->[0];
2312 }
2313}
2314
2eac77fc 2315sub _draw_flat_legend {
2316 return 0;
2317}
2318
35574351
TC
2319=item _composite()
2320
2321Returns a list of style fields that are stored as composites, and
2322should be merged instead of just being replaced.
2323
2324=cut
2325
2326sub _composite {
2327 qw(title legend text label dropshadow outline callout);
2328}
2329
2330sub _filter_region {
2331 my ($self, $img, $left, $top, $right, $bottom, $filter) = @_;
2332
2333 unless (ref $filter) {
2334 my $name = $filter;
2335 $filter = $self->_get_thing($name)
2336 or return;
2337 $filter->{type}
2338 or return $self->_error("no type for filter $name");
2339 }
2340
2341 $left > 0 or $left = 0;
2342 $top > 0 or $top = 0;
2343
2344 # newer versions of Imager let you work on just part of an image
2345 if ($img->can('masked') && !$self->{_style}{features}{_debugblur}) {
2346 my $masked = $img->masked(left=>$left, top=>$top,
2347 right=>$right, bottom=>$bottom);
2348 $masked->filter(%$filter);
2349 }
2350 else {
2351 # for older versions of Imager
2352 my $subset = $img->crop(left=>$left, top=>$top,
2353 right=>$right, bottom=>$bottom);
2354 $subset->filter(%$filter);
2355 $img->paste(left=>$left, top=>$top, img=>$subset);
2356 }
2357}
2358
23591;
2360__END__
2361
2362=back
2363
2364=head1 SEE ALSO
2365
2366Imager::Graph::Pie(3), Imager(3), perl(1).
2367
2368=head1 AUTHOR
2369
2370Tony Cook <tony@develop-help.com>
2371
54ada35d
TC
2372=head1 LICENSE
2373
2374Imager::Graph is licensed under the same terms as perl itself.
2375
35574351
TC
2376=head1 BLAME
2377
2378Addi for producing a cool imaging module. :)
2379
2380=cut