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