Chart-v2.403.9/0000775000175000017500000000000014341172251012772 5ustar herbertherbertChart-v2.403.9/dist.ini0000644000175000017500000000265114341172251014440 0ustar herbertherbertname = Chart ;main_module = lib/Chart.pm ; will set automatically ;abstract = a series of charting modules ; .. ;version = 2.402.00 ; .. author = Chart-Group license = Perl_5 copyright_holder = Chart-Group copyright_year = 1997-2022 [Meta::Maintainers] maintainer = Herbert Breunung [Prereqs] perl = v5.12.0 Carp = 1.35 ;List::Util = 1.2 GD = 2 Graphics::Toolkit::Color = 1 [Prereqs / RuntimeSuggests] ;Alien::Font::Uni [Prereqs / TestRequires] File::Temp = 0.19 Test::More = 1.3 Test::Warn = 0.30 [MetaNoIndex] directory = t package = Chart::Base package = Chart::Constants package = Chart::BrushStyles namespace = Chart::Property namespace = Chart::Manual ; pollutes meta section 'provides' [MetaProvides::Package] [MungeFile] ;file = Readme.md [Git::GatherDir] exclude_filename = Readme.md exclude_match = ^old exclude_match = ^dev ; use RewriteVersion or VersionFromModule ;[VersionFromModule] [RewriteVersion] allow_decimal_underscore = 1 [Repository] [PodSyntaxTests] [AbstractFromPOD] [Pod2Readme] [MetaJSON] [MetaYAML] [Manifest] [MakeMaker] [License] [CPANFile] ;[Signature] [TestRelease] [ConfirmRelease] [UploadToCPAN] ;[PodSyntaxTests] ;[PodCoverageTests] ;[Pod2Html] ; dir = my_docs ; where to create HTML files ; ignore = bin/myscript1 ; what input file to ignore ; [=inc::Documentation] ; module = Chart::Manual Chart-v2.403.9/MANIFEST0000644000175000017500000000435014341172251014123 0ustar herbertherbert# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.025. CONTRIBUTING Changes LICENSE MANIFEST META.json META.yml Makefile.PL README TODO cpanfile dist.ini lib/Chart.pm lib/Chart/Bars.pm lib/Chart/Base.pm lib/Chart/BrushStyles.pm lib/Chart/Composite.pm lib/Chart/Constants.pm lib/Chart/Direction.pm lib/Chart/ErrorBars.pm lib/Chart/HorizontalBars.pm lib/Chart/Lines.pm lib/Chart/LinesPoints.pm lib/Chart/Manual.pod lib/Chart/Manual/Methods.pod lib/Chart/Manual/Properties.pod lib/Chart/Manual/Types.pod lib/Chart/Manual/Workflows.pod lib/Chart/Mountain.pm lib/Chart/Pareto.pm lib/Chart/Pie.pm lib/Chart/Points.pm lib/Chart/Property.pm lib/Chart/Property/DataType/Color.pm lib/Chart/Property/DataType/Font.pm lib/Chart/Split.pm lib/Chart/StackedBars.pm t/Humidity.t t/Math_1_over_x.t t/bars.t t/bars_10.t t/bars_11.t t/bars_12.t t/bars_2.t t/bars_3.t t/bars_4.t t/bars_5.t t/bars_6.t t/bars_7.t t/bars_8.t t/bars_9.t t/composite.t t/composite_1.t t/composite_2.t t/composite_3.t t/composite_4.t t/composite_5.t t/composite_6.t t/composite_7.t t/composite_8.t t/composite_f.t t/data/in.csv t/data/in.tsv t/direction_1.t t/direction_2.t t/direction_3.t t/direction_4.t t/error_1.t t/error_2.t t/f_ticks.t t/f_ticks_1.t t/hbars_1.t t/hbars_2.t t/hbars_3.t t/hbars_4.t t/lines.t t/lines_1.t t/lines_2.t t/lines_3.t t/lines_4.t t/lines_5.t t/lines_6.t t/lines_7.t t/lines_8.t t/lines_9.t t/linespoints.t t/linespoints_1.t t/linespoints_2.t t/linespoints_3.t t/linespoints_4.t t/linespoints_5.t t/linespoints_6.t t/linespoints_7.t t/mapbars.t t/mapcomp.t t/mountain.t t/mountain_2.t t/mountain_3.t t/mountain_4.t t/pareto_1.t t/pareto_2.t t/pareto_3.t t/patterns/PATTERN0.GIF t/patterns/PATTERN0.PNG t/patterns/PATTERN1.GIF t/patterns/PATTERN1.PNG t/patterns/PATTERN2.GIF t/patterns/PATTERN2.PNG t/patterns/PATTERN3.GIF t/patterns/PATTERN3.PNG t/patterns/PATTERN4.GIF t/patterns/PATTERN4.PNG t/patterns/PATTERN5.GIF t/patterns/PATTERN5.PNG t/pie_1.t t/pie_10.t t/pie_11.t t/pie_2.t t/pie_3.t t/pie_4.t t/pie_5.t t/pie_6.t t/pie_7.t t/pie_8.t t/pie_9.t t/points.t t/points_100.t t/points_2.t t/points_3.t t/points_4.t t/points_5.t t/scalarImage.t t/split_1.t t/split_2.t t/stackedbars.t t/stackedbars_2.t t/stackedbars_3.t t/stackedbars_4.t xt/author/pod-syntax.t Chart-v2.403.9/TODO0000644000175000017500000000656514341172251013474 0ustar herbertherbertROADMAP: ------- 3.0 rewrite complete ... 2.406 rewrite axis, tiks, label, box rendering 2.405 rewrite legend rendering with new elements 2.404 true type support and adding unifont (mountain patterns ?) we are here <------------------------------------------------- 2.403 replaced old pdf and html with new POD documentation with embedded HML img 2.402 new, powerful color management: HSL, hex_RGB, hash_RGB, Pantone colors 2.401 reformat distribution to be based on Dist::Zilla, solve CPAN issues TODO's: ------- General: -------- - Reorganize the relationship between autoscale, y_ticks, min_y_ticks, max_y_ticks, xy_plot, integer_ticks_only, skip_int_ticks, min_val, max_val and document it. (At the moment the autoscale is used as the default) - Add chart type candlestick (used for stock exchange plots) - include TrueType fonts - 3-D charts - add optional desciption text maybe with a picture (logo) ? - svg, html and webp backend? Plottype specific: ------------------ - Base class: : Some labels on the top of each axis not printed: i.e., the graph goes to 100.6 but 100.6 is not printed. : Define an option to force the first point sits at the left border of the graph, and the last point sits at the right border, without any spaces. : Add output for png without header as modperl generates the headers by itsself. : Add getopts(), : Add dumpGetOpts(), i.e. list all options settings relevant for the module(s) currently in use, and/or all options defined. - Composite: : When using bars in both, an option is necessary to define whether to put the bars above each other or to put aside. : Add an option to start x-ticks at the left (i.e. 0 point) side and end at the right side : When combining LinePoints and Mountain the use of min_val results in this error "Use of uninitialized value in multiplication ..." : support up to 3 chart types - for example: Mountain, StackedBars, LinesPoints (The number of chart types should be flexible in general) : Add option to limit the y-ticks to just one side : Define different brush_sizes for example with one Lines dataset and one LinesPoints dataset. - pie chart: : Add a flat pie chart where the legend appears on the left of the chart, and the chart itself is about 50% smaller : Add an option to reduce or remove all the border elements on the left, on the right and on the bottom : Allow to unset the title and remove the space occupied by the title, i.e., make the plot bigger : Add 3D appearance to the pies : Set arbitrary background color to the pie (and make the pie visible in the defined colors) : Setup the ray of the pie - Bars chart: : User would like to override the color of specific bars in a Chart::Bars graph. : Program should croak if a color is not defined for a bar. : Within one Bar graph, I have two datasets, but would like to change the width of one of them (it should have a width of 1, to indicate a limit). : Within one Bar graph, I have two datasets, but would like to change the width of one of them (it should have a width of 1, to indicate a limit). : Show values on each bar - xy Plot: : Add a feature to limit what is shown on the x axis : include logarithmic x- and y-axis : Let plot arbitrary xy-functions in a defined area, like y=sin(1/x)+2*x Chart-v2.403.9/LICENSE0000644000175000017500000004375214341172251014010 0ustar herbertherbertThis software is copyright (c) 1997-2022 by Chart-Group . This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 1997-2022 by Chart-Group . This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 1997-2022 by Chart-Group . This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Chart-v2.403.9/META.json0000644000175000017500000000652314341172251014417 0ustar herbertherbert{ "abstract" : "a series of charting modules", "author" : [ "Chart-Group " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.025, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Chart", "no_index" : { "directory" : [ "t" ], "namespace" : [ "Chart::Manual", "Chart::Property" ], "package" : [ "Chart::Base", "Chart::BrushStyles", "Chart::Constants" ] }, "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "develop" : { "requires" : { "Test::Pod" : "1.41" } }, "runtime" : { "requires" : { "Carp" : "1.35", "GD" : "2", "Graphics::Toolkit::Color" : "1", "perl" : "v5.12.0" } }, "test" : { "requires" : { "File::Temp" : "0.19", "Test::More" : "1.3", "Test::Warn" : "0.30" } } }, "provides" : { "Chart" : { "file" : "lib/Chart.pm", "version" : "v2.403.9" }, "Chart::Bars" : { "file" : "lib/Chart/Bars.pm", "version" : "v2.403.9" }, "Chart::Composite" : { "file" : "lib/Chart/Composite.pm", "version" : "v2.403.9" }, "Chart::Direction" : { "file" : "lib/Chart/Direction.pm", "version" : "v2.403.9" }, "Chart::ErrorBars" : { "file" : "lib/Chart/ErrorBars.pm", "version" : "v2.403.9" }, "Chart::HorizontalBars" : { "file" : "lib/Chart/HorizontalBars.pm", "version" : "v2.403.9" }, "Chart::Lines" : { "file" : "lib/Chart/Lines.pm", "version" : "v2.403.9" }, "Chart::LinesPoints" : { "file" : "lib/Chart/LinesPoints.pm", "version" : "v2.403.9" }, "Chart::Mountain" : { "file" : "lib/Chart/Mountain.pm", "version" : "v2.403.9" }, "Chart::Pareto" : { "file" : "lib/Chart/Pareto.pm", "version" : "v2.403.9" }, "Chart::Pie" : { "file" : "lib/Chart/Pie.pm", "version" : "v2.403.9" }, "Chart::Points" : { "file" : "lib/Chart/Points.pm", "version" : "v2.403.9" }, "Chart::Property" : { "file" : "lib/Chart/Property.pm", "version" : "v2.403.9" }, "Chart::Split" : { "file" : "lib/Chart/Split.pm", "version" : "v2.403.9" }, "Chart::StackedBars" : { "file" : "lib/Chart/StackedBars.pm", "version" : "v2.403.9" } }, "release_status" : "stable", "resources" : { "repository" : { "type" : "git", "url" : "git://github.com/lichtkind/Chart.git", "web" : "https://github.com/lichtkind/Chart" } }, "version" : "v2.403.9", "x_generated_by_perl" : "v5.30.0", "x_maintainers" : [ "Herbert Breunung " ], "x_serialization_backend" : "Cpanel::JSON::XS version 4.19", "x_spdx_expression" : "Artistic-1.0-Perl OR GPL-1.0-or-later" } Chart-v2.403.9/xt/0000775000175000017500000000000014341172251013425 5ustar herbertherbertChart-v2.403.9/xt/author/0000775000175000017500000000000014341172251014727 5ustar herbertherbertChart-v2.403.9/xt/author/pod-syntax.t0000644000175000017500000000025214341172251017217 0ustar herbertherbert#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); Chart-v2.403.9/README0000644000175000017500000003001414341172251013646 0ustar herbertherbertNAME Chart - a series of charting modules SYNOPSIS use Chart::type; (type is one of: Points, Lines, Bars, LinesPoints, Composite, StackedBars, Mountain, Pie, HorizontalBars, Split, ErrorBars, Pareto, Direction) $obj = Chart::type->new; $obj = Chart::type->new ( $png_width, $png_height ); $obj->set ( $key_1, $val_1, ... ,$key_n, $val_n ); $obj->set ( $key_1 => $val_1, ... $key_n => $val_n ); $obj->set ( %hash ); # GIFgraph.pm-style API to produce png formatted charts @data = ( \@x_tick_labels, \@dataset1, ... , \@dataset_n ); $obj->png ( "filename", \@data ); $obj->png ( $filehandle, \@data ); $obj->png ( FILEHANDLE, \@data ); $obj->cgi_png ( \@data ); # Graph.pm-style API $obj->add_pt ($label, $val_1, ... , $val_n); $obj->add_dataset ($val_1, ... , $val_n); $obj->png ( "filename" ); $obj->png ( $filehandle ); $obj->png ( FILEHANDLE ); $obj->cgi_png (); The similar functions are available for j-peg # Retrieve image map information $obj->set ( 'imagemap' => 'true' ); $imagemap_ref = $obj->imagemap_dump (); DESCRIPTION Chart helps you to create PNG and JPG images with visualizations of numeric data. This page gives you a summary how to use it. For a more thorough documentation and lots of example code please visit the Chart::Manual. use-ing Chart Okay, so you caught me. There's really no Chart::type module. All of the different chart types (Points, Lines, Bars, LinesPoints, Composite, StackedBars, Pie, Pareto, HorizontalBars, Split, ErrorBars, Direction and Mountain so far) are classes by themselves, each inheriting a bunch of methods from the Chart::Base class. Simply replace the word type with the type of chart you want and you're on your way. For example, use Chart::Lines; would invoke the lines module. Alternatively load all chart types at ones and write: use Chart; Getting an object The new method can either be called without arguments, in which case it returns an object with the default image size (400x300 pixels), or you can specify the width and height of the image. Just remember to replace type with the type of graph you want. For example, $obj = Chart::Bars->new (600,400); would return a Chart::Bars object containing a 600x400 pixel image. New also initializes most of the default variables, which you can subsequently change with the set method. Setting different options This is where the fun begins. Set looks for a hash of keys and values. You can pass it a hash that you've already constructed, like %hash = ( property_name => 'new value' ); $obj->set (%hash); or you can try just constructing the hash inside the set call, like $obj->set ( property_name => 'new value' ); Chart::Manual::Properties lists all currently supported keys and values. GIFgraph.pm-style API Sending the image to a file Invoking the png method causes the graph to be plotted and saved to a file. It takes the name of the output file and a reference to the data as arguments. For example, $obj->png ("foo.png", \@data); would plot the data in @data, and the save the image to foo.png. Of course, this then beggars the question "What should @data look like?". Well, just like GIFgraph, @data should contain references to arrays of data, with the first array reference pointing to an array of x-tick labels. For example, @data = ( [ 'foo', 'bar', 'junk' ], [ 30.2, 23.5, 92.1 ] ); would set up a graph with one dataset, and three data points in that set. In general, the @data array should look something like @data = ( \@x_tick_labels, \@dataset1, ... , \@dataset_n ); And no worries, I make my own internal copy of the data, so that it doesn't mess with yours. CGI and Chart Okay, so you're probably thinking, "Do I always have to save these images to disk? What if I want to use Chart to create dynamic images for my web site?" Well, here's the answer to that. $obj->cgi_png ( \@data ); The cgi_png method will print the chart, along with the appropriate http header, to stdout, allowing you to call chart-generating scripts directly from your html pages (ie. with a img src=image.pl HTML tag). The @data array should be set up the same way as for the normal png method. column based API You might ask, "But what if I just want to add a few points to the graph, and then display it, without all those references to references?". Well, friend, the solution is simple. Borrowing the add_pt idea from Matt Kruse's Graph module, you simply make a few calls to the add_pt method, like so: $obj->add_pt ('foo', 30, 25); $obj->add_pt ('bar', 16, 32); Or, if you want to be able to add entire datasets, simply use the add_dataset method: $obj->add_dataset ('foo', 'bar'); $obj->add_dataset (30, 16); $obj->add_dataset (25, 32); These methods check to make sure that the points and datasets you are adding are the same size as the ones already there. So, if you have two datasets currently stored, and try to add a data point with three different values, it will carp (per the Carp module) an error message. Similarly, if you try to add a dataset with 4 data points, and all the other datasets have 3 data points, it will carp an error message. Don't forget, when using this API, that I treat the first dataset as a series of x-tick labels. So, in the above examples, the graph would have two x-ticks, labeled 'foo' and 'bar', each with two data points. Pie and ErrorBars handle it different, look at the documentation to see how it works. Adding a datafile You can also add a complete datafile to a chart object. Just use the add_datafile() method. $obj->add_datafile('file', 'set' or 'pt'); file can be the name of the data file or a filehandle. 'set' or 'pt is the type of the datafile. If the parameter is 'set' then each line in the data file has to be a complete data set. The value of the set has to be separated by white spaces. For example the file looks like this: 'foo' 'bar' 30 16 25 32 If the parameter is 'pt', one line has to include all values of one data point separated by white spaces. For example: 'foo' 30 25 'bar' 16 32 Clearing the data A simple call to the clear_data method empties any values that may have been entered. $obj->clear_data (); Getting a copy of the data If you want a copy of the data that has been added so far, make a call to the get_data method like so: $dataref = $obj->get_data; It returns (you guessed it!) a reference to an array of references to datasets. So the x-tick labels would be stored as @x_labels = @{$dataref->[0]}; Sending the image to a file If you just want to print this chart to a file, all you have to do is pass the name of the file to the png() method. $obj->png ("foo.png"); Sending the image to a filehandle If you want to do something else with the image, you can also pass a filehandle (either a typeglob or a FileHandle object) to png, and it will print directly to that. $obj->png ($filehandle); $obj->png (FILEHANDLE); CGI and Chart Okay, so you're probably thinking (again), "Do I always have to save these images to disk? What if I want to use Chart to create dynamic images for my web site?" Well, here's the answer to that. $obj->cgi_png (); The cgi_png method will print the chart, along with the appropriate http header, to stdout, allowing you to call chart-generating scripts directly from your html pages (ie. with a img src=image.pl HTML tag). Produce a png image as a scalar Like scalar_jpeg() the image is produced as a scalar so that the programmer-user can do whatever the heck s/he wants to with it: $obj-scalar_png($dataref) Produce a jpeg image as a scalar Like scalar_png() the image is produced as a scalar so that the programmer-user can do whatever the heck s/he wants to with it: $obj-scalar_jpeg($dataref) Imagemap Support Chart can also return the pixel positioning information so that you can create image maps from the pngs Chart generates. Simply set the 'imagemap' option to 'true' before you generate the png, then call the imagemap_dump() method afterwards to retrieve the information. You will be returned a data structure almost identical to the @data array described above to pass the data into Chart. $imagemap_data = $obj->imagemap_dump (); Instead of single data values, you will be passed references to arrays of pixel information. For Bars, HorizontalBars and StackedBars charts, the arrays will contain two x-y pairs (specifying the upper left and lower right corner of the bar), like so ( $x1, $y1, $x2, $y2 ) = @{ $imagemap_data->[$dataset][$datapoint] }; For Lines, Points, ErrorBars, Split and LinesPoints, the arrays will contain a single x-y pair (specifying the center of the point), like so ( $x, $y ) = @{ $imagemap_data->[$dataset][$datapoint] }; A few caveats apply here. First of all, GD treats the upper-left corner of the png as the (0,0) point, so positive y values are measured from the top of the png, not the bottom. Second, these values will most likely contain long decimal values. GD, of course, has to truncate these to single pixel values. Since I don't know how GD does it, I can't truncate it the same way he does. In a worst-case scenario, this will result in an error of one pixel on your imagemap. If this is really an issue, your only option is to either experiment with it, or to contact Lincoln Stein and ask him. Third, please remember that the 0th dataset will be empty, since that's the place in the @data array for the data point labels. PLAN This module is currently under a complete rebuild, that will take place in two phases. First: rewrite all functionality within a modular architecture and hierarchical property system. This will be accessed via a central API using the so far unutilized Chart module 'my $c = Chart->new(...);'. This API will have in part different method and property names, but the old API will not be touched. In a second phase we will see hoch much new code can be used by the old modules and which new features can be brought to the legacy parts, which will be than discouraged, but not scrapped. TO DO * Include True Type Fonts * Violine and Box plots * Add some 3-D graphs. For more please check the TODO file. BUGS Probably quite a few, since it's been completely rewritten. As usual, please mail me with any bugs, patches, suggestions, comments, flames, death threats, etc. AUTHOR David Bonner (dbonner@cs.bu.edu) MAINTAINER * Chart Group (Chart@fs.wettzell.de) * Herbert Breunung (lichtkind@cpan.org) CONTRIBUTORS * Gregor Herrmann (gregoa@debian.org) * Chris Dolan (chris+rt@chrisdolan.net) * (jarmzet@yahoo.com) * Ricardo Signes (rjbs@cpan.org) * Petr Pisar (ppisar@redhat.com) COPYRIGHT Copyright(c) 1997-1998 by David Bonner, 1999 by Peter Clark, 2001 by the Chart group at BKG-Wettzell. 2022 by Herbert Breunung and Chart group All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Chart-v2.403.9/CONTRIBUTING0000644000175000017500000000060514341172251014623 0ustar herbertherbert Please submit Bug reports under https://rt.cpan.org/Dist/Display.html?Name=Chart (preferred) or if you like https://github.com/lichtkind/Chart/issues Patches are welcome under: https://github.com/lichtkind/Chart/pulls (preferred) but arrive also via https://rt.cpan.org/Dist/Display.html?Name=Chart Also feature Requests are welcome but please read the TODO first. Chart-v2.403.9/lib/0000775000175000017500000000000014341172251013540 5ustar herbertherbertChart-v2.403.9/lib/Chart/0000775000175000017500000000000014341172251014601 5ustar herbertherbertChart-v2.403.9/lib/Chart/Points.pm0000644000175000017500000001050514341172251016412 0ustar herbertherbert # xy charts with points (circles or other shapes) use v5.12; package Chart::Points; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Carp; use GD; use Chart::Base; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<# #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @fn private _draw_data # finally get around to plotting the data sub _draw_data { my $self = shift; my $data = $self->{'dataref'}; my $misccolor = $self->_color_role_to_index('misc'); my ( $x1, $x2, $x3, $y1, $y2, $y3, $mod ); my ( $width, $height, $delta, $map, $delta_num, $zero_offset ); my ( $i, $j, $color, $brush ); my $diff; # init the imagemap data field if they want it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # find the delta value between data points, as well # as the mapping constant $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta = $width / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); $diff = ( $self->{'max_val'} - $self->{'min_val'} ); $diff = 1 if $diff == 0; $map = $height / $diff; #for a xy-plot, use this delta and maybe an offset for the zero-axes if ( $self->true( $self->{'xy_plot'} ) ) { $diff = ( $self->{'x_max_val'} - $self->{'x_min_val'} ); $diff = 1 if $diff == 0; $delta_num = $width / $diff; if ( $self->{'x_min_val'} <= 0 && $self->{'x_max_val'} >= 0 ) { $zero_offset = abs( $self->{'x_min_val'} ) * abs($delta_num); } elsif ( $self->{'x_min_val'} > 0 || $self->{'x_max_val'} < 0 ) { $zero_offset = -$self->{'x_min_val'} * $delta_num; } else { $zero_offset = 0; } } # get the base x-y values if ( $self->false( $self->{'xy_plot'} ) ) { $x1 = $self->{'curr_x_min'} + ( $delta / 2 ); } else { $x1 = $self->{'curr_x_min'}; } if ( $self->{'min_val'} >= 0 ) { $y1 = $self->{'curr_y_max'}; $mod = $self->{'min_val'}; } elsif ( $self->{'max_val'} <= 0 ) { $y1 = $self->{'curr_y_min'}; $mod = $self->{'max_val'}; } else { $y1 = $self->{'curr_y_min'} + ( $map * $self->{'max_val'} ); $mod = 0; $self->{'gd_obj'}->line( $self->{'curr_x_min'}, $y1, $self->{'curr_x_max'}, $y1, $misccolor ); } # draw the points for $i ( 1 .. $self->{'num_datasets'} ) { # get the color for this dataset, and set the brush $color = $self->_color_role_to_index( 'dataset' . ( $i - 1 ) ); my $offset = 0; ( $brush, $offset ) = $self->_prepare_brush( $color, 'point', 'dataset' . ( $i - 1 ) ); $self->{'gd_obj'}->setBrush($brush); # draw every point for this dataset for $j ( 0 .. $self->{'num_datapoints'} ) { # don't try to draw anything if there's no data if ( defined( $data->[$i][$j] ) ) { if ( $self->true( $self->{'xy_plot'} ) ) { $x2 = $x1 + $delta_num * $data->[0][$j] + $zero_offset; $x3 = $x2; } else { $x2 = $x1 + ( $delta * $j ); $x3 = $x2; } $y2 = $y1 - ( ( $data->[$i][$j] - $mod ) * $map ); $y3 = $y2; # draw the point only if it is within the chart borders if ( $data->[$i][$j] <= $self->{'max_val'} && $data->[$i][$j] >= $self->{'min_val'} ) { $self->{'gd_obj'}->line( $x2, $y2, $x3, $y3, gdBrushed ); } # store the imagemap data if they asked for it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ $x2, $y2 ]; } } } } # and finaly box it off $self->{'gd_obj'} ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); return; } 1; # be a good module and return 1 Chart-v2.403.9/lib/Chart/Bars.pm0000644000175000017500000001633214341172251016031 0ustar herbertherbert # vertical bars use v5.12; package Chart::Bars; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Chart::Base; use GD; use Carp; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods # #<<<<<<<<<<<<<<<<<<<<<<<<<<# #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## _draw_data : plotting the data for (vertical) bars # # The user may define the kind of labelling the data by setting # 'label_values' to 'value' if she wants to have the absolut values # 'label_values' to 'none' if she wants to have no values (default) # sub _draw_data { my $self = shift; my $data = $self->{'dataref'}; my $misccolor = $self->_color_role_to_index('misc'); my ( $x1, $x2, $x3, $y1, $y2, $y3 ); my ( $width, $height, $delta1, $delta2, $map, $mod, $cut, $pink ); my ( $i, $j, $color ); my $temp = 0; my $font = $self->{'legend_font'}; my $fontW = $self->{'legend_font'}->width; my $fontH = $self->{'legend_font'}->height; my $textcolor = $self->_color_role_to_index('text'); # init the imagemap data field if they wanted it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # find the longest label # first we need the length of the values # draw the bars my $max_label_len = 0; for $i ( 1 .. $self->{'num_datasets'} ) { for $j ( 0 .. $self->{'num_datapoints'} ) { # don't try to draw anything if there's no data if ( defined( $data->[$i][$j] ) && $data->[$i][$j] =~ /^[\-\+]{0,1}\s*[\d\.eE\-\+]+/ ) { if ( defined $self->{'label_values'} && $self->{'label_values'} =~ /^value$/i ) { my $label = sprintf( "%.2f", $data->[$i][$j] ); my $label_length = length($label); $max_label_len = $label_length if ( $max_label_len < $label_length ); } } } } $max_label_len *= $fontH; # find both delta values ($delta1 for stepping between different # datapoint names, $delta2 for stepping between datasets for that # point) and the mapping constant $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta1 = ( $self->{'num_datapoints'} > 0 ) ? $width / ( $self->{'num_datapoints'} * 1 ) : $width; $map = ( ( $self->{'max_val'} - $self->{'min_val'} ) > 0 ) ? $height / ( $self->{'max_val'} - $self->{'min_val'} ) : $height; if ( $self->true( $self->{'spaced_bars'} ) ) { #OLD: $delta2 = $delta1 / ($self->{'num_datasets'} + 2); $delta2 = ( ( $self->{'num_datasets'} + 2 ) > 0 ) ? $delta1 / ( $self->{'num_datasets'} + 2 ) : $delta1; } else { $delta2 = ( $self->{'num_datasets'} > 0 ) ? $delta1 / $self->{'num_datasets'} : $delta1; } # get the base x-y values $x1 = $self->{'curr_x_min'}; if ( $self->{'min_val'} >= 0 ) { $y1 = $self->{'curr_y_max'}; $mod = $self->{'min_val'}; } elsif ( $self->{'max_val'} <= 0 ) { $y1 = $self->{'curr_y_min'}; $mod = $self->{'max_val'}; } else { $y1 = $self->{'curr_y_min'} + ( $map * $self->{'max_val'} ); $mod = 0; $self->{'gd_obj'}->line( $self->{'curr_x_min'}, $y1, $self->{'curr_x_max'}, $y1, $misccolor ); } # draw the bars for $i ( 1 .. $self->{'num_datasets'} ) { # get the color for this dataset $color = $self->_color_role_to_index( 'dataset' . ( $i - 1 ) ); # draw every bar for this dataset for $j ( 0 .. $self->{'num_datapoints'} ) { # don't try to draw anything if there's no data if ( defined( $data->[$i][$j] ) && $data->[$i][$j] =~ /^[\-\+]{0,1}\s*[\d\.eE\-\+]+/ ) { # find the bounds of the rectangle if ( $self->true( $self->{'spaced_bars'} ) ) { $x2 = ( $x1 + ( $j * $delta1 ) + ( $i * $delta2 ) ); } else { $x2 = $x1 + ( $j * $delta1 ) + ( ( $i - 1 ) * $delta2 ); } $y2 = $y1; $x3 = $x2 + $delta2; $y3 = $y1 - ( ( $data->[$i][$j] - $mod ) * $map ); # cut the bars off, if needed if ( $data->[$i][$j] > $self->{'max_val'} ) { $y3 = $y1 - ( ( $self->{'max_val'} - $mod ) * $map ); $cut = 1; } elsif ( $data->[$i][$j] < $self->{'min_val'} ) { $y3 = $y1 - ( ( $self->{'min_val'} - $mod ) * $map ); $cut = 1; } else { $cut = 0; } # draw the bar ## y2 and y3 are reversed in some cases because GD's fill ## algorithm is lame if ( $data->[$i][$j] > 0 ) { $self->{'gd_obj'}->filledRectangle( $x2, $y3, $x3, $y2, $color ); if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ $x2, $y3, $x3, $y2 ]; } if ( defined $self->{'label_values'} && $self->{'label_values'} =~ /^value$/i ) { # draw data my $labelX = $x2; my $labelY = $y3 + $fontH; #$max_label_len; if ( $labelY < 0 ) { $labelY = $y3; } my $label = sprintf( "%.2f", $data->[$i][$j] ); $self->{'gd_obj'}->stringUp( $font, $labelX + $fontW * 0.5, $labelY, $label, $textcolor ); } } else { $self->{'gd_obj'}->filledRectangle( $x2, $y2, $x3, $y3, $color ); if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ $x2, $y2, $x3, $y3 ]; } } # now outline it. outline red if the bar had been cut off unless ($cut) { $self->{'gd_obj'}->rectangle( $x2, $y3, $x3, $y2, $misccolor ); } else { $pink = $self->{'gd_obj'}->colorAllocate( 255, 0, 255 ); $self->{'gd_obj'}->rectangle( $x2, $y3, $x3, $y2, $pink ); } } else { if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ undef(), undef(), undef(), undef() ]; } } } } # and finaly box it off $self->{'gd_obj'} ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); return; } 1; # be a good module and return 1 Chart-v2.403.9/lib/Chart/BrushStyles.pm0000644000175000017500000001521214341172251017425 0ustar herbertherbert # styles for Points and LinesPoints classes use v5.12; package Chart::BrushStyles; our $VERSION = 'v2.403.9'; use Carp; use GD; use Chart::Constants; use Chart::Base; ## @fn OpenCircle # @param[in] *GD::Image $rbrush Reference to GD::Image # @param[in] int $radius Radius of the point in pixels # @param[in] int $color # # @brief Set the gdBrush object to have nice brushed object # representing a circle of the size \$radius. # # @details # Called by\n # use Chart::BrushStyles;\n # \@Chart::Points::ISA = qw(Chart::BrushStyles);\n # \$self->OpenCircle(\\\$rbrush,\$radius, \$newcolor);\n # to plot the GD::Image representing an open circle as the point # sub OpenCircle { my $self = shift; my $rbrush = shift; # reference to GD::Image my $radius = shift; # radius of the point in pixels my $color = shift; # Color of the not filled point # draw a filled circle if ( $radius < 2 ) { $radius = 2; } $$rbrush->arc( $radius, $radius, $radius, $radius, 0, 360, $color ); } ## @fn FilledCircle # @param[in] *GD::Image $rbrush Reference to GD::Image # @param[in] int $radius Radius of the point in pixels # @param[in] int $color Color of the filled point # @return nothing # # @brief Set the gdBrush object to have nice brushed object # representing a point of the size \$radius. # # @details # Called by\n # use Chart::BrushStyles;\n # \@Chart::Points::ISA = qw(Chart::BrushStyles);\n # \$self->FilledCircle(\\\$rbrush,\$radius, \$color);\n # to plot the GD::Image representing a filled circle as the point # sub FilledCircle { my $self = shift; my $rbrush = shift; # reference to GD::Image my $radius = shift; my $color = shift; # draw a filled circle if ( $radius < 2 ) { $radius = 2; } $$rbrush->arc( $radius, $radius, $radius, $radius, 0, 360, $color ); # and fill it $$rbrush->fill( $radius, $radius, $color ); } ## @fn Star # @param[in] *GD::Image $rbrush Reference to GD::Image # @param[in] int $radius Radius of the star in pixels # @param[in] int $color Color of the star # @return nothing # # @brief Set the gdBrush object to have nice brushed object # representing a star of the size \$radius. # # @details # Called by\n # use Chart::BrushStyles;\n # \@Chart::Points::ISA = qw(Chart::BrushStyles);\n # \$self->Star(\\\$rbrush,\$radius, \$color);\n # to get back an GD::Image representing a star as the point # sub Star { my $self = shift; my $rbrush = shift; # reference to GD::Image my $radius = shift; my $color = shift; my $R = $self->maximum( 2, int( $radius + 0.5 ) ); my $r = $self->maximum( 1, int( $R / 3 + 0.5 ) ); my $lRadius = $R; my $x1 = $lRadius + $R; # =$R*cos(0) + $R; my $y1 = $R; # =$R*sin(0) + $R my ( $x2, $y2 ); for ( my $iAngleCounter = 1 ; $iAngleCounter < 16 ; $iAngleCounter++ ) { my $phi = $iAngleCounter * Chart::Constants::PI / 8; $lRadius = ( ( $iAngleCounter & 1 ) == 0 ) ? $R : $r; $x2 = $lRadius * cos($phi); $y2 = $lRadius * sin($phi); $x2 += $R; $y2 += $R; #printf("$iAngleCounter: %4f, %4f %4f,%4f\n", $x1,$y1,$x2,$y2); $$rbrush->line( $x1, $y1, $x2, $y2, $color ); $x1 = $x2; $y1 = $y2; } # draw to the first point $x2 = $R + $R; $y2 = $R; $$rbrush->line( $x1, $y1, $x2, $y2, $color ); } ## @fn FilledDiamond # @param[in] *GD::Image $rbrush Reference to GD::Image # @param[in] int $radius Radius of the diamond in pixels # @param[in] int $color Color of the filled diamond # @return nothing # # @brief Set the gdBrush object to have nice brushed object # representing a filled diamond of the size \$radius. # # @details # Called by\n # use Chart::BrushStyles;\n # \@Chart::Points::ISA = qw(Chart::BrushStyles);\n # \$self->FilledDiamond(\\\$rbrush,\$radius, \$color);\n # to get back an GD::Image representing a filled diamond as the point # sub FilledDiamond { my $self = shift; my $rbrush = shift; # reference to GD::Image my $radius = shift; my $color = shift; my $R = $self->maximum( 2, int( $radius + 0.5 ) ); my $R2 = $R * 2; $$rbrush->line( $R, 1, $R2 - 1, $R, $color ); $$rbrush->line( $R2, $R, $R, $R2 - 1, $color ); $$rbrush->line( $R, $R2 - 1, 1, $R, $color ); $$rbrush->line( 1, $R, $R, 1, $color ); # and fill it $$rbrush->fill( $radius - 1, $radius - 1, $color ); } ## @fn OpenDiamond # @param[in] *GD::Image $rbrush Reference to GD::Image # @param[in] int $radius Radius of the diamond in pixels # @param[in] int $color Color of the diamond # @return nothing # # @brief Set the gdBrush object to have nice brushed object # representing a diamond of the size \$radius-1. # # @details # Called by\n # use Chart::BrushStyles;\n # \@Chart::Points::ISA = qw(Chart::BrushStyles);\n # \$self->OpenDiamond(\\\$rbrush,\$radius, \$color);\n # to get back an GD::Image representing a diamond as the point # sub OpenDiamond { my $self = shift; my $rbrush = shift; # reference to GD::Image my $radius = shift; my $color = shift; my $R = $self->maximum( 2, int( $radius + 0.5 ) ); my $R2 = $R * 2; $$rbrush->line( $R, 1, $R2 - 1, $R, $color ); $$rbrush->line( $R2, $R, $R, $R2 - 1, $color ); $$rbrush->line( $R, $R2 - 1, 1, $R, $color ); $$rbrush->line( 1, $R, $R, 1, $color ); } ## @fn OpenRectangle # @param[in] *GD::Image $rbrush Reference to GD::Image # @param[in] int $radius Radius of the rectangle in pixels # @param[in] int $color Color of the rectangle # @return nothing # # @brief Set the gdBrush object to have nice brushed object # representing a rectangle of the height \$radius-1 and width of $radius/2. # # @details # Called by\n # use Chart::BrushStyles;\n # \@Chart::Points::ISA = qw(Chart::BrushStyles);\n # \$self->OpenDiamond(\\\$rbrush,\$radius, \$color);\n # to get back an GD::Image representing a rectangle as the point # sub OpenRectangle { my $self = shift; my $rbrush = shift; # reference to GD::Image my $radius = shift; my $color = shift; if ( $radius < 2 ) { $radius = 2; } my $height = $radius; my $width = $radius / 2; if ( $width < 1 ) { $width = 1; } # draw a filled circle $$rbrush->line( -$width, -$height, $width, -$height, $color ); #$$rbrush->line( $width, -$height, $width, $height, $color ); #$$rbrush->line( $width, $height, -$width, $height, $color ); #$$rbrush->line( -$width, $height, -$width ,-$height,$color ); } 1; Chart-v2.403.9/lib/Chart/Split.pm0000644000175000017500000006267414341172251016247 0ustar herbertherbert## @file # Implementation of Chart::Split # # written and maintained by the # @author Chart Group at Geodetic Fundamental Station Wettzell (Chart@fs.wettzell.de) # @date 2015-03-01 # @version 2.4.10 # ## @class Chart::Split #Split class derived from class Base. # # This class provides all functions which are specific to # splitted plots # use v5.12; package Chart::Split; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Chart::Base; use GD; use Carp; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<# #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @fn private _draw_x_number_ticks #draw the ticks sub _draw_x_number_ticks { my $self = shift; my $data = $self->{'dataref'}; my $font = $self->{'tick_label_font'}; my $textcolor = $self->_color_role_to_index('text'); my $misccolor = $self->_color_role_to_index('misc'); my $num_points = $self->{'num_datapoints'}; my ( $h, $w, $width, $step, $start, $interval, $label, $stag, @labels ); my ( $x_start, $y_start, $y, $x, $lines, $delta, $ticks ); my $x_label_len = 1; my $y_label_len = 1; my $x_max = -0x80000000; $self->{'grid_data'}->{'x'} = []; # find the width $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $width = 1 if $width == 0; # make sure we got a real font unless ( ( ref $font ) eq 'GD::Font' ) { croak "The tick label font you specified isn\'t a GD Font object"; } # find out how big the font is ( $w, $h ) = ( $font->width, $font->height ); unless ( defined $self->{'start'} && defined $self->{'interval'} ) { croak "I need two values from you to draw a split chart: start and interval!"; } else { $interval = $self->{'interval'}; $start = $self->{'start'}; $ticks = $self->{'interval_ticks'} - 1; $label = $start; } #look after devision by zero! if ( $ticks == 0 ) { $ticks = 1; } #calculate the step between the ticks $step = $interval / $ticks; for ( 0 .. $ticks ) { push @labels, $self->{f_x_tick}->( sprintf( "%." . $self->{'precision'} . "f", $label ) ); $label += $step; } #find the biggest x value foreach ( @{ $data->[0] } ) { if ( $_ > $x_max ) { $x_max = $_; } } #find the length of the x and y labels foreach (@labels) { if ( length($_) > $x_label_len ) { $x_label_len = length($_); } } #find the amount of lines $lines = int( ( ( $x_max - $start ) / $interval ) + 0.99999999999 ); $lines = 1 if $lines == 0; #find the length, of the label. $y_label_len = length($lines); #get the starting point and the width if ( $lines > 1 ) { #if there are y-ticks if ( $self->{'y_axes'} =~ /^right$/i ) { $x_start = $self->{'curr_x_min'}; $width = $self->{'curr_x_max'} - $x_start - $self->{'text_space'} * 2 - $y_label_len * $w - $self->{'tick_len'}; } elsif ( $self->{'y_axes'} =~ /^both$/i ) { $x_start = $self->{'curr_x_min'} + ( $w * $y_label_len ) + 2 * $self->{'text_space'} + $self->{'tick_len'}; $width = $self->{'curr_x_max'} - $x_start - ( $w * $y_label_len ) - 2 * $self->{'text_space'} - $self->{'tick_len'}; } else { $x_start = $self->{'curr_x_min'} + ( $w * $y_label_len ) + 3 * $self->{'text_space'}; $width = $self->{'curr_x_max'} - $x_start; } } else { #if there are no y-axes $x_start = $self->{'curr_x_min'}; $width = $self->{'curr_x_max'} - $x_start; } #and the y_start value $y_start = $self->{'curr_y_max'} - $h - $self->{'text_space'}; #get the delta value $delta = $width / ($ticks); if ( !defined( $self->{'skip_x_ticks'} ) ) { $self->{'skip_x_ticks'} = 1; } #draw the labels if ( $self->{'x_ticks'} =~ /^normal$/i ) { if ( $self->{'skip_x_ticks'} > 1 ) { #draw a normal tick every nth label for ( 0 .. $#labels - 1 ) { if ( defined( $labels[ $_ * $self->{'skip_x_ticks'} ] ) ) { $x = $x_start + $delta * ( $_ * $self->{'skip_x_ticks'} ) - ( $w * length( $labels[ $_ * $self->{'skip_x_ticks'} ] ) ) / 2; $self->{'gd_obj'}->string( $font, $x, $y_start, $labels[ $_ * $self->{'skip_x_ticks'} ], $textcolor ); } } } elsif ( $self->{'custom_x_ticks'} ) { #draw only the normal ticks they wanted foreach ( @{ $self->{'custom_x_ticks'} } ) { if ( defined $labels[$_] ) { $x = $x_start + $delta * $_ - ( $w * length( $labels[$_] ) ) / 2; $self->{'gd_obj'}->string( $font, $x, $y_start, $labels[$_], $textcolor ); } } } else { for ( 0 .. $#labels ) { #draw all ticks normal if ( defined $labels[$_] ) { $x = $x_start + $delta * ($_) - ( $w * length( $labels[$_] ) ) / 2; $self->{'gd_obj'}->string( $font, $x, $y_start, $labels[$_], $textcolor ); } } } } elsif ( $self->{'x_ticks'} =~ /^staggered$/i ) { $stag = 0; if ( $self->{'skip_x_ticks'} > 1 ) { #draw a staggered tick every nth label for ( 0 .. $#labels - 1 ) { if ( defined( $labels[ $_ * $self->{'skip_x_ticks'} ] ) ) { $x = $x_start + $delta * ( $_ * $self->{'skip_x_ticks'} ) - ( $w * length( $labels[ $_ * $self->{'skip_x_ticks'} ] ) ) / 2; if ( $stag % 2 == 0 ) { $y_start -= $self->{'text_space'} + $h; } $self->{'gd_obj'}->string( $font, $x, $y_start, $labels[ $_ * $self->{'skip_x_ticks'} ], $textcolor ); if ( $stag % 2 == 0 ) { $y_start += $self->{'text_space'} + $h; } $stag++; } } } elsif ( $self->{'custom_x_ticks'} ) { # draw only the wanted ticks staggered foreach ( sort ( @{ $self->{'custom_x_ticks'} } ) ) { if ( defined $labels[$_] ) { $x = $x_start + $delta * $_ - ( $w * ( length( $labels[$_] ) ) ) / 2; if ( $stag % 2 == 0 ) { $y_start -= $self->{'text_space'} + $h; } $self->{'gd_obj'}->string( $font, $x, $y_start, $labels[$_], $textcolor ); if ( $stag % 2 == 0 ) { $y_start += $self->{'text_space'} + $h; } $stag++; } } } else { # draw all ticks staggered for ( 0 .. $#labels ) { if ( defined $labels[$_] ) { $x = $x_start + $delta * $_ - ( $w * ( length( $labels[$_] ) ) ) / 2; if ( $stag % 2 == 0 ) { $y_start -= $self->{'text_space'} + $h; } $self->{'gd_obj'}->string( $font, $x, $y_start, $labels[$_], $textcolor ); if ( $stag % 2 == 0 ) { $y_start += $self->{'text_space'} + $h; } $stag++; } } } } elsif ( $self->{'x_ticks'} =~ /^vertical$/i ) { $y_start = $self->{'curr_y_max'} - $self->{'text_space'}; if ( $self->{'skip_x_ticks'} > 1 ) { #draw every nth tick vertical for ( 0 .. $#labels ) { if ( defined $_ ) { $x = $x_start + $delta * ( $_ * $self->{'skip_x_ticks'} ) - $h / 2; $y = $y_start - ( $x_label_len - length( $labels[ $_ * $self->{'skip_x_ticks'} ] ) ) * $w; $self->{'gd_obj'}->stringUp( $font, $x, $y, $labels[ $_ * $self->{'skip_x_ticks'} ], $textcolor ); } } } elsif ( $self->{'custom_x_ticks'} ) { foreach ( @{ $self->{'custom_x_ticks'} } ) { #draw the ticks they want vertical if ( defined $labels[$_] ) { $x = $x_start + $delta * $_ - $h / 2; $y = $y_start - ( $x_label_len - length( $labels[$_] ) ) * $w; $self->{'gd_obj'}->stringUp( $font, $x, $y, $labels[$_], $textcolor ); } } } else { # draw all ticks vertical for ( 0 .. $#labels ) { if ( defined $labels[$_] ) { $x = $x_start + $delta * $_ - $h / 2; $y = $y_start - ( $x_label_len - length( $labels[$_] ) ) * $w; $self->{'gd_obj'}->stringUp( $font, $x, $y, $labels[$_], $textcolor ); } } } } #update the borders if ( $self->{'interval_ticks'} > 0 ) { if ( $self->{'x_ticks'} =~ /^normal$/i ) { $self->{'curr_y_max'} -= $h + $self->{'text_space'} * 2; } elsif ( $self->{'x_ticks'} =~ /^staggered$/i ) { $self->{'curr_y_max'} -= 2 * $h + 3 * $self->{'text_space'}; } elsif ( $self->{'x_ticks'} =~ /^vertical$/i ) { $self->{'curr_y_max'} -= $w * $x_label_len + $self->{'text_space'} * 2; } } #draw the ticks $y_start = $self->{'curr_y_max'}; $y = $y_start - $self->{'tick_len'}; if ( $self->{'skip_x_ticks'} > 1 ) { for ( 0 .. int( ($#labels) / $self->{'skip_x_ticks'} ) ) { $x = $x_start + $delta * ( $_ * $self->{'skip_x_ticks'} ); $self->{'gd_obj'}->line( $x, $y_start, $x, $y, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x; } } } elsif ( $self->{'custom_x_ticks'} ) { foreach ( @{ $self->{'custom_x_ticks'} } ) { if ( $_ <= $ticks ) { $x = $x_start + $delta * $_; $self->{'gd_obj'}->line( $x, $y_start, $x, $y, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x; } } } } else { for ( 0 .. $#labels ) { $x = $x_start + $_ * $delta; $self->{'gd_obj'}->line( $x, $y_start, $x, $y, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x; } } } #another update of the borders $self->{'curr_y_max'} -= $self->{'tick_len'} if $self->{'interval_ticks'} > 0; #finally return return; } ## @fn private _draw_x_ticks # override the function implemented in base sub _draw_x_ticks { my $self = shift; #Use always the _draw_x_tick funktion because we always do a xy_plot!!! $self->_draw_x_number_ticks(); #and return return 1; } ## @fn private _draw_y_ticks # override the function implemented in base sub _draw_y_ticks { my $self = shift; my $side = shift || 'left'; my $data = $self->{'dataref'}; my $font = $self->{'tick_label_font'}; my $textcolor = $self->_color_role_to_index('text'); my $misccolor = $self->_color_role_to_index('misc'); my @labels = @{ $self->{'y_tick_labels'} }; my $num_points = $self->{'num_datapoints'}; my ( $w, $h ); my ( $x_start, $x, $y_start, $y, $start, $interval ); my ( $height, $delta, $label, $lines, $label_len ); my ( $s, $f ); my $x_max = -0x80000000; $self->{grid_data}->{'y'} = []; $self->{grid_data}->{'y2'} = []; # find the height $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; # make sure we got a real font unless ( ( ref $font ) eq 'GD::Font' ) { croak "The tick label font you specified isn\'t a GD Font object"; } # find out how big the font is ( $w, $h ) = ( $font->width, $font->height ); #get the base variables $interval = $self->{'interval'}; $start = $self->{'start'}; #find the biggest x value foreach ( @{ $data->[0] } ) { if ( $_ > $x_max ) { $x_max = $_; } } #calculate the number of lines and the length $lines = int( ( ( $x_max - $start ) / $interval ) + 0.99999999999 ); $lines = 1 if $lines == 0; $label_len = length($lines); #get the space between two lines $delta = $height / $lines; #now draw them if ( $lines > 1 ) { if ( $side =~ /^right$/i ) { #get the starting point $x_start = $self->{'curr_x_max'}; $y_start = $self->{'curr_y_min'}; #draw the labels for $label ( 0 .. $lines - 1 ) { $x = $x_start - $self->{'text_space'} - $label_len * $w; $y = $y_start + $label * $delta + $delta / 2 - $h / 2; $self->{'gd_obj'}->string( $font, $x, $y, $label, $textcolor ); } #draw the ticks for $label ( 0 .. $lines ) { $x = $x_start - $self->{'text_space'} * 2 - $label_len * $w - $self->{'tick_len'}; $y = $y_start + $label * $delta; $self->{'gd_obj'}->line( $x_start - $self->{'text_space'}, $y, $x, $y, $misccolor ); #add data for grid_lines push @{ $self->{grid_data}->{'y'} }, $y; } #update the borders $self->{'curr_x_max'} = $x_start - $self->{'text_space'} * 2 - $label_len * $w - $self->{'tick_len'}; } elsif ( $side =~ /^both$/i ) { #get the starting point $x_start = $self->{'curr_x_min'}; $y_start = $self->{'curr_y_min'}; #first the left side #draw the labels for $label ( 0 .. $lines - 1 ) { $x = $self->{'curr_x_min'} + $self->{'text_space'} * 2; $y = $y_start + $label * $delta + $delta / 2 - $h / 2; $self->{'gd_obj'}->string( $font, $x, $y, $self->{'f_y_tick'}->($label), $textcolor ); } #draw the ticks for $label ( 0 .. $lines ) { $x = $x_start + $self->{'text_space'} * 2 + $label_len * $w + $self->{'tick_len'}; $y = $y_start + $label * $delta; $self->{'gd_obj'}->line( $x_start + $self->{'text_space'}, $y, $x, $y, $misccolor ); } #then the right side #get the starting point $x_start = $self->{'curr_x_max'}; $y_start = $self->{'curr_y_min'}; #draw the labels for $label ( 0 .. $lines - 1 ) { $x = $x_start - $self->{'text_space'} - $label_len * $w; $y = $y_start + $label * $delta + $delta / 2 - $h / 2; $self->{'gd_obj'}->string( $font, $x, $y, $self->{'f_y_tick'}->($label), $textcolor ); } #draw the ticks for $label ( 0 .. $lines ) { $x = $x_start - $self->{'text_space'} * 2 - $label_len * $w - $self->{'tick_len'}; $y = $y_start + $label * $delta; $self->{'gd_obj'}->line( $x_start - $self->{'text_space'}, $y, $x, $y, $misccolor ); #add data for grid_lines push @{ $self->{grid_data}->{'y'} }, $y; } #update the borders $self->{'curr_x_min'} += $self->{'text_space'} * 2 + $label_len * $w + $self->{'tick_len'}; $self->{'curr_x_max'} = $x_start - $self->{'text_space'} * 2 - $label_len * $w - $self->{'tick_len'}; } else { #get the starting point $x_start = $self->{'curr_x_min'}; $y_start = $self->{'curr_y_min'}; #draw the labels for $label ( 0 .. $lines - 1 ) { $x = $self->{'curr_x_min'} + $self->{'text_space'} * 2; $y = $y_start + $label * $delta + $delta / 2 - $h / 2; $self->{'gd_obj'}->string( $font, $x, $y, $self->{'f_y_tick'}->($label), $textcolor ); } #draw the ticks for $label ( 0 .. $lines ) { $x = $x_start + $label_len * $w + $self->{'tick_len'} + $self->{'text_space'} * 3; $y = $y_start + $label * $delta; $self->{'gd_obj'}->line( $x_start + $self->{'text_space'}, $y, $x, $y, $misccolor ); #this is also where we have to draw the grid_lines push @{ $self->{grid_data}->{'y'} }, $y; } #update the borders $self->{'curr_x_min'} = $x_start + $self->{'text_space'} * 3 + $label_len * $w; } } #finally return return 1; } ## @fn private _draw_data # plot the data sub _draw_data { my $self = shift; my $data = $self->{'dataref'}; my $misccolor = $self->_color_role_to_index('misc'); my $num_points = $self->{'num_datapoints'}; $num_points = 1 if $num_points == 0; my $num_sets = $self->{'num_datasets'}; $num_sets = 1 if $num_sets == 0; my ( $lines, $split, $width, $height, $delta_lines, $delta_sets, $map, $last_line ); my ( $akt_line, $akt_set, $akt_point, $color, $x_start, $y_start, $x, $y ); my ( $x_last, $y_last, $delta_point, $brush, $mod, $x_interval, $start ); my $i = 0; my $interval = ( $self->{'max_val'} - $self->{'min_val'} ); $interval = 1 if $interval == 0; my $x_max = -0x80000000; # find the height and the width $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $width = 1 if $width == 0; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $height = 1 if $height == 0; # init the imagemap data field if they asked for it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } #get the base values $x_interval = $self->{'interval'}; $x_interval = 1 if $x_interval == 0; $start = $self->{'start'}; #find the biggest x value foreach ( @{ $data->[0] } ) { if ( $_ > $x_max ) { $x_max = $_; } } #calculate the number of lines $lines = int( ( ( $x_max - $start ) / $x_interval ) + 0.99999999999 ); $lines = 1 if $lines == 0; #find delta_lines for the space between the lines #and delta_sets for the space of the datasets of one line #and the delta_point for the space between the datapoints $delta_lines = $height / $lines; $delta_sets = $delta_lines / $num_sets; $delta_point = $width / ($x_interval); #find $map, for the y values $map = $delta_sets / $interval; #find the mod and the y_start value #correct the start value, if scale is set! Otherwise the plot is to high or to low! #The corecction, isn't perfect, but it does a good job in most cases. if ( $self->{'min_val'} >= 0 ) { $mod = $self->{'min_val'}; if ( $self->{'scale'} > 1 ) { $y_start = $self->{'curr_y_min'} + ( $interval * $map / 2 ) * ( $self->{'scale'} - 1 ); } else { $y_start = $self->{'curr_y_min'}; } } elsif ( $self->{'max_val'} <= 0 ) { $mod = $self->{'min_val'}; if ( $self->{'scale'} > 1 ) { $y_start = $self->{'curr_y_min'} + ( $interval * $map / 2 ) * ( $self->{'scale'} - 1 ); } else { $y_start = $self->{'curr_y_min'}; } } else { $y_start = $self->{'curr_y_min'} + ( $map * $self->{'min_val'} ); $mod = 0; } #The upper right corner is the point, where we start $x_start = $self->{'curr_x_min'}; #draw the lines for $akt_set ( 0 .. $num_sets - 1 ) { for $akt_point ( 0 .. $self->{'num_datapoints'} - 1 ) { #get the color for this dataset $color = $self->_color_role_to_index( 'dataset' . $akt_set ); $brush = $self->_prepare_brush( $color, 'line' ); $self->{'gd_obj'}->setBrush($brush); #start with the first point at line number zero $last_line = 0; for $akt_line ( $last_line .. $lines - 1 ) { #update the last line. That makes it a little bit faster. $last_line = $akt_line; #Don't try to draw, if there is no data if ( defined $data->[0][$akt_point] ) { if ( $data->[0][$akt_point] <= ( ( $akt_line + 1 ) * $x_interval + $start ) && $data->[0][$akt_point] >= $akt_line * $x_interval + $start ) { #the current point $x = $x_start + ( $data->[0][$akt_point] - ( $akt_line * $x_interval ) - ($start) ) * $delta_point; $y = $y_start + $akt_line * $delta_lines + $akt_set * $delta_sets + $delta_sets - ( $data->[ 1 + $akt_set ][$akt_point] - $mod ) * $map * $self->{'scale'}; #draw the line $self->{'gd_obj'}->line( $x_last, $y_last, $x, $y, gdBrushed ) if $akt_point != 0; #calculate the start point for the next line #first if the next point is in the same line if ( defined( $data->[0][ $akt_point + 1 ] ) && $data->[0][ $akt_point + 1 ] <= ( ( $akt_line + 1 ) * $x_interval + $start ) && $data->[0][ $akt_point + 1 ] > $akt_line * $x_interval + $start ) { $x_last = $x; $y_last = $y; } #second, if the next point is not in the same line else { $x_last = $self->{'curr_x_min'}; $y_last = $y_start + ( $akt_line + 1 ) * $delta_lines + $akt_set * $delta_sets + $delta_sets - ( $data->[ 1 + $akt_set ][$akt_point] - $mod ) * $map * $self->{'scale'}; } # store the imagemap data if they asked for it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$akt_set][ $akt_point - 1 ] = [ $x_last, $y_last ]; $self->{'imagemap_data'}->[$akt_set][$akt_point] = [ $x, $y ]; } } else { #Go to the next line. Maybe the current point is in that line! next; } } else { if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$akt_set][ $akt_point - 1 ] = [ undef(), undef() ]; $self->{'imagemap_data'}->[$akt_set][$akt_point] = [ undef(), undef() ]; } } } } } $y_start = $self->{'curr_y_min'}; #draw some nice little lines for $akt_set ( 0 .. $num_sets - 1 ) { for $akt_line ( 0 .. $lines - 1 ) { #draw a line between the sets at the left side of the chart $self->{'gd_obj'}->line( $x_start, $y_start + $akt_line * $delta_lines + $akt_set * $delta_sets, $x_start + $self->{'tick_len'}, $y_start + $akt_line * $delta_lines + $akt_set * $delta_sets, $misccolor ); #draw a line between the sets at the right side of the chart $self->{'gd_obj'}->line( $self->{'curr_x_max'}, $y_start + $akt_line * $delta_lines + $akt_set * $delta_sets, $self->{'curr_x_max'} - $self->{'tick_len'}, $y_start + $akt_line * $delta_lines + $akt_set * $delta_sets, $misccolor ); } } #Box it off $self->{'gd_obj'} ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); #finally retrun return; } #be a good modul and return 1 1; Chart-v2.403.9/lib/Chart/Property/0000775000175000017500000000000014341172251016425 5ustar herbertherbertChart-v2.403.9/lib/Chart/Property/DataType/0000775000175000017500000000000014341172251020140 5ustar herbertherbertChart-v2.403.9/lib/Chart/Property/DataType/Font.pm0000644000175000017500000000307714341172251021411 0ustar herbertherbert # use v5.12; package Chart::Property::DataType::Font; our $VERSION = 'v2.403.9'; use Carp; ## constructor ######################################################### sub new { my $pkg = shift; my $def = shift; return unless ref $def eq 'HASH'; bless {}; } ## getter ############################################################## sub name { } sub bold { } sub size { } sub unicode { } sub truetype { } 1; __END__ =pod =head1 NAME Chart::Font - read only single font holding objects =head1 SYNOPSIS my $red = Chart::Color->new('red'); say $red->add('blue')->name; # magenta, mixed in RGB space Chart::Color->new( 0, 0, 255)->hsl # 240, 100, 50 = blue $blue->blend_with({H=> 0, S=> 0, L=> 80}, 0.1);# mix blue with a little grey $red->gradient( '#0000FF', 10); # 10 colors from red to blue $red->complementary( 3 ); # get fitting red green and blue =head1 DESCRIPTION =head1 COPYRIGHT & LICENSE Copyright 2022 Herbert Breunung. This program is free software; you can redistribute it and/or modify it under same terms as Perl itself. =head1 AUTHOR Herbert Breunung, =cut __END__ $im->string( GD::Font->Tiny , 20, 20, 'Tiny', 2); $im->string( GD::Font->Small , 20, 50, 'Small', 1); $im->string( GD::Font->MediumBold , 20, 80, 'MediumBold', 2); $im->string( GD::Font->Large , 20, 110, 'Large', 1); $im->string( GD::Font->Giant , 20, 140, 'Giant', 2); gdTinyFont gdSmallFont gdMediumBoldFont gdLargeFont gdGiantFont Chart-v2.403.9/lib/Chart/Property/DataType/Color.pm0000644000175000017500000000045314341172251021554 0ustar herbertherbert # Chart::Color: read only color holding object # with methods for relation, mixing and transitions use v5.12; package Chart::Property::DataType::Color; our $VERSION = 'v2.403.9'; use base qw(Graphics::Toolkit::Color); 1; __END__ =pod moved to L =cut Chart-v2.403.9/lib/Chart/Property.pm0000644000175000017500000000061114341172251016757 0ustar herbertherbert # use v5.12; package Chart::Property; our $VERSION = 'v2.403.9'; use Carp; 1; __END__ =pod =head1 NAME Chart::Property - managing properties of a chart =head1 COPYRIGHT & LICENSE Copyright 2022 Herbert Breunung. This program is free software; you can redistribute it and/or modify it under same terms as Perl itself. =head1 AUTHOR Herbert Breunung, =cut Chart-v2.403.9/lib/Chart/Manual/0000775000175000017500000000000014341172251016016 5ustar herbertherbertChart-v2.403.9/lib/Chart/Manual/Workflows.pod0000644000175000017500000000722314341172251020521 0ustar herbertherbert =encoding UTF-8 =head1 NAME Chart::Manual::Workflows - different ways to create charts =head1 OVERVIEW Four of the five major steps in creating a chart image are fixed in their order. First you have to L, secondly L create the object. After that you may change some L. And always as a last step you L, no matter if the output goes to STDOUT or into a file. The only flexibility lies in how you prefer to provide the data. And here you have three options. Most commonly you L at the time, which could be also understood as a row of the complete data table. Second option is to build the table L. Thirdly you can drop the complete table L, either by reference to a data structure or L containing the data. The last option is closed if you already given the object data. It is not advisable to reuse a chart object for further image creation outside of modern art projects. =head1 STEPS Most steps are already explained elsewhere and the OVERVIEW just links there. The missing bits are layed out here. =head2 use Chart As with any other Modul you have to: use Chart::[Type]; Type being a placeholder for a name of a chart type, which are: Bars, Composite, Direction, ErrorBars, HorizontalBars, Lines, LinesPoints, Mountain, Pareto, Pie, Points, Split, StackedBars. To know more about them read L. Alternatively write to load all chart types at ones with use Chart; Both are not importing any symbols in your name space but load L and L. =head2 drop data All the methods listed in the L, that create the final image, take as an optional, second argument data. This data may be delivered either as a reference to an array of arrays: my $data = [ [ 1, 4, 3 ... ], # data set 0 [ 5, 8, 2 ... ], # data set 1 ... ]; $graph->png( 'file.png', $data ); or in form of a file. Then the argument has to be a file name or a file handle (old school as in C or modern as in C<$FH>). Alternatively use the method L. =head2 data files Are arbitrary named text files containing one or several rows of numbers, which have to be separated by spaces or tabs (\t) (mixing allowed). Perl style comments or empty lines will be ignored, but rows containing different amount of numbers will cause problems. =head2 create image Currently we support only images in the PNG and JPEG format. The methods to create them are named straight forwardly: -EL and -EL. Both take the same arguments and produce image files. For shell scripting or web programming you need the image binary, which you get with: -EL or -EL. Some users might even want the L object for further processing by your perl programm. In that case please use: -EL or -EL. After having created a chart for web purposes, you also might want to utilize L. =head1 COPYRIGHT & LICENSE Copyright 2022 Herbert Breunung. This program is free software; you can redistribute it and/or modify it under same terms as Perl itself. =head1 AUTHOR Herbert Breunung, =cut Chart-v2.403.9/lib/Chart/Manual/Methods.pod0000644000175000017500000001510414341172251020124 0ustar herbertherbert =encoding UTF-8 =head1 NAME Chart::Manual::Methods - user API =head1 OVERVIEW This are all methods for the chart user. =head1 ALPHABETICALLY =head2 add_datafile Loads all data of a chart (one or more data sets) from a file. (Works only if no data yet added.) You have to either provide a filename or filehandle (OLD_SCHOOL or $scalar). $graph->add_dataset( 'file.tsv' ); $graph->add_dataset( $file_handle ); $graph->add_dataset( FILE_HANDLE ); An optional second argument, which defaults to C<'set'> can change the file format if set to C<'pt'>. In C<'set'> mode every row of the file content is fed to L, in C<'pt'> every row get loaded like via L. In other words: C<'pt'> transposes the data table. $graph->add_dataset( 'file.tsv', 'pt' ); The arbitrary named text files have to contain one or several rows of numbers. The numbers need to be separated by spaces or tabs (\t) (mixing allowed). Perl style comments or empty lines will be ignored, but rows containing different amount of numbers will cause problems. =head2 add_dataset Adding a list of values as one data set. That is one row in the overall data table. The first data set are usually x-axis labels (domain set). Make sure all sets have the same length. $graph->add_dataset( 1, 2, 3, ... ); $graph->add_dataset( [1, 2, 3, ...] ); For instances with L, L or L one data set is represented by a set of graphic items (points, bars or line) of one color. =head2 add_pt Adds (appends) to each already existing data set one value. That is a column in the overall data table. In this example it adds to set 0 the value 3, to set 1 the 6 and so forth. Make sure that the list lengths matches the number of already existing data sets. $graph->add_pt( 3, 6, 9, ... ); $graph->add_pt( [3, 6, 9, ...] ); =head2 cgi_jpeg Creates same JPEG image as L, but outputs to STDOUT. Since no file name or handle needed, only the optional data argument is acceptable. $graph->cgi_jpeg( ); my @data = ([1, 2, 3], # data set 0 [3, 4, 5]); # data set 1 $graph->cgi_jpeg( \@data ); =head2 cgi_png Creates same PNG image as L, but outputs to STDOUT. Since no file name or handle needed, only the optional data argument is acceptable. $graph->cgi_png( ); my $data = [[1, 2, 3], # data set 0 [3, 4, 5] ]; # data set 1 $graph->cgi_png( $data ); =head2 clear_data Needs no arguments and deletes all so far added data. =head2 get_data Return all data (array of arrays) given to a graph. =head2 imagemap_dump When creating a chart for web purposes by L or L, you maybe want the information, where the areas of interests in the image are located, that should react to a users click. (HTML tag map). These areas are bounding boxes around the drawn bars or points. You will get per box the values: x1, y1, x2, y2 in one array. These arrays are again in an Array holding, all boxes from one data set. The highest level array again holds all arrays of all data sets, beginning with index C<1>. This method can only be called, if the functionality is activated by setting the property: L to C<'true'>. $graph->set( imagemap => 'true'); my $image_map = $graph->imagemap_dump(); say "coordinates of first bar, first data set:"; say for @{$image_map->[1][0]}; =head2 jpeg Creates an JPEG image from given data and properties. Accepts a file name or a file handle (raw or in a SCALAR). The method closes the file handle. $graph->jpeg( 'image.jpg' ); $graph->jpeg( $file_handle ); $graph->jpeg( FILE_HANDLE ); $graph->jpeg( 'image.jpg', $data ); $graph->jpeg( 'image.jpg', 'data_file.tsv' ); $graph->jpeg( 'image.jpg', $file_handle ); $graph->jpeg( 'image.jpg', FILE_HANDLE ); The second, optional argument is the data in form of an array of arrays reference. This only works, if there is no data already given to the object. Alternatively the data can also be loaded from a file, just provide the filename or filehandle (modern in SCALAR or old school). Read more about the file format at L and note that this method has another option for loading transposed data tables. =head2 new Creates a new chart object. Takes two optional arguments, which are the width and height of the to be produced image in pixels. Defaults for that are 400 x 300. my $graph = Chart::Bars->new ( ); my $graph = Chart::Bars->new (600, 700); Instead of Bars, you can also use: Composite, Direction, ErrorBars, HorizontalBars, Lines, LinesPoints, Mountain, Pareto, Pie, Points, Split and StackedBars. To know more about them read L. =head2 png Creates an PNG image from given data and properties. Accepts a file name or a file handle (raw or in a SCALAR). The method closes the file handle. $graph->png( 'image.png' ); $graph->png( $file_handle ); $graph->png( FILE_HANDLE ); $graph->png( 'image.png', $data ); $graph->png( 'image.png', 'data_file.tsv' ); $graph->png( 'image.png', $file_handle ); $graph->png( 'image.png', FILE_HANDLE ); The second, optional argument is the data in form of an array of arrays reference. This only works, if there is no data already given to the object. Alternatively the data can also be loaded from a file, just provide the filename or filehandle (modern in SCALAR or old school). Read more about the file format at L and note that this method has another option for loading transposed data tables. =head2 scalar_jpeg Creates same JPEG image as L but returns the image binary into a variable, not to STDOUT or a file. my $image_binary = $graph->scalar_jpeg(); my $image_binary = $graph->scalar_jpeg( $data ); =head2 scalar_png Creates same PNG image as L but returns the image binary into a variable, not to STDOUT or a file. my $image_binary = $graph->scalar_png(); my $image_binary = $graph->scalar_png( $data ); =head2 set Method to change one or more chart properties in hash form. $chart->set ( property_name => 'new value', ... ); $chart->set ( %properties ); Different chart types react to different properties, which are all listed and explained under L. =head1 COPYRIGHT & LICENSE Copyright 2022 Herbert Breunung. This program is free software; you can redistribute it and/or modify it under same terms as Perl itself. =head1 AUTHOR Herbert Breunung, =cut Chart-v2.403.9/lib/Chart/Manual/Properties.pod0000644000175000017500000006561514341172251020671 0ustar herbertherbert =encoding UTF-8 =head1 NAME Chart::Manual::Properties - complete guide to Chart's changeable properties =head1 INTRO Individual properties of a chart can be changed via method C: $chart_object->set( %properties); $chart_object->set( key => 'value', ... ); This page documents all viable keys and values for this calls. The first section lists the most common data types and their acceptable values. Please note that all chart L reacts only to a distinct subset of properties. The L
therefore groups all properties by chart type or set of types, where they have an effect. The descriptions in this section are shortened to achive a tabular overview. A fuller description of each property provides the L, where they are alpabetically sorted. The general layout of a chart image: =for HTML

layout

=head1 DATA TYPES Every key expects values of a certain data type. The more often occurring types are sorted, enumerated and decibed in this section. =head2 boolean Yes or no decisions you can answer with the perlish C<0> or C<1> as well as with C<'true'> or C<'false'>, which might be shortened to C<'t'> and C<'T'> or C<'f'> and C<'F'>. C<'none'> as an alias of C<'false'> is also an option. =head2 code Anonymous subroutine that usually takes the first argument and returns a transformed result: sub { $_[0] + 1 } =head2 color The usual options are: names: 'blue' All names are tabulated visually and by name under L. You can also define a color with integer values in the RGB or HSL space. Acceptable ranges are for RGB: 3x 0..255 and for HSL: 0..359, 2 x 0..100. RGB triplets: [0,255,255] RGB hashes: { Red => 0, Green => 255, Blue => 255 } RGB hashes: { R => 0, G => 255, B => 255 } HSL hashes: { H => 240, S => 100, L => 50 } HSL hashes: { hue => 240, saturation => 100, lightness => 50 } Detailed explanation and even more options you find under L. =head2 font There are a handful GD built in font object, than can be referenced: GD::Font->Tiny GD::Font->Small GD::Font->MediumBold GD::Font->Large GD::Font->Giant =head2 integer Whole numbers (without decimals) to set position, size and alike. =head2 positive integer Integers greater or equal zero. =head2 string Short texts for title, sub_title and labels on axis, tics and legend. Line breaks (C) are only acceptable inside single quotes and in the L. =head1 BY CHART TYPE Summary of all properties, grouped by chart type where there applicable. Sections are named after the Chart type in question, except B and B. The format is: name ... L, description; C. Click on name for a longer explanation. =head2 All Properties available in all chart types: L ... L of whole image background ; C<'white'> L ..... L of points/lines/bars of dataset number x = 0..64 L ... L of vertical and horizontal grid lines in the plot background; C<'black'> L . L of vertical grid lines; C<'black'> L . L of horizontal grid lines; C<'black'> L ......... L of all text ; C<'black'> L ........ L of text above the graph ; C<'black'> L ...... L of x-axis label text; C<'black'> L ...... L of y-axis (left side) label text; C<'black'> L .... L of y-axis (right side) label text; C<'black'> L ....... L of boxes, ticks, axis and alike; C<'black'> L ................ L that transforms "would have" into actual tick label on left x-axis; C L ............... L that transforms "would have" into actual tick label on right y-axis; C L .......... space between graph and title + legend in pixel; C<10> L ...... L: sets plot background to grey; C<'true'> L ............ L: enable imagemap_dump(); C<'false'> L .......... L of axis labels; GD::Font->MediumBold L .............. qw[left right top bottom none]: placement of the legend; C<'right'> L ......... L of the text in the legend; GD::Font->Small. L ....... (array ref): labels of colors in legend; C L .......... L: space between graph parts and image edge in pixel; C<10> L ........... L: text below the L in smaller letters; C<''> L .......... L: extra space around any text; C<2> L ............... L: text on top of a chart; C<''> L .......... L of the title text; GD::Font->Large L ......... L: full image background transparency; C<'false'> L ............. L: x-axis label text; C<''> L ............. L:label on the standard, left y-axis; C<''> L ............ L:label on right y-axis, if different; C<''> =head2 NoPie Properties available in all chart types of kinda xy-plot, not L: L ..... (array ref): [0,3,4] displays 0th, 3rd, and 4th x-tick; C L ......... L: draw vertical and horizontal grid lines; C<'false'> L ....... L: forces y-axis to include zero; C<'false'> L . L: draw x-axis ticks with label only on whole values; C<'false'> L ............ L: maximum value on y-axis; C L ........ L: maximum of ticks and labels to draw on x-axis; C<100> L ........ L: maximum of ticks and labels to draw on y-axis; C<100> L ............ L: maximum value on y-axis; C L ........ L: minimum of ticks and labels to draw on x-axis; C<6> (min 2) L ........ L: minimum of ticks and labels to draw on y-axis; C<6> (min 2) L .......... L: nr. of numerals after the decimal point in axis labels; C<3> L ..... L: draw only n'th tick on whole x-value; C<1> (if L) L ....... L: draw only every n'th tick with label; C<1> L ............... L: sort data in ascending order; C<'false'> L .... L of tick labels; GD::Font->Small L ........... L: length of the x- and y-ticks in pixels; C<4> L ....... L: vertical grid lines matching x ticks; C<'false'> L ............ qw[normal staggered vertical]: style of x-axis labels; C<'normal'> L ............ L: forces x-y-graph, with numeric x-axis; C<'false'> L ............. (C<'left'>, C<'right'>, C<'both'>): position of y-axis;C<'left'> L ....... L: horizontal grid lines matching y ticks; C<'false'> =head2 Bars This includes Bars StackedBars and HorizontalBars. L ........ L: leave space between the groups of bars; C<'true'> =head2 HorizontalBars L ....... L: draw every n'th tick with label on y-axis; C<1> =head2 Composite L ....... brush style of points associated with left y-axis; C L ....... brush style of points associated with right y-axis; C L ..... (array of arrays) which data sets produce which chart types; C L .......... L that transforms "would have" into actual label on left y-axis; C L .......... L that transforms "would have" into actual label on right y-axis; C L Length of the color example line in legend in pixel; C<20> L ........... L maximum y value on the left axis; C L ........... L maximum y value on the right axis; C L ........... L minimum y value on the left axis; C L ........... L minimum y value on the right axis; C L ........ L both y-axis have same min and max; C L ........... L: number of y ticks on left y-axis; C L ........... L: number of y ticks on right y-axis; C =head2 Direction L ..... L: angle between radial lines; C<30> L .............. L: paint points as arrows; C<'false'> L ......... L: width of the lines in pixels; C<6> L ............... L: connected points with lines; C<'false'> L ........ L: max nr. of coordinate grid circles; C<100> L ........ L: min nr. of coordinate grid circles; C<4> (min. 2) L ............... L: use odd numbered data sets to build pairs with the next; C<'false'> L .............. L: paint points at all (or only lines); C<'false'> L .............. L: reverse x axis; C<'false'> L ............ L: radius of points in pixels; C<18> =head2 ErrorBars L ......... L: width of the lines in pixels; C<6> L ............ L: radius of points in pixels; C<18> L ......... L: same values for upper and lower error bounds; C<'false'> =head2 Lines including chart type LinesPoints L ......... L: width of the lines in pixels; C<6> =head2 Points including chart type LinesPoints L ......... shape of the points; C<'FilledCircle'> L ............ L: radius of points in pixels; C<18> =head2 Pie L ....... qw[percent value both none]: label content on pie slice label; C<'percent'> L qw[percent value both none]: label content in the legend; C<'value'> L ....... L: draw lines connecting pie slices and label; C<'false'> L ............... L: percentage of visible radius; C<1> (full pie) =head2 Split L ........... L: interval of a plot segment; C<'undef'> L ..... L: number of ticks on x-axis; C<5> L .............. L: factor for y-values; C<1> L .............. L: start value of the first interval; C<'undef'> =head1 ALPHABETICALLY =head2 angle_interval L only: how many radial lines should be drawn. The default value is 30, which means that a line will be drawn every 30 degrees. Valid Values are: 0, 5, 10, 15, 20, 30, 45 and 60. If you choose 0, direction will draw no line. =head2 arrow L only: L that if C<'true'>, chart will draw a arrow from the center to the point. Defaults to C<'false'>. =head2 brush_size L only: Integer sets the width of the lines in pixels. Default is 6. =head2 background see L =head2 brushStyle Sets the shape of points for Chart::Points, Chart::LinesPoints. Possible values are: 'FilledCircle', 'circle', 'donut', 'OpenCircle', 'fatPlus', 'triangle', 'upsidedownTriangle', 'square', 'hollowSquare', 'OpenRectangle', 'FilledDiamond', 'OpenDiamond', 'Star', 'OpenStar'. Default: 'FilledCircle'. Look at Demo at L. =head2 brush_style1 L only: brush style of points associated with left y-axis. =head2 brush_style2 L only: brush style of points associated with right y-axis. =head2 colors The key C is special, because there are a lot of things that need to be colored. That's why its value is a hash with keys within, which name what exactly needs to be colored. Their value has to be a L definition (name, RGB array ref or HSL hash ref): $obj->set('colors' => {'background' => [255,255,255]}); sets the background color to white (which is the default). Valid keys are: 'background' (background color for the image) 'title' (color of the title) 'text' (all the text in the chart) 'x_label' (color of the x-axis label) 'y_label' (color of the first y axis label) 'y_label2' (color of the second y axis label) 'grid_lines' (color of the grid lines) 'x_grid_lines' (color of the x grid lines - for x axis ticks) 'y_grid_lines' (color of the y grid lines - for to left y axis ticks) 'y2_grid_lines' (color of the y2 grid lines - for right y axis ticks) 'dataset0'..'dataset63' (the different datasets) 'misc' (everything else, ie. axis, ticks, box around the legend) NB. For composite charts, there is a limit of 8 datasets per component. The colors for 'dataset8' through 'dataset15' become the colors for 'dataset0' through 'dataset7' for the second component chart. =head2 composite_info L only: information about which data set gets visualized by which chart type. It should be a reference to an array of array references, containing information like the following: $obj->set ('composite_info' => [ ['Bars', [1,2]], ['Lines', [3,4] ] ]); This example would set the two component charts to be a bar chart and a line chart. It would use the first two data sets for the bar chart (note that the numbering starts at 1, not zero like most of the other numbered things in Chart), and the second two data sets for the line chart. The default is undef. NB. Chart::Composite can only do two component charts. =head2 custom_x_ticks Used in L, L, L, L, and L charts. This option allows you to you to specify exactly which x-ticks and x-tick labels should be drawn. It should be assigned a reference to an array of desired ticks. Just remember that I'm counting from the 0th element of the array. (ie., if 'custom_x_ticks' is assigned [0,3,4], then the 0th, 3rd, and 4th x-ticks will be displayed) =head2 datasetx see L =head2 f_x_tick Needs a reference to a function (L) which uses the x-tick labels generated by the '@data[0]' as the argument. The result of this function can reformat the labels. For instance $obj -> set ('f_x_tick' => \&formatter ); An example for the function formatter: x labels are seconds since an event. The referenced function can transform this seconds to hour, minutes and seconds. =head2 f_y_tick The same situation as for 'f_x_tick' but now used for y labels. =head2 f_y_tick1 L only: L ref to a function which has one argument and has to return a string which labels the first resp. second y axis. Both default to undef. =head2 f_y_tick2 L only: L ref to a function which has one argument and has to return a string which labels the first resp. second y axis. Both default to undef. =head2 graph_border L only: Sets the number of pixels used as a border between the title/labels and the actual graph within the image. Defaults to 10. =head2 grey_background Puts a nice soft grey background on the data plot area when set to C<'true'>. Default is C<'true'>. =head2 grid_lines L: draw grid lines matching up to x and y ticks. Default is C<'false'>. =head2 imagemap Lets Chart know you're going to ask for information about the placement of the data for use in creating an image map from the png. This information can be retrieved using the L method. NB. that the imagemap_dump() method cannot be called until after the Chart has been L. =head2 include_zero If C<'true'>, forces the y-axis to include zero if it is not in the dataset range. Default is C<'false'>. In general, it is better to use this, than to set the L if that is all you want to achieve. =head2 integer_ticks_only L specifies to draw the x- and y-ticks at floating point values (as normal) or when set to C<'true'> only at integer values. Default: C<'false'> =head2 interval L only: Sets the interval of one partition of plot. Defaults 'undef'. =head2 interval_ticks L only: Number of ticks for the x-axis. Defaults to 5. =head2 label_font This option changes the L of the axis labels. Default is GD::Font->MediumBold. =head2 label_values L only: What kind of value labels to show alongside the pie. Valid values are C<'percent'>, C<'value'>, C<'both'> and C<'none'>. Defaults to C<'percent'>. =head2 legend Specifies the placement of the legend. Valid values are C<'left'>, C<'right'>, C<'top'>, C<'bottom'>. Setting this to C<'none'> tells chart not to draw a legend. Default is C<'right'>. =head2 legend_example_size L only: Length of the example line in the legend in pixels. Defaults to C<20>. =head2 legend_font This option changes the L of the text in the legend. Default is GD::Font->Small. =head2 legend_labels Array reference containing texts, which are the labels assigned to each color in the legend. Amount has to correspond to the amount of data sets. @labels = ('foo', 'bar'); $obj->set ('legend_labels' => \@labels); Default is empty, in which case C<'Dataset 1'>, C<'Dataset 2'>, etc. are used as the labels. =head2 legend_label_values L only: What labels to draw in the legend. Valid values are c<'percent'>, C<'value'>, C<'both'> and C<'none'>. Defaults to C<'value'>. =head2 legend_lines L only: L to decide if lines connecting pie slices and label are drawn. Default is C<'false'>. =head2 label_values L only: Labels to draw beside the pie slices. Valid values are C<'percent'>, C<'value'>, C<'both'> and C<'none'>. Defaults to C<'percent'>. =head2 line L only: If you turn this option to C<'true'>, then point will be connected with lines. Defaults to C<'false'>. =head2 max_circles L only: Sets the maximum number of circles in the coordinate system. Default is 100. This limit is used to avoid plotting an unreasonable large number of ticks if non-round values are used for the min_val and max_val. =head2 max_val Sets the maximum y-value on the graph, overriding the normal auto-scaling. Default is undef. =head2 max_val1 L only: Maximum y-value for the first (left y-axis) components. Default to undef. =head2 max_val2 L only: Maximum y-value for the second (right y-axis) components. Default to undef. =head2 max_x_ticks Work similar as 'max_y_ticks' and 'min_y_ticks'. Of course, only for a xy_plot. =head2 max_y_ticks Sets the maximum number of y_ticks to draw when generating a scale. Default is 100. This limit is used to avoid plotting an unreasonable large number of ticks if non-round values are used for the min_val and max_val. The value for 'max_y_ticks' should be at least 5 times larger than 'min_y_ticks'. =head2 min_circles L only: Sets the minimum number of circles when generating a scale for direction. Default is 4, minimum is 2. =head2 min_val Sets the minimum y-value on the graph, overriding the normal auto-scaling. Default is undef. Caution: should be used when setting 'max_val' and 'min_val' to floating point or non-round numbers. This is because the scale must start & end on a tick, ticks must have round-number intervals, and include round numbers. Example: Suppose your data set has a range of 35-114 units. If you specify them as the 'min_val' & 'max_val', the y_axis will be plotted with 80 ticks every 1 unit.. If no 'min_val' & 'max_val', the system will auto scale the range to 30-120 with 10 ticks every 10 units. If the 'min_val' & 'max_val' are specified to excessive precision, they may be overridden by the system, plotting a maximum 'max_y_ticks' ticks. =head2 min_val1 L only: Minimum y-value for the first (left y-axis) component. Default to undef. =head2 min_val2 L only: Minimum y-value for the second (right y-axis) component. Default to undef. =head2 min_x_ticks Work similar as 'max_y_ticks' and 'min_y_ticks'. Of course, only for a xy_plot. =head2 min_y_ticks Sets the minimum number of y_ticks to draw when generating a scale. Default is 6, The minimum is 2. =head2 no_cache Adds Pragma: no-cache to the http header while output for CGI. Be careful with this one, as Netscape 4.5 is unfriendly with POST using this method. =head2 pairs L only: L if C<'true'>, Chart uses the first dataset as a set of degrees and the second dataset as a set of values. Then, the third set is a set of degrees and the fourth a set of values ... If 'pairs' is set to C<'false'>, Chart uses the first dataset as a set of angels and all following datasets as sets of values. Defaults to C<'false'>. =head2 png_border Sets the number of pixels used as a border between the graph and the edges of the image. Defaults to 10. =head2 point Indicates to draw points in a direction chart. C<'true'> or C<'false'> possible. Defaults to C<'true'>. =head2 polar L only: If set C<'true'>, the maximum x value is in the center of the coordinate system. Defaults to C<'false'>. =head2 precision Sets the number of numerals after the decimal point. Affects in most cases the y-axis. But also the x-axis if L was set and also the labels in a pie chart. Defaults to C<3>. =head2 pt_size L and L only: Sets the radius of the points in pixels. Default is C<18>. =head2 ring L only: sets the "thickness" of the pie, the percentage of the radius, which is visible. Defaults to C<1> (full pie chart). Good values are between C<0.2> and C<0.4>. =head2 scale L only: Every y-value will be multiplied with that value, but the scale won't change. Which means that split allows one to overdraw certain rows! Only useful if you want to give prominence to the maximal amplitudes of the data. Defaults to C<1>. =head2 same_error L only: It tells chart that you want use the same error value of a data point if set to C<'true'>. Look at the documentation to see how the module ErrorBars works. Default: C<'false'>. =head2 same_y_axes L only: Forces both component charts to use the same maximum and minimum y-values if set to C<'true'>. This helps to keep the composite charts from being too confusing. Default is C. =head2 skip_int_ticks If C<'true'> the labels and ticks will be drawn every nth tick. Of course in horizontalBars it affects the x-axis. Default to C<1>, no skipping. =head2 skip_x_ticks Sets the number of x-ticks and x-tick labels to skip. (ie. if 'skip_x_ticks' was set to 4, Chart would draw every 4th x-tick and x-tick label). Default is C. =head2 skip_y_ticks L only: Draw only every n'th tick with label on y-axis. does for other charts. Defaults to C<1> (draw all). =head2 sort In a xy-plot, the data will be sorted ascending if set to C<'true'>. (Should be set if the data isn't sorted, especially in Lines, Split and LinesPoints) In a Pareto Chart the data will be sorted descending. Defaults to C<'false'>. =head2 spaced_bars L only: Leaves space between the groups of bars at each data point when set to C<'true'>. This just makes it easier to read a bar chart. Default is C<'true'>. =head2 start L only: Sets the start value of the first interval. If the x coordinate of the first data point is zero, you should 'set' to zero. Default is C<'undef'>. =head2 sub_title Write a sub-title under the L in smaller letters. Default is empty. =head2 text_space Sets the amount of space left on the sides (lext and right) of text, (title, legend, label) to make it more readable. Defaults to C<2>. =head2 tick_label_font This is the font for the tick labels. It also needs a GD font object as an argument. Default is GD::Font->Small. =head2 tick_len Sets the length of the x- and y-ticks in pixels. Default is C<4>. =head2 title Content of title text. If empty, no title is drawn. It recognizes '\n' as a newline, and acts accordingly. Remember, if you want to use normal quotation marks instead of single quotation marks then you have to quote "\\n". Default is empty. =head2 title_font This option changes the L of the title. Default is GD::Font->Large. =head2 transparent Makes the background of the whole image transparent if set to C<'true'>. Useful for making web page images. Default is C<'false'>. =head2 x_grid_lines Draws vertical grid lines matching up to x ticks if set to C<'true'>. Default is C<'false'>. =head2 x_label Tells Chart what to use for the x-axis label. If empty, no label is drawn. Default is empty. =head2 x_ticks Specifies how to draw the x-tick labels. Valid values are 1: C<'normal'>, 2: C<'staggered'> (alternating on upper and lower row - for very long labels), and 3: C<'vertical'> (the labels are draw upwards in right angle). Default is C<'normal'>. =head2 xy_plot Forces Chart to plot a x-y-graph, which means, that the x-axis is also numeric if set to C<'true'>. Very useful for mathematical graphs. Works for Lines, Points, LinesPoints and ErrorBars. Split makes always a xy_plot. Defaults to C<'false'>. =head2 y_label Tells Chart what to use for labels on the standard, left y-axis. If empty, no label is drawn. Default is empty (C<''>). =head2 y_label2 Text of the label on the second, right y-axis (if different from left). If empty, no label is drawn. Default is empty (C<''>). =head2 y_axes Tells Chart where to place the y-axis. Has no effect on Composite and Pie. Valid values are C<'left'>, C<'right'> and C<'both'>. Defaults to C<'left'>. =head2 y_grid_lines Draws horizontal grid lines matching up to y ticks if set to C<'true'>. Default is C<'false'>. =head2 y_ticks1 L only: number of y ticks to use on the first and second y-axis (y_ticks2). Please note that if you just set the 'y_ticks' option, both axes will use that number of y ticks. Both default to undef. =head2 y_ticks2 see L =head1 COPYRIGHT & LICENSE Copyright 2022 David Bonner, Herbert Breunung. This program is free software; you can redistribute it and/or modify it under same terms as Perl itself. =head1 AUTHOR David Bonner, Chart Group, Herbert Breunung, =cut Chart-v2.403.9/lib/Chart/Manual/Types.pod0000644000175000017500000006437514341172251017643 0ustar herbertherbert =encoding UTF-8 =head1 NAME Chart::Manual::Types - all chart types by example =head1 OVERVIEW This page illustrates all supported chart types, describes their special abilities and programming needs. Detailled information is linked whenever possible. Currently available are xycharts with points, lines, mountains, bar charts, stacked bars, error bars, pie and ring charts with split and polar coordinate systems. Also composite charts are possible. Generally, each chart type here is implementd by a class: Chart::* (name), which inherits most of its methods from Chart::Base. Every L takes two arguments, which are width and height of the later generated image. =head2 Bars =for HTML

dual bar chart

The class Chart::Bars creates a chart made up of a series of vertical bars. The length of each bar depicts one value of your second data set. The first data set however, defines the domain. In this example the first value of the first (domain) data set is 'camel'. So the first value of the second set (300) is positioned above the first tick on the x-axis labeled 'camel'. Right beside it is a differently colored bar, depticting the first value of the third data set (800). Since the option L is set to C<'true'> on default, both bars are separated from the next group of bars. In this example it also makes also sense to activate the horizontal L and give them a subtle color. Further important was it to set the property L to zero, so that the bars could be seen in full length and not from the min value of the data sets (300) on upwards. L set to zero just drops the decimals on the tick label so the chart looks a little cleaner. All the the other color set serv the same purpose. use Chart::Bars; my $g = Chart::Bars->new( 500, 300 ); $g->add_dataset( qw/ camel cat dog bear shell/ ); $g->add_dataset( 300, 400, 800, 500, 900 ); $g->add_dataset( 800, 600, 300, 300, 400 ); $g->set( title => 'Bars !', x_label => 'Group', y_label => 'Value', y_grid_lines => 'true', colors => { y_grid_lines => 'gray70', misc => 'gray55', text => 'gray55', x_label => 'gray40', y_label => 'gray40', title => 'gray20', }, min_val => 0, precision => 0, # spaced_bars => 'false', ); $g->png("bars.png"); =head2 Composite =for HTML

composite chart

The class Chart::Composite creates a two component chart with two types of charts which are layered one above each other. Just set the option composite info. For example, you can create a two component chart with bars and lines. A composite chart does not make sense with all combinations of chart types, but it works pretty good with Lines, Points, LinesPoints and Bars. Note that two similar chart types may come into visual conflict. Chart::Composite can do only composite charts made up of two components. In this example are the data sets one and two displayed as L and the next two als L as set by the property L. Please read these sections too. Otherwise we only colored the last data set for better contrast und cut the decimals from the axis labels via L. use Chart::Composite; my $g = Chart::Composite->new( ); $g->add_dataset( 1, 2, 3 ); $g->add_dataset( 10, 20, 30 ); $g->add_dataset( 15, 25, 32 ); $g->add_dataset( 7, 24, 23 ); $g->add_dataset( 5.1, 7.5, 9.9 ); $g->set( title => 'Composite Chart', composite_info => [ [ 'Bars', [ 1, 2 ] ], [ 'LinesPoints', [ 3, 4 ] ] ], include_zero => 'true', precision => 0, colors => { dataset3 => 'darkorange', } ); $g->png("composite.png"); =head2 Direction =for HTML

polar chart

The class Chart::Direction creates a diagram based on polar coordinates. This type of diagram is occasionally referred to as a radial or as a radar chart, which has a circle as x-axis and its values define an angle. The y-value in the center is L and the most outer circle is at L. In order to reverse that you have to set L C<'true'>. In our example we preferred to have a real comparison between arrow lengths so we artificially put zero as C by setting L C<'true'>. The just mentioned arrow style we achieved by deactivating L (drawing small circles) and turning on L. An additional C<'true'> L would connect the points (arrow heads) with lines (as well as the first and last). As in L<\Lines> and L<\Points>: L defines the point size and L the line thickness. Usually the background of each chart (just inside the coordinate system) is a light gray - here deactivated. Radial grid lines are drawn every 45 degrees (L). As with most chart types, the first data set defines the domain : the x-values of all following data sets, which then define the associated y-values (and therefore all sets have to have the same length). Because we set in this example L C<'true'>, now every odd numbered data set is the domain for the next set. But they still all sets have to have the same length. use Chart::Direction; my $g = Chart::Direction->new( 500, 500 ); $g->add_dataset( 210, 220, 200, 215, 225, 200 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->add_dataset( 120, 130, 110, 125, 135, 110 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->add_dataset( 300, 310, 290, 305, 315, 290 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->set( title => 'Direction Demo', angle_interval => 45, precision => 0, arrow => 'true', point => 'false', include_zero => 'true', legend => 'none', grey_background => 'false', pairs => 'true', ); $g->png("direction.png"); =head2 ErrorBars =for HTML

error bar chart

The class Chart::ErrorBars creates a point chart with error bars, which are vertical lines, depicting the uncertainty - a range which is possible for that value. As seen in the example, we need four data sets to define a series of error bars. The first data set are the x-values of the error bars, the second the y-values. The third set holds the lenght of the upper part of the error bar and the fourth set the lower part (distance between main point and the lower bound of the error bar). The fourth set might be omitted, when property L is set C<'true'>. In this case upper and lower part of the error bar have the same size. Because all this produced only one set of error bars with one color, there is no need for a legend. That is why it is switched off by setting property L to C<'none'>. The label on the x-axis are painted in the C<'staggered'> style, so they don't overlap. L refers to the diameter in pixel of the errors bars central point. L is the thickness of the bar. For better readability C<'both'> L were labeled. use Chart::ErrorBars; my $g = Chart::ErrorBars->new( 500, 400 ); $g->add_dataset(qw(1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2 2.1 2.2 2.3 2.4 2.5)); $g->add_dataset(qw(1 1.1 1.2 1.1 1.14 1.15 1.26 1.2 1.1 1.19 1.2 1.4 1.6 2.0 2.5 3.1)); $g->add_dataset(qw(0.4 0.1 0.2 0.1 0.14 0.15 0.26 0.27 0.1 0.19 0.2 0.1 0.1 0.2 0.1 0.3)); $g->add_dataset(qw(0.2 0.11 0.12 0.11 0.2 0.3 0.12 0.27 0.11 0.3 0.2 0.2 0.2 0.1 0.1 0.2)); $g->set( title => 'Error Bars Demo', legend => 'none', y_axes => 'both', x_ticks => 'staggered', pt_size => 12, brush_size => 2, grid_lines => 'true', colors => { grid_lines => 'gray70', misc => 'gray65', }, ); $g->png("error.png"); =head2 HorizontalBars =for HTML

horizontal bar chart

The class Chart::HorizontalBars creates a chart of horizontally oriented bars. Same rules apply as in L, except due negative values in our data sets here L doesn't have to be set to zero. And instead of L we activate and color L. Deactivating the grey background of the plot just adds a little friendliness. use Chart::HorizontalBars; my $g = Chart::HorizontalBars->new( 600, 600 ); $g->add_dataset( qw/ camel cat dog bear shell/ ); $g->add_dataset( -300, 400, 800, -500, 200 ); $g->add_dataset( 800, -600, 300, 300, 400 ); $g->set( title => 'Bars !', x_label => 'Group', y_label => 'Value', x_grid_lines => 'true', precision => 0, colors => { x_grid_lines => 'gray70', misc => 'gray55', text => 'gray55', x_label => 'gray40', y_label => 'gray40', title => 'gray20', } grey_background => 'false', ); $g->png("hbars.png"); =head2 Lines =for HTML

xy chart with lines

The class Chart::Lines creates a chart with lines that connect the would be data points. If you want to make the points more visible, use L. The only special property is L, the thickness of the lines, which was not even utilized in this example. To make things nicer we put only some softer colors to the horizontal (y) grid lines and the box and ticks (misc) and placed the legend on the bottom so that the chart doesn't get sqeezed by it. use Chart::Lines; my $g = Chart::Lines->new( 600, 400 ); $g->add_dataset( 'foo', 'bar', 'whee', 'ding', 'bat', 'bit' ); $g->add_dataset( 3.2, 4.34, 9.456, 10.459, 11.24234, 14.0234 ); $g->add_dataset( -1.3, 8.4, 5.34, 3.234, 4.33, 13.09 ); $g->add_dataset( 5, 7, 2, 10, 12, 2.3445 ); $g->set( title => 'Lines Chart', legend => 'bottom' , y_label => 'y label 1', precision => 0, y_grid_lines => 'true', colors => { y_label => 'orangepeel', y_grid_lines => [ 190, 190, 190 ], misc => [ 100, 100, 100 ], }, ); $g->png("lines.png"); =head2 LinesPoints =for HTML

xy chart with connected points

The class Chart::LinesPoints creates chart, which is a combination of L and L: shaped symbols connected by lines. This requires a combination of the special properties of both chart types: L (point shape), L (point diameter) and L (line thickness). In our example we named both axis via L and L and set L off, which is not really needed, since it is on C<'false'> per default. This allows you to give the x-axis none numerical, custom tick labels, which are by accident the numbers 1 .. 17, as the first data row shows. The purpose of this maneuver is to not have zero as the first column label. Because the origin of coordinate system is usually in the left lower corner, we used a trick to flip the y-axis having the smallest values up. We negated all values in the data, so that 8 is lower than 2, because -8 is smaller than -2. Than we transformed the y-axis labels with a function, that negates the value of the original label, erasing the minus sign. For additional clarity, we put the names of the teams into the legend, which is per default on the right side. And to make the code more compact, we packed the first (just labels) and the four real data sets (rows) together into an array or arrays and gave it as second argument directly to the drawing method. use Chart::LinesPoints; my $g = Chart::LinesPoints->new( 600, 300 ); $g->set( title => 'Soccer Season 2002', legend_labels => ['NY Soccer Club', 'Denver Tigers', 'Houston Spacecats', 'Washington Presidents'], y_label => 'position in the table', x_label => 'day of play', grid_lines => 'true', f_y_tick => sub { - $_[0] }, # xy_plot => 'true', integer_ticks_only => 'true', colors => { grid_lines => 'gray70', }, ); $g->png("linespoints.png", [ [qw(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17)], [qw(-7 -5 -6 -8 -9 -7 -5 -4 -3 -2 -4 -6 -3 -5 -3 -4 -6)], [qw(-1 -1 -1 -1 -2 -2 -3 -3 -4 -4 -6 -3 -2 -2 -2 -1 -1)], [qw(-4 -4 -3 -2 -1 -1 -1 -2 -1 -1 -3 -2 -4 -3 -4 -2 -2)], [qw(-6 -3 -2 -3 -3 -3 -2 -1 -2 -3 -1 -1 -1 -1 -1 -3 -3)], ]); =head2 Mountain =for HTML

mountain chart

The class Chart::Mountain creates a mountain chart, which is a line chart (see L), where the area under the curve, right to the curve below is filled with the color of the data set. In the following example we use custom colors in hex notation (as supported by Chart::Color) which are getting mapped onto the colors settings of L .. dataset4. As always, the first data set (row in the data table) holds the domain or x-axis labels. Another specialty of mountain charts are patterns (not provided !), to fill the area with. We load them via GD and give them over to the C<'patterns'> property. Patterns are small images with one color and the second being transparent. use Chart::Mountain; my @data = ( ["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th" ], [ 3, 7, 8, 2, 4 , 8.5, 2, 5, 9], [ 4, 2, 5, 6, 3 , 2.5, 3, 3, 4], [ 7, 3, 2, 8, 8.5, 2 , 9, 4, 5], ); my @hex_colors = ('#0099FF', '#00CC00', '#EEAA00', '#FF0099','#3333FF'); my $PNG; my @patterns = map { open( $PNG, '<', "./patterns/PATTERN$_.PNG" ) or die "Can't load pattern $_"; GD::Image->newFromPng( $PNG ); } 0 .. 4; my $g = new Chart::Mountain( 500, 300); $g->set( title => 'Mountain Chart with Patterns', x_label => 'Lengths', y_label => 'Height', grid_lines => 'true', patterns => \@patterns, precision => 1, colors => { grid_lines => 'gray70', misc => 'gray55', map { ( "dataset$_" => $hex_colors[$_] ) } 0 .. $#hex_colors, }, ); $g->png( 'mountain.png', \@data ); =head2 Pareto =for HTML

pareto chart

The class Chart::Pareto creates a combination of a L and a L chart. The bars display absolute values of the data set (Pareto accepts only one), while the line represent the accumulation (sum of all values from the start on the left up to this value). For a better orientation the absolute values should be sorted. In case your data set is not already, change the property L to C<'true'>. (Note that the days of the week are not in chronological order, but in the order of decreasing sale amounts.) The first given data set is like in most cases the domain (x-axis labels). So the color of the first data set containing numbers has the color of L and the accumulation gets the color of dataset1 (which are per default also red and green, but in reverse order). We choose a Pantone Report designer red, which sticks out but is not too shrill. For better optics we set L off, so that the bars touch each other and give a nice counterweight to the red color of the line. It's also a bit nicer, when the red labels above the red line don't stick out the chart, so we increased the L from 5180 to 5500. Finally to prevent the y-axis from overcrowding, we set labels (and y_grid_lines) only every 250 by setting L and activating L. use Chart::Pareto; my $g = Chart::Pareto->new( 450, 400 ); $g->add_dataset( 'Mo', 'Tue', 'We', 'Th', 'Fr', 'Sa', 'Su' ); $g->add_dataset( 2500, 1000, 250, 700, 100, 610, 20 ); $g->set( title => 'Sold Tickets for Beethovens 9th', y_label => 'Sold Tickets', x_label => '! sold out in the first week !', sort => 'true', max_val => 5500, integer_ticks_only => 'true', skip_int_ticks => 250, y_grid_lines => 'true', spaced_bars => 'false', legend => 'none', colors => { title => 'darkblue', dataset0 => 'green', dataset1 => 'aurorared', x_label => 'aurorared', y_grid_lines => 'white', }, ); $g->png("pareto.png"); =head2 Pie =for HTML

pie chart

The class Chart::Pie creates a pie or ring chart. The first added data set must contain the labels and the second set the values. Our example displays a ring chart with a thickness of 35% (of the radius). If the L property is omitted, the chart form falls back to regular pie. Every ring slice is labeled with the stated label, plus the percentage of its value, as defined with the property L. Connecting lines between label and slice are drawn because L is set to C<'true'>. The actual legend is placed on the bottom, in order to leave the ring as large as possible. The legend again shows the association between color and data point and its value because L is set to C<'values'>. Unlike other chart types, where one data set is correlated with a color, here every slice has to have its own color. Thats why the first data point has the color of L the second of dataset1 and so forth. In most cases the default colors are good enough, unless you have special meanings in mind. Please also note the multi line (row) title text. use Chart::Pie; my $g = Chart::Pie->new( 500, 450 ); $g->add_dataset( qw/eins zwei drei vier fuenf sechs sieben acht neun zehn/ ); $g->add_dataset( 120, 50, 100, 80, 40, 45, 150, 60, 110, 50 ); $g->set( 'title' => 'Pie\nDemo Chart', 'legend' => 'bottom', 'legend_label_values' => 'value', 'label_values' => 'percent', 'legend_lines' => 'true', 'ring' => 0.35, ); $g->png("pie.png"); =head2 Points =for HTML

xy chart with points point styles demo

The class Chart::Points creates a xy-chart (also called scattergram), where the individual data points are marked with a symbol. The shape of the symbol is selected by the property L, which was not utilized in this example, in order to use the default shape: a circle of a diameter set by L. All shapes can be seen in the demo above, right. (If you want in addition lines, connecting the points, check L.) The first data set comprises the domain set, displayed on the x-axis. L set to zero cuts the decimals on the y-axis labels and keeps it clean. The method C appends to every data set another value, adding another column to the chart. L does just adds frame of 10 pixel width around the entire image. They middle gray grid lines are just easy for the eyes. use Chart::Points; my $g = Chart::Points->new(); $g->add_dataset( 'foo', 'bar', 'blank' ); $g->add_dataset( 3, 4, 9 ); $g->add_dataset( 8, 6, 0 ); $g->add_dataset( 5, 7, 2 ); $g->add_pt( 'dat', 1, 5, 7 ); $g->set( title => 'Points Chart', pt_size => 18, # brushStyle => 'Star', precision => 0, grid_lines => 'true', png_border => 10, colors => { grid_lines => 'gray70', }, ); $g->png("points.png"); =head2 Split =for HTML

multi chart

The class Chart::Split creates a L chart where both x and y axes are assumed to be numeric. Split charts are mainly intended for cases where many data points are spread over a wide x range while at the same time the y range is limited. Typical examples are weather or seismic data. The x axis will be split into several intervals of the same length (specified with the mandatory option L and starting at L). Chart::Split will draw only positive x coordinates. The y axis will not be labelled with the y values. Rather, the axis will show only the sequence numbers of the intervals. use Chart::Split; my $g = Chart::Split->new( 500, 500 ); my @domain = 1 .. 4000; my @rnd = map { srand( time() / $_ * 50 ); rand(10) } @domain, 4001; my @diff = map { abs $rnd[$_-1] - $rnd[$_] } @domain; pop @rnd; $g->add_dataset(@domain); $g->add_dataset(@rnd); $g->add_dataset(@diff); $g->set( title => "Random Numbers Test", x_label => "4000 Random Numbers", start => 0, interval => 400, brush_size => 1, interval_ticks => 0, legend => 'bottom', legend_labels => ['random numbers', 'difference' ], colors => { title => 'darkblue', text => 'gray45', misc => 'gray45', }, ); $g->png("split.png"); =head2 StackedBars =for HTML

stacked bar chart

The class Chart::StackedBars is a variant of L that stacks bars belonging to one x-value on top of each other, instead of putting them beside each other. Data sets 0..n are ordered from the bottom up. They are in most cases more intuitive than L charts, because its easier to intuit linear than quadratic ratios. As in the Bars example we activated horizontal grid lines, which were subtle colored. To surpress decimals on the y-axis L was turned down. And as in most cases - the first data set is the domain, which will be drawn as x-axis labels. use Chart::StackedBars; my $g = Chart::StackedBars->new( 600, 400 ); $g->add_dataset( 'camel', 'dromedar', 'llama', 'vicuna'); $g->add_dataset( 3, 4, 9, 10, ); $g->add_dataset( 8, 6, 1, 12, ); $g->add_dataset( 5, 7, 2, 13, ); $g->set( title => 'Stacked Bars', legend => 'left', precision => 0, y_grid_lines => 'true', grey_background => 'false', colors => { grid_lines => 'gray80', misc => 'gray55', text => 'gray55', x_label => 'gray40', y_label => 'gray40', title => 'gray20', } ); $g->png("stackedbars.png"); =head1 COPYRIGHT & LICENSE Copyright 2022 David Bonner, Herbert Breunung. This program is free software; you can redistribute it and/or modify it under same terms as Perl itself. =head1 AUTHOR David Bonner, Herbert Breunung, =cut Chart-v2.403.9/lib/Chart/Composite.pm0000644000175000017500000013537314341172251017113 0ustar herbertherbert# charts composed of several types use v5.12; package Chart::Composite; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Chart::Base; use GD; use Carp; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @method int set(%opts) # @param[in] %opts Hash of options to the Chart # @return ok or croak # # @brief # Set all options # # @details # Overwrite the set function of class Base to pass # options to the sub-objects later sub set { my $self = shift; my %opts = @_; # basic error checking on the options, just warn 'em unless ( $#_ % 2 ) { carp "Whoops, some option to be set didn't have a value.\n", "You might want to look at that.\n"; } # store the options they gave us unless ( $self->{'opts'} ) { $self->{'opts'} = {}; } # now set 'em for ( keys %opts ) { $self->{$_} = $opts{$_}; $self->{'opts'}{$_} = $opts{$_}; } # now return return; } ## @fn imagemap_dump() # @brief # Overwrite function imagemap_dump of base class # # @details # Get the information to turn the chart into an imagemap # had to override it to reassemble the \@data array correctly # # @return Reference to an array of the image sub imagemap_dump { my $self = shift; my ( $i, $j ); my @map; my $dataset_count = 0; # croak if they didn't ask me to remember the data, or if they're asking # for the data before I generate it unless ( ( $self->true( $self->{'imagemap'} ) ) && $self->{'imagemap_data'} ) { croak "You need to set the imagemap option to true, and then call the png method, before you can get the imagemap data"; } #make a copy of the imagemap data #this is the data of the first component for $i ( 1 .. $#{ $self->{'sub_0'}->{'imagemap_data'} } ) { for $j ( 0 .. $#{ $self->{'sub_0'}->{'imagemap_data'}->[$i] } ) { $map[$i][$j] = \@{ $self->{'sub_0'}->{'imagemap_data'}->[$i][$j] }; } $dataset_count++; } #and add the data of the second component for $i ( 1 .. $#{ $self->{'sub_1'}->{'imagemap_data'} } ) { for $j ( 0 .. $#{ $self->{'sub_1'}->{'imagemap_data'}->[$i] } ) { $map[ $i + $dataset_count ][$j] = \@{ $self->{'sub_1'}->{'imagemap_data'}->[$i][$j] }; } } # return their copy return \@map; } # private routine sub __print_array { my @a = @_; my $i; my $li = $#a; $li++; print STDERR "Anzahl der Elemente = $li\n"; $li--; for ( $i = 0 ; $i <= $li ; $i++ ) { print STDERR "\t$i\t$a[$i]\n"; } } #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @fn private int _check_data # Overwrite _check_data of Chart::Base and check the internal data to be displayed. # # Make sure the data isn't really weird # and collect some basic info about it\n # @return status of check sub _check_data { my $self = shift; my $length = 0; # first things first, make sure we got the composite_info unless ( ( $self->{'composite_info'} ) && ( $#{ $self->{'composite_info'} } == 1 ) ) { croak "Chart::Composite needs to be told what kind of components to use"; } # make sure we don't end up dividing by zero if they ask for # just one y_tick if ( $self->{'y_ticks'} == 1 ) { $self->{'y_ticks'} = 2; carp "The number of y_ticks displayed must be at least 2"; } # remember the number of datasets $self->{'num_datasets'} = $#{ $self->{'dataref'} }; # remember the number of points in the largest dataset $self->{'num_datapoints'} = 0; for ( 0 .. $self->{'num_datasets'} ) { if ( scalar( @{ $self->{'dataref'}[$_] } ) > $self->{'num_datapoints'} ) { $self->{'num_datapoints'} = scalar( @{ $self->{'dataref'}[$_] } ); } } # find the longest x-tick label, and remember how long it is for ( @{ $self->{'dataref'}[0] } ) { if ( length($_) > $length ) { $length = length($_); } } $self->{'x_tick_label_length'} = $length; # now split the data into sub-objects $self->_split_data; return; } ## @fn private _split_data # split data to the composited classes # # create sub-objects for each type, store the appropriate # data sets in each one, and stick the correct values into # them (ie. 'gd_obj'); sub _split_data { my $self = shift; my @types = ( $self->{'composite_info'}[0][0], $self->{'composite_info'}[1][0] ); my ( $ref, $i, $j ); # Already checked for number of components in _check_data, above. # we can only do two at a time # if ($self->{'composite_info'}[2]) { # croak "Sorry, Chart::Composite can only do two chart types at a time"; # } # load the individual modules require "Chart/" . $types[0] . ".pm"; require "Chart/" . $types[1] . ".pm"; # create the sub-objects $self->{'sub_0'} = ( "Chart::" . $types[0] )->new(); $self->{'sub_1'} = ( "Chart::" . $types[1] )->new(); # forward options to sub-objects (set the min_val, max_val, brush_size, y_ticks, $self->{'sub_0'}->set( %{ $self->{'opts'} } ); $self->{'sub_1'}->set( %{ $self->{'opts'} } ); $self->{'sub_0'}->set( 'min_val' => $self->{'opts'}{'min_val1'} ) if defined $self->{'opts'}{'min_val1'}; $self->{'sub_0'}->set( 'max_val' => $self->{'opts'}{'max_val1'} ) if defined $self->{'opts'}{'max_val1'}; $self->{'sub_1'}->set( 'min_val' => $self->{'opts'}{'min_val2'} ) if defined $self->{'opts'}{'min_val2'}; $self->{'sub_1'}->set( 'max_val' => $self->{'opts'}{'max_val2'} ) if defined $self->{'opts'}{'max_val2'}; $self->{'sub_0'}->set( 'y_ticks' => $self->{'opts'}{'y_ticks1'} ) if $self->{'opts'}{'y_ticks1'}; $self->{'sub_1'}->set( 'y_ticks' => $self->{'opts'}{'y_ticks2'} ) if $self->{'opts'}{'y_ticks2'}; $self->{'sub_0'}->set( 'brush_size' => $self->{'opts'}{'brush_size1'} ) if $self->{'opts'}{'brush_size1'}; $self->{'sub_1'}->set( 'brush_size' => $self->{'opts'}{'brush_size2'} ) if $self->{'opts'}{'brush_size2'}; $self->{'sub_0'}->set( 'brushStyle' => $self->{'opts'}{'brushStyle1'} ) if $self->{'opts'}{'brushStyle1'}; $self->{'sub_0'}->set( 'brushStyle' => $self->{'opts'}{'brushStyle1'} ) if $self->{'opts'}{'brush_style1'}; $self->{'sub_1'}->set( 'brushStyle' => $self->{'opts'}{'brushStyle2'} ) if $self->{'opts'}{'brushStyle2'}; $self->{'sub_1'}->set( 'brushStyle' => $self->{'opts'}{'brushStyle2'} ) if $self->{'opts'}{'brush_style2'}; # f_y_tick for left and right axis $self->{'sub_0'}->set( 'f_y_tick' => $self->{'opts'}{'f_y_tick1'} ) if defined $self->{'opts'}{'f_y_tick1'}; $self->{'sub_1'}->set( 'f_y_tick' => $self->{'opts'}{'f_y_tick2'} ) if defined $self->{'opts'}{'f_y_tick2'}; # replace the gd_obj fields $self->{'sub_0'}->{'gd_obj'} = $self->{'gd_obj'}; $self->{'sub_1'}->{'gd_obj'} = $self->{'gd_obj'}; # let the sub-objects know they're sub-objects $self->{'sub_0'}->{'component'} = 'true'; $self->{'sub_1'}->{'component'} = 'true'; # give each sub-object its data $self->{'component_datasets'} = []; for $i ( 0 .. 1 ) { $ref = []; $self->{'component_datasets'}[$i] = $self->{'composite_info'}[$i][1]; push @{$ref}, $self->{'dataref'}[0]; for $j ( @{ $self->{'composite_info'}[$i][1] } ) { $self->_color_role_to_index( 'dataset' . ( $j - 1 ) ); # allocate color index push @{$ref}, $self->{'dataref'}[$j]; } $self->{ 'sub_' . $i }->_copy_data($ref); } # and let them check it $self->{'sub_0'}->_check_data; $self->{'sub_1'}->_check_data; # realign the y-axes if they want if ( $self->true( $self->{'same_y_axes'} ) ) { if ( $self->{'sub_0'}{'min_val'} < $self->{'sub_1'}{'min_val'} ) { $self->{'sub_1'}{'min_val'} = $self->{'sub_0'}{'min_val'}; } else { $self->{'sub_0'}{'min_val'} = $self->{'sub_1'}{'min_val'}; } if ( $self->{'sub_0'}{'max_val'} > $self->{'sub_1'}{'max_val'} ) { $self->{'sub_1'}{'max_val'} = $self->{'sub_0'}{'max_val'}; } else { $self->{'sub_0'}{'max_val'} = $self->{'sub_1'}{'max_val'}; } $self->{'sub_0'}->_check_data; $self->{'sub_1'}->_check_data; } # find out how big the y-tick labels will be from sub_0 and sub_1 $self->{'y_tick_label_length1'} = $self->{'sub_0'}->{'y_tick_label_length'}; $self->{'y_tick_label_length2'} = $self->{'sub_1'}->{'y_tick_label_length'}; # now return return; } ## @fn private int _draw_legend() # let the user know what all the pretty colors mean # @return status # sub _draw_legend { my $self = shift; my ($length); # check to see if they have as many labels as datasets, # warn them if not if ( ( $#{ $self->{'legend_labels'} } >= 0 ) && ( ( scalar( @{ $self->{'legend_labels'} } ) ) != $self->{'num_datasets'} ) ) { carp "The number of legend labels and datasets doesn\'t match"; } # init a field to store the length of the longest legend label unless ( $self->{'max_legend_label'} ) { $self->{'max_legend_label'} = 0; } # fill in the legend labels, find the longest one for ( 1 .. $self->{'num_datasets'} ) { unless ( $self->{'legend_labels'}[ $_ - 1 ] ) { $self->{'legend_labels'}[ $_ - 1 ] = "Dataset $_"; } $length = length( $self->{'legend_labels'}[ $_ - 1 ] ); if ( $length > $self->{'max_legend_label'} ) { $self->{'max_legend_label'} = $length; } } # different legend types if ( $self->{'legend'} eq 'bottom' ) { $self->_draw_bottom_legend; } elsif ( $self->{'legend'} eq 'right' ) { $self->_draw_right_legend; } elsif ( $self->{'legend'} eq 'left' ) { $self->_draw_left_legend; } elsif ( $self->{'legend'} eq 'top' ) { $self->_draw_top_legend; } elsif ( $self->{'legend'} eq 'none' ) { $self->_draw_none_legend; } else { carp "I can't put a legend there\n"; } # and return return 1; } ## @fn private int _draw_top_legend() # put the legend on the top of the data plot # # Overwrite the base class _draw_top_legend # # @return status sub _draw_top_legend { my $self = shift; my @labels = @{ $self->{'legend_labels'} }; my ( $x1, $y1, $x2, $y2, $empty_width, $max_label_width ); my ( $cols, $rows, $color ); my ( $col_width, $row_height, $i, $j, $r, $c, $index, $x, $y, $sub, $w, $h ); my ( $yh, $yi ); # for boxing legends my $font = $self->{'legend_font'}; my ( %colors, @datasets ); my $max_legend_example = 0; $yh = 0; # copy the current boundaries into the sub-objects $self->_sub_update; # init the legend_example_height $self->_legend_example_height_init; ## Make datasetI numbers match indexes of @{ $self->{'dataref'} }[1.....]. # # modify the dataset color table entries to avoid duplicating # # dataset colors (this limits the number of possible data sets # # for each component to 8) # for (0..7) { # $self->{'sub_1'}{'color_table'}{'dataset'.$_} # = $self->{'color_table'}{'dataset'.($_+8)}; # } # modify the dataset color table entries to avoid duplicating # dataset colors. my ( $n0, $n1 ) = map { scalar @{ $self->{'composite_info'}[$_][1] } } 0 .. 1; for ( 0 .. $n1 - 1 ) { $self->{'sub_1'}{'color_table'}{ 'dataset' . $_ } = $self->{'color_table'}{ 'dataset' . ( $_ + $n0 ) }; } # make sure we use the right colors for the legend @datasets = @{ $self->{'composite_info'}[0][1] }; $i = 0; for ( 0 .. $#datasets ) { $colors{ $datasets[$_] - 1 } = $self->{'color_table'}{ 'dataset' . ($i) }; $i++; } @datasets = @{ $self->{'composite_info'}[1][1] }; $i = 0; for ( 0 .. $#datasets ) { $colors{ $datasets[$_] - 1 } = $self->{'color_table'}{ 'dataset' . ( $i + $n0 ) }; $i++; } # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The subtitle font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # get some base x coordinates $x1 = $self->{'curr_x_min'} + $self->{'graph_border'} + $self->{'y_tick_label_length1'} * $self->{'tick_label_font'}->width + $self->{'tick_len'} + ( 3 * $self->{'text_space'} ); $x2 = $self->{'curr_x_max'} - $self->{'graph_border'} - $self->{'y_tick_label_length2'} * $self->{'tick_label_font'}->width - $self->{'tick_len'} - ( 3 * $self->{'text_space'} ); if ( $self->{'y_label'} ) { $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'}; } if ( $self->{'y_label2'} ) { $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'}; } # figure out how wide the widest label is, then figure out how many # columns we can fit into the allotted space $empty_width = $x2 - $x1 - ( 2 * $self->{'legend_space'} ); $max_label_width = $self->{'max_legend_label'} * $self->{'legend_font'}->width + 4 * $self->{'text_space'} + $self->{'legend_example_size'}; $cols = int( $empty_width / $max_label_width ); unless ($cols) { $cols = 1; } $col_width = $empty_width / $cols; # figure out how many rows we need and how tall they are $rows = int( $self->{'num_datasets'} / $cols ); unless ( ( $self->{'num_datasets'} % $cols ) == 0 ) { $rows++; } unless ($rows) { $rows = 1; } $row_height = $h + $self->{'text_space'}; # box the legend off $y1 = $self->{'curr_y_min'}; $y2 = $self->{'curr_y_min'} + $self->{'text_space'} + ( $rows * $row_height ) + ( 2 * $self->{'legend_space'} ); $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $self->_color_role_to_index('misc') ); $max_legend_example = $y2 - $y1; # leave some space inside the legend $x1 += $self->{'legend_space'} + $self->{'text_space'}; $x2 -= $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; $y2 -= $self->{'legend_space'} + $self->{'text_space'}; # draw in the actual legend $r = 0; # current row $c = 0; # current column $yi = 0; # current dataset for $i ( 0 .. 1 ) { for $j ( 0 .. $#{ $self->{'component_datasets'}[$i] } ) { # get the color $color = $self->{ 'sub_' . $i }->{'color_table'}{ 'dataset' . $j }; $index = $self->{'component_datasets'}[$i][$j] - 1; # index in label list # find the x-y coordinates for the beginning of the example line $x = $x1 + ( $col_width * $c ); $y = $y1 + ( $row_height * $r ) + $h / 2; # draw the example line if legend_example_height==1 or ==0 if ( $rows == 1 ) { if ( $self->{ 'legend_example_height' . $yi } < $max_legend_example ) { $yh = $self->{ 'legend_example_height' . $yi }; } else { $yh = $max_legend_example; } } else { if ( $self->{ 'legend_example_height' . $yi } < $row_height ) { $yh = $self->{ 'legend_example_height' . $yi }; } else { $yh = $row_height; } } $yi++; if ( $yh <= 1 ) { $self->{'gd_obj'}->line( $x, $y, $x + $self->{'legend_example_size'}, $y, $color ); } else { # draw the example bar if legend_example_height > 1 $yh = int( $yh / 2 ); $self->{'gd_obj'}->filledRectangle( $x, $y - $yh, $x + $self->{'legend_example_size'}, $y + $yh, $color ); } # find the x-y coordinates for the beginning of the label $x += $self->{'legend_example_size'} + 2 * $self->{'text_space'}; $y -= $h / 2; # now draw the label $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index], $color ); # keep track of which row/column we're using $r = ( $r + 1 ) % $rows; if ( $r == 0 ) { $c++; } } } # mark of the space used $self->{'curr_y_min'} += $rows * $row_height + $self->{'text_space'} + 2 * $self->{'legend_space'}; return; } ## @fn private int _draw_right_legend() # put the legend on the right of the chart # # Overwrite the base class _draw_right_legend # # @return status sub _draw_right_legend { my $self = shift; my @labels = @{ $self->{'legend_labels'} }; my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h ); my ($yh) = 0; # for boxing legend my $font = $self->{'legend_font'}; my ( %colors, @datasets, $i ); my $max_legend_example = 0; # copy the current boundaries and colors into the sub-objects $self->_sub_update; # init the legend exapmle height $self->_legend_example_height_init; # # modify the dataset color table entries to avoid duplicating # # dataset colors (this limits the number of possible data sets # # for each component to 8) # for (0..7) { # $self->{'sub_1'}{'color_table'}{'dataset'.$_} # = $self->{'color_table'}{'dataset'.($_+8)}; # } # modify the dataset color table entries to avoid duplicating # dataset colors. my ( $n0, $n1 ) = map { scalar @{ $self->{'composite_info'}[$_][1] } } 0 .. 1; for ( 0 .. $n1 - 1 ) { $self->{'sub_1'}{'color_table'}{ 'dataset' . $_ } = $self->{'color_table'}{ 'dataset' . ( $_ + $n0 ) }; } # make sure we use the right colors for the legend @datasets = @{ $self->{'composite_info'}[0][1] }; $i = 0; for ( 0 .. $#datasets ) { $colors{ $datasets[$_] - 1 } = $self->{'color_table'}{ 'dataset' . ($_) }; $i++; } @datasets = @{ $self->{'composite_info'}[1][1] }; $i = 0; for ( 0 .. $#datasets ) { $colors{ $datasets[$_] - 1 } = $self->{'color_table'}{ 'dataset' . ( $i + $n0 ) }; $i++; } # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The subtitle font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # get the miscellaneous color $misccolor = $self->_color_role_to_index('misc'); # find out how wide the largest label is $width = ( 2 * $self->{'text_space'} ) + ( $self->{'max_legend_label'} * $w ) + $self->{'legend_example_size'} + ( 2 * $self->{'legend_space'} ); # box the thing off $x1 = $self->{'curr_x_max'} - $width; $x2 = $self->{'curr_x_max'}; $y1 = $self->{'curr_y_min'} + $self->{'graph_border'}; $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'} + ( $self->{'num_datasets'} * ( $h + $self->{'text_space'} ) ) + ( 2 * $self->{'legend_space'} ); $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor ); # leave that nice space inside the legend box $x1 += $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; # now draw the actual legend for ( 0 .. $#labels ) { # get the color $color = $colors{$_}; # find the max_legend_example $max_legend_example = $self->{'legend_space'} + $h; # find the x-y coords $x2 = $x1; $x3 = $x2 + $self->{'legend_example_size'}; $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2; # draw the example line if legend_example_height==1 or ==0 if ( $self->{ 'legend_example_height' . $_ } < $max_legend_example ) { $yh = $self->{ 'legend_example_height' . $_ }; } else { $yh = $max_legend_example; } if ( $yh <= 1 ) { $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color ); } else { $yh = int( $yh / 2 ); $self->{'gd_obj'}->filledRectangle( $x2, $y2 - $yh, $x3, $y2 + $yh, $color ); } # now the label $x2 = $x3 + ( 2 * $self->{'text_space'} ); $y2 -= $h / 2; $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color ); } # mark off the used space $self->{'curr_x_max'} -= $width; # and return return; } ## @fn private int _draw_left_legend() # draw the legend at the left of the data plot # # Overwrite the base class _draw_left_legend # # @return status sub _draw_left_legend { my $self = shift; my @labels = @{ $self->{'legend_labels'} }; my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h ); my $yh; # for boxing legend my $font = $self->{'legend_font'}; my ( %colors, @datasets, $i ); my $max_legend_example = 0; # copy the current boundaries and colors into the sub-objects $self->_sub_update; # init the legend_example height $self->_legend_example_height_init; # # modify the dataset color table entries to avoid duplicating # # dataset colors (this limits the number of possible data sets # # for each component to 8) # for (0..7) { # $self->{'sub_1'}{'color_table'}{'dataset'.$_} # = $self->{'color_table'}{'dataset'.($_+8)}; # } # modify the dataset color table entries to avoid duplicating # dataset colors. my ( $n0, $n1 ) = map { scalar @{ $self->{'composite_info'}[$_][1] } } 0 .. 1; for ( 0 .. $n1 - 1 ) { $self->{'sub_1'}{'color_table'}{ 'dataset' . $_ } = $self->{'color_table'}{ 'dataset' . ( $_ + $n0 ) }; } # make sure we use the right colors for the legend @datasets = @{ $self->{'composite_info'}[0][1] }; $i = 0; for ( 0 .. $#datasets ) { $colors{ $datasets[$_] - 1 } = $self->{'color_table'}{ 'dataset' . ($i) }; $i++; } @datasets = @{ $self->{'composite_info'}[1][1] }; $i = 0; for ( 0 .. $#datasets ) { $colors{ $datasets[$_] - 1 } = $self->{'color_table'}{ 'dataset' . ( $i + $n0 ) }; $i++; } # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The subtitle font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # get the miscellaneous color $misccolor = $self->_color_role_to_index('misc'); # find out how wide the largest label is $width = ( 2 * $self->{'text_space'} ) + ( $self->{'max_legend_label'} * $w ) + $self->{'legend_example_size'} + ( 2 * $self->{'legend_space'} ); # get some base x-y coordinates $x1 = $self->{'curr_x_min'}; $x2 = $self->{'curr_x_min'} + $width; $y1 = $self->{'curr_y_min'} + $self->{'graph_border'}; $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'} + ( $self->{'num_datasets'} * ( $h + $self->{'text_space'} ) ) + ( 2 * $self->{'legend_space'} ); # box the legend off $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor ); # leave that nice space inside the legend box $x1 += $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; # now draw the actual legend for ( 0 .. $#labels ) { # get the color $color = $colors{$_}; # find the max_legend_example $max_legend_example = $self->{'legend_space'} + $h; # find the x-y coords $x2 = $x1; $x3 = $x2 + $self->{'legend_example_size'}; $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2; # draw the example line if legend_example_height==1 or ==0 if ( $self->{ 'legend_example_height' . $_ } < $max_legend_example ) { $yh = $self->{ 'legend_example_height' . $_ }; } else { $yh = $max_legend_example; } if ( $yh <= 1 ) { $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color ); } else { # draw the example bar if legend_example_height > 1 $yh = int( $yh / 2 ); $self->{'gd_obj'}->filledRectangle( $x2, $y2 - $yh, $x3, $y2 + $yh, $color ); } # now the label $x2 = $x3 + ( 2 * $self->{'text_space'} ); $y2 -= $h / 2; $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color ); } # mark off the used space $self->{'curr_x_min'} += $width; # and return return 1; } ## @fn private int _draw_bottom_legend() # put the legend on the bottom of the chart # # Overwrite the base class _draw_bottom_legend # # @return status sub _draw_bottom_legend { my $self = shift; my @labels = @{ $self->{'legend_labels'} }; my ( $x1, $y1, $x2, $y2, $empty_width, $max_label_width, $cols, $rows, $color ); my ( $col_width, $row_height, $i, $j, $r, $c, $index, $x, $y, $sub, $w, $h ); my ( $yh, $yi ); # for boxing legend my $font = $self->{'legend_font'}; my ( %colors, @datasets ); my $max_legend_example = 0; $yh = 0; # copy the current boundaries and colors into the sub-objects $self->_sub_update; # init the legend example height $self->_legend_example_height_init; # # modify the dataset color table entries to avoid duplicating # # dataset colors (this limits the number of possible data sets # # for each component to 8) # for (0..7) { # $self->{'sub_1'}{'color_table'}{'dataset'.$_} # = $self->{'color_table'}{'dataset'.($_+8)}; # } # modify the dataset color table entries to avoid duplicating # dataset colors. my ( $n0, $n1 ) = map { scalar @{ $self->{'composite_info'}[$_][1] } } 0 .. 1; for ( 0 .. $n1 - 1 ) { $self->{'sub_1'}{'color_table'}{ 'dataset' . $_ } = $self->{'color_table'}{ 'dataset' . ( $_ + $n0 ) }; } @datasets = @{ $self->{'composite_info'}[0][1] }; $i = 0; for ( 0 .. $#datasets ) { $colors{ $datasets[$_] - 1 } = $self->{'color_table'}{ 'dataset' . ($i) }; $i++; } @datasets = @{ $self->{'composite_info'}[1][1] }; $i = 0; for ( 0 .. $#datasets ) { $colors{ $datasets[$_] - 1 } = $self->{'color_table'}{ 'dataset' . ( $i + $n0 ) }; $i++; } # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The subtitle font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # figure out how many columns we can fit $x1 = $self->{'curr_x_min'} + $self->{'graph_border'} + $self->{'y_tick_label_length1'} * $self->{'tick_label_font'}->width + $self->{'tick_len'} + ( 3 * $self->{'text_space'} ); $x2 = $self->{'curr_x_max'} - $self->{'graph_border'} - $self->{'y_tick_label_length2'} * $self->{'tick_label_font'}->width - $self->{'tick_len'} - ( 3 * $self->{'text_space'} ); if ( $self->{'y_label'} ) { $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'}; } if ( $self->{'y_label2'} ) { $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'}; } $empty_width = $x2 - $x1 - ( 2 * $self->{'legend_space'} ); $max_label_width = $self->{'max_legend_label'} * $self->{'legend_font'}->width + 4 * $self->{'text_space'} + $self->{'legend_example_size'}; $cols = int( $empty_width / $max_label_width ); unless ($cols) { $cols = 1; } $col_width = $empty_width / $cols; # figure out how many rows we need $rows = int( $self->{'num_datasets'} / $cols ); unless ( ( $self->{'num_datasets'} % $cols ) == 0 ) { $rows++; } unless ($rows) { $rows = 1; } $row_height = $h + $self->{'text_space'}; # box it off $y1 = $self->{'curr_y_max'} - $self->{'text_space'} - ( $rows * $row_height ) - ( 2 * $self->{'legend_space'} ); $y2 = $self->{'curr_y_max'}; $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $self->_color_role_to_index('misc') ); # get the max_legend_example_height $max_legend_example = $y2 - $y1; $x1 += $self->{'legend_space'} + $self->{'text_space'}; $x2 -= $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; $y2 -= $self->{'legend_space'} + $self->{'text_space'}; # draw in the actual legend $r = 0; $c = 0; $yi = 0; # current dataset for $i ( 0 .. 1 ) { for $j ( 0 .. $#{ $self->{'component_datasets'}[$i] } ) { $color = $self->{ 'sub_' . $i }->{'color_table'}{ 'dataset' . $j }; $index = $self->{'component_datasets'}[$i][$j] - 1; $x = $x1 + ( $col_width * $c ); $y = $y1 + ( $row_height * $r ) + $h / 2; # draw the example line if legend_example_height==1 or ==0 if ( $rows == 1 ) { if ( $self->{ 'legend_example_height' . $yi } < $max_legend_example ) { $yh = $self->{ 'legend_example_height' . $yi }; } else { $yh = $max_legend_example; } } else { if ( $self->{ 'legend_example_height' . $yi } < $row_height ) { $yh = $self->{ 'legend_example_height' . $yi }; } else { $yh = $row_height; } } $yi++; if ( $yh <= 1 ) { $self->{'gd_obj'}->line( $x, $y, $x + $self->{'legend_example_size'}, $y, $color ); } else { # draw the example bar if legend_example_height > 1 $yh = int( $yh / 2 ); $self->{'gd_obj'}->filledRectangle( $x, $y - $yh, $x + $self->{'legend_example_size'}, $y + $yh, $color ); } $x += $self->{'legend_example_size'} + 2 * $self->{'text_space'}; $y -= $h / 2; $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index], $color ); # keep track of which row/column we're using $r = ( $r + 1 ) % $rows; if ( $r == 0 ) { $c++; } } } # mark of the space used $self->{'curr_y_max'} -= ( $rows * $row_height ) + 2 * $self->{'text_space'} + 2 * $self->{'legend_space'}; return; } ## @fn private int _draw_none_legend() # no legend to draw.. just update the color tables for subs # # This routine overwrites this function of the Base class # # @return status sub _draw_none_legend { my $self = shift; my $status = 1; $self->_sub_update(); # for (0..7) { # $self->{'sub_1'}{'color_table'}{'dataset'.$_} # = $self->{'color_table'}{'dataset'.($_+8)}; # } # modify the dataset color table entries to avoid duplicating # dataset colors. my ( $n0, $n1 ) = map { scalar @{ $self->{'composite_info'}[$_][1] } } 0 .. 1; for ( 0 .. $n1 - 1 ) { $self->{'sub_1'}{'color_table'}{ 'dataset' . $_ } = $self->{'color_table'}{ 'dataset' . ( $_ + $n0 ) }; } return $status; } ## @fn private int _draw_ticks() # draw the ticks and tick labels # # Overwrites function _draw_ticks() of base class # # @return status sub _draw_ticks { my $self = shift; #draw the x ticks again if ( $self->true( $self->{'xy_plot'} ) ) { $self->_find_x_scale; # The following statement is necessary as the # _draw_x_number_ticks() located in Base.pm does nothing know # about different y_tick_label_length variables! # This is a hack here $self->{'y_tick_label_length'} = $self->{'y_tick_label_length1'}; $self->_draw_x_number_ticks; } else { $self->_draw_x_ticks; } # update the boundaries in the sub-objects $self->_boundary_update( $self, $self->{'sub_0'} ); $self->_boundary_update( $self, $self->{'sub_1'} ); # now the y ticks $self->_draw_y_ticks; # then return return; } ## @fn private int _draw_x_ticks() # draw the x-ticks and their labels # # Overwrites function _draw_x_ticks() of base class # # @return status sub _draw_x_ticks { my $self = shift; my $data = $self->{'dataref'}; my $font = $self->{'tick_label_font'}; my $textcolor = $self->_color_role_to_index('text'); my $misccolor = $self->_color_role_to_index('misc'); my ( $h, $w ); my ( $x1, $x2, $y1, $y2 ); my ( $width, $delta ); my ($stag); $self->{'grid_data'}->{'x'} = []; # make sure we got a real font unless ( ( ref $font ) eq 'GD::Font' ) { croak "The tick label font you specified isn\'t a GD Font object"; } # get the height and width of the font ( $h, $w ) = ( $font->height, $font->width ); # allow for the amount of space the y-ticks will push the # axes over to the right and to the left ## _draw_y_ticks allows 3 * text_space, not 2 * ; this caused mismatch between ## the ticks (and grid lines) and the data. # $x1 = $self->{'curr_x_min'} + ($w * $self->{'y_tick_label_length1'}) # + (2 * $self->{'text_space'}) + $self->{'tick_len'}; # $x2 = $self->{'curr_x_max'} - ($w * $self->{'y_tick_label_length2'}) # - (2 * $self->{'text_space'}) - $self->{'tick_len'}; $x1 = $self->{'curr_x_min'} + ( $w * $self->{'y_tick_label_length1'} ) + ( 3 * $self->{'text_space'} ) + $self->{'tick_len'}; $x2 = $self->{'curr_x_max'} - ( $w * $self->{'y_tick_label_length2'} ) - ( 3 * $self->{'text_space'} ) - $self->{'tick_len'}; $y1 = $self->{'curr_y_max'} - $h - $self->{'text_space'}; # get the delta value, figure out how to draw the labels $width = $x2 - $x1; $delta = $width / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); if ( $delta <= ( $self->{'x_tick_label_length'} * $w ) ) { unless ( $self->{'x_ticks'} =~ /^vertical$/i ) { $self->{'x_ticks'} = 'staggered'; } } # now draw the labels if ( $self->{'x_ticks'} =~ /^normal$/i ) { # normal ticks if ( $self->{'skip_x_ticks'} ) { for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_x_ticks'} ) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * ( $_ * $self->{'skip_x_ticks'} ) ) - ( $w * length( $self->{'f_x_tick'}->( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ) ) ) / 2; $self->{'gd_obj'} ->string( $font, $x2, $y1, $self->{'f_x_tick'}->( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ), $textcolor ); } } elsif ( $self->{'custom_x_ticks'} ) { for ( @{ $self->{'custom_x_ticks'} } ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - ( $w * length( $self->{'f_x_tick'}->( $data->[0][$_] ) ) ) / 2; $self->{'gd_obj'}->string( $font, $x2, $y1, $self->{'f_x_tick'}->( $data->[0][$_] ), $textcolor ); } } else { for ( 0 .. $self->{'num_datapoints'} - 1 ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - ( $w * length( $self->{'f_x_tick'}->( $data->[0][$_] ) ) ) / 2; $self->{'gd_obj'}->string( $font, $x2, $y1, $self->{'f_x_tick'}->( $data->[0][$_] ), $textcolor ); } } } elsif ( $self->{'x_ticks'} =~ /^staggered$/i ) { # staggered ticks if ( $self->{'skip_x_ticks'} ) { $stag = 0; for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_x_ticks'} ) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * ( $_ * $self->{'skip_x_ticks'} ) ) - ( $w * length( $self->{'f_x_tick'}->( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ) ) ) / 2; if ( ( $stag % 2 ) == 1 ) { $y1 -= $self->{'text_space'} + $h; } $self->{'gd_obj'} ->string( $font, $x2, $y1, $self->{'f_x_tick'}->( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ), $textcolor ); if ( ( $stag % 2 ) == 1 ) { $y1 += $self->{'text_space'} + $h; } $stag++; } } elsif ( $self->{'custom_x_ticks'} ) { $stag = 0; for ( sort ( @{ $self->{'custom_x_ticks'} } ) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - ( $w * length( $self->{'f_x_tick'}->( $data->[0][$_] ) ) ) / 2; if ( ( $stag % 2 ) == 1 ) { $y1 -= $self->{'text_space'} + $h; } $self->{'gd_obj'}->string( $font, $x2, $y1, $self->{'f_x_tick'}->( $data->[0][$_] ), $textcolor ); if ( ( $stag % 2 ) == 1 ) { $y1 += $self->{'text_space'} + $h; } $stag++; } } else { for ( 0 .. $self->{'num_datapoints'} - 1 ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - ( $w * length( $self->{'f_x_tick'}->( $data->[0][$_] ) ) ) / 2; if ( ( $_ % 2 ) == 1 ) { $y1 -= $self->{'text_space'} + $h; } $self->{'gd_obj'}->string( $font, $x2, $y1, $self->{'f_x_tick'}->( $data->[0][$_] ), $textcolor ); if ( ( $_ % 2 ) == 1 ) { $y1 += $self->{'text_space'} + $h; } } } } elsif ( $self->{'x_ticks'} =~ /^vertical$/i ) { # vertical ticks $y1 = $self->{'curr_y_max'} - $self->{'text_space'}; if ( defined( $self->{'skip_x_ticks'} ) && $self->{'skip_x_ticks'} > 1 ) { for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_x_ticks'} ) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * ( $_ * $self->{'skip_x_ticks'} ) ) - $h / 2; $y2 = $y1 - ( ( $self->{'x_tick_label_length'} - length( $self->{'f_x_tick'}->( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ) ) ) * $w ); $self->{'gd_obj'} ->stringUp( $font, $x2, $y2, $self->{'f_x_tick'}->( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ), $textcolor ); } } elsif ( $self->{'custom_x_ticks'} ) { for ( @{ $self->{'custom_x_ticks'} } ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - $h / 2; $y2 = $y1 - ( ( $self->{'x_tick_label_length'} - length( $self->{'f_x_tick'}->( $data->[0][$_] ) ) ) * $w ); $self->{'gd_obj'}->stringUp( $font, $x2, $y2, $self->{'f_x_tick'}->( $data->[0][$_] ), $textcolor ); } } else { for ( 0 .. $self->{'num_datapoints'} - 1 ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - $h / 2; $y2 = $y1 - ( ( $self->{'x_tick_label_length'} - length( $self->{'f_x_tick'}->( $data->[0][$_] ) ) ) * $w ); $self->{'gd_obj'}->stringUp( $font, $x2, $y2, $self->{'f_x_tick'}->( $data->[0][$_] ), $textcolor ); } } } else { # error time carp "I don't understand the type of x-ticks you specified"; } # update the current y-max value if ( $self->{'x_ticks'} =~ /^normal$/i ) { $self->{'curr_y_max'} -= $h + ( 2 * $self->{'text_space'} ); } elsif ( $self->{'x_ticks'} =~ /^staggered$/i ) { $self->{'curr_y_max'} -= ( 2 * $h ) + ( 3 * $self->{'text_space'} ); } elsif ( $self->{'x_ticks'} =~ /^vertical$/i ) { $self->{'curr_y_max'} -= ( $w * $self->{'x_tick_label_length'} ) + ( 2 * $self->{'text_space'} ); } # now plot the ticks $y1 = $self->{'curr_y_max'}; $y2 = $self->{'curr_y_max'} - $self->{'tick_len'}; if ( $self->{'skip_x_ticks'} ) { for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_x_ticks'} ) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * ( $_ * $self->{'skip_x_ticks'} ) ); $self->{'gd_obj'}->line( $x2, $y1, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x2; } } } elsif ( $self->{'custom_x_ticks'} ) { for ( @{ $self->{'custom_x_ticks'} } ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ); $self->{'gd_obj'}->line( $x2, $y1, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x2; } } } else { for ( 0 .. $self->{'num_datapoints'} - 1 ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ); $self->{'gd_obj'}->line( $x2, $y1, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x2; } } } # update the current y-max value $self->{'curr_y_max'} -= $self->{'tick_len'}; # and return return; } ## @fn private int _draw_y_ticks() # draw the y-ticks and their labels # # Overwrites function _draw_y_ticks() of base class # # @return status sub _draw_y_ticks { my $self = shift; # let the first guy do his $self->{'sub_0'}->_draw_y_ticks('left'); # and update the other two objects $self->_boundary_update( $self->{'sub_0'}, $self ); $self->_boundary_update( $self->{'sub_0'}, $self->{'sub_1'} ); # now draw the other ones $self->{'sub_1'}->_draw_y_ticks('right'); # and update the other two objects $self->_boundary_update( $self->{'sub_1'}, $self ); $self->_boundary_update( $self->{'sub_1'}, $self->{'sub_0'} ); # then return return; } ## @fn private _draw_data # finally get around to plotting the data for composite chart sub _draw_data { my $self = shift; # do a grey background if they want it if ( $self->true( $self->{'grey_background'} ) ) { $self->_grey_background; $self->{'sub_0'}->{'grey_background'} = 'false'; $self->{'sub_1'}->{'grey_background'} = 'false'; } # draw grid again if necessary (if grey background ruined it..) unless ( !$self->true( $self->{grey_background} ) ) { $self->_draw_grid_lines if ( $self->true( $self->{grid_lines} ) ); $self->_draw_x_grid_lines if ( $self->true( $self->{x_grid_lines} ) ); $self->_draw_y_grid_lines if ( $self->true( $self->{y_grid_lines} ) ); $self->_draw_y2_grid_lines if ( $self->true( $self->{y2_grid_lines} ) ); } # do a final bounds update $self->_boundary_update( $self, $self->{'sub_0'} ); $self->_boundary_update( $self, $self->{'sub_1'} ); # init the imagemap data field if they wanted it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # now let the component modules go to work $self->{'sub_0'}->_draw_data; $self->{'sub_1'}->_draw_data; return; } ## @fn private _sub_update() # update all the necessary information in the sub-objects # # Only for Chart::Composite sub _sub_update { my $self = shift; my $sub0 = $self->{'sub_0'}; my $sub1 = $self->{'sub_1'}; # update the boundaries $self->_boundary_update( $self, $sub0 ); $self->_boundary_update( $self, $sub1 ); # copy the color tables $sub0->{'color_table'} = { %{ $self->{'color_table'} } }; $sub1->{'color_table'} = { %{ $self->{'color_table'} } }; # now return return; } ## @fn private _boundary_update() # copy the current gd_obj boundaries from one object to another # # Only for Chart::Composite sub _boundary_update { my $self = shift; my $from = shift; my $to = shift; $to->{'curr_x_min'} = $from->{'curr_x_min'}; $to->{'curr_x_max'} = $from->{'curr_x_max'}; $to->{'curr_y_min'} = $from->{'curr_y_min'}; $to->{'curr_y_max'} = $from->{'curr_y_max'}; return; } ## @fn private int _draw_y_grid_lines() # draw grid_lines for y # # Overwrites this function of Base sub _draw_y_grid_lines { my ($self) = shift; $self->{'sub_0'}->_draw_y_grid_lines(); return; } ## @fn private int _draw_y2_grid_lines() # draw grid_lines for y # # Overwrites this function of Base sub _draw_y2_grid_lines { my ($self) = shift; $self->{'sub_1'}->_draw_y2_grid_lines(); return; } ## @fn private _legend_example_height_values # init the legend_example_height_values # sub _legend_example_height_init { my $self = shift; my $a = $self->{'num_datasets'}; my ( $b, $e ) = ( 0, 0 ); my $bis = '..'; if ( $self->false( $self->{'legend_example_height'} ) ) { for my $i ( 0 .. $a ) { $self->{ 'legend_example_height' . $i } = 1; } } if ( $self->true( $self->{'legend_example_height'} ) ) { for my $i ( 0 .. $a ) { if ( defined( $self->{ 'legend_example_height' . $i } ) ) { } else { ( $self->{ 'legend_example_height' . $i } ) = 1; } } for $b ( 0 .. $a ) { for $e ( 0 .. $a ) { my $anh = sprintf( $b . $bis . $e ); if ( defined( $self->{ 'legend_example_height' . $anh } ) ) { if ( $b > $e ) { croak "Please reverse the datasetnumber in legend_example_height\n"; } for ( my $n = $b ; $n <= $e ; $n++ ) { $self->{ 'legend_example_height' . $n } = $self->{ 'legend_example_height' . $anh }; } } } } } } ## be a good module and return 1 1; Chart-v2.403.9/lib/Chart/LinesPoints.pm0000644000175000017500000002507314341172251017413 0ustar herbertherbert# # line charts (plots) # LinesPoints class connect the given x-/y-values by straight lines # and the x-/y-values are plotted by points. # use v5.12; package Chart::LinesPoints; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Chart::Base; use GD; use Carp; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<# #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @fn private _draw_data # draw the data sub _draw_data { my $self = shift; my $data = $self->{'dataref'}; my $misccolor = $self->_color_role_to_index('misc'); my ( $x1, $x2, $x3, $y1, $y2, $y3, $mod, $abs_x_max, $abs_y_max ); my ( $width, $height, $delta, $map, $t_x_min, $t_x_max, $t_y_min, $t_y_max ); my ( $i, $j, $color, $brush, $zero_offset, $delta_num ); my $repair_top_flag = 0; my $repair_bottom_flag = 0; my $diff; # init the imagemap data field if they want it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # find the delta value between data points, as well # as the mapping constant $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta = $width / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); $diff = ( $self->{'max_val'} - $self->{'min_val'} ); $diff = 1 if $diff == 0; $map = $height / $diff; # get the base x-y values if ( $self->true( $self->{'xy_plot'} ) ) { $x1 = $self->{'curr_x_min'}; } else { $x1 = $self->{'curr_x_min'} + ( $delta / 2 ); } if ( $self->{'min_val'} >= 0 ) { $y1 = $self->{'curr_y_max'}; $mod = $self->{'min_val'}; } elsif ( $self->{'max_val'} <= 0 ) { $y1 = $self->{'curr_y_min'}; $mod = $self->{'max_val'}; } else { $y1 = $self->{'curr_y_min'} + ( $map * $self->{'max_val'} ); $mod = 0; $self->{'gd_obj'}->line( $self->{'curr_x_min'}, $y1, $self->{'curr_x_max'}, $y1, $misccolor ); } #for a xy-plot, use this delta and maybe an offset for the zero-axes if ( $self->true( $self->{'xy_plot'} ) ) { my ( $xmin, $xmax ) = ( $self->{'x_min_val'}, $self->{'x_max_val'} ); if ( $self->{'xlabels'} ) { ( $xmin, $xmax ) = @{ $self->{'xrange'} }; } $diff = $xmax - $xmin; $diff = 1 if $diff == 0; $delta_num = $width / $diff; if ( $xmin <= 0 && $xmax >= 0 ) { $zero_offset = abs($xmin) * abs($delta_num); } elsif ( $xmin > 0 || $xmax < 0 ) { $zero_offset = -$xmin * $delta_num; } else { $zero_offset = 0; } } # draw the lines for $i ( 1 .. $self->{'num_datasets'} ) { # get the color for this dataset, and set the brush $color = $self->_color_role_to_index( 'dataset' . ( $i - 1 ) ); $brush = $self->_prepare_brush( $color, 'line', 'dataset' . ( $i - 1 ) ); $self->{'gd_obj'}->setBrush($brush); # draw every line for this dataset for $j ( 1 .. $self->{'num_datapoints'} ) { # don't try to draw anything if there's no data if ( defined( $data->[$i][$j] ) and defined( $data->[$i][ $j - 1 ] ) ) { if ( $self->true( $self->{'xy_plot'} ) ) { $x2 = $x1 + $delta_num * $data->[0][ $j - 1 ] + $zero_offset; $x3 = $x1 + $delta_num * $data->[0][$j] + $zero_offset; } else { $x2 = $x1 + ( $delta * ( $j - 1 ) ); $x3 = $x1 + ( $delta * $j ); } $y2 = $y1 - ( ( $data->[$i][ $j - 1 ] - $mod ) * $map ); $y3 = $y1 - ( ( $data->[$i][$j] - $mod ) * $map ); #set the flags, if the lines are out of the borders of the chart if ( ( $data->[$i][$j] > $self->{'max_val'} ) || ( $data->[$i][ $j - 1 ] > $self->{'max_val'} ) ) { $repair_top_flag = 1; } if ( ( $self->{'max_val'} <= 0 ) && ( ( $data->[$i][$j] > $self->{'max_val'} ) || ( $data->[$i][ $j - 1 ] > $self->{'max_val'} ) ) ) { $repair_top_flag = 1; } if ( ( $data->[$i][$j] < $self->{'min_val'} ) || ( $data->[$i][ $j - 1 ] < $self->{'min_val'} ) ) { $repair_bottom_flag = 1; } # draw the line # ---------------- # stepline option added by G.ST. 2005/02 #---------------- if ( $self->true( $self->{'stepline'} ) ) { if ( $self->{'stepline_mode'} =~ /^begin$/i ) { $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, gdBrushed ); $self->{'gd_obj'}->line( $x3, $y2, $x3, $y3, gdBrushed ); } else { $self->{'gd_obj'}->line( $x2, $y2, $x2, $y3, gdBrushed ); $self->{'gd_obj'}->line( $x2, $y3, $x3, $y3, gdBrushed ); } } # ----------------------------------- # end stepline option #------------------------------------ else { $self->{'gd_obj'}->line( $x2, $y2, $x3, $y3, gdBrushed ); } } } # reset the brush for points $brush = $self->_prepare_brush( $color, 'point', 'dataset' . ( $i - 1 ) ); $self->{'gd_obj'}->setBrush($brush); # draw every point for this dataset for $j ( 0 .. $self->{'num_datapoints'} ) { # don't try to draw anything if there's no data if ( defined( $data->[$i][$j] ) ) { if ( $self->true( $self->{'xy_plot'} ) ) { $x2 = $x1 + $delta_num * $data->[0][$j] + $zero_offset; $x3 = $x2; } else { $x2 = $x1 + ( $delta * $j ); $x3 = $x2; } $y2 = $y1 - ( ( $data->[$i][$j] - $mod ) * $map ); $y3 = $y2; # draw the point $self->{'gd_obj'}->line( $x2, $y2, $x3, $y3, gdBrushed ); # remember the imagemap data if they wanted it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ $x2, $y2 ]; } } else { if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ undef(), undef() ]; } } } } # and finaly box it off $self->{'gd_obj'} ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); #get the width and the heigth of the complete picture ( $abs_x_max, $abs_y_max ) = $self->{'gd_obj'}->getBounds(); #repair the chart, if the lines are out of the borders of the chart if ($repair_top_flag) { #overwrite the ugly mistakes # $self->{'gd_obj'}->filledRectangle ($self->{'curr_x_min'}, 0, $self->{'gd_obj'}->filledRectangle( $self->{'curr_x_min'} - ( $self->{'brush_size'} / 2 ), 0, $self->{'curr_x_max'}, $self->{'curr_y_min'} - 2, $self->_color_role_to_index('background') ); #save the actual x and y values $t_x_min = $self->{'curr_x_min'}; $t_x_max = $self->{'curr_x_max'}; $t_y_min = $self->{'curr_y_min'}; $t_y_max = $self->{'curr_y_max'}; #get back to the point, where everything began $self->{'curr_x_min'} = 0; $self->{'curr_y_min'} = 0; $self->{'curr_x_max'} = $abs_x_max; $self->{'curr_y_max'} = $abs_y_max; #draw the title again if ( $self->{'title'} ) { $self->_draw_title; } #draw the sub title again if ( $self->{'sub_title'} ) { $self->_draw_sub_title; } #draw the top legend again if ( $self->{'legend'} =~ /^top$/i ) { $self->_draw_top_legend; } #reset the actual values $self->{'curr_x_min'} = $t_x_min; $self->{'curr_x_max'} = $t_x_max; $self->{'curr_y_min'} = $t_y_min; $self->{'curr_y_max'} = $t_y_max; } if ($repair_bottom_flag) { #overwrite the ugly mistakes # $self->{'gd_obj'}->filledRectangle ($self->{'curr_x_min'}, $self->{'curr_y_max'}+1, $self->{'gd_obj'}->filledRectangle( $self->{'curr_x_min'} - ( $self->{'brush_size'} / 2 ), $self->{'curr_y_max'} + 1, $self->{'curr_x_max'}, $abs_y_max, $self->_color_role_to_index('background') ); #save the actual x and y values $t_x_min = $self->{'curr_x_min'}; $t_x_max = $self->{'curr_x_max'}; $t_y_min = $self->{'curr_y_min'}; $t_y_max = $self->{'curr_y_max'}; #get back to the point, where everything began $self->{'curr_x_min'} = 0; $self->{'curr_y_min'} = 0; $self->{'curr_x_max'} = $abs_x_max; $self->{'curr_y_max'} = $abs_y_max - 1; # mark off the graph_border space $self->{'curr_y_max'} -= 2 * $self->{'graph_border'}; #draw the bottom legend again if ( $self->{'legend'} =~ /^bottom$/i ) { $self->_draw_bottom_legend; } #draw the x label again if ( $self->{'x_label'} ) { $self->_draw_x_label; } #get back to the start point for the ticks $self->{'curr_x_min'} = $self->{'temp_x_min'}; $self->{'curr_y_min'} = $self->{'temp_y_min'}; $self->{'curr_x_max'} = $self->{'temp_x_max'}; $self->{'curr_y_max'} = $self->{'temp_y_max'}; #draw the x ticks again $self->_draw_x_ticks; #reset the actual values $self->{'curr_x_min'} = $t_x_min; $self->{'curr_x_max'} = $t_x_max; $self->{'curr_y_min'} = $t_y_min; $self->{'curr_y_max'} = $t_y_max; } return; } ## be a good module and return 1 1; Chart-v2.403.9/lib/Chart/StackedBars.pm0000644000175000017500000004215414341172251017331 0ustar herbertherbertuse v5.12; package Chart::StackedBars; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Chart::Base; use GD; use Carp; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<# #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @fn private _check_data # override check_data to make sure we don't get datasets with positive # and negative values mixed sub _check_data { my $self = shift; my $data = $self->{'dataref'}; my $length = 0; my ( $i, $j, $posneg ); my $composite; # remember the number of datasets if ( defined $self->{'composite_info'} ) { if ( $self->{'composite_info'}[0][0] =~ /^StackedBars$/i ) { $composite = 0; } if ( $self->{'composite_info'}[1][0] =~ /^StackedBars$/i ) { $composite = 1; } # $self->{'num_datasets'} = $#{$data}; ### $self->{'num_datasets'} = ( $#{ $self->{'composite_info'}[$composite][1] } ) + 1; } else { $self->{'num_datasets'} = $#{$data}; } # remember the number of points in the largest dataset $self->{'num_datapoints'} = 0; for ( 0 .. $self->{'num_datasets'} ) { if ( scalar( @{ $data->[$_] } ) > $self->{'num_datapoints'} ) { $self->{'num_datapoints'} = scalar( @{ $data->[$_] } ); } } # make sure the datasets don't mix pos and neg values for $i ( 0 .. $self->{'num_datapoints'} - 1 ) { $posneg = ''; for $j ( 1 .. $self->{'num_datasets'} ) { if ( $data->[$j][$i] > 0 ) { if ( $posneg eq 'neg' ) { croak "The values for a Chart::StackedBars data point must either be all positive or all negative"; } else { $posneg = 'pos'; } } elsif ( $data->[$j][$i] < 0 ) { if ( $posneg eq 'pos' ) { croak "The values for a Chart::StackedBars data point must either be all positive or all negative"; } else { $posneg = 'neg'; } } } } # find good min and max y-values for the plot $self->_find_y_scale; # find the longest x-tick label for ( @{ $data->[0] } ) { if ( length($_) > $length ) { $length = length($_); } } # now store it in the object $self->{'x_tick_label_length'} = $length; return; } ## @fn private _find_y_range sub _find_y_range { my $self = shift; # This finds the minimum and maximum point-sum over all x points, # where the point-sum is the sum of the dataset values for that point. # If the y value in any dataset is undef for a given x, it simply # adds nothing to the sum. my $data = $self->{'dataref'}; my $max = undef; my $min = undef; for my $i ( 0 .. $#{ $data->[0] } ) { # data point my $sum = $data->[1]->[$i] || 0; for my $dataset ( @$data[ 2 .. $#$data ] ) { # order not important my $datum = $dataset->[$i]; $sum += $datum if defined $datum; } if ( defined $max ) { if ( $sum > $max ) { $max = $sum } elsif ( $sum < $min ) { $min = $sum } } else { $min = $max = $sum } } # make sure all-positive or all-negative charts get anchored at # zero so that we don't cut out some parts of the bars if ( ( $max > 0 ) && ( $min > 0 ) ) { $min = 0; } if ( ( $min < 0 ) && ( $max < 0 ) ) { $max = 0; } ( $min, $max ); } # ## override _find_y_scale to account for stacked bars # sub _find_y_scale { # my $self = shift; # my $raw = $self->{'dataref'}; # my $data = [@{$raw->[1]}]; # my ($i, $j, $max, $min); # my ($order, $mult, $tmp); # my ($range, $delta, @dec, $y_ticks); # my $labels = []; # my $length = 0; # # # use realy weird max and min values # $max = -999999999999; # $min = 999999999999; # # # go through and stack them # for $i (0..$self->{'num_datapoints'}-1) { # for $j (2..$self->{'num_datasets'}) { # $data->[$i] += $raw->[$j][$i]; # } # } # # # get max and min values # for $i (0..$self->{'num_datapoints'}-1) { # if ($data->[$i] > $max) { # $max = $data->[$i]; # } # if ($data->[$i] < $min) { # $min = $data->[$i]; # } # } # # # make sure all-positive or all-negative charts get anchored at # # zero so that we don't cut out some parts of the bars # if (($max > 0) && ($min > 0)) { # $min = 0; # } # if (($min < 0) && ($max < 0)) { # $max = 0; # } # # # calculate good max value # if ($max < -10) { # $tmp = -$max; # $order = int((log $tmp) / (log 10)); # $mult = int ($tmp / (10 ** $order)); # $tmp = ($mult - 1) * (10 ** $order); # $max = -$tmp; # } # elsif ($max < 0) { # $max = 0; # } # elsif ($max > 10) { # $order = int((log $max) / (log 10)); # $mult = int ($max / (10 ** $order)); # $max = ($mult + 1) * (10 ** $order); # } # elsif ($max >= 0) { # $max = 10; # } # # # now go for a good min # if ($min < -10) { # $tmp = -$min; # $order = int((log $tmp) / (log 10)); # $mult = int ($tmp / (10 ** $order)); # $tmp = ($mult + 1) * (10 ** $order); # $min = -$tmp; # } # elsif ($min < 0) { # $min = -10; # } # elsif ($min > 10) { # $order = int ((log $min) / (log 10)); # $mult = int ($min / (10 ** $order)); # $min = $mult * (10 ** $order); # } # elsif ($min >= 0) { # $min = 0; # } # # # put the appropriate min and max values into the object if necessary # unless (defined ($self->{'max_val'})) { # $self->{'max_val'} = $max; # } # unless (defined ($self->{'min_val'})) { # $self->{'min_val'} = $min; # } # # # generate the y_tick labels, store them in the object # # figure out which one is going to be the longest # $range = $self->{'max_val'} - $self->{'min_val'}; # $y_ticks = $self->{'y_ticks'} - 1; # ## Don't adjust y_ticks if the user specified custom labels # if ($self->{'integer_ticks_only'} =~ /^true$/i && ! $self->{'y_tick_labels'}) { # unless (($range % $y_ticks) == 0) { # while (($range % $y_ticks) != 0) { # $y_ticks++; # } # $self->{'y_ticks'} = $y_ticks + 1; # } # } # # $delta = $range / $y_ticks; # for (0..$y_ticks) { # $tmp = $self->{'min_val'} + ($delta * $_); # @dec = split /\./, $tmp; # if ($dec[1] && (length($dec[1]) > 3)) { # $tmp = sprintf("%.3f", $tmp); # } # $labels->[$_] = $tmp; # if (length($tmp) > $length) { # $length = length($tmp); # } # } # # # store it in the object # $self->{'y_tick_labels'} = $labels; # $self->{'y_tick_label_length'} = $length; # # # and return # return; # } ## @fn private _draw_data # finally get around to plotting the data sub _draw_data { my $self = shift; my $raw = $self->{'dataref'}; my $data = []; my $misccolor = $self->_color_role_to_index('misc'); my ( $width, $height, $delta, $map, $mod ); my ( $x1, $y1, $x2, $y2, $x3, $y3, $i, $j, $color, $cut ); my $pink = $self->{'gd_obj'}->colorAllocate( 255, 0, 255 ); # init the imagemap data field if they want it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # width and height of remaining area, delta for width of bars, mapping value $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; if ( $self->true( $self->{'spaced_bars'} ) ) { $delta = ( $width / ( $self->{'num_datapoints'} * 2 ) ); } else { $delta = $width / $self->{'num_datapoints'}; } $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $map = $height / ( $self->{'max_val'} - $self->{'min_val'} ); # get the base x and y values $x1 = $self->{'curr_x_min'}; if ( $self->{'min_val'} >= 0 ) { $y1 = $self->{'curr_y_max'}; $mod = $self->{'min_val'}; } elsif ( $self->{'max_val'} <= 0 ) { $y1 = $self->{'curr_y_min'}; $mod = $self->{'max_val'}; } else { $y1 = $self->{'curr_y_min'} + ( $map * $self->{'max_val'} ); $mod = 0; $self->{'gd_obj'}->line( $self->{'curr_x_min'}, $y1, $self->{'curr_x_max'}, $y1, $misccolor ); } # create another copy of the data, but stacked $data->[1] = [ @{ $raw->[1] } ]; for $i ( 0 .. $self->{'num_datapoints'} - 1 ) { for $j ( 2 .. $self->{'num_datasets'} ) { $data->[$j][$i] = $data->[ $j - 1 ][$i] + $raw->[$j][$i]; } } # draw the damn bars for $i ( 0 .. $self->{'num_datapoints'} - 1 ) { # init the y values for this datapoint $y2 = $y1; for $j ( 1 .. $self->{'num_datasets'} ) { # get the color $color = $self->_color_role_to_index( 'dataset' . ( $j - 1 ) ); # set up the geometry for the bar if ( $self->true( $self->{'spaced_bars'} ) ) { $x2 = $x1 + ( 2 * $i * $delta ) + ( $delta / 2 ); $x3 = $x2 + $delta; } else { $x2 = $x1 + ( $i * $delta ); $x3 = $x2 + $delta; } $y3 = $y1 - ( ( $data->[$j][$i] - $mod ) * $map ); #cut the bars off, if needed if ( $data->[$j][$i] > $self->{'max_val'} ) { $y3 = $y1 - ( ( $self->{'max_val'} - $mod ) * $map ); $cut = 1; } elsif ( $data->[$j][$i] < $self->{'min_val'} ) { $y3 = $y1 - ( ( $self->{'min_val'} - $mod ) * $map ); $cut = 1; } else { $cut = 0; } # draw the bar ## y2 and y3 are reversed in some cases because GD's fill ## algorithm is lame if ( $data->[$j][$i] > 0 ) { $self->{'gd_obj'}->filledRectangle( $x2, $y3, $x3, $y2, $color ); if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$j][$i] = [ $x2, $y3, $x3, $y2 ]; } } else { $self->{'gd_obj'}->filledRectangle( $x2, $y2, $x3, $y3, $color ); if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$j][$i] = [ $x2, $y2, $x3, $y3 ]; } } # now outline it. outline red if the bar had been cut off unless ($cut) { $self->{'gd_obj'}->rectangle( $x2, $y2, $x3, $y3, $misccolor ); } else { $self->{'gd_obj'}->rectangle( $x2, $y2, $x3, $y3, $misccolor ); $self->{'gd_obj'}->rectangle( $x2, $y1, $x3, $y3, $pink ); } # now bootstrap the y values $y2 = $y3; } } # and finaly box it off $self->{'gd_obj'} ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); return; } ## @fn private _draw_left_legend sub _draw_left_legend { my $self = shift; my @labels = @{ $self->{'legend_labels'} }; my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush ); my $font = $self->{'legend_font'}; # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The subtitle font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # get the miscellaneous color $misccolor = $self->_color_role_to_index('misc'); # find out how wide the largest label is $width = ( 2 * $self->{'text_space'} ) + ( $self->{'max_legend_label'} * $w ) + $self->{'legend_example_size'} + ( 2 * $self->{'legend_space'} ); # get some base x-y coordinates $x1 = $self->{'curr_x_min'}; $x2 = $self->{'curr_x_min'} + $width; $y1 = $self->{'curr_y_min'} + $self->{'graph_border'}; $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'} + ( $self->{'num_datasets'} * ( $h + $self->{'text_space'} ) ) + ( 2 * $self->{'legend_space'} ); # box the legend off $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor ); # leave that nice space inside the legend box $x1 += $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; # now draw the actual legend for ( 0 .. $#labels ) { # get the color my $c = $self->{'num_datasets'} - $_ - 1; # color of the datasets in the legend if ( $self->{'dataref'}[1][0] < 0 ) { $color = $self->_color_role_to_index( 'dataset' . $_ ); } else { $color = $self->_color_role_to_index( 'dataset' . $c ); } # find the x-y coords $x2 = $x1; $x3 = $x2 + $self->{'legend_example_size'}; $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2; # do the line first $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color ); # reset the brush for points $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $_ } ); $self->{'gd_obj'}->setBrush($brush); # draw the point $self->{'gd_obj'}->line( int( ( $x3 + $x2 ) / 2 ), $y2, int( ( $x3 + $x2 ) / 2 ), $y2, gdBrushed ); # now the label $x2 = $x3 + ( 2 * $self->{'text_space'} ); $y2 -= $h / 2; # order of the datasets in the legend if ( $self->{'dataref'}[1][0] < 0 ) { $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color ); } else { $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$c], $color ); } } # mark off the used space $self->{'curr_x_min'} += $width; # and return return 1; } ## @fn private _draw_right_legend sub _draw_right_legend { my $self = shift; my @labels = @{ $self->{'legend_labels'} }; my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush ); my $font = $self->{'legend_font'}; # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The subtitle font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # get the miscellaneous color $misccolor = $self->_color_role_to_index('misc'); # find out how wide the largest label is $width = ( 2 * $self->{'text_space'} ) + ( $self->{'max_legend_label'} * $w ) + $self->{'legend_example_size'} + ( 2 * $self->{'legend_space'} ); # get some starting x-y values $x1 = $self->{'curr_x_max'} - $width; $x2 = $self->{'curr_x_max'}; $y1 = $self->{'curr_y_min'} + $self->{'graph_border'}; $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'} + ( $self->{'num_datasets'} * ( $h + $self->{'text_space'} ) ) + ( 2 * $self->{'legend_space'} ); # box the legend off $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor ); # leave that nice space inside the legend box $x1 += $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; # now draw the actual legend for ( 0 .. $#labels ) { # get the color my $c = $self->{'num_datasets'} - $_ - 1; # color of the datasets in the legend if ( $self->{'dataref'}[1][0] < 0 ) { $color = $self->_color_role_to_index( 'dataset' . $_ ); } else { $color = $self->_color_role_to_index( 'dataset' . $c ); } # find the x-y coords $x2 = $x1; $x3 = $x2 + $self->{'legend_example_size'}; $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2; # do the line first $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color ); # reset the brush for points $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $_ } ); $self->{'gd_obj'}->setBrush($brush); # draw the point $self->{'gd_obj'}->line( int( ( $x3 + $x2 ) / 2 ), $y2, int( ( $x3 + $x2 ) / 2 ), $y2, gdBrushed ); # now the label $x2 = $x3 + ( 2 * $self->{'text_space'} ); $y2 -= $h / 2; # order of the datasets in the legend if ( $self->{'dataref'}[1][0] < 0 ) { $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color ); } else { $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$c], $color ); } } # mark off the used space $self->{'curr_x_max'} -= $width; # and return return 1; } ## be a good module and return 1 1; Chart-v2.403.9/lib/Chart/Manual.pod0000644000175000017500000000275514341172251016531 0ustar herbertherbert =encoding UTF-8 =head1 NAME Chart::Manual - Chart user documentation index =head1 SYNOPSIS Chart is a collection of modules for visualizing numerical data. It is designed for: (in that order) maximal flexibility, feature richness and minimal dependencies. It can write PNG and JPG images into files and STDOUT (for web and shell usage). The early API is mostly borrowed from Martien Verbruggen's L module. =head1 TYPES Example programs with output for each chart type: L =head1 WORKFLOWS Ways to use Chart, broadly described: L =head1 METHODS Descriptions of all methods geared toward the user: L =head1 PROPERTIES The method C allows to directly change particular chart properties. All of these explained in detail with their acceptable and default values are sorted there by name and topic or type: L =head1 FUTURE CHANGES Much of what is documented here will be superseded by a new API provided by upcoming version 3.0. The old API will be discouraged but not deprecated, except maybe the module Chart::Composite and some properties. The new API will aply only to objects created by Chart-Enew(...); =head1 COPYRIGHT & LICENSE Copyright 2022 David Bonner, Herbert Breunung. This program is free software; you can redistribute it and/or modify it under same terms as Perl itself. =head1 AUTHOR David Bonner, Chart Group, Herbert Breunung, =cut Chart-v2.403.9/lib/Chart/Pareto.pm0000644000175000017500000002265414341172251016400 0ustar herbertherbertuse v5.12; package Chart::Pareto; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Chart::Base; use GD; use Carp; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<# #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @fn private _find_y_scale #calculate the range with the sum dataset1. all datas has to be positiv sub _find_y_range { my $self = shift; my $data = $self->{'dataref'}; my $sum = 0; for ( my $i = 0 ; $i < $self->{'num_datapoints'} ; $i++ ) { if ( $data->[1][$i] >= 0 ) { $sum += $data->[1][$i]; } else { carp "We need positiv data, if we want to draw a pareto graph!!"; return 0; } } #store the sum $self->{'sum'} = $sum; #return the range ( 0, $sum ); } ## @fn private _sort_data # sort the data sub _sort_data { my $self = shift; my $data = $self->{'dataref'}; my @labels = @{ $data->[0] }; my @values = @{ $data->[1] }; # sort the values and their labels @labels = @labels[ sort { $values[$b] <=> $values[$a] } 0 .. $#labels ]; @values = sort { $b <=> $a } @values; #save the sorted values and their labels @{ $data->[0] } = @labels; @{ $data->[1] } = @values; #finally return return 1; } ## @fn private _draw_legend # let them know what all the pretty colors mean sub _draw_legend { my $self = shift; my ($length); my $num_dataset; # check to see if legend type is none.. if ( $self->{'legend'} =~ /^none$/ ) { return 1; } # check to see if they have as many labels as datasets, # warn them if not if ( ( $#{ $self->{'legend_labels'} } >= 0 ) && ( ( scalar( @{ $self->{'legend_labels'} } ) ) != 2 ) ) { carp "I need two legend labels. One for the data and one for the sum."; } # init a field to store the length of the longest legend label unless ( $self->{'max_legend_label'} ) { $self->{'max_legend_label'} = 0; } # fill in the legend labels, find the longest one unless ( $self->{'legend_labels'}[0] ) { $self->{'legend_labels'}[0] = "Dataset"; } unless ( $self->{'legend_labels'}[1] ) { $self->{'legend_labels'}[1] = "Running sum"; } if ( length( $self->{'legend_labels'}[0] ) > length( $self->{'legend_labels'}[1] ) ) { $self->{'max_legend_label'} = length( $self->{'legend_labels'}[0] ); } else { $self->{'max_legend_label'} = length( $self->{'legend_labels'}[1] ); } #set the number of datasets to 2, and store it $num_dataset = $self->{'num_datasets'}; $self->{'num_datasets'} = 2; # different legend types if ( $self->{'legend'} eq 'bottom' ) { $self->_draw_bottom_legend; } elsif ( $self->{'legend'} eq 'right' ) { $self->_draw_right_legend; } elsif ( $self->{'legend'} eq 'left' ) { $self->_draw_left_legend; } elsif ( $self->{'legend'} eq 'top' ) { $self->_draw_top_legend; } else { carp "I can't put a legend there (at " . $self->{'legend'} . ")\n"; } #reload the number of datasets $self->{'num_datasets'} = $num_dataset; # and return return 1; } ## @fn private _draw_data # finally get around to plotting the data sub _draw_data { my $self = shift; my $data = $self->{'dataref'}; my $misccolor = $self->_color_role_to_index('misc'); my ( $x1, $x2, $x3, $y1, $y2, $y3, $y1_line, $y2_line, $x1_line, $x2_line, $h, $w ); my ( $width, $height, $delta1, $delta2, $map, $mod, $cut ); my ( $i, $j, $color, $line_color, $percent, $per_label, $per_label_len ); my $sum = $self->{'sum'}; my $curr_sum = 0; my $font = $self->{'legend_font'}; my $pink = $self->{'gd_obj'}->colorAllocate( 255, 0, 255 ); my $diff; # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The subtitle font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # init the imagemap data field if they wanted it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # find both delta values ($delta1 for stepping between different # datapoint names, $delta2 for setpping between datasets for that # point) and the mapping constant $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta1 = $width / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); $diff = ( $self->{'max_val'} - $self->{'min_val'} ); $diff = 1 if $diff == 0; $map = $height / $diff; if ( $self->true( $self->{'spaced_bars'} ) ) { $delta2 = $delta1 / 3; } else { $delta2 = $delta1; } # get the base x-y values $x1 = $self->{'curr_x_min'}; $y1 = $self->{'curr_y_max'}; $y1_line = $y1; $mod = $self->{'min_val'}; $x1_line = $self->{'curr_x_min'}; # draw the bars and the lines $color = $self->_color_role_to_index('dataset0'); $line_color = $self->_color_role_to_index('dataset1'); # draw every bar for this dataset for $j ( 0 .. $self->{'num_datapoints'} ) { # don't try to draw anything if there's no data if ( defined( $data->[1][$j] ) ) { #calculate the percent value for this data and the actual sum; $curr_sum += $data->[1][$j]; $percent = int( $curr_sum / ( $sum || 1 ) * 100 ); # find the bounds of the rectangle if ( $self->true( $self->{'spaced_bars'} ) ) { $x2 = $x1 + ( $j * $delta1 ) + $delta2; } else { $x2 = $x1 + ( $j * $delta1 ); } $y2 = $y1; $x3 = $x2 + $delta2; $y3 = $y1 - ( ( $data->[1][$j] - $mod ) * $map ); #cut the bars off, if needed if ( $data->[1][$j] > $self->{'max_val'} ) { $y3 = $y1 - ( ( $self->{'max_val'} - $mod ) * $map ); $cut = 1; } elsif ( $data->[1][$j] < $self->{'min_val'} ) { $y3 = $y1 - ( ( $self->{'min_val'} - $mod ) * $map ); $cut = 1; } else { $cut = 0; } # draw the bar ## y2 and y3 are reversed in some cases because GD's fill ## algorithm is lame $self->{'gd_obj'}->filledRectangle( $x2, $y3, $x3, $y2, $color ); if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[1][$j] = [ $x2, $y3, $x3, $y2 ]; } # now outline it. outline red if the bar had been cut off unless ($cut) { $self->{'gd_obj'}->rectangle( $x2, $y3, $x3, $y2, $misccolor ); } else { $self->{'gd_obj'}->rectangle( $x2, $y3, $x3, $y2, $pink ); } $x2_line = $x3; if ( $self->{'max_val'} >= $curr_sum ) { #get the y value $y2_line = $y1 - ( ( $curr_sum - $mod ) * $map ); #draw the line $self->{'gd_obj'}->line( $x1_line, $y1_line, $x2_line, $y2_line, $line_color ); #draw a little rectangle at the end of the line $self->{'gd_obj'}->filledRectangle( $x2_line - 2, $y2_line - 2, $x2_line + 2, $y2_line + 2, $line_color ); #draw the label for the percent value $per_label = $percent . '%'; $per_label_len = length($per_label) * $w; $self->{'gd_obj'}->string( $font, $x2_line - $per_label_len - 1, $y2_line - $h - 1, $per_label, $line_color ); #update the values for next the line $y1_line = $y2_line; $x1_line = $x2_line; } else { #get the y value $y2_line = $y1 - ( ( $self->{'max_val'} - $mod ) * $map ); #draw the line $self->{'gd_obj'}->line( $x1_line, $y1_line, $x2_line, $y2_line, $pink ); #draw a little rectangle at the end of the line $self->{'gd_obj'}->filledRectangle( $x2_line - 2, $y2_line - 2, $x2_line + 2, $y2_line + 2, $pink ); #draw the label for the percent value $per_label = $percent . '%'; $per_label_len = length($per_label) * $w; $self->{'gd_obj'}->string( $font, $x2_line - $per_label_len - 1, $y2_line - $h - 1, $per_label, $pink ); #update the values for the next line $y1_line = $y2_line; $x1_line = $x2_line; } } else { if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[1][$j] = [ undef(), undef(), undef(), undef() ]; } } } # and finaly box it off $self->{'gd_obj'} ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); return; } ## be a good module and return 1 1; Chart-v2.403.9/lib/Chart/Constants.pm0000644000175000017500000000024314341172251017110 0ustar herbertherbert # set up initial constant values use v5.12; package Chart::Constants; our $VERSION = 'v2.403.9'; use constant PI => 4 * atan2( 1, 1 ); 1; # be a good module Chart-v2.403.9/lib/Chart/Direction.pm0000644000175000017500000010031114341172251017051 0ustar herbertherbert # circular oriented chart like rotating vectors use v5.12; package Chart::Direction; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Chart::Base; use GD; use Carp; use POSIX; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @method int set(%opts) # @param[in] %opts Hash of options to the Chart # @return ok or croak # # @brief # Set all options # # @details # main method for customizing the chart, lets users # specify values for different parameters\n # dont check the number of points in the added datasets in a polarplot\n # overwrite Base method # sub set { my $self = shift; my %opts = @_; # basic error checking on the options, just warn 'em unless ( $#_ % 2 ) { carp "Whoops, some option to be set didn't have a value.\n", "You might want to look at that.\n"; } # set the options for ( keys %opts ) { $self->{$_} = $opts{$_}; # if someone wants to change the grid_lines color, we should set all # the colors of the grid_lines if ( $_ =~ /^colors$/ ) { my %hash = %{ $opts{$_} }; foreach my $key ( sort keys %hash ) { if ( $key =~ /^grid_lines$/ ) { $self->{'colors'}{'y_grid_lines'} = $hash{'grid_lines'}; $self->{'colors'}{'x_grid_lines'} = $hash{'grid_lines'}; $self->{'colors'}{'y2_grid_lines'} = $hash{'grid_lines'}; } } } } if ( $self->false( $self->{'polar'} ) && ( defined $self->{'croak'} ) ) { carp "New data set to be added has an incorrect number of points"; } # now return return 1; } ## @method int add_dataset(@data) # Add many datasets to the dataref # # Graph API\n # Overwrite Base method # # @param @data Dataset to add # sub add_dataset { my $self = shift; my @data = @_; # error check the data (carp, don't croak) if ( $self->{'dataref'} && ( $#{ $self->{'dataref'}->[0] } != $#data ) ) { # carp "New data set to be added has an incorrect number of points"; $self->{'croak'} = 'true'; } # copy it into the dataref push @{ $self->{'dataref'} }, [@data]; # now return return 1; } #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @fn private int _find_y_scale() # we use the find_y_scale methode to determine the labels of the circles # and the amount of them # @return status # # This function is an overwrite to the same function found in the base class # Chart::Base # sub _find_y_scale { my $self = shift; # Predeclare vars. my ( $d_min, $d_max ); # Dataset min & max. my ( $p_min, $p_max ); # Plot min & max. my ( $tickInterval, $tickCount, $skip ); my @tickLabels; # List of labels for each tick. my $maxtickLabelLen = 0; # The length of the longest tick label. # Find the datatset minimum and maximum. ( $d_min, $d_max ) = $self->_find_y_range(); # Force the inclusion of zero if the user has requested it. if ( $self->true( $self->{'include_zero'} ) ) { if ( ( $d_min * $d_max ) > 0 ) # If both are non zero and of the same sign. { if ( $d_min > 0 ) # If the whole scale is positive. { $d_min = 0; } else # The scale is entirely negative. { $d_max = 0; } } } # Allow the dataset range to be overidden by the user. # f_min/max are booleans which indicate that the min & max should not be modified. my $f_min = defined $self->{'min_val'}; $d_min = $self->{'min_val'} if $f_min; my $f_max = defined $self->{'max_val'}; $d_max = $self->{'max_val'} if $f_max; # Assert against the min is larger than the max. if ( $d_min > $d_max ) { croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)"; } # Calculate the width of the dataset. (posibly modified by the user) my $d_width = $d_max - $d_min; # If the width of the range is zero, forcibly widen it # (to avoid division by zero errors elsewhere in the code). if ( 0 == $d_width ) { $d_min--; $d_max++; $d_width = 2; } # Descale the range by converting the dataset width into # a floating point exponent & mantisa pair. my ( $rangeExponent, $rangeMantisa ) = $self->_sepFP($d_width); my $rangeMuliplier = 10**$rangeExponent; # Find what tick # to use & how many ticks to plot, # round the plot min & max to suatable round numbers. ( $tickInterval, $tickCount, $p_min, $p_max ) = $self->_calcTickInterval( $d_min / $rangeMuliplier, $d_max / $rangeMuliplier, $f_min, $f_max, $self->{'min_circles'} + 1, $self->{'max_circles'} + 1 ); # Restore the tickInterval etc to the correct scale $_ *= $rangeMuliplier foreach ( $tickInterval, $p_min, $p_max ); #get the precision for the labels my $precision = $self->{'precision'}; # Now sort out an array of tick labels. if ( $self->false( $self->{'polar'} ) ) { for ( my $labelNum = $p_min ; $labelNum <= $p_max ; $labelNum += $tickInterval ) { my $labelText; if ( defined $self->{f_y_tick} ) { # Is _default_f_tick function used? if ( $self->{f_y_tick} == \&Chart::Base::_default_f_tick ) { $labelText = sprintf( "%." . $precision . "f", $labelNum ); } else { # print \&_default_f_tick; $labelText = $self->{f_y_tick}->($labelNum); } } else { $labelText = sprintf( "%." . $precision . "f", $labelNum ); } push @tickLabels, $labelText; $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText; } } else { # polar == true for ( my $labelNum = $p_max ; $labelNum >= $p_min ; $labelNum -= $tickInterval ) { my $labelText; if ( defined $self->{f_y_tick} ) { # Is _default_f_tick function used? if ( $self->{f_y_tick} == \&Chart::Base::_default_f_tick ) { $labelText = sprintf( "%." . $precision . "f", $labelNum ); } else { # print \&_default_f_tick; $labelText = $self->{f_y_tick}->($labelNum); } } else { $labelText = sprintf( "%." . $precision . "f", $labelNum ); } push @tickLabels, $labelText; $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText; } } # Store the calculated data. $self->{'min_val'} = $p_min, $self->{'max_val'} = $p_max, $self->{'y_ticks'} = $tickCount, $self->{'y_tick_labels'} = \@tickLabels, $self->{'y_tick_label_length'} = $maxtickLabelLen; # and return. return 1; } ## @fn private _calcTickInterval($dataset_min, $dataset_max, $flag_fixed_min, $flag_fixed_max, $minTicks, $maxTicks) # @brief # Calculates the ticks for direction in normalised units. # # @details # Calculate the Interval between ticks in y direction # and compare the number of ticks to # the user given values min_y_ticks, max_y_ticks # # @param[in] dataset_min Minimal value in y direction # @param[in] dataset_max Maximal value in y direction # @param[in] flag_fixed_min Indicator whether the dataset_min value is fixed # @param[in] flag_fixed_max Indicator whether the dataset_max value is fixed # @param[in] minTicks Minimal number of ticks wanted # @param[in] maxTicks Maximal number of ticks wanted # @return $tickInterval, $tickCount, $pMin, $pMax # sub _calcTickInterval { my $self = shift; my ( $min, $max, # The dataset min & max. $minF, $maxF, # Indicates if those min/max are fixed. $minTicks, $maxTicks, # The minimum & maximum number of ticks. ) = @_; # Verify the supplied 'min_y_ticks' & 'max_y_ticks' are sensible. if ( $minTicks < 2 ) { carp "Chart::Direction : Incorrect value for 'min_circles', too small.\n"; $minTicks = 2; } if ( $maxTicks < 5 * $minTicks ) { carp "Chart::Direction : Incorrect value for 'max_circles', too small.\n"; $maxTicks = 5 * $minTicks; } my $width = $max - $min; my @divisorList; for ( my $baseMul = 1 ; ; $baseMul *= 10 ) { TRY: foreach my $tryMul ( 1, 2, 5 ) { # Calc a fresh, smaller tick interval. my $divisor = $baseMul * $tryMul; # Count the number of ticks. my ( $tickCount, $pMin, $pMax ) = $self->_countTicks( $min, $max, 1 / $divisor ); # Look a the number of ticks. if ( $maxTicks < $tickCount ) { # If it is too high, Backtrack. $divisor = pop @divisorList; # just for security: if ( !defined($divisor) || $divisor == 0 ) { $divisor = 1; } ( $tickCount, $pMin, $pMax ) = $self->_countTicks( $min, $max, 1 / $divisor ); carp "Chart::Direction : Caution: Tick limit of $maxTicks exceeded. Backing of to an interval of " . 1 / $divisor . " which plots $tickCount ticks\n"; return ( 1 / $divisor, $tickCount, $pMin, $pMax ); } elsif ( $minTicks > $tickCount ) { # If it is too low, try again. next TRY; } else { # Store the divisor for possible later backtracking. push @divisorList, $divisor; # if the min or max is fixed, check they will fit in the interval. next TRY if ( $minF && ( int( $min * $divisor ) != ( $min * $divisor ) ) ); next TRY if ( $maxF && ( int( $max * $divisor ) != ( $max * $divisor ) ) ); # If everything passes the tests, return. return ( 1 / $divisor, $tickCount, $pMin, $pMax ); } } } die "can't happen!"; } ## @fn private int _draw_y_ticks() # draw the circles and the axes # # Overwrites _draw_y_ticks() of Base class # # @return status sub _draw_y_ticks { my $self = shift; my $data = $self->{'dataref'}; my $misccolor = $self->_color_role_to_index('misc'); my $textcolor = $self->_color_role_to_index('text'); my $background = $self->_color_role_to_index('background'); my @labels = @{ $self->{'y_tick_labels'} }; my ( $width, $height, $centerX, $centerY, $diameter ); my ( $pi, $font, $fontW, $fontH, $labelX, $labelY, $label_offset ); my ( $dia_delta, $dia, $x, $y, @label_degrees, $arc, $angle_interval ); # set up initial constant values $pi = 3.14159265358979323846, $font = $self->{'legend_font'}, $fontW = $self->{'legend_font'}->width, $fontH = $self->{'legend_font'}->height, $angle_interval = $self->{'angle_interval'}; if ( $self->true( $self->{'grey_background'} ) ) { $background = $self->_color_role_to_index('grey_background'); } # init the imagemap data field if they wanted it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # find width and height $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; # find center point, from which the pie will be drawn around $centerX = int( $width / 2 + $self->{'curr_x_min'} ); $centerY = int( $height / 2 + $self->{'curr_y_min'} ); # always draw a circle, which means the diameter will be the smaller # of the width and height. let enough space for the labels. ## @todo calculate the width of the labels if ( $width < $height ) { $diameter = $width - 110; } else { $diameter = $height - 80; } #the difference between the diameter of two following circles; $dia_delta = ceil( $diameter / ( $self->{'y_ticks'} - 1 ) ); #store the calculated data $self->{'centerX'} = $centerX; $self->{'centerY'} = $centerY; $self->{'diameter'} = $diameter; #draw the axes and its labels # set up an array of labels for the axes if ( $angle_interval == 0 ) { @label_degrees = (); } elsif ( $angle_interval <= 5 && $angle_interval > 0 ) { @label_degrees = qw(180 175 170 165 160 155 150 145 140 135 130 125 120 115 110 105 100 95 90 85 80 75 70 65 60 55 50 45 40 35 30 25 20 15 10 5 0 355 350 345 340 335 330 325 320 315 310 305 300 295 290 285 280 275 270 265 260 255 250 245 240 235 230 225 220 215 210 205 200 195 190 185); $angle_interval = 5; } elsif ( $angle_interval <= 10 && $angle_interval > 5 ) { @label_degrees = qw(180 170 160 150 140 130 120 110 100 90 80 70 60 50 40 30 20 10 0 350 340 330 320 310 300 290 280 270 260 250 240 230 220 210 200 190); $angle_interval = 10; } elsif ( $angle_interval <= 15 && $angle_interval > 10 ) { @label_degrees = qw(180 165 150 135 120 105 90 75 60 45 30 15 0 345 330 315 300 285 270 255 240 225 210 195); $angle_interval = 15; } elsif ( $angle_interval <= 20 && $angle_interval > 15 ) { @label_degrees = qw(180 160 140 120 100 80 60 40 20 0 340 320 300 280 260 240 220 200); $angle_interval = 20; } elsif ( $angle_interval <= 30 && $angle_interval > 20 ) { @label_degrees = qw(180 150 120 90 60 30 0 330 300 270 240 210); $angle_interval = 30; } elsif ( $angle_interval <= 45 && $angle_interval > 30 ) { @label_degrees = qw(180 135 90 45 0 315 270 225); $angle_interval = 45; } elsif ( $angle_interval <= 90 && $angle_interval > 45 ) { @label_degrees = qw(180 90 0 270); $angle_interval = 90; } else { carp "The angle_interval must be between 0 and 90!\nCorrected value: 30"; @label_degrees = qw(180 150 120 90 60 30 0 330 300 270 240 210); $angle_interval = 30; } $arc = 0; foreach (@label_degrees) { #calculated the coordinates of the end point of the line $x = sin($arc) * ( $diameter / 2 + 10 ) + $centerX; $y = cos($arc) * ( $diameter / 2 + 10 ) + $centerY; #some ugly correcture if ( $_ == '270' ) { $y++; } #draw the line $self->{'gd_obj'}->line( $centerX, $centerY, $x, $y, $misccolor ); #calculate the string point $x = sin($arc) * ( $diameter / 2 + 30 ) + $centerX - 8; $y = cos($arc) * ( $diameter / 2 + 28 ) + $centerY - 6; #draw the labels $self->{'gd_obj'}->string( $font, $x, $y, $_ . '\B0', $textcolor ); $arc += ( ($angle_interval) / 360 ) * 2 * $pi; } #draw the circles $dia = 0; foreach (@labels) { $self->{'gd_obj'}->arc( $centerX, $centerY, $dia, $dia, 0, 360, $misccolor ); $dia += $dia_delta; } $self->{'gd_obj'}->filledRectangle( $centerX - length( $labels[0] ) / 2 * $fontW - 2, $centerY + 2, $centerX + 2 + $diameter / 2, $centerY + $fontH + 2, $background ); #draw the labels of the circles $dia = 0; foreach (@labels) { $self->{'gd_obj'}->string( $font, $centerX + $dia / 2 - length($_) / 2 * $fontW, $centerY + 2, $_, $textcolor ); $dia += $dia_delta; } return 1; } ## @fn private int _draw_x_ticks() # We don't need x ticks, it's all done in _draw_y_ticks # @return status # # Overwrites the corresponding function in Base # sub _draw_x_ticks { my $self = shift; return 1; } ## @fn private _draw_data # finally get around to plotting the data for direction charts sub _draw_data { my $self = shift; my $data = $self->{'dataref'}; my $misccolor = $self->_color_role_to_index('misc'); my $textcolor = $self->_color_role_to_index('text'); my $background = $self->_color_role_to_index('background'); my ( $width, $height, $centerX, $centerY, $diameter ); my ( $mod, $map, $i, $j, $brush, $color, $x, $y, $winkel, $first_x, $first_y ); my ( $arrow_x, $arrow_y, $m ); $color = 1; my $pi = 3.14159265358979323846; my $len = 10; my $alpha = 1; my $last_x = undef; my $last_y = undef; my $diff; my $n = 0; if ( $self->true( $self->{'pairs'} ) ) { my $a = $self->{'num_datasets'} / 2; my $b = ceil($a); my $c = $b - $a; if ( $c == 0 ) { croak "Wrong number of datasets for 'pairs'"; } } # init the imagemap data field if they wanted it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # find width and height $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; # get the base values if ( $self->false( $self->{'polar'} ) ) { $mod = $self->{'min_val'}; } else { $mod = $self->{'max_val'}; } $centerX = $self->{'centerX'}; $centerY = $self->{'centerY'}; $diameter = $self->{'diameter'}; $diff = $self->{'max_val'} - $self->{'min_val'}; $diff = 1 if $diff < 1; $map = $diameter / 2 / $diff; $brush = $self->_prepare_brush( $color, 'point' ); $self->{'gd_obj'}->setBrush($brush); # draw every line for this dataset if ( $self->false( $self->{'pairs'} ) ) { for $j ( 1 .. $self->{'num_datasets'} ) { $color = $self->_color_role_to_index( 'dataset' . ( $j - 1 ) ); for $i ( 0 .. $self->{'num_datapoints'} - 1 ) { # don't try to draw anything if there's no data if ( defined( $data->[$j][$i] ) && $data->[$j][$i] <= $self->{'max_val'} && $data->[$j][$i] >= $self->{'min_val'} ) { #calculate the point $winkel = ( 180 - ( $data->[0][$i] % 360 ) ) / 360 * 2 * $pi; if ( $self->false( $self->{'polar'} ) ) { $x = ceil( $centerX + sin($winkel) * ( $data->[$j][$i] - $mod ) * $map ); $y = ceil( $centerY + cos($winkel) * ( $data->[$j][$i] - $mod ) * $map ); } else { $x = ceil( $centerX + sin($winkel) * ( $mod - $data->[$j][$i] ) * $map ); $y = ceil( $centerY + cos($winkel) * ( $mod - $data->[$j][$i] ) * $map ); } # set the x and y values back if ( $i == 0 ) { $first_x = $x; $first_y = $y; $last_x = $x; $last_y = $y; } if ( $self->true( $self->{'point'} ) ) { $brush = $self->_prepare_brush( $color, 'point' ); $self->{'gd_obj'}->setBrush($brush); #draw the point $self->{'gd_obj'}->line( $x + 1, $y, $x, $y, gdBrushed ); } if ( $self->true( $self->{'line'} ) ) { $brush = $self->_prepare_brush( $color, 'line' ); $self->{'gd_obj'}->setBrush($brush); #draw the line if ( defined $last_x ) { $self->{'gd_obj'}->line( $x, $y, $last_x, $last_y, gdBrushed ); } } if ( $self->true( $self->{'arrow'} ) ) { $brush = $self->_prepare_brush( $color, 'line' ); $self->{'gd_obj'}->setBrush($brush); #draw the arrow if ( $data->[$j][$i] > $self->{'min_val'} ) { $self->{'gd_obj'}->line( $x, $y, $centerX, $centerY, gdBrushed ); $arrow_x = $x - cos( $winkel - $alpha ) * $len; $arrow_y = $y + sin( $winkel - $alpha ) * $len; $self->{'gd_obj'}->line( $x, $y, $arrow_x, $arrow_y, gdBrushed ); $arrow_x = $x + sin( $pi / 2 - $winkel - $alpha ) * $len; $arrow_y = $y - cos( $pi / 2 - $winkel - $alpha ) * $len; $self->{'gd_obj'}->line( $x, $y, $arrow_x, $arrow_y, gdBrushed ); } } $last_x = $x; $last_y = $y; # store the imagemap data if they asked for it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$j][$i] = [ $x, $y ]; } } else { if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$j][$i] = [ undef(), undef() ]; } } } # end for # draw the last line to the first point if ( $self->true( $self->{'line'} ) ) { $self->{'gd_obj'}->line( $x, $y, $first_x, $first_y, gdBrushed ); } } # end for $j } if ( $self->true( $self->{'pairs'} ) ) { for ( $j = 1 ; $j <= $self->{'num_datasets'} ; $j += 2 ) { if ( $j == 1 ) { $color = $self->_color_role_to_index( 'dataset' . ( $j - 1 ) ); } else { $color = $self->_color_role_to_index( 'dataset' . ( $j / 2 - 0.5 ) ); } ##### $color = $self->_color_role_to_index('dataset'.(1)); ##################### for $i ( 0 .. $self->{'num_datapoints'} - 1 ) { # don't try to draw anything if there's no data if ( defined( $data->[$j][$i] ) && $data->[$j][$i] <= $self->{'max_val'} && $data->[$j][$i] >= $self->{'min_val'} ) { # calculate the point $winkel = ( 180 - ( $data->[$n][$i] % 360 ) ) / 360 * 2 * $pi; if ( $self->false( $self->{'polar'} ) ) { $x = ceil( $centerX + sin($winkel) * ( $data->[$j][$i] - $mod ) * $map ); $y = ceil( $centerY + cos($winkel) * ( $data->[$j][$i] - $mod ) * $map ); } else { $x = ceil( $centerX + sin($winkel) * ( $mod - $data->[$j][$i] ) * $map ); $y = ceil( $centerY + cos($winkel) * ( $mod - $data->[$j][$i] ) * $map ); } # set the x and y values back if ( $i == 0 ) { $first_x = $x; $first_y = $y; $last_x = $x; $last_y = $y; } if ( $self->true( $self->{'point'} ) ) { $brush = $self->_prepare_brush( $color, 'point' ); $self->{'gd_obj'}->setBrush($brush); #draw the point $self->{'gd_obj'}->line( $x + 1, $y, $x, $y, gdBrushed ); } if ( $self->true( $self->{'line'} ) ) { $brush = $self->_prepare_brush( $color, 'line' ); $self->{'gd_obj'}->setBrush($brush); #draw the line if ( defined $last_x ) { $self->{'gd_obj'}->line( $x, $y, $last_x, $last_y, gdBrushed ); } else { } } if ( $self->true( $self->{'arrow'} ) ) { $brush = $self->_prepare_brush( $color, 'line' ); $self->{'gd_obj'}->setBrush($brush); #draw the arrow if ( $data->[$j][$i] > $self->{'min_val'} ) { $self->{'gd_obj'}->line( $x, $y, $centerX, $centerY, gdBrushed ); $arrow_x = $x - cos( $winkel - $alpha ) * $len; $arrow_y = $y + sin( $winkel - $alpha ) * $len; $self->{'gd_obj'}->line( $x, $y, $arrow_x, $arrow_y, gdBrushed ); $arrow_x = $x + sin( $pi / 2 - $winkel - $alpha ) * $len; $arrow_y = $y - cos( $pi / 2 - $winkel - $alpha ) * $len; $self->{'gd_obj'}->line( $x, $y, $arrow_x, $arrow_y, gdBrushed ); } } $last_x = $x; $last_y = $y; # store the imagemap data if they asked for it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$j][$i] = [ $x, $y ]; } } # end if ( defined ... else { if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$j][$i] = [ undef(), undef() ]; } } } #end for $i # draw the last line to the first point if ( $self->true( $self->{'line'} ) ) { $self->{'gd_obj'}->line( $x, $y, $first_x, $first_y, gdBrushed ); } $n += 2; } # end for $j } # end if pairs # now outline it $self->{'gd_obj'} ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); return; } ## @fn private int _prepare_brush($color,$type) # set the gdBrush object to trick GD into drawing fat lines # # # @param[in] color Color to be used # @param[in] type Type of line # @return status sub _prepare_brush { my $self = shift; my $color = shift; my $type = shift; my ( $radius, @rgb, $brush, $white, $newcolor ); @rgb = $self->{'gd_obj'}->rgb($color); # get the appropriate brush size if ( $type eq 'line' ) { $radius = $self->{'brush_size'} / 2; } elsif ( $type eq 'point' ) { $radius = $self->{'pt_size'} / 2; } # create the new image $brush = GD::Image->new( $radius * 2, $radius * 2 ); # get the colors, make the background transparent $white = $brush->colorAllocate( 255, 255, 255 ); $newcolor = $brush->colorAllocate(@rgb); $brush->transparent($white); # draw the circle $brush->arc( $radius - 1, $radius - 1, $radius, $radius, 0, 360, $newcolor ); # fill it if we're using lines $brush->fill( $radius - 1, $radius - 1, $newcolor ); # set the new image as the main object's brush return $brush; } ## @fn private int _draw_legend() # let them know what all the pretty colors mean # @return status # # Overwrite corresponding function of Base # sub _draw_legend { my $self = shift; my $length; # check to see if legend type is none.. if ( $self->{'legend'} =~ /^none$/ ) { return 1; } # check to see if they have as many labels as datasets, # warn them if not if ( ( $#{ $self->{'legend_labels'} } >= 0 ) && ( ( scalar( @{ $self->{'legend_labels'} } ) ) != $self->{'num_datasets'} ) ) { carp "The number of legend labels and datasets doesn\'t match"; } # init a field to store the length of the longest legend label unless ( $self->{'max_legend_label'} ) { $self->{'max_legend_label'} = 0; } # fill in the legend labels, find the longest one if ( $self->false( $self->{'pairs'} ) ) { for ( 1 .. $self->{'num_datasets'} ) { unless ( $self->{'legend_labels'}[ $_ - 1 ] ) { $self->{'legend_labels'}[ $_ - 1 ] = "Dataset $_"; } $length = length( $self->{'legend_labels'}[ $_ - 1 ] ); if ( $length > $self->{'max_legend_label'} ) { $self->{'max_legend_label'} = $length; } } #end for } if ( $self->true( $self->{'pairs'} ) ) { for ( 1 .. ceil( $self->{'num_datasets'} / 2 ) ) { unless ( $self->{'legend_labels'}[ $_ - 1 ] ) { $self->{'legend_labels'}[ $_ - 1 ] = "Dataset $_"; } $length = length( $self->{'legend_labels'}[ $_ - 1 ] ); if ( $length > $self->{'max_legend_label'} ) { $self->{'max_legend_label'} = $length; } } } # different legend types if ( $self->{'legend'} eq 'bottom' ) { $self->_draw_bottom_legend; } elsif ( $self->{'legend'} eq 'right' ) { $self->_draw_right_legend; } elsif ( $self->{'legend'} eq 'left' ) { $self->_draw_left_legend; } elsif ( $self->{'legend'} eq 'top' ) { $self->_draw_top_legend; } else { carp "I can't put a legend there (at " . $self->{'legend'} . ")\n"; } # and return return 1; } ## @fn private array _find_y_range() # Find minimum and maximum value of y data sets. # # @return ( min, max, flag_all_integers ) # # Overwrites corresponding Base function # sub _find_y_range { my $self = shift; my $data = $self->{'dataref'}; my $max = undef; my $min = undef; my $k = 1; my $dataset = 1; my $datum; if ( $self->false( $self->{'pairs'} ) ) { for $dataset ( @$data[ 1 .. $#$data ] ) { # print "dataset @$dataset\n"; for $datum (@$dataset) { if ( defined $datum ) { # Prettier, but probably slower: # $max = $datum unless defined $max && $max >= $datum; # $min = $datum unless defined $min && $min <= $datum; if ( defined $max ) { if ( $datum > $max ) { $max = $datum; } elsif ( $datum < $min ) { $min = $datum; } } else { $min = $max = $datum; } } #endif defined } # end for } } if ( $self->true( $self->{'pairs'} ) ) { # only every second dataset must be checked for $dataset ( @$data[$k] ) { for $datum (@$dataset) { if ( defined $datum ) { # Prettier, but probably slower: # $max = $datum unless defined $max && $max >= $datum; # $min = $datum unless defined $min && $min <= $datum; if ( defined $max ) { if ( $datum > $max ) { $max = $datum; } elsif ( $datum < $min ) { $min = $datum; } } else { $min = $max = $datum; } } } $k += 2; } } ( $min, $max ); } ## be a good module and return 1 1; Chart-v2.403.9/lib/Chart/Base.pm0000644000175000017500000036055614341172251016026 0ustar herbertherbert # Chart::Base : all other drawing classes ared derived from this # provides all common functions use v5.12; package Chart::Base; our $VERSION = 'v2.403.9'; use FileHandle; use Carp; use GD; use GD::Image; use Chart::Property::DataType::Color; #use Chart::Property::DataType::Font; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods # #<<<<<<<<<<<<<<<<<<<<<<<<<<# sub new { # Standard constructor my $proto = shift; my $class = ref($proto) || $proto; my $self = (bless {}, $class) ; $self->_init(@_); return $self; } # set (%options) --> 1 | croak # main method for customizing the chart, by specify parameters # options are saved locally to be able to output them via @see getopts() sub set { my $self = shift; my %opts = @_; # basic error checking on the options, just warn 'em unless ( $#_ % 2 ) { carp "Whoops, some option to be set didn't have a value.\n", "You might want to look at that.\n"; } # set the options for ( keys %opts ) { $self->{$_} = $opts{$_}; $self->{saveopts}->{$_} = $opts{$_}; # if someone wants to change the grid_lines color, we should set all # the colors of the grid_lines if ( $_ =~ /^colors$/ ) { my %hash = %{ $opts{$_} }; foreach my $key ( sort keys %hash ) { if ( $key =~ /^grid_lines$/ ) { if ( ref( $hash{'grid_lines'} ) eq 'ARRAY' ) { my @aLocal = ( $hash{'grid_lines'}[0], $hash{'grid_lines'}[1], $hash{'grid_lines'}[2] ); $self->{'colors'}{'y_grid_lines'} = [@aLocal]; $self->{'colors'}{'x_grid_lines'} = [@aLocal]; $self->{'colors'}{'y2_grid_lines'} = [@aLocal]; } elsif ( ref( \$hash{'grid_lines'} ) eq 'SCALAR' ) { my $sLocal = $hash{'grid_lines'}; $self->{'colors'}{'y_grid_lines'} = $sLocal; $self->{'colors'}{'x_grid_lines'} = $sLocal; $self->{'colors'}{'y2_grid_lines'} = $sLocal; } else { carp "colors{'grid_lines'} is not SCALAR and not ARRAY\n" } } } } } return 1; } ## @method int add_pt(@data) # Graph API\n # Add one dataset (as a list) to the dataref # # @param data Dataset to add ## @method add_pt(\\\@data) # Graph API\n # Add one dataset (as a reference to a list) to the dataref # via #
# for ( 0 .. $#data )
# {
#    push @{ $self->{'dataref'}->[$_] }, $data[$_];
# }
# 
# # @param data Dataset to add # sub add_pt { my $self = shift; my @data = (); if ( ref $_[0] eq 'ARRAY' ) { my $rdata = shift; @data = @$rdata if @$rdata; } elsif ( ( ref \$_[0] ) =~ /^SCALAR/ ) { @data = @_ if defined $_[0]; } else { croak "Not an array or reference to array" } # error check the data (carp, don't croak) if ( $self->{'dataref'} && ( $#{ $self->{'dataref'} } != $#data ) ) { carp "New point to be added has an incorrect number of data sets"; return 0; } # copy it into the dataref push @{ $self->{'dataref'}->[$_] }, $data[$_] for 0 .. $#data; # now return return 1; } ## @method int add_dataset(@data) # Graph API\n # Add many datasets (implemented as a list) # to the dataref, # # @param data Dataset (list) to add ## @method int add_dataset(\\\@data) # Graph API\n # Add many datasets (implemented as a references to alist) # to the dataref, # # @param data Dataset (reference to a list) to add sub add_dataset { my $self = shift; my @data = (); if ( ref $_[0] eq 'ARRAY' ) { my $rdata = shift; @data = @$rdata if @$rdata; } elsif ( ( ref \$_[0] ) =~ /^SCALAR/ ) { @data = @_ if defined $_[0]; } else { croak "Not an array or reference to array"; return; } # error check the data (carp, don't croak) if ( $self->{'dataref'} && ( $#{ $self->{'dataref'}->[0] } != $#data ) ) { carp "New data set to be added has an incorrect number of points"; } # copy it into the dataref push @{ $self->{'dataref'} }, [@data]; # now return return 1; } ## @method int add_datafile($filename,$format) # Graph API\n # it's also possible to add a complete datafile\n # Uses # @see add_pt # @see add_dataset # # @param[in] filename Name of file which contents is to be added # @param[in] format 'pt' or 'set' to distiguish between function add_pt() in case of 'pt' # or function add_dataset() in case of 'set' sub add_datafile { my $self = shift; my $filename = shift; my $format = shift // 'set'; my ( $File, @array ); carp "Need format for the data file: 'set' (default) or 'pt' \n" unless $format eq 'set' or $format eq 'pt'; # do some ugly checking to see if they gave me # a filehandle or a file name if ( ( ref \$filename ) eq 'SCALAR' ) { open( $File, $filename ) or croak "Can't open the datafile: $filename.\n"; } elsif ( ( ref \$filename ) =~ /^(?:REF|GLOB)$/ ) { # either a FileHandle object or a regular file handle $File = $filename; } else { carp "I'm not sure what kind of datafile you gave me,\n", "but it wasn't a filename or a filehandle.\n"; } while (<$File>) { @array = split /\s*,\s*|\s+/; next unless @array and substr($array[0], 0, 1) ne '#'; if ($format eq 'set'){ $self->add_dataset( @array ) } else { $self->add_pt( @array ) } } close($File); } ## @method int clear_data() # Clear Graph API (by undefining 'dataref' # @return Status of function sub clear_data { my $self = shift; $self->{'dataref'} = undef; return 1; } ## @method arrayref get_data() # Get array of data of the last graph # @return Reference to data set of the last graph sub get_data { my $self = shift; my $ref = []; my ( $i, $j ); # give them a copy, not a reference into the object for $i ( 0 .. $#{ $self->{'dataref'} } ) { @{ $ref->[$i] } = @{ $self->{'dataref'}->[$i] } ## speedup, compared to... # for $j (0..$#{$self->{'dataref'}->[$i]}) { # $ref->[$i][$j] = $self->{'dataref'}->[$i][$j]; # } } # return it return $ref; } ## @method int png($file, $dataref) # Produce the graph of options set in png format. # # called after the options are set, this method # invokes all my private methods to actually # draw the chart and plot the data # @see _set_colors # @see _copy_data # @see _check_data # @see _draw # @param[in] file Name of file to write graph to # @param[in] dataref Reference to external data space # @return Status of the plot sub png { my $self = shift; my $file = shift; my $dataref = shift; my $fh; # do some ugly checking to see if they gave me # a filehandle or a file name if ( ( ref \$file ) eq 'SCALAR' ) { # they gave me a file name # Try to delete an existing file if ( -f $file ) { my $number_deleted_files = unlink $file; if ( $number_deleted_files != 1 ) { croak "Error: File \"$file\" did already exist, but it failed to delete it"; } } $fh = FileHandle->new(">$file"); if ( !defined $fh ) { croak "Error: File \"$file\" could not be created!\n"; } } elsif ( ( ref \$file ) =~ /^(?:REF|GLOB)$/ ) { # either a FileHandle object or a regular file handle $fh = $file; } else { croak "I'm not sure what you gave me to write this png to,\n", "but it wasn't a filename or a filehandle.\n"; } # allocate the background color $self->_set_colors(); # make sure the object has its copy of the data $self->_copy_data($dataref); # do a sanity check on the data, and collect some basic facts # about the data $self->_check_data(); # pass off the real work to the appropriate subs $self->_draw(); # now write it to the file handle, and don't forget # to be nice to the poor ppl using nt binmode $fh; print $fh $self->{'gd_obj'}->png(); # now exit return 1; } ## @method int cgi_png($dataref) # Produce the graph of options set in png format to be directly # written for CGI. # # called after the options are set, this method # invokes all my private methods to actually # draw the chart and plot the data # @param[in] dataref Reference to external data space # @return Status of the plot sub cgi_png { my $self = shift; my $dataref = shift; # allocate the background color $self->_set_colors(); # make sure the object has its copy of the data $self->_copy_data($dataref); # do a sanity check on the data, and collect some basic facts # about the data $self->_check_data(); # pass off the real work to the appropriate subs $self->_draw(); # print the header (ripped the crlf octal from the CGI module) if ( $self->true( $self->{no_cache} ) ) { print "Content-type: image/png\015\012Pragma: no-cache\015\012\015\012"; } else { print "Content-type: image/png\015\012\015\012"; } # now print the png, and binmode it first so Windows-XX likes us binmode STDOUT; print STDOUT $self->{'gd_obj'}->png(); # now exit return 1; } ## @method int scalar_png($dataref) # Produce the graph of options set in PNG format to be directly returned # # called after the options are set, this method # invokes all my private methods to actually # draw the chart and return the image to the caller # # @param dataref Reference to data # @return returns the png image as a scalar value, so that # the programmer-user can do whatever the heck # s/he wants to with it sub scalar_png { my $self = shift; my $dataref = shift; #allocate the background color $self->_set_colors(); # make sure the object has its copy of the data $self->_copy_data($dataref); # do a sanity check on the data, and collect some basic facts # about the data $self->_check_data(); # pass off the real work to the appropriate subs $self->_draw(); # returns the png image as a scalar value, so that # the programmer/user can do whatever the she/he wants to with it return $self->{'gd_obj'}->png(); } ## @method int jpeg($file,$dataref) # Produce the graph of options set in JPG format to be directly plotted.\n # # Called after the options are set, this method # invokes all my private methods to actually # draw the chart and plot the data. # The output has the jpeg format in opposite to png format produced by # @see png # # Uses the following private functions:\n # @see _set_colors # @see _copy_data # @see _check_data # @see _draw # # @param[in] file Name of file to write graph to # @param[in] dataref Reference to external data space # @return Status of the plot # sub jpeg { my $self = shift; my $file = shift; my $dataref = shift; my $fh; # do some ugly checking to see if they gave me # a filehandle or a file name if ( ( ref \$file ) eq 'SCALAR' ) { # they gave me a file name # Try to delete an existing file if ( -f $file ) { my $number_deleted_files = unlink $file; if ( $number_deleted_files != 1 ) { croak "Error: File \"$file\" did already exist, but it fails to delete it"; } } $fh = FileHandle->new(">$file"); if ( !defined $fh ) { croak "Error: File \"$file\" could not be created!\n"; } } elsif ( ( ref \$file ) =~ /^(?:REF|GLOB)$/ ) { # either a FileHandle object or a regular file handle $fh = $file; } else { croak "I'm not sure what you gave me to write this jpeg to,\n", "but it wasn't a filename or a filehandle.\n"; } # allocate the background color $self->_set_colors(); # make sure the object has its copy of the data $self->_copy_data($dataref); # do a sanity check on the data, and collect some basic facts # about the data $self->_check_data; # pass off the real work to the appropriate subs $self->_draw(); # now write it to the file handle, and don't forget # to be nice to the poor ppl using Windows-XX binmode $fh; print $fh $self->{'gd_obj'}->jpeg( [100] ); # high quality need # now exit return 1; } ## @method int cgi_jpeg($dataref) # Produce the graph of options set in JPG format to be directly # for CGI. # # called after the options are set, this method # invokes all my private methods to actually # draw the chart and plot the data # @param[in] dataref Reference to external data space # @return Status of the plot sub cgi_jpeg { my $self = shift; my $dataref = shift; # allocate the background color $self->_set_colors(); # make sure the object has its copy of the data $self->_copy_data($dataref); # do a sanity check on the data, and collect some basic facts # about the data $self->_check_data(); # pass off the real work to the appropriate subs $self->_draw(); # print the header (ripped the crlf octal from the CGI module) if ( $self->true( $self->{no_cache} ) ) { print "Content-type: image/jpeg\015\012Pragma: no-cache\015\012\015\012"; } else { print "Content-type: image/jpeg\015\012\015\012"; } # now print the jpeg, and binmode it first so Windows-XX likes us binmode STDOUT; print STDOUT $self->{'gd_obj'}->jpeg( [100] ); # now exit return 1; } ## @method int scalar_jpeg($dataref) # Produce the graph of options set in JPG format to be directly returned # # called after the options are set, this method # invokes all my private methods to actually # draw the chart and return the image to the caller # # @param dataref Reference to data area # @return returns the jpeg image as a scalar value, so that # the programmer-user can do whatever the heck # s/he wants to with it sub scalar_jpeg { my $self = shift; my $dataref = shift; # allocate the background color $self->_set_colors(); # make sure the object has its copy of the data $self->_copy_data($dataref); # do a sanity check on the data, and collect some basic facts # about the data $self->_check_data(); # pass off the real work to the appropriate subs $self->_draw(); # returns the jpeg image as a scalar value, so that # the programmer-user can do whatever the heck # s/he wants to with it $self->{'gd_obj'}->jpeg( [100] ); } ## @method int make_gd($dataref) # Produce the graph of options set in GD format to be directly # # called after the options are set, this method # invokes all my private methods to actually # draw the chart and plot the data # @param dataref Reference to data # @return Status of the plot sub make_gd { my $self = shift; my $dataref = shift; # allocate the background color $self->_set_colors(); # make sure the object has its copy of the data $self->_copy_data($dataref); # do a sanity check on the data, and collect some basic facts # about the data $self->_check_data(); # pass off the real work to the appropriate subs $self->_draw(); # return the GD::Image object that we've drawn into return $self->{'gd_obj'}; } ## @method imagemap_dump() # get the information to turn the chart into an imagemap # # @return Reference to an array of the image sub imagemap_dump { my $self = shift; my $ref = []; my ( $i, $j ); # croak if they didn't ask me to remember the data, or if they're asking # for the data before I generate it unless ( ( $self->true( $self->{'imagemap'} ) ) && $self->{'imagemap_data'} ) { croak "You need to set the imagemap option to true, and then call the png method, before you can get the imagemap data"; } # can't just return a ref to my internal structures... for $i ( 0 .. $#{ $self->{'imagemap_data'} } ) { for $j ( 0 .. $#{ $self->{'imagemap_data'}->[$i] } ) { $ref->[$i][$j] = [ @{ $self->{'imagemap_data'}->[$i][$j] } ]; } } # return their copy return $ref; } ## @method minimum (@array) # determine minimum of an array of values # @param array List of numerical values (\@array) # @return Minimal value of list of values sub minimum { my $self = shift; my @array = @_; return undef if !@array; my $min = $array[0]; for ( my $iIndex = 0 ; $iIndex < scalar @array ; $iIndex++ ) { $min = $array[$iIndex] if ( $min > $array[$iIndex] ); } $min; } ## @method maximum(@array) # determine maximum of an array of values # @param array List of numerical values (@array) # @return Maximal value of list of values sub maximum { my $self = shift; my @array = @_; return undef if !@array; my $max = $array[0]; for ( my $iIndex = 0 ; $iIndex < scalar @array ; $iIndex++ ) { $max = $array[$iIndex] if ( $max < $array[$iIndex] ); } $max; } ## @method arccos($a) # Function arccos(a) # @param a Value # @return arccos(a) sub arccos { my $self = shift; my $a = shift; return ( atan2( sqrt( 1 - $a * $a ), $a ) ); } ## @method arcsin($a) # Function arcsin(a) # @param a Value # @return arcsin(a) sub arcsin { my $self = shift; my $a = shift; return ( atan2( $a, sqrt( 1 - $a * $a ) ) ); } ## @method true($arg) # determine true value of argument # @param[in] arg Bool value to check for true # @return 1 if argument is equal to TRUE, true, 1, t, T, and defined sub true { my $pkg = shift; my $arg = shift; if ( !defined($arg) ) { return 0; } if ( $arg eq 'true' || $arg eq 'TRUE' || $arg eq 't' || $arg eq 'T' || $arg eq '1' ) { return 1; } return 0; } ## @method false($arg) # determine false value of argument # @param[in] arg Bool value to check for true # @return 1 if argument is equal to false, FALSE, 0, f, F or undefined sub false { my $pkg = shift; my $arg = shift; if ( !defined($arg) ) { return 1; } if ( $arg eq 'false' || $arg eq 'FALSE' || $arg eq 'f' || $arg eq 'F' || $arg eq '0' || $arg eq 'none' ) { return 1; } return 0; } ## @method modulo($a,$b) # Calculate float($a % $b) as the internal operator '%' # does only calculate in integers # @param[in] a a in a%b # @param[in] b b in a%b # @return $a % $b in float sub modulo { my $pkg = shift; my $a = shift; my $b = shift; my $erg = 0.0; if ( !defined($a) || !defined($b) || $b == 0 ) { die "Modulo needs valid parameters!" #return $erg; } my $div = $a / $b; $erg = $a - int($div) * $b; return $erg; } #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @fn private int _init($x,$y) # Initialize all default options here # @param[in] x Width of the final image in pixels (Default: 400) # @param[in] y Height of the final image in pixels (Default: 300) # sub _init { my $self = shift; my $x = shift || 400; # give them a 400x300 image my $y = shift || 300; # unless they say otherwise # get the gd object # Reference to new GD::Image $self->{'gd_obj'} = GD::Image->new( $x, $y ); # start keeping track of used space # actual current y min Value $self->{'curr_y_min'} = 0; $self->{'curr_y_max'} = $y; # maximum pixel in y direction (down) $self->{'curr_x_min'} = 0; $self->{'curr_x_max'} = $x; # maximum pixel in x direction (right) # use a 10 pixel border around the whole png $self->{'png_border'} = 10; # leave some space around the text fields $self->{'text_space'} = 2; # and leave some more space around the chart itself $self->{'graph_border'} = 10; # leave a bit of space inside the legend box $self->{'legend_space'} = 4; # set some default fonts $self->{'title_font'} = gdLargeFont, $self->{'sub_title_font'} = gdLargeFont, $self->{'legend_font'} = gdSmallFont, $self->{'label_font'} = gdMediumBoldFont, $self->{'tick_label_font'} = gdSmallFont; # put the legend on the bottom of the chart $self->{'legend'} = 'right'; # default to an empty list of labels $self->{'legend_labels'} = []; # use 20 pixel length example lines in the legend $self->{'legend_example_size'} = 20; # Set the maximum & minimum number of ticks to use. $self->{'y_ticks'} = 6, $self->{'min_y_ticks'} = 6, $self->{'max_y_ticks'} = 100, $self->{'x_number_ticks'} = 1, $self->{'min_x_ticks'} = 6, $self->{'max_x_ticks'} = 100; # make the ticks 4 pixels long $self->{'tick_len'} = 4; # no custom y tick labels $self->{'y_tick_labels'} = undef; # no patterns $self->{'patterns'} = undef; # let the lines in Chart::Lines be 6 pixels wide $self->{'brush_size'} = 6; # let the points in Chart::Points and Chart::LinesPoints be 18 pixels wide $self->{'pt_size'} = 18; # use the old non-spaced bars $self->{'spaced_bars'} = 'true'; # use the new grey background for the plots $self->{'grey_background'} = 'true'; # don't default to transparent $self->{'transparent'} = 'false'; # default to "normal" x_tick drawing $self->{'x_ticks'} = 'normal'; # we're not a component until Chart::Composite says we are $self->{'component'} = 'false'; # don't force the y-axes in a Composite chare to be the same $self->{'same_y_axes'} = 'false'; # plot rectangeles in the legend instead of lines in a composite chart $self->{'legend_example_height'} = 'false'; # don't force integer y-ticks $self->{'integer_ticks_only'} = 'false'; # don't forbid a false zero scale. $self->{'include_zero'} = 'false'; # don't waste time/memory by storing imagemap info unless they ask $self->{'imagemap'} = 'false'; # default for grid_lines is off $self->{grid_lines} = 'false', $self->{x_grid_lines} = 'false', $self->{y_grid_lines} = 'false', $self->{y2_grid_lines} = 'false'; # default for no_cache is false. (it breaks netscape 4.5) $self->{no_cache} = 'false'; # default value for skip_y_ticks for the labels $self->{skip_y_ticks} = 1; # default value for skip_int_ticks only for integer_ticks_only $self->{skip_int_ticks} = 1; # default value for precision $self->{precision} = 3; # default value for legend label values in pie charts $self->{legend_label_values} = 'value'; # default value for the labels in a pie chart $self->{label_values} = 'percent'; # default position for the y-axes $self->{y_axes} = 'left'; # copies of the current values at the x-ticks function $self->{temp_x_min} = 0; $self->{temp_x_max} = 0; $self->{temp_y_min} = 0; $self->{temp_y_max} = 0; # Instance for a sum $self->{sum} = 0; # Don't sort the data unless they ask $self->{'sort'} = 'false'; # The Interval for drawing the x-axes in the split module $self->{'interval'} = undef; # The start value for the split chart $self->{'start'} = undef; # How many ticks do i have to draw at the x-axes in one interval of a split-plot? $self->{'interval_ticks'} = 6; # Draw the Lines in the split-chart normal $self->{'scale'} = 1; # Make a x-y plot $self->{'xy_plot'} = 'false'; # min and max for xy plot $self->{'x_min_val'} = 1; $self->{'x_max_val'} = 1; # use the same error value in ErrorBars $self->{'same_error'} = 'false'; # Set the minimum and maximum number of circles to draw in a direction chart $self->{'min_circles'} = 4, $self->{'max_circles'} = 100; # set the style of a direction diagramm $self->{'point'} = 'true', $self->{'line'} = 'false', $self->{'arrow'} = 'false'; # The number of angel axes in a direction Chart $self->{'angle_interval'} = 30; # dont use different 'x_axes' in a direction Chart $self->{'pairs'} = 'false'; # polarplot for a direction Chart (not yet tested) $self->{'polar'} = 'false'; # guiding lines in a Pie Chart $self->{'legend_lines'} = 'false'; # Ring Chart instead of Pie $self->{'ring'} = 1; # width of ring; i.e. normal pie # stepline for Lines, LinesPoints $self->{'stepline'} = 'false'; $self->{'stepline_mode'} = 'end'; # begin, end # used function to transform x- and y-tick labels to strings $self->{f_x_tick} = \&_default_f_tick, $self->{f_y_tick} = \&_default_f_tick, $self->{f_z_tick} = \&_default_f_tick; # default color specs for various color roles. # Subclasses should extend as needed. my $d = 0; $self->{'colors_default_spec'} = { background => 'white', misc => 'black', text => 'black', y_label => 'black', y_label2 => 'black', grid_lines => 'black', grey_background => 'gray90', ( map { 'dataset' . $d++ => $_ } qw (flamescarlet forestgreen navy olive lightseagreen purple orangepeel gold2 chartreuse3 cornflowerblue mediumpurple2 deeppink2 galaxyblue hazelnut pottersclay BlueViolet PaleGreen1 DarkBlue orange2 chocolate1 LightGreen pink light_purple light_blue plum yellow turquoise light_green brown PaleGreen2 MediumPurple PeachPuff1 orange3 chocolate2 olive light_purple light_blue yellow turquoise light_green brown DarkOrange PaleGreen3 SlateBlue BlueViolet PeachPuff2 orange4 chocolate3 LightGreen light_purple light_blue light_green snow1 honeydew3 SkyBlue1 cyan3 DarkOliveGreen1 IndianRed3 orange1 LightPink3 MediumPurple1 snow3 LavenderBlush1 SkyBlue3 DarkSlateGray1 DarkOliveGreen3 sienna1 orange3 PaleVioletRed1 MediumPurple3 seashell1 LavenderBlush3 LightSkyBlue1) ), }; # get default color specs for some color roles from alternate role. # Subclasses should extend as needed. $self->{'colors_default_role'} = { 'x_grid_lines' => 'grid_lines', 'y_grid_lines' => 'grid_lines', 'y2_grid_lines' => 'grid_lines', # should be added by Chart::Composite... }; # Define style to plot dots in Points and Lines $self->{'brushStyle'} = 'FilledCircle'; # and return return 1; } ## @fn private int _copy_data($extern_ref) # Copy external data via a reference to internal memory. # # Remember the external reference.\n # Therefore, this function can anly be called once! # @param extern_ref Reference to external data space sub _copy_data { my $self = shift; my $extern_ref = shift; # look to see if they used the other api if ( $self->{'dataref'} ) { return 1; # we've already got a copy, thanks } else { return unless ref $extern_ref eq 'ARRAY'; $self->{'dataref'} = [ map { \@$_ } @$extern_ref ]; # clone AoA return 1; } } ## @fn private int _check_data # Check the internal data to be displayed. # # Make sure the data isn't really weird # and collect some basic info about it\n # Not logical data is 'carp'ed.\n # @return status of check sub _check_data { my $self = shift; my $length = 0; # first make sure there's something there croak "Call me again when you have some data to chart" unless scalar @{ $self->{'dataref'} } >= 2; # make sure we don't end up dividing by zero if they ask for # just one y_tick if ($self->{'y_ticks'} <= 1) { $self->{'y_ticks'} = 2; carp "The number of y_ticks displayed must be at least 2"; } # remember the number of datasets $self->{'num_datasets'} = $#{ $self->{'dataref'} }; # remember the number of points in the largest dataset $self->{'num_datapoints'} = 0; for ( 0 .. $self->{'num_datasets'} ) { if ( scalar( @{ $self->{'dataref'}[$_] } ) > $self->{'num_datapoints'} ) { $self->{'num_datapoints'} = scalar( @{ $self->{'dataref'}[$_] } ); } } # find good min and max y-values for the plot $self->_find_y_scale(); # find the longest x-tick label $length = 0; for ( @{ $self->{'dataref'}->[0] } ) { next if !defined($_); if ( length( $self->{f_x_tick}->($_) ) > $length ) { $length = length( $self->{f_x_tick}->($_) ); } } if ( $length <= 0 ) { $length = 1; } # make sure $length is positive and greater 0 # now store it in the object $self->{'x_tick_label_length'} = $length; # find x-scale, if a x-y plot is wanted # makes only sense for some charts if ( $self->true( $self->{'xy_plot'} ) && ( $self->isa('Chart::Lines') || $self->isa('Chart::Points') || $self->isa('Chart::LinesPoints') || $self->isa('Chart::Split') || $self->isa('Chart::ErrorBars') ) ) { $self->_find_x_scale; } return 1; } ## @fn private int _draw # Plot the chart to the gd object\n # Calls: # @see _draw_title # @see _draw_sub_title # @see _sort_data # @see _plot # # @return status sub _draw { my $self = shift; # leave the appropriate border on the png $self->{'curr_x_max'} -= $self->{'png_border'}; $self->{'curr_x_min'} += $self->{'png_border'}; $self->{'curr_y_max'} -= $self->{'png_border'}; $self->{'curr_y_min'} += $self->{'png_border'}; # draw in the title $self->_draw_title() if $self->{'title'}; # have to leave this here for backwards compatibility $self->_draw_sub_title() if $self->{'sub_title'}; # sort the data if they want to (mainly here to make sure # pareto charts get sorted) $self->_sort_data() if ( $self->true( $self->{'sort'} ) ); # start drawing the data (most methods in this will be # overridden by the derived classes) # include _draw_legend() in this to ensure that the legend # will be flush with the chart $self->_plot(); # and return return 1; } ## @fn private int _set_colors # specify my colors # @return status sub _set_colors { my $self = shift; my $index = $self->_color_role_to_index('background'); # allocate GD color if ( $self->true( $self->{'transparent'} ) ) { $self->{'gd_obj'}->transparent($index); } # all other roles are initialized by calling $self->_color_role_to_index(ROLENAME); # and return return 1; } ## @fn private int _color_role_to_index # return a (list of) color index(es) corresponding to the (list of) role(s) # # @details wantarray # is a special keyword which returns a flag indicating # which context your subroutine has been called in. # It will return one of three values. # # @li true: If your subroutine has been called in list context # @li false: If your subroutine has been called in scalar context # @li undef: If your subroutine has been called in void context # # @return a (list of) color index(es) corresponding to the (list of) role(s) in \\\@_. sub _color_role_to_index { my $self = shift; # Return a (list of) color index(es) corresponding to the (list of) role(s) in @_. my @result = map { my $role = $_; my $index = $self->{'color_table'}->{$role}; unless ( defined $index ) { my $spec = $self->{'colors'}->{$role} || $self->{'colors_default_spec'}->{$role} || $self->{'colors_default_spec'}->{ $self->{'colors_default_role'}->{$role} }; my @rgb = $self->_color_spec_to_rgb( $role, $spec ); my $string = sprintf " RGB(%d,%d,%d)", map { $_ + 0 } @rgb; $index = $self->{'color_table'}->{$string}; unless ( defined $index ) { $index = $self->{'gd_obj'}->colorAllocate(@rgb); $self->{'color_table'}->{$string} = $index; } $self->{'color_table'}->{$role} = $index; } $index; } @_; ( wantarray && @_ > 1 ? @result : $result[0] ); } sub _color_spec_to_rgb { my ($self, $role, $spec) = @_; # color role name (from set) for error msg my $color = Chart::Property::DataType::Color->new( $spec ); return croak "Unrecognized color for $role\n" unless ref $color; $color->rgb; } ## @fn private int _brushStyles_of_roles # return a (list of) brushStyles corresponding to the (list of) role(s) # # @param list_of_roles List of roles (\\\@list_of_roles) # @return (list of) brushStyle(s) corresponding to the (list of) role(s) in \\\@_. # sub _brushStyles_of_roles { my $self = shift; map {exists $self->{'brushStyles'}{$_} ? $self->{'brushStyles'}{$_} : $self->{'brushStyle'}} @_; } ## @fn private int _draw_title # draw the title for the chart # # The title was defined by the user in set('title' => ....)\n # The user may define some title lines by separating them via character '\\n';\n # The used font is taken from 'title_font';\n # The used color is calculated by function '_color_role_to_index' # based on 'title' or 'text'\n # @see _color_role_to_index # @return status sub draw_text { my ($self, $text, $font, $color) = @_; # !!! 1; } sub _draw_title { my $self = shift; my $font = $self->{'title_font'}; my $color; my ( $h, $w, @lines, $x, $y ); #get the right color $color = ( defined $self->{'colors'}{'title'} ) ? $self->_color_role_to_index('title') : $self->_color_role_to_index('text'); # make sure we're actually using a real font croak "The title font you specified isn\'t a GD Font object" unless ref $font eq 'GD::Font' or ref $font eq 'Chart::Font'; # get the height and width of the font ( $h, $w ) = ( $font->height, $font->width ); # split the title into lines @lines = split( /\\n/, $self->{'title'} ); # write the first line $x = ( $self->{'curr_x_max'} - $self->{'curr_x_min'} ) / 2 + $self->{'curr_x_min'} - ( length( $lines[0] ) * $w ) / 2; $y = $self->{'curr_y_min'} + $self->{'text_space'}; #----------------------------------------------------------- # Tests for Version 2.5 # ttf are found in /var/share/fonts/truetype/freefont/ # /var/share/fonts/truetype # Sketch for further processing # if ( $font ~= /^gd/ && ! -f $font ) # { # $self->{'gd_obj'}->string( $font, $x, $y, $lines[0], $color ); # } # elsif ( -f $font ) # { # my $fontname = '/var/share/fonts/truetype/freefont/FreeSerifBoldItalic.ttf'; # $self->{'gd_obj'}->stringFT( $color, $fontname, 8,0, $x, $y, $lines[0] ); # } # my $fontname = '/var/share/fonts/truetype/freefont/FreeSerifBoldItalic.ttf'; # # size, angle # $self->{'gd_obj'}->stringFT( $color, $fontname, 12,0, $x, $y, $lines[0] ); #----------------------------------------------------------------- $self->{'gd_obj'}->string( $font, $x, $y, $lines[0], $color ); # now loop through the rest of them # (the font is decreased in width and height by 1 if ( $w > 1 ) { $w-- } if ( $h > 1 ) { $h-- } for ( 1 .. $#lines ) { $self->{'curr_y_min'} += $self->{'text_space'} + $h; $x = ( $self->{'curr_x_max'} - $self->{'curr_x_min'} ) / 2 + $self->{'curr_x_min'} - ( length( $lines[$_] ) * $w ) / 2; $y = $self->{'curr_y_min'} + $self->{'text_space'}; $self->{'gd_obj'}->string( $font, $x, $y, $lines[$_], $color ); } # mark off that last space $self->{'curr_y_min'} += 2 * $self->{'text_space'} + $h; # and return return 1; } ## @fn private int _draw_sub_title() # draw the sub-title for the chart # @see _draw_title\n # _draw_sub_title() is more or less obsolete as _draw_title() does the same # by writing more than one line as the title. # Both use decreased width and height of the font by one. # @return status sub _draw_sub_title { my $self = shift; my $font = $self->{'sub_title_font'}; my $text = $self->{'sub_title'}; return 1 if length($text) == 0; # nothing to plot #get the right color my $color; if ( defined $self->{'colors'}{'title'} ) { $color = $self->_color_role_to_index('title'); } else { $color = $self->_color_role_to_index('text'); } my ( $h, $w, $x, $y ); # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The subtitle font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); if ( $h > 1 && $w > 1 ) { $h--, $w-- } # figure out the placement $x = ( $self->{'curr_x_max'} - $self->{'curr_x_min'} ) / 2 + $self->{'curr_x_min'} - ( length($text) * $w ) / 2; $y = $self->{'curr_y_min'}; # now draw the subtitle $self->{'gd_obj'}->string( $font, $x, $y, $text, $color ); # Adapt curr_y_min $self->{'curr_y_min'} += $self->{'text_space'} + $h; # and return return 1; } ## @fn private int _sort_data() # sort the data nicely (mostly for the pareto charts and xy-plots) # @return status sub _sort_data { my $self = shift; my $data_ref = $self->{'dataref'}; my @data = @{ $self->{'dataref'} }; my @sort_index; #sort the data with slices @sort_index = sort { $data[0][$a] <=> $data[0][$b] } ( 0 .. scalar( @{ $data[1] } ) - 1 ); for ( 1 .. $#data ) { @{ $self->{'dataref'}->[$_] } = @{ $self->{'dataref'}->[$_] }[@sort_index]; } @{ $data_ref->[0] } = sort { $a <=> $b } @{ $data_ref->[0] }; #finally return return 1; } ## @fn private int _find_x_scale() # For a xy-plot do the same for the x values, as '_find_y_scale' does for the y values! # @see _find_y_scale # @return status sub _find_x_scale { my $self = shift; my @data = @{ $self->{'dataref'} }; my ( $i, $j ); my ( $d_min, $d_max ); my ( $p_min, $p_max, $f_min, $f_max ); my ( $tickInterval, $tickCount, $skip ); my @tickLabels; my $maxtickLabelLen = 0; #look, if we have numbers #see also if we only have integers for $i ( 0 .. ( $self->{'num_datasets'} ) ) { for $j ( 0 .. ( $self->{'num_datapoints'} - 1 ) ) { # the following regular Expression matches all possible numbers, including scientific numbers # iff data is defined if ( defined $data[$i][$j] and $data[$i][$j] !~ m/^[\+\-]?((\.\d+)|(\d+\.?\d*))([eE][+-]?\d+)?[fFdD]?$/ ) { croak "<$data[$i][$j]> You should give me numbers for drawing a xy plot!\n"; } } } #find the dataset min and max ( $d_min, $d_max ) = $self->_find_x_range(); # Force the inclusion of zero if the user has requested it. if ( $self->true( $self->{'include_zero'} ) ) { if ( ( $d_min * $d_max ) > 0 ) # If both are non zero and of the same sign. { if ( $d_min > 0 ) # If the whole scale is positive. { $d_min = 0; } else # The scale is entirely negative. { $d_max = 0; } } } # Calculate the width of the dataset. (possibly modified by the user) my $d_width = $d_max - $d_min; # If the width of the range is zero, forcebly widen it # (to avoid division by zero errors elsewhere in the code). if ( 0 == $d_width ) { $d_min--, $d_max++, $d_width = 2; } # Descale the range by converting the dataset width into # a floating point exponent & mantisa pair. my ( $rangeExponent, $rangeMantisa ) = $self->_sepFP($d_width); my $rangeMuliplier = 10**$rangeExponent; # Find what tick # to use & how many ticks to plot, # round the plot min & max to suatable round numbers. ( $tickInterval, $tickCount, $p_min, $p_max ) = $self->_calcXTickInterval( $d_min / $rangeMuliplier, $d_max / $rangeMuliplier, $f_min, $f_max, $self->{'min_x_ticks'}, $self->{'max_x_ticks'} ); # Restore the tickInterval etc to the correct scale $_ *= $rangeMuliplier foreach ( $tickInterval, $p_min, $p_max ); #get the precision for the labels my $precision = $self->{'precision'}; # Now sort out an array of tick labels. for ( my $labelNum = $p_min ; $labelNum < $p_max + $tickInterval / 2 ; $labelNum += $tickInterval ) { my $labelText; if ( defined $self->{f_y_tick} ) { # Is _default_f_tick function used? if ( $self->{f_y_tick} == \&_default_f_tick ) { $labelText = sprintf( "%." . $precision . "f", $labelNum ); } else { $labelText = $self->{f_y_tick}->($labelNum); } } else { $labelText = sprintf( "%." . $precision . "f", $labelNum ); } push @tickLabels, $labelText; $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText; } # Store the calculated data. $self->{'x_min_val'} = $p_min, $self->{'x_max_val'} = $p_max, $self->{'x_tick_labels'} = \@tickLabels, $self->{'x_tick_label_length'} = $maxtickLabelLen, $self->{'x_number_ticks'} = $tickCount; return 1; } ## @fn private int _find_y_scale() # find good values for the minimum and maximum y-value on the chart # @return status # # New version, re-written by David Pottage of Tao Group.\n # This code is *AS IS* and comes with *NO WARRANTY*\n # # This Sub calculates correct values for the following class local variables, # if they have not been set by the user. # # max_val, min_val: The maximum and minimum values for the y axis.\n # y_ticks: The number of ticks to plot on the y scale, including # the end points. e.g. If the scale runs from 0 to 50, # with ticks every 10, y_ticks will have the value of 6.\n # y_tick_labels: An array of strings, each is a label for the y axis.\n # y_tick_labels_length: The length to allow for B tick labels. (How long is # the longest?) sub _find_y_scale { my $self = shift; # Predeclare vars. my ( $d_min, $d_max ); # Dataset min & max. my ( $p_min, $p_max ); # Plot min & max. my ( $tickInterval, $tickCount, $skip ); my @tickLabels; # List of labels for each tick. my $maxtickLabelLen = 0; # The length of the longest tick label. my $prec_test = 0; # Boolean which indicate if precision < |rangeExponent| my $temp_rangeExponent; my $flag_all_integers = 1; # assume true # Find the dataset minimum and maximum. ( $d_min, $d_max, $flag_all_integers ) = $self->_find_y_range(); # Force the inclusion of zero if the user has requested it. if ( $self->true( $self->{'include_zero'} ) ) { #print "include_zero = true\n"; if ( ( $d_min * $d_max ) > 0 ) # If both are non zero and of the same sign. { if ( $d_min > 0 ) # If the whole scale is positive. { $d_min = 0; } else # The scale is entirely negative. { $d_max = 0; } } } if ( $self->true( $self->{'integer_ticks_only'} ) ) { # Allow the dataset range to be overidden by the user. # f_min/f_max are booleans which indicate that the min & max should not be modified. my $f_min = 0; if ( defined $self->{'min_val'} ) { $f_min = 1; } $d_min = $self->{'min_val'} if $f_min; my $f_max = 0; if ( defined $self->{'max_val'} ) { $f_max = 1; } $d_max = $self->{'max_val'} if $f_max; # Assert against defined min and max. if ( !defined $d_min || !defined $d_max ) { croak "No min_val or max_val is defined"; } # Assert against the min is larger than the max. if ( $d_min > $d_max ) { croak "The specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)"; } # The user asked for integer ticks, force the limits to integers. # & work out the range directly. #$p_min = $self->_round2Tick($d_min, 1, -1); #$p_max = $self->_round2Tick($d_max, 1, 1); $skip = $self->{skip_int_ticks}; $skip = 1 if $skip < 1; $p_min = $self->_round2Tick( $d_min, 1, -1 ); $p_max = $self->_round2Tick( $d_max, 1, 1 ); if ( ( $p_max - $p_min ) == 0 ) { $p_max++ if ( $f_max != 1 ); # p_max is not defined by the user $p_min-- if ( $f_min != 1 ); # p_min is not defined by the user $p_max++ if ( ( $p_max - $p_min ) == 0 ); } $tickInterval = $skip; $tickCount = ( $p_max - $p_min ) / $skip + 1; # Now sort out an array of tick labels. for ( my $labelNum = $p_min ; $labelNum < $p_max + $tickInterval / 3 ; $labelNum += $tickInterval ) { my $labelText; if ( defined $self->{f_y_tick} ) { # Is _default_f_tick function used? if ( $self->{f_y_tick} == \&_default_f_tick ) { $labelText = sprintf( "%d", $labelNum ); } else { $labelText = $self->{f_y_tick}->($labelNum); } } else { $labelText = sprintf( "%d", $labelNum ); } push @tickLabels, $labelText; $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText; } } else { # Allow the dataset range to be overidden by the user. # f_min/f_max are booleans which indicate that the min & max should not be modified. my $f_min = 0; if ( defined $self->{'min_val'} ) { $f_min = 1; } $d_min = $self->{'min_val'} if $f_min; my $f_max = 0; if ( defined $self->{'max_val'} ) { $f_max = 1; } $d_max = $self->{'max_val'} if $f_max; # print "fmin $f_min fmax $f_max\n"; # print "dmin $d_min dmax $d_max\n"; # Assert against defined min and max. if ( !defined $d_min || !defined $d_max ) { croak "No min_val or max_val is defined"; } # Assert against the min is larger than the max. if ( $d_min > $d_max ) { croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)"; } # Calculate the width of the dataset. (possibly modified by the user) my $d_width = $d_max - $d_min; # If the width of the range is zero, forcibly widen it # (to avoid division by zero errors elsewhere in the code). if ( $d_width == 0 ) { $d_min--, $d_max++, $d_width = 2; } # Descale the range by converting the dataset width into # a floating point exponent & mantisa pair. my ( $rangeExponent, $rangeMantisa ) = $self->_sepFP($d_width); my $rangeMuliplier = 10**$rangeExponent; # print "fmin $f_min fmax $f_max\n"; # print "dmin $d_min dmax $d_max\n"; # Find what tick # to use & how many ticks to plot, # round the plot min & max to suitable round numbers. ( $tickInterval, $tickCount, $p_min, $p_max ) = $self->_calcTickInterval( $d_min / $rangeMuliplier, $d_max / $rangeMuliplier, $f_min, $f_max, $self->{'min_y_ticks'}, $self->{'max_y_ticks'} ); # Restore the tickInterval etc to the correct scale $_ *= $rangeMuliplier foreach ( $tickInterval, $p_min, $p_max ); # Is precision < |rangeExponent|? if ( $rangeExponent < 0 ) { $temp_rangeExponent = -$rangeExponent; } else { $temp_rangeExponent = $rangeExponent; } # print "pmin $p_min pmax $p_max\n"; # print "range exponent $rangeExponent\n"; #get the precision for the labels my $precision = $self->{'precision'}; if ( $temp_rangeExponent != 0 && $rangeExponent < 0 && $temp_rangeExponent > $precision ) { $prec_test = 1; } # Now sort out an array of tick labels. for ( my $labelNum = $p_min ; $labelNum < $p_max + $tickInterval / 2 ; $labelNum += $tickInterval ) { my $labelText; if ( defined $self->{f_y_tick} ) { # Is _default_f_tick function used? if ( ( $self->{f_y_tick} == \&_default_f_tick ) && ( $prec_test == 0 ) ) { $labelText = sprintf( "%." . $precision . "f", $labelNum ); } # If precision <|rangeExponent| print the labels whith exponents elsif ( ( $self->{f_y_tick} == \&_default_f_tick ) && ( $prec_test == 1 ) ) { $labelText = $self->{f_y_tick}->($labelNum); # print "precision $precision\n"; # print "temp range exponent $temp_rangeExponent\n"; # print "range exponent $rangeExponent\n"; # print "labelText $labelText\n"; } else { $labelText = $self->{f_y_tick}->($labelNum); } } else { $labelText = sprintf( "%." . $precision . "f", $labelNum ); } push @tickLabels, $labelText; $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText; } # end for } # Store the calculated data. #### begin debugging output #if ( defined $self->{'y_ticks'} ) #{ # print "_find_y_scale: self->{'y_ticks'}=".$self->{'y_ticks'}."\n"; #} #else #{ # print "_find_y_scale: self->{'y_ticks'}= NOT DEFINED\n"; #} #if ( defined $self->{'min_val'} ) #{ # print "_find_y_scale: self->{'min_val'}=".$self->{'min_val'}."\n"; #} #else #{ # print "_find_y_scale: self->{'min_val'}=NOT DEFINED\n"; #} #if ( defined $self->{'max_val'} ) #{ # print "_find_y_scale: self->{'max_val'}=".$self->{'max_val'}."\n"; #} #else #{ # print "_find_y_scale: self->{'max_val'}= NOT DEFINED\n"; #} #### end debugging output $self->{'min_val'} = $p_min, $self->{'max_val'} = $p_max, $self->{'y_ticks'} = $tickCount, $self->{'y_tick_labels'} = \@tickLabels, $self->{'y_tick_label_length'} = $maxtickLabelLen; ################## #print statement is for debug only #print "_find_y_scale: min_val = $p_min, max_val=$p_max\n"; ################## # and return. return 1; } ## @fn private _calcTickInterval($dataset_min, $dataset_max, $flag_fixed_min, $flag_fixed_max, $minTicks, $maxTicks) # @brief # Calculate the Interval between ticks in y direction # # @details # Calculate the Interval between ticks in y direction # and compare the number of ticks to # the user's given values min_y_ticks, max_y_ticks. # # @param[in] dataset_min Minimal value in y direction # @param[in] dataset_max Maximal value in y direction # @param[in] flag_fixed_min Indicator whether the dataset_min value is fixed # @param[in] flag_fixed_max Indicator whether the dataset_max value is fixed # @param[in] minTicks Minimal number of ticks wanted # @param[in] maxTicks Maximal number of ticks wanted # @return Array of ($tickInterval, $tickCount, $pMin, $pMax) # sub _calcTickInterval { my $self = shift; my ( $dataset_min, $dataset_max, # The dataset min & max. $flag_fixed_min, $flag_fixed_max, # Indicates if those min/max are fixed. $minTicks, $maxTicks, # The minimum & maximum number of ticks. ) = @_; # print "calcTickInterval dataset_min $dataset_min dataset_max $dataset_max flag_fixed_min $flag_fixed_min flag_mixed_max $flag_fixed_max\n"; # Verify the supplied 'min_y_ticks' & 'max_y_ticks' are sensible. if ( $minTicks < 2 ) { #print STDERR "Chart::Base::_calcTickInterval : Incorrect value for 'min_y_ticks', too small (less than 2).\n"; $minTicks = 2; } if ( $maxTicks < 5 * $minTicks ) { #print STDERR "Chart::Base::_calcTickInterval : Incorrect value for 'max_y_ticks', too small (<5*minTicks).\n"; $maxTicks = 5 * $minTicks; } my $width = $dataset_max - $dataset_min; my @divisorList; for ( my $baseMul = 1 ; ; $baseMul *= 10 ) { TRY: foreach my $tryMul ( 1, 2, 5 ) { # Calc a fresh, smaller tick interval. my $divisor = $baseMul * $tryMul; # Count the number of ticks. my ( $tickCount, $pMin, $pMax ) = $self->_countTicks( $dataset_min, $dataset_max, 1 / $divisor ); # Look a the number of ticks. if ( $maxTicks < $tickCount ) { # If it is to high, Backtrack. $divisor = pop @divisorList; # just for security: if ( !defined($divisor) || $divisor == 0 ) { $divisor = 1; } ( $tickCount, $pMin, $pMax ) = $self->_countTicks( $dataset_min, $dataset_max, 1 / $divisor ); #print STDERR "\nChart::Base : Caution: Tick limit of $maxTicks exceeded. Backing of to an interval of ".1/$divisor." which plots $tickCount ticks\n"; return ( 1 / $divisor, $tickCount, $pMin, $pMax ); } elsif ( $minTicks > $tickCount ) { # If it is too low, try again. next TRY; } else { # Store the divisor for possible later backtracking. push @divisorList, $divisor; # if the min or max is fixed, check they will fit in the interval. next TRY if ( $flag_fixed_min && ( int( $dataset_min * $divisor ) != ( $dataset_min * $divisor ) ) ); next TRY if ( $flag_fixed_max && ( int( $dataset_max * $divisor ) != ( $dataset_max * $divisor ) ) ); # If everything passes the tests, return. return ( 1 / $divisor, $tickCount, $pMin, $pMax ); } } } die "can't happen!"; } ## @fn private int _calcXTickInterval($min,$max,$minF,$maxF,$minTicks,$maxTicks) # @brief # Calculate the Interval between ticks in x direction # # @details # Calculate the Interval between ticks in x direction # and compare the number of ticks to # the user's given values minTicks, maxTicks. # # @param[in] min Minimal value of dataset in x direction # @param[in] max Maximal value of dataset in x direction # @param[in] minF Inddicator if those min value is fixed # @param[in] maxF Inddicator if those max value is fixed # @param[in] minTicks Minimal number of tick in x direction # @param[in] maxTicks Maximal number of tick in x direction # @return $tickInterval, $tickCount, $pMin, $pMax sub _calcXTickInterval { my $self = shift; my ( $min, $max, # The dataset min & max. $minF, $maxF, # Indicates if those min/max are fixed. $minTicks, $maxTicks, # The minimum & maximum number of ticks. ) = @_; # Verify the supplied 'min_y_ticks' & 'max_y_ticks' are sensible. if ( $minTicks < 2 ) { #print STDERR "Chart::Base::_calcXTickInterval : Incorrect value for 'min_y_ticks', too small.\n"; $minTicks = 2; } if ( $maxTicks < 5 * $minTicks ) { #print STDERR "Chart::Base::_calcXTickInterval : Incorrect value for 'max_y_ticks', to small.\n"; $maxTicks = 5 * $minTicks; } my $width = $max - $min; my @divisorList; for ( my $baseMul = 1 ; ; $baseMul *= 10 ) { TRY: foreach my $tryMul ( 1, 2, 5 ) { # Calc a fresh, smaller tick interval. my $divisor = $baseMul * $tryMul; # Count the number of ticks. my ( $tickCount, $pMin, $pMax ) = $self->_countTicks( $min, $max, 1 / $divisor ); #print STDERR "Chart::Base::_calcXTickInterval : tickCount = $tickCount, maxTicks = $maxTicks\n"; # Look a the number of ticks. if ( $maxTicks < $tickCount ) { # If it is to high, Backtrack. $divisor = pop @divisorList; # just for security: if ( !defined($divisor) || $divisor == 0 ) { $divisor = 1; } ( $tickCount, $pMin, $pMax ) = $self->_countTicks( $min, $max, 1 / $divisor ); #print STDERR "\nChart::Base : Caution: Tick limit of $maxTicks exceeded. Backing of to an interval of ".1/$divisor." which plots $tickCount ticks\n"; return ( 1 / $divisor, $tickCount, $pMin, $pMax ); } elsif ( $minTicks > $tickCount ) { # If it is too low, try again. next TRY; } else { # Store the divisor for possible later backtracking. push @divisorList, $divisor; # if the min or max is fixed, check they will fit in the interval. next TRY if ( $minF && ( int( $min * $divisor ) != ( $min * $divisor ) ) ); next TRY if ( $maxF && ( int( $max * $divisor ) != ( $max * $divisor ) ) ); # If everything passes the tests, return. return ( 1 / $divisor, $tickCount, $pMin, $pMax ); } } } croak "can't happen!"; } ## @fn private int _countTicks($min,$max,$interval) # # @brief # Works out how many ticks would be displayed at that interval # # @param min Minimal value # @param max Maximal value # @param interval value # @return ($tickCount, $minR, $maxR) # # @details # # e.g min=2, max=5, interval=1, result is 4 ticks.\n # written by David Pottage of Tao Group.\n # $minR = $self->_round2Tick( $min, $interval, -1);\n # $maxR = $self->_round2Tick( $max, $interval, 1);\n # $tickCount = ( $maxR/$interval ) - ( $minR/$interval ) +1; sub _countTicks { my $self = shift; my ( $min, $max, $interval ) = @_; my $minR = $self->_round2Tick( $min, $interval, -1 ); my $maxR = $self->_round2Tick( $max, $interval, 1 ); my $tickCount = ( $maxR / $interval ) - ( $minR / $interval ) + 1; return ( $tickCount, $minR, $maxR ); } ## @fn private int _round2Tick($input, $interval, $roundUP) # Rounds up or down to the next tick of interval size. # # $roundUP can be +1 or -1 to indicate if rounding should be up or down.\n # written by David Pottage of Tao Group. # # @param input # @param interval # @param roundUP # @return retN*interval sub _round2Tick { my $self = shift; my ( $input, $interval, $roundUP ) = @_; return $input if $interval == 0; die unless 1 == $roundUP * $roundUP; my $intN = int( $input / $interval ); my $fracN = ( $input / $interval ) - $intN; my $retN = ( ( 0 == $fracN ) || ( ( $roundUP * $fracN ) < 0 ) ) ? $intN : $intN + $roundUP; return $retN * $interval; } ## @fn private array _sepFP($num) # @brief # Seperates a number into it's base 10 floating point exponent & mantisa. # @details # written by David Pottage of Tao Group. # # @param num Floating point number # @return ( exponent, mantissa) sub _sepFP { my $self = shift; my ($num) = @_; return ( 0, 0 ) if $num == 0; my $sign = ( $num > 0 ) ? 1 : -1; $num *= $sign; my $exponent = int( log($num) / log(10) ); my $mantisa = $sign * ( $num / ( 10**$exponent ) ); return ( $exponent, $mantisa ); } ## @fn private array _find_y_range() # Find minimum and maximum value of y data sets. # # @return ( min, max, flag_all_integers ) sub _find_y_range { my $self = shift; my $data = $self->{'dataref'}; my $max = undef; my $min = undef; my $flag_all_integers = 1; # assume true for my $dataset ( @$data[ 1 .. $#$data ] ) { for my $datum (@$dataset) { if ( defined $datum ) { #croak "Missing data (dataset)"; if ($flag_all_integers) { # it's worth looking for integers if ( $datum !~ /^[\-\+]?\d+$/ ) { $flag_all_integers = 0; # No } } if ( $datum =~ /^[\-\+]?\s*[\d\.eE\-\+]+/ ) { if ( defined $max && $max =~ /^[\-\+]{0,}\s*[\d\.eE\-\+]+/ ) { if ( $datum > $max ) { $max = $datum; } elsif ( !defined $min ) { $min = $datum; } elsif ( $datum < $min ) { $min = $datum; } } else { $min = $max = $datum } } } } } # Return: ( $min, $max, $flag_all_integers ); } ## @fn private array _find_x_range() # Find minimum and maximum value of x data sets # @return ( min, max ) sub _find_x_range { my $self = shift; my $data = $self->{'dataref'}; my $max = undef; my $min = undef; for my $datum ( @{ $data->[0] } ) { if ( defined $datum && $datum =~ /^[\-\+]{0,1}\s*[\d\.eE\-\+]+/ ) { if ( defined $max && $max =~ /^[\-\+]{0,1}\s*[\d\.eE\-\+]+/ ) { if ( $datum > $max ) { $max = $datum } elsif ( $datum < $min ) { $min = $datum } } else { $min = $max = $datum } } } return ( $min, $max ); } ## @fn private int _plot() # main sub that controls all the plotting of the actual chart # @return status sub _plot { my $self = shift; # draw the legend first $self->_draw_legend(); # mark off the graph_border space $self->{'curr_x_min'} += $self->{'graph_border'}; $self->{'curr_x_max'} -= $self->{'graph_border'}; $self->{'curr_y_min'} += $self->{'graph_border'}; $self->{'curr_y_max'} -= $self->{'graph_border'}; # draw the x- and y-axis labels $self->_draw_x_label if $self->{'x_label'}; $self->_draw_y_label('left') if $self->{'y_label'}; $self->_draw_y_label('right') if $self->{'y_label2'}; # draw the ticks and tick labels $self->_draw_ticks(); # give the plot a grey background if they want it $self->_grey_background if ( $self->true( $self->{'grey_background'} ) ); #draw the ticks again if grey_background has ruined it in a Direction Chart. if ( $self->true( $self->{'grey_background'} ) && $self->isa("Chart::Direction") ) { $self->_draw_ticks; } $self->_draw_grid_lines if ( $self->true( $self->{'grid_lines'} ) ); $self->_draw_x_grid_lines if ( $self->true( $self->{'x_grid_lines'} ) ); $self->_draw_y_grid_lines if ( $self->true( $self->{'y_grid_lines'} ) ); $self->_draw_y2_grid_lines if ( $self->true( $self->{'y2_grid_lines'} ) ); # plot the data $self->_draw_data(); # and return return 1; } ## @fn private int _draw_legend() # let the user know what all the pretty colors mean.\n # The user define the position of the legend by setting option # 'legend' to 'top', 'bottom', 'left', 'right' or 'none'. # The legend is positioned at the defined place, respectively. # @return status sub _draw_legend { my $self = shift; my $length; # check to see if legend type is none.. if ( $self->{'legend'} =~ /^none$/ || length( $self->{'legend'} ) == 0 ) { return 1; } # check to see if they have as many labels as datasets, # warn them if not if ( ( $#{ $self->{'legend_labels'} } >= 0 ) && ( ( scalar( @{ $self->{'legend_labels'} } ) ) != $self->{'num_datasets'} ) ) { carp "The number of legend labels and datasets doesn\'t match"; } # init a field to store the length of the longest legend label unless ( $self->{'max_legend_label'} ) { $self->{'max_legend_label'} = 0; } # fill in the legend labels, find the longest one for ( 1 .. $self->{'num_datasets'} ) { unless ( $self->{'legend_labels'}[ $_ - 1 ] ) { $self->{'legend_labels'}[ $_ - 1 ] = "Dataset $_"; } $length = length( $self->{'legend_labels'}[ $_ - 1 ] ); if ( $length > $self->{'max_legend_label'} ) { $self->{'max_legend_label'} = $length; } } # different legend types if ( $self->{'legend'} eq 'bottom' ) { $self->_draw_bottom_legend; } elsif ( $self->{'legend'} eq 'right' ) { $self->_draw_right_legend; } elsif ( $self->{'legend'} eq 'left' ) { $self->_draw_left_legend; } elsif ( $self->{'legend'} eq 'top' ) { $self->_draw_top_legend; } elsif ( $self->{'legend'} eq 'none' || length( $self->{'legend'} ) == 0 ) { $self->_draw_none_legend; } else { carp "I can't put a legend there (at " . $self->{'legend'} . ")\n"; } # and return return 1; } ## @fn private int _draw_bottom_legend() # put the legend on the bottom of the chart # @return status sub _draw_bottom_legend { my $self = shift; my @labels = @{ $self->{'legend_labels'} }; my ( $x1, $y1, $x2, $x3, $y2 ); my ( $empty_width, $max_label_width, $cols, $rows, $color, $brush ); my ( $col_width, $row_height, $r, $c, $index, $x, $y, $w, $h, $axes_space ); my $font = $self->{'legend_font'}; # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # find the base x values $axes_space = ( $self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width ) + $self->{'tick_len'} + ( 3 * $self->{'text_space'} ); $x1 = $self->{'curr_x_min'} + $self->{'graph_border'}; $x2 = $self->{'curr_x_max'} - $self->{'graph_border'}; if ( $self->{'y_axes'} =~ /^right$/i ) { $x2 -= $axes_space; } elsif ( $self->{'y_axes'} =~ /^both$/i ) { $x2 -= $axes_space; $x1 += $axes_space; } if ( $self->{'y_label'} ) { $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'}; } if ( $self->{'y_label2'} ) { $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'}; } # figure out how wide the columns need to be, and how many we # can fit in the space available $empty_width = ( $x2 - $x1 ) - ( 2 * $self->{'legend_space'} ); $max_label_width = $self->{'max_legend_label'} * $w + ( 4 * $self->{'text_space'} ) + $self->{'legend_example_size'}; $cols = int( $empty_width / $max_label_width ); unless ($cols) { $cols = 1; } $col_width = $empty_width / $cols; # figure out how many rows we need, remember how tall they are $rows = int( $self->{'num_datasets'} / $cols ); unless ( ( $self->{'num_datasets'} % $cols ) == 0 ) { $rows++; } unless ($rows) { $rows = 1; } $row_height = $h + $self->{'text_space'}; # box the legend off $y1 = $self->{'curr_y_max'} - $self->{'text_space'} - ( $rows * $row_height ) - ( 2 * $self->{'legend_space'} ); $y2 = $self->{'curr_y_max'}; $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $self->_color_role_to_index('misc') ); $x1 += $self->{'legend_space'} + $self->{'text_space'}; $x2 -= $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; $y2 -= $self->{'legend_space'} + $self->{'text_space'}; my $text_color = $self->_color_role_to_index( 'text' ); # draw in the actual legend for $r ( 0 .. $rows - 1 ) { for $c ( 0 .. $cols - 1 ) { $index = ( $r * $cols ) + $c; # find the index in the label array if ( $labels[$index] ) { # get the color $color = $self->_color_role_to_index( 'dataset' . $index ); # get the x-y coordinate for the start of the example line $x = $x1 + ( $col_width * $c ); $y = $y1 + ( $row_height * $r ) + $h / 2; # now draw the example line $self->{'gd_obj'}->line( $x, $y, $x + $self->{'legend_example_size'}, $y, $color ); # reset the brush for points $brush = $self->_prepare_brush( $color, 'point', 'dataset' . $index ); $self->{'gd_obj'}->setBrush($brush); # draw the point $x3 = int( $x + $self->{'legend_example_size'} / 2 ); $self->{'gd_obj'}->line( $x3, $y, $x3, $y, gdBrushed ); # adjust the x-y coordinates for the start of the label $x += $self->{'legend_example_size'} + ( 2 * $self->{'text_space'} ); $y = $y1 + ( $row_height * $r ); # now draw the label $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index], $text_color ); } } } # mark off the space used $self->{'curr_y_max'} -= $rows * $row_height + 2 * $self->{'text_space'} + 2 * $self->{'legend_space'}; # now return return 1; } ## @fn private int _draw_right_legend() # put the legend on the right of the chart # @return status sub _draw_right_legend { my $self = shift; my @labels = @{ $self->{'legend_labels'} }; my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush ); my $font = $self->{'legend_font'}; # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The subtitle font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # get the miscellaneous color $misccolor = $self->_color_role_to_index('misc'); # find out how wide the largest label is $width = ( 2 * $self->{'text_space'} ) + ( $self->{'max_legend_label'} * $w ) + $self->{'legend_example_size'} + ( 2 * $self->{'legend_space'} ); # get some starting x-y values $x1 = $self->{'curr_x_max'} - $width; $x2 = $self->{'curr_x_max'}; $y1 = $self->{'curr_y_min'} + $self->{'graph_border'}; $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'} + ( $self->{'num_datasets'} * ( $h + $self->{'text_space'} ) ) + ( 2 * $self->{'legend_space'} ); # box the legend off $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor ); # leave that nice space inside the legend box $x1 += $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; # now draw the actual legend for ( 0 .. $#labels ) { # get the color my $c = $self->{'num_datasets'} - $_ - 1; # color of the datasets in the legend $color = $self->_color_role_to_index( 'dataset' . $_ ); # find the x-y coords $x2 = $x1; $x3 = $x2 + $self->{'legend_example_size'}; $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2; # do the line first $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color ); # reset the brush for points my $offset = 0; ( $brush, $offset ) = $self->_prepare_brush( $color, 'point', 'dataset' . $_ ); $self->{'gd_obj'}->setBrush($brush); # draw the point $self->{'gd_obj'}->line( int( ( $x3 + $x2 ) / 2 ), $y2, int( ( $x3 + $x2 ) / 2 ), $y2, gdBrushed ); # now the label $x2 = $x3 + ( 2 * $self->{'text_space'} ); $y2 -= $h / 2; # order of the datasets in the legend $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color ); } # mark off the used space $self->{'curr_x_max'} -= $width; # and return return 1; } ## @fn private int _draw_top_legend() # put the legend on top of the chart # @return status sub _draw_top_legend { my $self = shift; my @labels = @{ $self->{'legend_labels'} }; my ( $x1, $y1, $x2, $x3, $y2, $empty_width, $max_label_width ); my ( $cols, $rows, $color, $brush ); my ( $col_width, $row_height, $r, $c, $index, $x, $y, $w, $h, $axes_space ); my $font = $self->{'legend_font'}; # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The subtitle font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # find the base x values $axes_space = ( $self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width ) + $self->{'tick_len'} + ( 3 * $self->{'text_space'} ); $x1 = $self->{'curr_x_min'} + $self->{'graph_border'}; $x2 = $self->{'curr_x_max'} - $self->{'graph_border'}; if ( $self->{'y_axes'} =~ /^right$/i ) { $x2 -= $axes_space; } elsif ( $self->{'y_axes'} =~ /^both$/i ) { $x2 -= $axes_space; $x1 += $axes_space; } # figure out how wide the columns can be, and how many will fit $empty_width = ( $x2 - $x1 ) - ( 2 * $self->{'legend_space'} ); $max_label_width = ( 4 * $self->{'text_space'} ) + ( $self->{'max_legend_label'} * $w ) + $self->{'legend_example_size'}; $cols = int( $empty_width / $max_label_width ); unless ($cols) { $cols = 1; } $col_width = $empty_width / $cols; # figure out how many rows we need and remember how tall they are $rows = int( $self->{'num_datasets'} / $cols ); unless ( ( $self->{'num_datasets'} % $cols ) == 0 ) { $rows++; } unless ($rows) { $rows = 1; } $row_height = $h + $self->{'text_space'}; # box the legend off $y1 = $self->{'curr_y_min'}; $y2 = $self->{'curr_y_min'} + $self->{'text_space'} + ( $rows * $row_height ) + ( 2 * $self->{'legend_space'} ); $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $self->_color_role_to_index('misc') ); # leave some space inside the legend $x1 += $self->{'legend_space'} + $self->{'text_space'}; $x2 -= $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; $y2 -= $self->{'legend_space'} + $self->{'text_space'}; # draw in the actual legend for $r ( 0 .. $rows - 1 ) { for $c ( 0 .. $cols - 1 ) { $index = ( $r * $cols ) + $c; # find the index in the label array if ( $labels[$index] ) { # get the color $color = $self->_color_role_to_index( 'dataset' . $index ); # find the x-y coords $x = $x1 + ( $col_width * $c ); $y = $y1 + ( $row_height * $r ) + $h / 2; # draw the line first $self->{'gd_obj'}->line( $x, $y, $x + $self->{'legend_example_size'}, $y, $color ); # reset the brush for points $brush = $self->_prepare_brush( $color, 'point', 'dataset' . $index ); $self->{'gd_obj'}->setBrush($brush); # draw the point $x3 = int( $x + $self->{'legend_example_size'} / 2 ); $self->{'gd_obj'}->line( $x3, $y, $x3, $y, gdBrushed ); # now the label $x += $self->{'legend_example_size'} + ( 2 * $self->{'text_space'} ); $y -= $h / 2; $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index], $color ); } } } # mark off the space used $self->{'curr_y_min'} += ( $rows * $row_height ) + $self->{'text_space'} + 2 * $self->{'legend_space'}; # now return return 1; } ## @fn private int _draw_left_legend() # put the legend on the left of the chart # @return status sub _draw_left_legend { my $self = shift; my @labels = @{ $self->{'legend_labels'} }; my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush ); my $font = $self->{'legend_font'}; # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The subtitle font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # get the miscellaneous color $misccolor = $self->_color_role_to_index('misc'); # find out how wide the largest label is $width = ( 2 * $self->{'text_space'} ) + ( $self->{'max_legend_label'} * $w ) + $self->{'legend_example_size'} + ( 2 * $self->{'legend_space'} ); # get some base x-y coordinates $x1 = $self->{'curr_x_min'}; $x2 = $self->{'curr_x_min'} + $width; $y1 = $self->{'curr_y_min'} + $self->{'graph_border'}; $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'} + ( $self->{'num_datasets'} * ( $h + $self->{'text_space'} ) ) + ( 2 * $self->{'legend_space'} ); # box the legend off $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor ); # leave that nice space inside the legend box $x1 += $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; # now draw the actual legend for ( 0 .. $#labels ) { # get the color my $c = $self->{'num_datasets'} - $_ - 1; # color of the datasets in the legend $color = $self->_color_role_to_index( 'dataset' . $_ ); # find the x-y coords $x2 = $x1; $x3 = $x2 + $self->{'legend_example_size'}; $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2; # do the line first $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color ); # reset the brush for points $brush = $self->_prepare_brush( $color, 'point', 'dataset' . $_ ); $self->{'gd_obj'}->setBrush($brush); # draw the point $self->{'gd_obj'}->line( int( ( $x3 + $x2 ) / 2 ), $y2, int( ( $x3 + $x2 ) / 2 ), $y2, gdBrushed ); # now the label $x2 = $x3 + ( 2 * $self->{'text_space'} ); $y2 -= $h / 2; # order of the datasets in the legend $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color ); } # mark off the used space $self->{'curr_x_min'} += $width; # and return return 1; } ## @fn private int _draw_none_legend() # no legend to draw.. # Just return in this case. This routine may be overwritten by # subclasses. # @return 1 sub _draw_none_legend { my $self = shift; my $status = 1; return $status; } ## @fn private int _draw_x_label() # draw the label for the x-axis # # Get font for labels\n # Get the color of x_label or text\n # Get size of font\n # and write x-Label # # @return status sub _draw_x_label { my $self = shift; my $label = $self->{'x_label'}; my $font = $self->{'label_font'}; my $color; my ( $h, $w, $x, $y ); #get the right color if ( defined $self->{'colors'}->{'x_label'} ) { $color = $self->_color_role_to_index('x_label'); } else { $color = $self->_color_role_to_index('text'); } # make sure it's a real GD Font object unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The x-axis label font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # make sure it goes in the right place $x = ( $self->{'curr_x_max'} - $self->{'curr_x_min'} ) / 2 + $self->{'curr_x_min'} - ( length($label) * $w ) / 2; $y = $self->{'curr_y_max'} - ( $self->{'text_space'} + $h ); # now write it $self->{'gd_obj'}->string( $font, $x, $y, $label, $color ); # mark the space written to as used $self->{'curr_y_max'} -= $h + 2 * $self->{'text_space'}; # and return return 1; } ## @fn private int _draw_y_label() # draw the label for the y-axis # @return status sub _draw_y_label { my $self = shift; my $side = shift; my $font = $self->{'label_font'}; my ( $label, $h, $w, $x, $y, $color ); # get the label if ( $side eq 'left' ) { $label = $self->{'y_label'}; $color = $self->_color_role_to_index('y_label'); } elsif ( $side eq 'right' ) { $label = $self->{'y_label2'}; $color = $self->_color_role_to_index('y_label2'); } # make sure it's a real GD Font object unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The x-axis label font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # make sure it goes in the right place if ( $side eq 'left' ) { $x = $self->{'curr_x_min'} + $self->{'text_space'}; } elsif ( $side eq 'right' ) { $x = $self->{'curr_x_max'} - $self->{'text_space'} - $h; } $y = ( $self->{'curr_y_max'} - $self->{'curr_y_min'} ) / 2 + $self->{'curr_y_min'} + ( length($label) * $w ) / 2; # write it $self->{'gd_obj'}->stringUp( $font, $x, $y, $label, $color ); # mark the space written to as used if ( $side eq 'left' ) { $self->{'curr_x_min'} += $h + 2 * $self->{'text_space'}; } elsif ( $side eq 'right' ) { $self->{'curr_x_max'} -= $h + 2 * $self->{'text_space'}; } # now return return 1; } ## @fn private int _draw_ticks() # draw the ticks and tick labels # @return status sub _draw_ticks { my $self = shift; #if the user wants an xy_plot, calculate the x-ticks too if ( $self->true( $self->{'xy_plot'} ) && ( $self->isa('Chart::Lines') || $self->isa('Chart::Points') || $self->isa('Chart::LinesPoints') || $self->isa('Chart::Split') || $self->isa('Chart::ErrorBars') ) ) { $self->_draw_x_number_ticks; } else { # draw the x ticks with strings $self->_draw_x_ticks; } # now the y ticks $self->_draw_y_ticks( $self->{'y_axes'} ); # then return return 1; } ## @fn private int _draw_x_number_ticks() # draw the ticks and tick labels # @return status sub _draw_x_number_ticks { my $self = shift; my $data = $self->{'dataref'}; my $font = $self->{'tick_label_font'}; my $textcolor = $self->_color_role_to_index('text'); my $misccolor = $self->_color_role_to_index('misc'); my ( $h, $w, $x1, $y1, $y2, $x2, $delta, $width, $label ); my @labels = @{ $self->{'x_tick_labels'} }; $self->{'grid_data'}->{'x'} = []; #make sure we have a real font unless ( ( ref $font ) eq 'GD::Font' ) { croak "The tick label font you specified isn't a GD font object"; } #get height and width of the font ( $h, $w ) = ( $font->height, $font->width ); #store actual borders, for a possible later repair $self->{'temp_x_min'} = $self->{'curr_x_min'}; $self->{'temp_x_max'} = $self->{'curr_x_max'}; $self->{'temp_y_max'} = $self->{'curr_y_max'}; $self->{'temp_y_min'} = $self->{'curr_y_min'}; #get the right x-value and width #The one and only way to get the RIGHT x value and the width if ( $self->{'y_axes'} =~ /^right$/i ) { $x1 = $self->{'curr_x_min'}; $width = $self->{'curr_x_max'} - $x1 - ( $w * $self->{'y_tick_label_length'} ) - 3 * $self->{'text_space'} - $self->{'tick_len'}; } elsif ( $self->{'y_axes'} =~ /^both$/i ) { $x1 = $self->{'curr_x_min'} + ( $w * $self->{'y_tick_label_length'} ) + 3 * $self->{'text_space'} + $self->{'tick_len'}; $width = $self->{'curr_x_max'} - $x1 - ( $w * $self->{'y_tick_label_length'} ) - ( 3 * $self->{'text_space'} ) - $self->{'tick_len'}; } else { $x1 = $self->{'curr_x_min'} + ( $w * $self->{'y_tick_label_length'} ) + 3 * $self->{'text_space'} + $self->{'tick_len'}; $width = $self->{'curr_x_max'} - $x1; } #get the delta value $delta = $width / ( $self->{'x_number_ticks'} - 1 ); #draw the labels $y2 = $y1; if ( $self->{'x_ticks'} =~ /^normal/i ) { #just normal ticks #get the point for updating later $y1 = $self->{'curr_y_max'} - 2 * $self->{'text_space'} - $h - $self->{'tick_len'}; #get the start point $y2 = $y1 + $self->{'tick_len'} + $self->{'text_space'}; if ( $self->{'xlabels'} ) { unless ( $self->{'xrange'} ) { croak "Base.pm: xrange must be specified with xlabels!\n"; } my $xmin = $self->{'xrange'}[0]; my $xmax = $self->{'xrange'}[1]; my @labels = @{ $self->{'xlabels'}[0] }; my @vals = @{ $self->{'xlabels'}[1] }; my $delta = $width / ( $xmax - $xmin ); for ( 0 .. $#labels ) { my $label = $labels[$_]; my $val = $vals[$_]; $x2 = $x1 + ( $delta * ( $val - $xmin ) ) - ( 0.5 * $w * length($label) ); $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor ); #print "write x-label '".$label."' at ($x2,$y2)\n"; } } else { my $last_x = 'undefined'; for ( 0 .. $#labels ) { $label = $self->{f_x_tick}->( $self->{'x_tick_labels'}[$_] ); $x2 = $x1 + ( $delta * $_ ) - ( 0.5 * $w * length($label) ); if ( $last_x eq 'undefined' or $last_x < $x2 ) { $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor ); $last_x = $x2 + ( $w * length($label) ); } #print "last_x = $last_x, write string '".$label."' at ($x2,$y2) to '$_'\n"; } } } elsif ( $self->{'x_ticks'} =~ /^staggered/i ) { #staggered ticks #get the point for updating later $y1 = $self->{'curr_y_max'} - 3 * $self->{'text_space'} - 2 * $h - $self->{'tick_len'}; if ( $self->{'xlabels'} ) { unless ( $self->{'xrange'} ) { croak "Base.pm: xrange must be specified with xlabels!\n"; } my $xmin = $self->{'xrange'}[0]; my $xmax = $self->{'xrange'}[1]; my @labels = @{ $self->{'xlabels'}[0] }; my @vals = @{ $self->{'xlabels'}[1] }; my $delta = $width / ( $xmax - $xmin ); for ( 0 .. $#labels ) { my $label = $labels[$_]; my $val = $vals[$_]; $x2 = $x1 + ( $delta * ( $val - $xmin ) ) - ( 0.5 * $w * length($label) ); unless ( $_ % 2 ) { $y2 = $y1 + $self->{'text_space'} + $self->{'tick_len'}; } else { $y2 = $y1 + $h + 2 * $self->{'text_space'} + $self->{'tick_len'}; } $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor ); #print "write x-label '".$label."' at ($x2,$y2)\n"; } } else { for ( 0 .. $#labels ) { $label = $self->{f_x_tick}->( $self->{'x_tick_labels'}[$_] ); $x2 = $x1 + ( $delta * $_ ) - ( 0.5 * $w * length($label) ); unless ( $_ % 2 ) { $y2 = $y1 + $self->{'text_space'} + $self->{'tick_len'}; } else { $y2 = $y1 + $h + 2 * $self->{'text_space'} + $self->{'tick_len'}; } $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor ); } } } elsif ( $self->{'x_ticks'} =~ /^vertical/i ) { #vertical ticks #get the point for updating later $y1 = $self->{'curr_y_max'} - 2 * $self->{'text_space'} - $w * $self->{'x_tick_label_length'} - $self->{'tick_len'}; if ( $self->{'xlabels'} ) { unless ( $self->{'xrange'} ) { croak "Base.pm: xrange must be specified with xlabels!\n"; } my $xmin = $self->{'xrange'}[0]; my $xmax = $self->{'xrange'}[1]; my @labels = @{ $self->{'xlabels'}[0] }; my @vals = @{ $self->{'xlabels'}[1] }; my $delta = $width / ( $xmax - $xmin ); for ( 0 .. $#labels ) { my $label = $labels[$_]; my $val = $vals[$_]; $y2 = $y1 + $self->{'tick_len'} + $w * length($label) + $self->{'text_space'}; $x2 = $x1 + ( $delta * ( $val - $xmin ) ) - ( $h / 2 ); $self->{'gd_obj'}->stringUp( $font, $x2, $y2, $label, $textcolor ); #print "write x-label '".$label."' at ($x2,$y2)\n"; } } else { for ( 0 .. $#labels ) { $label = $self->{f_x_tick}->( $self->{'x_tick_labels'}[$_] ); #get the start point $y2 = $y1 + $self->{'tick_len'} + $w * length($label) + $self->{'text_space'}; $x2 = $x1 + ( $delta * $_ ) - ( $h / 2 ); $self->{'gd_obj'}->stringUp( $font, $x2, $y2, $label, $textcolor ); } } } else { croak "I don't understand the type of x-ticks you specified\n" . "x-ticks must be one of 'normal', 'staggered' or 'vertical' but not of '" . $self->{'x_ticks'} . "'."; } #update the curr y max value $self->{'curr_y_max'} = $y1; #draw the ticks $y1 = $self->{'curr_y_max'}; $y2 = $self->{'curr_y_max'} + $self->{'tick_len'}; #draw grid lines if ( $self->{'xlabels'} ) { unless ( $self->{'xrange'} ) { croak "Base.pm: xrange must be specified with xlabels!\n"; } my $xmin = $self->{'xrange'}[0]; my $xmax = $self->{'xrange'}[1]; my @vals = @{ $self->{'xlabels'}[1] }; my $delta = $width / ( $xmax - $xmin ); for ( 0 .. $#vals ) { my $val = $vals[$_]; $x2 = ($x1) + ( $delta * ( $val - $xmin ) ); $self->{'gd_obj'}->line( $x2, $y1, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x2; } } } else { for ( 0 .. $#labels ) { $x2 = $x1 + ( $delta * $_ ); $self->{'gd_obj'}->line( $x2, $y1, $x2, $y2, $misccolor ); if ( ( $self->true( $self->{'grid_lines'} ) ) or ( $self->true( $self->{'x_grid_lines'} ) ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x2; } } } return 1; } ## @fn private int _draw_x_ticks() # draw the x-ticks and their labels # @return status sub _draw_x_ticks { my $self = shift; my $data = $self->{'dataref'}; my $font = $self->{'tick_label_font'}; my $textcolor = $self->_color_role_to_index('text'); my $misccolor = $self->_color_role_to_index('misc'); my $label; my ( $h, $w ); my ( $x1, $x2, $y1, $y2 ); my ( $width, $delta ); my ($stag); $self->{'grid_data'}->{'x'} = []; # make sure we got a real font unless ( ( ref $font ) eq 'GD::Font' ) { croak "The tick label font you specified isn\'t a GD Font object"; } # get the height and width of the font ( $h, $w ) = ( $font->height, $font->width ); # maybe, we need the actual x and y values later for drawing the x-ticks again # in the draw function in the lines modul. So copy them. $self->{'temp_x_min'} = $self->{'curr_x_min'}; $self->{'temp_x_max'} = $self->{'curr_x_max'}; $self->{'temp_y_min'} = $self->{'curr_y_min'}; $self->{'temp_y_max'} = $self->{'curr_y_max'}; # allow for the amount of space the y-ticks will push the # axes over to the right ## _draw_y_ticks allows 3 * text_space, not 1 * ; this caused mismatch between ## the ticks (and grid lines) and the data. # $x1 = $self->{'curr_x_min'} + ($w * $self->{'y_tick_label_length'}) # + $self->{'text_space'} + $self->{'tick_len'}; ## And, what about the right-tick space?? Only affects Composite, I guess.... #The one and only way to get the RIGHT x value and the width if ( $self->{'y_axes'} =~ /^right$/i ) { $x1 = $self->{'curr_x_min'}; $width = $self->{'curr_x_max'} - $x1 - $self->{'tick_len'} - ( $w * $self->{'y_tick_label_length'} ) - - ( 3 * $self->{'text_space'} ); } elsif ( $self->{'y_axes'} =~ /^both$/i ) { $x1 = $self->{'curr_x_min'} + ( $w * $self->{'y_tick_label_length'} ) + 3 * $self->{'text_space'} + $self->{'tick_len'}; $width = $self->{'curr_x_max'} - $x1 - $self->{'tick_len'} - ( $w * $self->{'y_tick_label_length'} ) - 3 * $self->{'text_space'}; } else { $x1 = $self->{'curr_x_min'} + ( $w * $self->{'y_tick_label_length'} ) + 3 * $self->{'text_space'} + $self->{'tick_len'}; $width = $self->{'curr_x_max'} - $x1; } #the same for the y value, but not so tricky $y1 = $self->{'curr_y_max'} - $h - $self->{'text_space'}; # get the delta value, figure out how to draw the labels $delta = $width / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); if ( !defined( $self->{'skip_x_ticks'} ) ) { $self->{'skip_x_ticks'} = 1; } elsif ( $self->{'skip_x_ticks'} == 0 ) { $self->{'skip_x_ticks'} = 1; } if ( $delta <= ( $self->{'x_tick_label_length'} * $w ) / $self->{'skip_x_ticks'} ) { if ( $self->{'x_ticks'} =~ /^normal$/i ) { $self->{'x_ticks'} = 'staggered'; } } # now draw the labels if ( $self->{'x_ticks'} =~ /^normal$/i ) { # normal ticks if ( $self->{'skip_x_ticks'} > 1 ) { # draw only every nth tick and label for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_x_ticks'} ) ) { if ( defined( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ) ) { $label = $self->{f_x_tick}->( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ); $x2 = $x1 + ( $delta / 2 ) + ( $delta * ( $_ * $self->{'skip_x_ticks'} ) ) - ( $w * length($label) ) / 2; $self->{'gd_obj'}->string( $font, $x2, $y1, $label, $textcolor ); } } } elsif ( $self->{'custom_x_ticks'} ) { # draw only the ticks they wanted for ( @{ $self->{'custom_x_ticks'} } ) { if ( defined($_) ) { $label = $self->{f_x_tick}->( $data->[0][$_] ); $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - ( $w * length($label) ) / 2; $self->{'gd_obj'}->string( $font, $x2, $y1, $label, $textcolor ); } } } else { for ( 0 .. $self->{'num_datapoints'} - 1 ) { if ( defined($_) ) { $label = $self->{f_x_tick}->( $data->[0][$_] ); $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - ( $w * length($label) ) / 2; $self->{'gd_obj'}->string( $font, $x2, $y1, $label, $textcolor ); } } } } elsif ( $self->{'x_ticks'} =~ /^staggered$/i ) { # staggered ticks if ( $self->{'skip_x_ticks'} > 1 ) { $stag = 0; for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_x_ticks'} ) ) { if ( defined( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * ( $_ * $self->{'skip_x_ticks'} ) ) - ( $w * length( $self->{f_x_tick}->( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ) ) ) / 2; if ( ( $stag % 2 ) == 1 ) { $y1 -= $self->{'text_space'} + $h; } $self->{'gd_obj'} ->string( $font, $x2, $y1, $self->{f_x_tick}->( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ), $textcolor ); $y1 += $self->{'text_space'} + $h if $stag % 2; $stag++; } } } elsif ( $self->{'custom_x_ticks'} ) { $stag = 0; for ( sort ( @{ $self->{'custom_x_ticks'} } ) ) { # sort to make it look good if ( defined($_) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - ( $w * length( $self->{f_x_tick}->( $data->[0][$_] ) ) ) / 2; $y1 -= $self->{'text_space'} + $h if $stag % 2; $self->{'gd_obj'}->string( $font, $x2, $y1, $self->{f_x_tick}->( $data->[0][$_] ), $textcolor ); $y1 += $self->{'text_space'} + $h if $stag % 2; $stag++; } } } else { for ( 0 .. $self->{'num_datapoints'} - 1 ) { if ( defined( $self->{f_x_tick}->( $data->[0][$_] ) ) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - ( $w * length( $self->{f_x_tick}->( $data->[0][$_] ) ) ) / 2; if ( ( $_ % 2 ) == 1 ) { $y1 -= $self->{'text_space'} + $h; } $self->{'gd_obj'}->string( $font, $x2, $y1, $self->{f_x_tick}->( $data->[0][$_] ), $textcolor ); if ( ( $_ % 2 ) == 1 ) { $y1 += $self->{'text_space'} + $h; } } } } } elsif ( $self->{'x_ticks'} =~ /^vertical$/i ) { # vertical ticks $y1 = $self->{'curr_y_max'} - $self->{'text_space'}; if ( $self->{'skip_x_ticks'} > 1 ) { for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_x_ticks'} ) ) { if ( defined($_) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * ( $_ * $self->{'skip_x_ticks'} ) ) - $h / 2; $y2 = $y1 - ( ( $self->{'x_tick_label_length'} - length( $self->{f_x_tick}->( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ) ) ) * $w ); $self->{'gd_obj'} ->stringUp( $font, $x2, $y2, $self->{f_x_tick}->( $data->[0][ $_ * $self->{'skip_x_ticks'} ] ), $textcolor ); } } } elsif ( $self->{'custom_x_ticks'} ) { for ( @{ $self->{'custom_x_ticks'} } ) { if ( defined($_) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - $h / 2; $y2 = $y1 - ( ( $self->{'x_tick_label_length'} - length( $self->{f_x_tick}->( $data->[0][$_] ) ) ) * $w ); $self->{'gd_obj'}->stringUp( $font, $x2, $y2, $self->{f_x_tick}->( $data->[0][$_] ), $textcolor ); } } } else { for ( 0 .. $self->{'num_datapoints'} - 1 ) { if ( defined($_) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ) - $h / 2; $y2 = $y1 - ( ( $self->{'x_tick_label_length'} - length( $self->{f_x_tick}->( $data->[0][$_] ) ) ) * $w ); $self->{'gd_obj'}->stringUp( $font, $x2, $y2, $self->{f_x_tick}->( $data->[0][$_] ), $textcolor ); } } } } else { # error time carp "I don't understand the type of x-ticks you specified"; } # update the current y-max value if ( $self->{'x_ticks'} =~ /^normal$/i ) { $self->{'curr_y_max'} -= $h + ( 2 * $self->{'text_space'} ); } elsif ( $self->{'x_ticks'} =~ /^staggered$/i ) { $self->{'curr_y_max'} -= ( 2 * $h ) + ( 3 * $self->{'text_space'} ); } elsif ( $self->{'x_ticks'} =~ /^vertical$/i ) { $self->{'curr_y_max'} -= ( $w * $self->{'x_tick_label_length'} ) + ( 2 * $self->{'text_space'} ); } # now plot the ticks $y1 = $self->{'curr_y_max'}; $y2 = $self->{'curr_y_max'} - $self->{'tick_len'}; if ( $self->{'skip_x_ticks'} > 1 ) { for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_x_ticks'} ) ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * ( $_ * $self->{'skip_x_ticks'} ) ); $self->{'gd_obj'}->line( $x2, $y1, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x2; } } } elsif ( $self->{'custom_x_ticks'} ) { for ( @{ $self->{'custom_x_ticks'} } ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ); $self->{'gd_obj'}->line( $x2, $y1, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x2; } } } else { for ( 0 .. $self->{'num_datapoints'} - 1 ) { $x2 = $x1 + ( $delta / 2 ) + ( $delta * $_ ); $self->{'gd_obj'}->line( $x2, $y1, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x2; } } } # update the current y-max value $self->{'curr_y_max'} -= $self->{'tick_len'}; } ## @fn private int _draw_y_ticks() # draw the y-ticks and their labels # @return status sub _draw_y_ticks { my $self = shift; my $side = shift || 'left'; my $data = $self->{'dataref'}; my $font = $self->{'tick_label_font'}; my $textcolor = $self->_color_role_to_index('text'); my $misccolor = $self->_color_role_to_index('misc'); my @labels = @{ $self->{'y_tick_labels'} }; my ( $w, $h ); my ( $x1, $x2, $y1, $y2 ); my ( $height, $delta, $label ); my ( $s, $f ); $self->{grid_data}->{'y'} = []; $self->{grid_data}->{'y2'} = []; # make sure we got a real font croak "The tick label font you specified isn\'t a GD Font object" unless ( ref $font ) eq 'GD::Font'; # find out how big the font is ( $w, $h ) = ( $font->width, $font->height ); # figure out which ticks not to draw if ( $self->{'min_val'} >= 0 ) { $s = 1; $f = $#labels; } elsif ( $self->{'max_val'} <= 0 ) { $s = 0; $f = $#labels; # -1 entfernt } else { $s = 0; $f = $#labels; } # now draw them if ( $side eq 'right' ) { # put 'em on the right side of the chart # get the base x-y values, and the delta value $x1 = $self->{'curr_x_max'} - $self->{'tick_len'} - ( 3 * $self->{'text_space'} ) - ( $w * $self->{'y_tick_label_length'} ); $y1 = $self->{'curr_y_max'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $self->{'y_ticks'} = 2 if $self->{'y_ticks'} < 2; $delta = $height / ( $self->{'y_ticks'} - 1 ); # update the curr_x_max value $self->{'curr_x_max'} = $x1; # now draw the ticks $x2 = $x1 + $self->{'tick_len'}; for ( $s .. $f ) { $y2 = $y1 - ( $delta * $_ ); $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'y2_grid_lines'} ) ) { $self->{'grid_data'}->{'y2'}->[$_] = $y2; } } # update the current x-min value $x1 += $self->{'tick_len'} + ( 2 * $self->{'text_space'} ); $y1 -= $h / 2; # now draw the labels for ( 0 .. $#labels ) { $y2 = $y1 - ( $delta * $_ ); $self->{'gd_obj'}->string( $font, $x1, $y2, $self->{'y_tick_labels'}[$_], $textcolor ); } } elsif ( $side eq 'both' ) { # put the ticks on the both sides, left side first # get the base x-y values $x1 = $self->{'curr_x_min'} + $self->{'text_space'}; $y1 = $self->{'curr_y_max'} - $h / 2; # now draw the labels $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta = $height / ( $self->{'y_ticks'} - 1 ); for ( 0 .. $#labels ) { $label = $self->{'y_tick_labels'}[$_]; $y2 = $y1 - ( $delta * $_ ); $x2 = $x1 + ( $w * $self->{'y_tick_label_length'} ) - ( $w * length($label) ); $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor ); } # and update the current x-min value $self->{'curr_x_min'} += ( 3 * $self->{'text_space'} ) + ( $w * $self->{'y_tick_label_length'} ); # now draw the ticks (skipping the one at zero); $x1 = $self->{'curr_x_min'}; $x2 = $self->{'curr_x_min'} + $self->{'tick_len'}; $y1 += $h / 2; for ( $s .. $f ) { $y2 = $y1 - ( $delta * $_ ); $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor ); if ( $self->true( $self->{grid_lines} ) or $self->true( $self->{'y_grid_lines'} ) ) { $self->{'grid_data'}->{'y'}->[$_] = $y2; } } # update the current x-min value $self->{'curr_x_min'} += $self->{'tick_len'}; ## now the right side # get the base x-y values, and the delta value $x1 = $self->{'curr_x_max'} - $self->{'tick_len'} - ( 3 * $self->{'text_space'} ) - ( $w * $self->{'y_tick_label_length'} ); $y1 = $self->{'curr_y_max'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta = $height / ( $self->{'y_ticks'} - 1 ); # update the curr_x_max value $self->{'curr_x_max'} = $x1; # now draw the ticks (skipping the one at zero); $x2 = $x1 + $self->{'tick_len'}; for ( $s .. $f ) { $y2 = $y1 - ( $delta * $_ ); $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor ); # draw tick_line if ( $self->true( $self->{grid_lines} ) or $self->true( $self->{'y2_grid_lines'} ) ) { $self->{'grid_data'}->{'y2'}->[$_] = $y2; } } # update the current x-min value $x1 += $self->{'tick_len'} + ( 2 * $self->{'text_space'} ); $y1 -= $h / 2; # now draw the labels for ( 0 .. $#labels ) { $y2 = $y1 - ( $delta * $_ ); $self->{'gd_obj'}->string( $font, $x1, $y2, $self->{'y_tick_labels'}[$_], $textcolor ); } } else { # just the left side # get the base x-y values $x1 = $self->{'curr_x_min'} + $self->{'text_space'}; $y1 = $self->{'curr_y_max'} - $h / 2; # now draw the labels $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $self->{'y_ticks'} = 2 if $self->{'y_ticks'} < 2; $delta = $height / ( $self->{'y_ticks'} - 1 ); for ( 0 .. $#labels ) { $label = $self->{'y_tick_labels'}[$_]; $y2 = $y1 - ( $delta * $_ ); $x2 = $x1 + ( $w * $self->{'y_tick_label_length'} ) - ( $w * length($label) ); $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor ); } # and update the current x-min value $self->{'curr_x_min'} += ( 3 * $self->{'text_space'} ) + ( $w * $self->{'y_tick_label_length'} ); # now draw the ticks $x1 = $self->{'curr_x_min'}; $x2 = $self->{'curr_x_min'} + $self->{'tick_len'}; $y1 += $h / 2; for ( $s .. $f ) { $y2 = $y1 - ( $delta * $_ ); $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'y_grid_lines'} ) ) { $self->{'grid_data'}->{'y'}->[$_] = $y2; } } # update the current x-min value $self->{'curr_x_min'} += $self->{'tick_len'}; } # and return return 1; } ## @fn private int _grey_background() # put a grey background on the plot of the data itself # @return status sub _grey_background { my $self = shift; # draw it $self->{'gd_obj'} ->filledRectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $self->_color_role_to_index('grey_background') ); # now return return 1; } ## @fn private int _draw_grid_lines() # draw grid_lines # @return status sub _draw_grid_lines { my $self = shift; $self->_draw_x_grid_lines(); $self->_draw_y_grid_lines(); $self->_draw_y2_grid_lines(); return 1; } ## @fn private int _draw_x_grid_lines() # draw grid_lines for x # @return status sub _draw_x_grid_lines { my $self = shift; my $grid_role = shift || 'x_grid_lines'; my $gridcolor = $self->_color_role_to_index($grid_role); my ( $x, $y, $i ); foreach $x ( @{ $self->{grid_data}->{'x'} } ) { if ( defined $x ) { $self->{gd_obj}->line( ( $x, $self->{'curr_y_min'} + 1 ), $x, ( $self->{'curr_y_max'} - 1 ), $gridcolor ); } } return 1; } ## @fn private int _draw_y_grid_lines() # draw grid_lines for y # @return status sub _draw_y_grid_lines { my $self = shift; my $grid_role = shift || 'y_grid_lines'; my $gridcolor = $self->_color_role_to_index($grid_role); my ( $x, $y, $i ); #Look if I'm an HorizontalBars object if ( $self->isa('Chart::HorizontalBars') ) { for ( $i = 0 ; $i < ( $#{ $self->{grid_data}->{'y'} } ) + 1 ; $i++ ) { $y = $self->{grid_data}->{'y'}->[$i]; $self->{gd_obj}->line( ( $self->{'curr_x_min'} + 1 ), $y, ( $self->{'curr_x_max'} - 1 ), $y, $gridcolor ); } } else { # loop for y values is a little different. This is to discard the first # and last values we were given - the top/bottom of the chart area. for ( $i = 1 ; $i < ( $#{ $self->{grid_data}->{'y'} } ) + 1 ; $i++ ) { ### $y = $self->{grid_data}->{'y'}->[$i]; $self->{gd_obj}->line( ( $self->{'curr_x_min'} + 1 ), $y, ( $self->{'curr_x_max'} - 1 ), $y, $gridcolor ); } } return 1; } ## @fn private int _draw_y2_grid_lines() # draw grid_lines for y # @return status sub _draw_y2_grid_lines { my $self = shift; my $grid_role = shift || 'y2_grid_lines'; my $gridcolor = $self->_color_role_to_index($grid_role); my ( $x, $y, $i ); #Look if I'm an HorizontalBars object if ( $self->isa('Chart::HorizontalBars') ) { for ( $i = 0 ; $i < ( $#{ $self->{grid_data}->{'y'} } ) + 1 ; $i++ ) { $y = $self->{grid_data}->{'y'}->[$i]; $self->{gd_obj}->line( ( $self->{'curr_x_min'} + 1 ), $y, ( $self->{'curr_x_max'} - 1 ), $y, $gridcolor ); } } else { # loop for y2 values is a little different. This is to discard the first # and last values we were given - the top/bottom of the chart area. for ( $i = 1 ; $i < $#{ $self->{grid_data}->{'y2'} } ; $i++ ) { $y = $self->{grid_data}->{'y2'}->[$i]; $self->{gd_obj}->line( ( $self->{'curr_x_min'} + 1 ), $y, ( $self->{'curr_x_max'} - 1 ), $y, $gridcolor ); } } return 1; } ## @fn private int _prepare_brush($color,$type,$role) # prepare brush # # @details # set the gdBrush object to tick GD into drawing fat lines & points # of interesting shapes # Needed by "Lines", "Points" and "LinesPoints" # All hacked up by Richard Dice Sunday 16 May 1999 # # @param color # @param type 'line','point' # @param role # # @return status sub _prepare_brush { my $self = shift; my $color = shift; my $type = shift; my $role = shift || 'default'; my $brushStyle = $self->{'brushStyle'}; if ( defined $role ) { my (@brushStyles) = $self->_brushStyles_of_roles($role); $brushStyle = $brushStyles[0]; } #print STDERR "role=$role\n"; # decide what $type should be in the event that a param isn't # passed -- this is necessary to preserve backward compatibility # with apps that use this module prior to putting _prepare_brush # in with Base.pm if ( !defined($type) ) { $type = 'point'; } if ( ( !length($type) ) || ( !grep { $type eq $_ } ( 'line', 'point' ) ) ) { $brushStyle = $self->{'brushStyle'}; $type = 'line' if ref $self eq 'Chart::Lines'; $type = 'point' if ref $self eq 'Chart::Points'; } my ( $radius, @rgb, $brush, $white, $newcolor ); # get the rgb values for the desired color @rgb = $self->{'gd_obj'}->rgb($color); # get the appropriate brush size if ( $type eq 'line' ) { $radius = $self->{'brush_size'} / 2; } elsif ( $type eq 'point' ) { $radius = $self->{'pt_size'} / 2; } # create the new image $brush = GD::Image->new( $radius * 2, $radius * 2 ); # get the colors, make the background transparent $white = $brush->colorAllocate( 255, 255, 255 ); $newcolor = $brush->colorAllocate(@rgb); $brush->transparent($white); # draw the circle if ( $type eq 'line' ) { $brush->arc( $radius - 1, $radius - 1, $radius, $radius, 0, 360, $newcolor ); $brush->fill( $radius - 1, $radius - 1, $newcolor ); # RLD # # Does $brush->fill really have to be here? Dunno... this # seems to be a relic from earlier code # # Note that 'line's don't benefit from a $brushStyle... yet. # It shouldn't be too tough to hack this in by taking advantage # of GD's gdStyled facility } if ( $type eq 'point' ) { $brushStyle = $self->{'brushStyle'} unless grep { $brushStyle eq $_ } ( 'FilledCircle', 'circle', 'donut', 'OpenCircle', 'triangle', 'upsidedownTriangle', 'square', 'hollowSquare', 'OpenRectangle', 'fatPlus', 'Star', 'OpenStar', 'FilledDiamond', 'OpenDiamond' ); my ( $xc, $yc ) = ( $radius, $radius ); if ( grep { $brushStyle eq $_ } ( 'default', 'circle', 'donut', 'OpenCircle', 'FilledCircle' ) ) { $brush->arc( $xc, $yc, $radius, $radius, 0, 360, $newcolor ); $brush->fill( $xc, $yc, $newcolor ); # draw a white (and therefore transparent) circle in the middle # of the existing circle to make the "donut", if appropriate if ( $brushStyle eq 'donut' || $brushStyle eq 'OpenCircle' ) { $brush->arc( $xc, $yc, int( $radius / 2 ), int( $radius / 2 ), 0, 360, $white ); $brush->fill( $xc, $yc, $white ); } } if ( grep { $brushStyle eq $_ } ( 'triangle', 'upsidedownTriangle' ) ) { my $poly = new GD::Polygon; my $sign = ( $brushStyle eq 'triangle' ) ? 1 : (-1); my $z = int( 0.8 * $radius ); # scaling factor # co-ords are chosen to make an equilateral triangle $poly->addPt( $xc, $yc - ( $z * $sign ) ); $poly->addPt( $xc + int( ( sqrt(3) * $z ) / 2 ), $yc + ( int( $z / 2 ) * $sign ) ); $poly->addPt( $xc - int( ( sqrt(3) * $z ) / 2 ), $yc + ( int( $z / 2 ) * $sign ) ); $brush->filledPolygon( $poly, $newcolor ); } if ( $brushStyle eq 'fatPlus' ) { my $poly = new GD::Polygon; my $z = int( 0.3 * $radius ); $poly->addPt( $xc + $z, $yc + $z ); $poly->addPt( $xc + 2 * $z, $yc + $z ); $poly->addPt( $xc + 2 * $z, $yc - $z ); $poly->addPt( $xc + $z, $yc - $z ); $poly->addPt( $xc + $z, $yc - 2 * $z ); $poly->addPt( $xc - $z, $yc - 2 * $z ); $poly->addPt( $xc - $z, $yc - $z ); $poly->addPt( $xc - 2 * $z, $yc - $z ); $poly->addPt( $xc - 2 * $z, $yc + $z ); $poly->addPt( $xc - $z, $yc + $z ); $poly->addPt( $xc - $z, $yc + 2 * $z ); $poly->addPt( $xc + $z, $yc + 2 * $z ); $brush->filledPolygon( $poly, $newcolor ); } if ( $brushStyle eq 'Star' || $brushStyle eq 'OpenStar' ) { my $poly = new GD::Polygon; my $z = int($radius); my $sz = int( $z / 3 * 1.75 ); # small z my $x1 = int( $xc + $z ); my $y1 = int($yc); my ( $x2, $y2 ); my $xyRatio = $self->_xyRatio(); $poly->addPt( $x1, $y1 ); $x2 = $xc + int( $sz * 0.5 ); $y2 = $yc - int( $sz * 0.5 ); $poly->addPt( $x2, $y2 ); $x2 = $xc; $y2 = $yc - $z; $poly->addPt( $x2, $y2 ); $x2 = $xc - int( $sz * 0.5 ); $y2 = $yc - int( $sz * 0.5 ); $poly->addPt( $x2, $y2 ); $x2 = $xc - $z; $y2 = $yc; $poly->addPt( $x2, $y2 ); $x2 = $xc - int( $sz * 0.5 ); $y2 = $yc + int( $sz * 0.5 ); $poly->addPt( $x2, $y2 ); $x2 = $xc; $y2 = $yc + $z; $poly->addPt( $x2, $y2 ); $x2 = $xc + int( $sz * 0.5 ); $y2 = $yc + int( $sz * 0.5 ); $poly->addPt( $x2, $y2 ); if ( $brushStyle eq 'OpenStar' ) { $brush->polygon( $poly, $newcolor ); } else { $brush->filledPolygon( $poly, $newcolor ); } } if ( grep { $brushStyle eq $_ } ( 'square', 'hollowSquare', 'OpenRectangle' ) ) { my $z = int( 0.5 * $radius ); $brush->filledRectangle( $xc - $z, $yc - $z, $xc + $z, $yc + $z, $newcolor ); if ( $brushStyle eq 'hollowSquare' || $brushStyle eq 'OpenRectangle' ) { $z = int( $z / 2 ); $brush->filledRectangle( $xc - $z, $yc - $z, $xc + $z, $yc + $z, $white ); } } if ( grep { $brushStyle eq $_ } ( 'FilledDiamond', 'OpenDiamond' ) ) { my $z = int( 0.75 * $radius ); $brush->line( $xc + $z, $yc, $xc, $yc + $z, $newcolor ); $brush->line( $xc, $yc + $z, $xc - $z, $yc, $newcolor ); $brush->line( $xc - $z, $yc, $xc, $yc - $z, $newcolor ); $brush->line( $xc, $yc - $z, $xc + $z, $yc, $newcolor ); if ( $brushStyle eq 'FilledDiamond' ) { # and fill it $brush->fill( $radius - 1, $radius - 1, $newcolor ); } } } # set the new image as the main object's brush return $brush; } ## @fn private int _default_f_tick # default tick conversion function # This function is pointed to be $self->{f_x_tick} resp. $self->{f_y_tick} # if the user does not provide another function # # @return status sub _default_f_tick { my $label = shift; return $label; } ## @fn private float _xyRatio # Get ratio width_x/width_y # # @return ratio width_x and width_y sub _xyRatio { my $self = shift; my $width_x = $self->{'curr_x_max'} - $self->{'curr_x_min'} + 1; my $width_y = $self->{'curr_y_max'} - $self->{'curr_y_min'} + 1; return $width_x / $width_y; } ## @fn private float _xPixelInReal # Get width of one Pixel in real coordinates in x-direction # # @return width(interval) of reality in x direction # sub _xPixelInReal { my $self = shift; my $width_x = $self->{'curr_x_max'} - $self->{'curr_x_min'} + 1; my ( $min, $max ) = $self->_find_x_range(); my $xRealWidth = $max - $min; return $xRealWidth / $width_x; } ## @fn private float _yPixelInReal # Get width of one Pixel in real coordinates in y-direction # # @return width(interval) of reality in y direction # sub _yPixelInReal { my $self = shift; my $width_y = $self->{'curr_y_max'} - $self->{'curr_y_min'} + 1; my ( $min, $max, $flag_all_integers ) = $self->_find_y_range(); my $yRealWidth = $max - $min; return $yRealWidth / $width_y; } 1; # be a good module and return positive Chart-v2.403.9/lib/Chart/ErrorBars.pm0000644000175000017500000003561514341172251017050 0ustar herbertherbert # chart type: error bar - vertical bars # which represent errors or standard deviations use v5.12; package Chart::ErrorBars; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Chart::Base; use GD; use Carp; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<# #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @fn private _draw_data # finally get around to plotting the data # # Overwrites Base function # sub _draw_data { my $self = shift; my $data = $self->{'dataref'}; my $misccolor = $self->_color_role_to_index('misc'); my ( $x1, $x2, $x3, $y1, $y2, $y3, $mod, $y_error_up, $y_error_down ); my ( $width, $height, $delta, $map, $delta_num, $zero_offset, $flag ); my ( $i, $j, $color, $brush ); my $dataset = 0; my $diff; # init the imagemap data field if they want it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # find the delta value between data points, as well # as the mapping constant $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta = $width / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); $diff = $self->{'max_val'} - $self->{'min_val'}; $diff = 1 if $diff == 0; $map = $height / $diff; # for a xy-plot, use this delta and maybe an offset for the zero-axes if ( $self->true( $self->{'xy_plot'} ) ) { $diff = $self->{'x_max_val'} - $self->{'x_min_val'}; $diff = 1 if $diff == 0; $delta_num = $width / $diff; if ( $self->{'x_min_val'} <= 0 && $self->{'x_max_val'} >= 0 ) { $zero_offset = abs( $self->{'x_min_val'} ) * abs($delta_num); } elsif ( $self->{'x_min_val'} > 0 || $self->{'x_max_val'} < 0 ) { $zero_offset = -$self->{'x_min_val'} * $delta_num; } else { $zero_offset = 0; } } # get the base x-y values if ( $self->false( $self->{'xy_plot'} ) ) { $x1 = $self->{'curr_x_min'} + ( $delta / 2 ); } else { $x1 = $self->{'curr_x_min'}; } if ( $self->{'min_val'} >= 0 ) { $y1 = $self->{'curr_y_max'}; $mod = $self->{'min_val'}; } elsif ( $self->{'max_val'} <= 0 ) { $y1 = $self->{'curr_y_min'}; $mod = $self->{'max_val'}; } else { $y1 = $self->{'curr_y_min'} + ( $map * $self->{'max_val'} ); $mod = 0; $self->{'gd_obj'}->line( $self->{'curr_x_min'}, $y1, $self->{'curr_x_max'}, $y1, $misccolor ); } # first of all box it off $self->{'gd_obj'} ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); # draw the points for $i ( 1 .. $self->{'num_datasets'} ) { if ( $self->false( $self->{'same_error'} ) ) { # get the color for this dataset, and set the brush $color = $self->_color_role_to_index( 'dataset' . ($dataset) ); # draw every point for this dataset $dataset++ if ( ( $i - 1 ) % 3 == 0 ); for $j ( 0 .. $self->{'num_datapoints'} ) { #get the brush for points $brush = $self->_prepare_brush( $color, 'point' ); $self->{'gd_obj'}->setBrush($brush); # only draw if the current set is really a dataset and no errorset if ( ( $i - 1 ) % 3 == 0 ) { # don't try to draw anything if there's no data if ( defined( $data->[$i][$j] ) ) { if ( $self->true( $self->{'xy_plot'} ) ) { $x2 = $x1 + $delta_num * $data->[0][$j] + $zero_offset + 1; $x3 = $x2; } else { $x2 = $x1 + ( $delta * $j ) + 1; $x3 = $x2; } $y2 = $y1 - ( ( $data->[$i][$j] - $mod ) * $map ); $y3 = $y2; $y_error_up = $y2 - abs( $data->[ $i + 1 ][$j] ) * $map; $y_error_down = $y2 + abs( $data->[ $i + 2 ][$j] ) * $map; # draw the point only if it is within the chart borders if ( $data->[$i][$j] <= $self->{'max_val'} && $data->[$i][$j] >= $self->{'min_val'} ) { $self->{'gd_obj'}->line( $x2, $y2, $x3, $y3, gdBrushed ); $flag = 'true'; } #reset the brush for lines $brush = $self->_prepare_brush( $color, 'line' ); $self->{'gd_obj'}->setBrush($brush); #draw the error bars if ( $self->true($flag) ) { # the upper lines $self->{'gd_obj'}->line( $x2, $y2, $x3, $y_error_up, gdBrushed ); $self->{'gd_obj'}->line( $x2 - 3, $y_error_up, $x3 + 3, $y_error_up, gdBrushed ); # the down lines $self->{'gd_obj'}->line( $x2, $y2, $x3, $y_error_down, gdBrushed ); $self->{'gd_obj'}->line( $x2 - 3, $y_error_down, $x3 + 3, $y_error_down, gdBrushed ); $flag = 'false'; } # store the imagemap data if they asked for it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ $x2, $y2 ]; } } } } } else { # get the color for this dataset, and set the brush $color = $self->_color_role_to_index( 'dataset' . ($dataset) ); # draw every point for this dataset $dataset++ if ( ( $i - 1 ) % 2 == 0 ); for $j ( 0 .. $self->{'num_datapoints'} ) { #get the brush for points $brush = $self->_prepare_brush( $color, 'point' ); $self->{'gd_obj'}->setBrush($brush); # only draw if the current set is really a dataset and no errorset if ( ( $i - 1 ) % 2 == 0 ) { # don't try to draw anything if there's no data if ( defined( $data->[$i][$j] ) ) { if ( $self->true( $self->{'xy_plot'} ) ) { $x2 = $x1 + $delta_num * $data->[0][$j] + $zero_offset; $x3 = $x2; } else { $x2 = $x1 + ( $delta * $j ); $x3 = $x2; } $y2 = $y1 - ( ( $data->[$i][$j] - $mod ) * $map ); $y3 = $y2; $y_error_up = $y2 - abs( $data->[ $i + 1 ][$j] ) * $map; $y_error_down = $y2 + abs( $data->[ $i + 1 ][$j] ) * $map; # draw the point only if it is within the chart borders if ( $data->[$i][$j] <= $self->{'max_val'} && $data->[$i][$j] >= $self->{'min_val'} ) { $self->{'gd_obj'}->line( $x2, $y2, $x3, $y3, gdBrushed ); $flag = 'true'; } #reset the brush for lines $brush = $self->_prepare_brush( $color, 'line' ); $self->{'gd_obj'}->setBrush($brush); #draw the error bars if ( $self->true($flag) ) { # the upper lines $self->{'gd_obj'}->line( $x2, $y2, $x3, $y_error_up, gdBrushed ); $self->{'gd_obj'}->line( $x2 - 3, $y_error_up, $x3 + 3, $y_error_up, gdBrushed ); # the down lines $self->{'gd_obj'}->line( $x2, $y2, $x3, $y_error_down, gdBrushed ); $self->{'gd_obj'}->line( $x2 - 3, $y_error_down, $x3 + 3, $y_error_down, gdBrushed ); $flag = 'false'; } # store the imagemap data if they asked for it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ $x2, $y2 ]; } } } #end for } } } return 1; } ## @fn private _prepare_brush # set the gdBrush object to trick GD into drawing fat lines # # Overwrite Base function # sub _prepare_brush { my $self = shift; my $color = shift; my $type = shift; my ( $radius, @rgb, $brush, $white, $newcolor ); # get the rgb values for the desired color @rgb = $self->{'gd_obj'}->rgb($color); # get the appropriate brush size if ( $type eq 'line' ) { $radius = $self->{'brush_size'} / 2; } elsif ( $type eq 'point' ) { $radius = $self->{'pt_size'} / 2; } # create the new image $brush = GD::Image->new( $radius * 2, $radius * 2 ); # get the colors, make the background transparent $white = $brush->colorAllocate( 255, 255, 255 ); $newcolor = $brush->colorAllocate(@rgb); $brush->transparent($white); # draw the circle $brush->arc( $radius - 1, $radius - 1, $radius, $radius, 0, 360, $newcolor ); # fill it if we're using lines $brush->fill( $radius - 1, $radius - 1, $newcolor ); # set the new image as the main object's brush return $brush; } ## @fn private int _draw_legend() # let them know what all the pretty colors mean # @return status ## let them know what all the pretty colors mean sub _draw_legend { my $self = shift; my ( $length, $step, $temp, $post_length ); my $j = 0; # check to see if legend type is none.. if ( $self->{'legend'} =~ /^none$/ ) { return 1; } #just for later checking and warning if ( $#{ $self->{'legend_labels'} } >= 0 ) { $post_length = scalar( @{ $self->{'legend_labels'} } ); } #look if every second or eyery third dataset is a set for data if ( $self->false( $self->{'same_error'} ) ) { $step = 3; } else { $step = 2; } # init a field to store the length of the longest legend label unless ( $self->{'max_legend_label'} ) { $self->{'max_legend_label'} = 0; } # fill in the legend labels, find the longest one for ( my $i = 1 ; $i < $self->{'num_datasets'} ; $i += $step ) { my $label = $j + 1; unless ( $self->{'legend_labels'}[$j] ) { $self->{'legend_labels'}[$j] = "Dataset $label"; } $length = length( $self->{'legend_labels'}[$j] ); if ( $length > $self->{'max_legend_label'} ) { $self->{'max_legend_label'} = $length; } $j++; } #we just have to label the datasets in the legend #we'll reset it, to draw the sets $temp = $self->{'num_datasets'}; $self->{'num_datasets'} = $j; # check to see if they have as many labels as datasets, # warn them if not if ( ( $post_length > 0 ) && ( $post_length != $j ) ) { carp "The number of legend labels and datasets doesn\'t match"; } # different legend types if ( $self->{'legend'} eq 'bottom' ) { $self->_draw_bottom_legend; } elsif ( $self->{'legend'} eq 'right' ) { $self->_draw_right_legend; } elsif ( $self->{'legend'} eq 'left' ) { $self->_draw_left_legend; } elsif ( $self->{'legend'} eq 'top' ) { $self->_draw_top_legend; } else { carp "I can't put a legend there (at " . $self->{'legend'} . ")\n"; } #reset the number of dataset to make sure that everything goes right $self->{'num_datasets'} = $temp; # and return return 1; } #find the range of the x scale, don't forget the errors! sub _find_y_range { my $self = shift; my $data = $self->{'dataref'}; my $max = undef; my $min = undef; if ( $self->false( $self->{'same_error'} ) ) { for my $i ( 1 .. $self->{'num_datasets'} ) { if ( ( $i - 1 ) % 3 == 0 ) { for my $j ( 0 .. $self->{'num_datapoints'} ) { if ( defined( $data->[$i][$j] ) && defined( $data->[ $i + 1 ][$j] ) && defined( $data->[ $i + 2 ][$j] ) ) { if ( defined $max ) { if ( ( $data->[$i][$j] + abs( $data->[ $i + 1 ][$j] ) ) > $max ) { $max = $data->[$i][$j] + abs( $data->[ $i + 1 ][$j] ); } if ( ( $data->[$i][$j] - abs( $data->[ $i + 2 ][$j] ) ) < $min ) { $min = $data->[$i][$j] - abs( $data->[ $i + 2 ][$j] ); } } else { $min = $max = $data->[$i][$j]; } } } } } return ( $min, $max ); } else { for my $i ( 1 .. $self->{'num_datasets'} ) { if ( ( $i - 1 ) % 2 == 0 ) { for my $j ( 0 .. $self->{'num_datapoints'} ) { if ( defined( $data->[$i][$j] ) && defined( $data->[ $i + 1 ][$j] ) ) { if ( defined $max ) { if ( ( $data->[$i][$j] + $data->[ $i + 1 ][$j] ) > $max ) { $max = $data->[$i][$j] + $data->[ $i + 1 ][$j]; } if ( ( $data->[$i][$j] - $data->[ $i + 1 ][$j] ) < $min ) { $min = $data->[$i][$j] - $data->[ $i + 1 ][$j]; } } else { $min = $max = $data->[$i][$j]; } } } } } return ( $min, $max ); } } ## be a good module and return 1 1; Chart-v2.403.9/lib/Chart/Lines.pm0000644000175000017500000002442414341172251016215 0ustar herbertherbert # chart type with lines use v5.12; package Chart::Lines; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Chart::Base; use GD; use Carp; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<# #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @method private _draw_data # finally get around to plotting the data for lines sub _draw_data { my $self = shift; my $data = $self->{'dataref'}; my $misccolor = $self->_color_role_to_index('misc'); my ( $x1, $x2, $x3, $y1, $y2, $y3, $mod, $abs_x_max, $abs_y_max, $tan_alpha ); my ( $width, $height, $delta, $delta_num, $map, $t_x_min, $t_x_max, $t_y_min, $t_y_max ); my ( $i, $j, $color, $brush, $zero_offset ); my $repair_top_flag = 0; my $repair_bottom_flag = 0; # init the imagemap data field if they asked for it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # find the delta value between data points, as well # as the mapping constant $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta = $width / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); $map = $height / ( $self->{'max_val'} - $self->{'min_val'} ); #for a xy-plot, use this delta and maybe an offset for the zero-axes if ( $self->true( $self->{'xy_plot'} ) ) { $delta_num = $width / ( $self->{'x_max_val'} - $self->{'x_min_val'} ); if ( $self->{'x_min_val'} <= 0 && $self->{'x_max_val'} >= 0 ) { $zero_offset = abs( $self->{'x_min_val'} ) * abs($delta_num); } elsif ( $self->{'x_min_val'} > 0 || $self->{'x_max_val'} < 0 ) { $zero_offset = -$self->{'x_min_val'} * $delta_num; } else { $zero_offset = 0; } } # get the base x-y values if ( $self->true( $self->{'xy_plot'} ) ) { $x1 = $self->{'curr_x_min'}; } else { $x1 = $self->{'curr_x_min'} + ( $delta / 2 ); } if ( $self->{'min_val'} >= 0 ) { $y1 = $self->{'curr_y_max'}; $mod = $self->{'min_val'}; } elsif ( $self->{'max_val'} <= 0 ) { $y1 = $self->{'curr_y_min'}; $mod = $self->{'max_val'}; } else { $y1 = $self->{'curr_y_min'} + ( $map * $self->{'max_val'} ); $mod = 0; $self->{'gd_obj'}->line( $self->{'curr_x_min'}, $y1, $self->{'curr_x_max'}, $y1, $misccolor ); } # draw the lines for $i ( 1 .. $self->{'num_datasets'} ) { # get the color for this dataset, and set the brush $color = $self->_color_role_to_index( 'dataset' . ( $i - 1 ) ); $brush = $self->_prepare_brush($color); $self->{'gd_obj'}->setBrush($brush); # draw every line for this dataset for $j ( 1 .. $self->{'num_datapoints'} - 1 ) { # don't try to draw anything if there's no data if ( defined( $data->[$i][$j] ) and defined( $data->[$i][ $j - 1 ] ) ) { if ( $self->true( $self->{'xy_plot'} ) ) { $x2 = $x1 + $delta_num * $data->[0][ $j - 1 ] + $zero_offset; $x3 = $x1 + $delta_num * $data->[0][$j] + $zero_offset; } else { $x2 = $x1 + ( $delta * ( $j - 1 ) ); $x3 = $x1 + ( $delta * $j ); } $y2 = $y1 - ( ( $data->[$i][ $j - 1 ] - $mod ) * $map ); $y3 = $y1 - ( ( $data->[$i][$j] - $mod ) * $map ); # now draw the line # ---------------- # stepline option added by G.ST. 2005/02 #---------------- if ( $self->true( $self->{'stepline'} ) ) { if ( $self->{'stepline_mode'} =~ /^begin$/i ) { $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, gdBrushed ); $self->{'gd_obj'}->line( $x3, $y2, $x3, $y3, gdBrushed ); } else { $self->{'gd_obj'}->line( $x2, $y2, $x2, $y3, gdBrushed ); $self->{'gd_obj'}->line( $x2, $y3, $x3, $y3, gdBrushed ); } } # ----------------------------------- # end stepline option #------------------------------------ else { $self->{'gd_obj'}->line( $x2, $y2, $x3, $y3, gdBrushed ); } # set the flags, if the lines are out of the borders of the chart if ( ( $data->[$i][$j] > $self->{'max_val'} ) || ( $data->[$i][ $j - 1 ] > $self->{'max_val'} ) ) { $repair_top_flag = 1; } if ( ( $self->{'max_val'} <= 0 ) && ( ( $data->[$i][$j] > $self->{'max_val'} ) || ( $data->[$i][ $j - 1 ] > $self->{'max_val'} ) ) ) { $repair_top_flag = 1; } if ( ( $data->[$i][$j] < $self->{'min_val'} ) || ( $data->[$i][ $j - 1 ] < $self->{'min_val'} ) ) { $repair_bottom_flag = 1; } # store the imagemap data if they asked for it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][ $j - 1 ] = [ $x2, $y2 ]; $self->{'imagemap_data'}->[$i][$j] = [ $x3, $y3 ]; } } else { if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][ $j - 1 ] = [ undef(), undef() ]; $self->{'imagemap_data'}->[$i][$j] = [ undef(), undef() ]; } } } } # and finaly box it off $self->{'gd_obj'} ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); #get the width and the heigth of the complete picture ( $abs_x_max, $abs_y_max ) = $self->{'gd_obj'}->getBounds(); #repair the chart, if the lines are out of the borders of the chart if ($repair_top_flag) { #overwrite the ugly mistakes $self->{'gd_obj'}->filledRectangle( $self->{'curr_x_min'} - ( $self->{'brush_size'} / 2 ), 0, $self->{'curr_x_max'}, $self->{'curr_y_min'} - 1, $self->_color_role_to_index('background') ); #save the actual x and y values $t_x_min = $self->{'curr_x_min'}; $t_x_max = $self->{'curr_x_max'}; $t_y_min = $self->{'curr_y_min'}; $t_y_max = $self->{'curr_y_max'}; #get back to the point, where everything began $self->{'curr_x_min'} = 0; $self->{'curr_y_min'} = 0; $self->{'curr_x_max'} = $abs_x_max; $self->{'curr_y_max'} = $abs_y_max; #draw the title again if ( $self->{'title'} ) { $self->_draw_title; } #draw the sub title again if ( $self->{'sub_title'} ) { $self->_draw_sub_title; } #draw the top legend again if ( $self->{'legend'} =~ /^top$/i ) { $self->_draw_top_legend; } #reset the actual values $self->{'curr_x_min'} = $t_x_min; $self->{'curr_x_max'} = $t_x_max; $self->{'curr_y_min'} = $t_y_min; $self->{'curr_y_max'} = $t_y_max; } if ($repair_bottom_flag) { #overwrite the ugly mistakes $self->{'gd_obj'}->filledRectangle( $self->{'curr_x_min'} - ( $self->{'brush_size'} / 2 ), $self->{'curr_y_max'} + 1, $self->{'curr_x_max'}, $abs_y_max, $self->_color_role_to_index('background') ); #save the actual x and y values $t_x_min = $self->{'curr_x_min'}; $t_x_max = $self->{'curr_x_max'}; $t_y_min = $self->{'curr_y_min'}; $t_y_max = $self->{'curr_y_max'}; #get back to the point, where everything began $self->{'curr_x_min'} = 0; $self->{'curr_y_min'} = 0; $self->{'curr_x_max'} = $abs_x_max; $self->{'curr_y_max'} = $abs_y_max - 1; # mark off the graph_border space $self->{'curr_y_max'} -= 2 * $self->{'graph_border'}; #draw the bottom legend again if ( $self->{'legend'} =~ /^bottom$/i ) { $self->_draw_bottom_legend; } #draw the x label again if ( $self->{'x_label'} ) { $self->_draw_x_label; } #get back to the start point for the ticks $self->{'curr_x_min'} = $self->{'temp_x_min'}; $self->{'curr_y_min'} = $self->{'temp_y_min'}; $self->{'curr_x_max'} = $self->{'temp_x_max'}; $self->{'curr_y_max'} = $self->{'temp_y_max'}; #draw the x ticks again if ( $self->true( $self->{'xy_plot'} ) ) { $self->_draw_x_number_ticks; } else { $self->_draw_x_ticks; } #reset the actual values $self->{'curr_x_min'} = $t_x_min; $self->{'curr_x_max'} = $t_x_max; $self->{'curr_y_min'} = $t_y_min; $self->{'curr_y_max'} = $t_y_max; } return; } ## @fn private int _prepare_brush($color) # set the gdBrush object to trick GD into drawing fat lines # sub _prepare_brush { my $self = shift; my $color = shift; my $radius = $self->{'brush_size'} / 2; my ( @rgb, $brush, $white, $newcolor ); # get the rgb values for the desired color @rgb = $self->{'gd_obj'}->rgb($color); # create the new image $brush = GD::Image->new( $radius * 2, $radius * 2 ); # get the colors, make the background transparent $white = $brush->colorAllocate( 255, 255, 255 ); $newcolor = $brush->colorAllocate(@rgb); $brush->transparent($white); # draw the circle $brush->arc( $radius - 1, $radius - 1, $radius, $radius, 0, 360, $newcolor ); # set the new image as the main object's brush return $brush; } ## be a good module and return 1 1; Chart-v2.403.9/lib/Chart/Pie.pm0000644000175000017500000012352314341172251015660 0ustar herbertherbert # pie and doughnut (ring) charts use v5.12; package Chart::Pie; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Carp; use GD; use Chart::Constants; use Chart::Base ; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<# #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @fn private _draw_data # @brief # finally get around to plotting the data # # @details # The user may define the kind of labelling the data by setting\n # 'label_values' to 'percent' if she wants to have the percentages\n # 'label_values' to 'values' if she wants to have the absolut values\n # 'label_values' to 'both' if she wants to have absolut values and percentages\n # 'label_values' to 'none' if she wants to have neither absolute values nor percentages\n # 'ring' to a number less then 1 to define a ring as output; # if 'ring' is 1 ore greater a full pie is plotted\n # sub _draw_data { my $self = shift; my $data = $self->{'dataref'}; my $misccolor = $self->_color_role_to_index('misc'); my $textcolor = $self->_color_role_to_index('text'); my $background = $self->_color_role_to_index('background'); my ( $width, $height, $centerX, $centerY, $diameter, $diameter_previous, $text_diameter ); my $dataset_sum; my ( $start_degrees, $end_degrees, $label_degrees, $label_old_degrees, $labelY_repeat_count ); my ( $pi, $rd2dg, $dg2rd ); my ( $font, $fontW, $fontH, $labelX, $labelY ); my $label; my ( $i, $j, $color ); my $label_length = 0; my $degrees = 0; my $insidecolor; my $forbidden_degrees = 0; # last occupied degree my %labelinfo; my $max_val_len = 0; my $max_label_len = 0; # set up initial constant values $pi = Chart::Constants::PI; $dg2rd = $pi / 180; # Degree to Radians $rd2dg = 180 / $pi; # Radian to Degree $start_degrees = 0; $end_degrees = 0; $font = $self->{'legend_font'}; $fontW = $self->{'legend_font'}->width; $fontH = $self->{'legend_font'}->height; $label_degrees = $labelY_repeat_count = 0; # init the imagemap data field if they wanted it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # find width and height of the plotting area $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; # okay, add up all the numbers of all the datasets, to get the # sum total. This will be used to determine the percentage # of each dataset. Obviously, negative numbers might be bad :) $dataset_sum = 0; for $j ( 0 .. $self->{'num_datapoints'} ) { if ( defined $data->[1][$j] && $data->[1][$j] > 0 ) { #add to sum $dataset_sum += $data->[1][$j]; #don't allow negativ values if ( $data->[1][$j] < 0 ) { croak "We need positiv data for a pie chart (which is not true for data[$j])!"; } } } # find the longest label # first we need the length of the values $max_label_len = 1; for $j ( 0 .. ( $self->{'num_datapoints'} - 1 ) ) { # don't try to draw anything if there's no data $labelinfo{$j}{data} = 'undefined'; if ( defined( $data->[1][$j] ) && $data->[1][$j] > 0 ) { $labelinfo{$j}{data} = $data->[1][$j]; $label = $data->[0][$j]; $labelinfo{$j}{labeldata} = $label; if ( defined $self->{'label_values'} ) { if ( $self->{'label_values'} =~ /^percent$/i ) { $label = sprintf( "%s %4.2f%%", $label, $data->[1][$j] / ( $dataset_sum || 1 ) * 100 ); } elsif ( $self->{'label_values'} =~ /^value$/i ) { if ( $data->[1][$j] =~ /\./ ) { $label = sprintf( "%s %.2f", $label, $data->[1][$j] ); } else { $label = sprintf( "%s %d", $label, $data->[1][$j] ); } } elsif ( $self->{'label_values'} =~ /^both$/i ) { if ( $data->[1][$j] =~ /\./ ) { $label = sprintf( "%s %4.2f%% %.2f", $label, $data->[1][$j] / ( $dataset_sum || 1 ) * 100, $data->[1][$j] ); } else { $label = sprintf( "%s %4.2f%% %d", $label, $data->[1][$j] / ( $dataset_sum || 1 ) * 100, $data->[1][$j] ); } } elsif ( $self->{'label_values'} =~ /^none$/i ) { $label = sprintf( "%s", $label ); } } $label_length = length($label); $labelinfo{$j}{labelstring} = $label, $labelinfo{$j}{labellength} = $label_length; } $max_label_len = $label_length if ( $max_label_len < $label_length ); } $max_label_len *= $fontW; # find center point, from which the pie will be drawn around $centerX = int( $width / 2 ) + $self->{'curr_x_min'}; $centerY = int( $height / 2 ) + $self->{'curr_y_min'}; # @details # always draw a circle, which means the diameter will be the smaller # of the width and height. let enough space for the labels.\n # Calculate the space needed for the labels by taking # into account the angles where the labels will be plotted my $labeldistance = 2 * $self->maximum( $fontW, $fontH ); $start_degrees = 0; $end_degrees = 0; my $max_radius = $self->minimum( $width, $height ) / 2; my $radius = $max_radius; for $j ( 0 .. ( $self->{'num_datapoints'} - 1 ) ) { # So, get the degree offset for this dataset $end_degrees = $start_degrees + ( $data->[1][$j] / ( $dataset_sum || 1 ) * 360 ); $degrees = $start_degrees + ( $end_degrees - $start_degrees ) / 2; # stick the label in the middle of the slice $label_degrees = ( $start_degrees + $end_degrees ) / 2; #print "Vor Modulo: label_degrees = $label_degrees\n"; $label_degrees = $self->modulo( $label_degrees, 360.0 ); #print "Nach Modulo: label_degrees = $label_degrees\n"; $label = $labelinfo{$j}{labelstring}; $label_length = $labelinfo{$j}{labellength} || 0; #!!DEBUG!!!! #print "Text=".$label.", StartDegrees=".$start_degrees. # ", end=".$end_degrees. # ", label_degree=".$label_degrees."\n"; # 0 degrees means east # 90 degrees means south # 180 degrees means west # 270 degrees means north # 360 degrees means east again if ( ( $label_degrees >= 270 && $label_degrees <= 360 ) || ( $label_degrees >= 0 && $label_degrees <= 90 ) ) { # right side of the circle { # $x value in respect of label arc my $x = $radius * sin( $dg2rd * $label_degrees ); my $y = $radius * cos( $dg2rd * $label_degrees ); #!!DEBUG!!!! #print "Text=".$label.": x=$x, y=$y\n"; # i.e. the startpoint is at $centerX+$x, $centerY-$y #test #$self->{'gd_obj'}->rectangle( $centerX, $centerY, $centerX+$x, $centerY-$y, $misccolor ); # theoretical right value in respect to radius and length of label my $right_pos = $centerX + $x + $label_length * $fontW; if ( $right_pos > $self->{'curr_x_max'} ) { # too far right, correct x $right_pos = $self->{'curr_x_max'}; $x = $right_pos - $centerX - $label_length * $fontW; #!!DEBUG!!!! #print "too far right: Text=".$label.": x=$x, y=$y\n"; } # theoretical top position in respect to radius and height of label # (Remark: direction to the top of the picture counts backwards!) my $top_pos = $centerY - $y - $fontH; if ( $top_pos < $self->{'curr_y_min'} ) { # too far up, correct $y $top_pos = $self->{'curr_y_min'}; $y = $centerY - $top_pos - $fontH; #!!DEBUG!!!! #print "too far up: Text=".$label.": x=$x, y=$y\n"; } my $down_pos = $centerY + $y + $fontH; if ( $down_pos > $self->{'curr_y_max'} ) { $down_pos = $self->{'curr_y_max'}; $y = $down_pos - $centerY - $fontH; #!!DEBUG!!!! #print "too far down: Text=".$label.": x=$x, y=$y\n"; } #test #$self->{'gd_obj'}->rectangle( $centerX+$x, $centerY-$y, $right_pos, $top_pos, $textcolor ); #$self->{'gd_obj'} # ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); #return; #!!DEBUG!!!! #print "before Line 270: label_degrees=$label_degrees, cos()=". cos( $dg2rd * $label_degrees ). ", sin()=". sin( $dg2rd * $label_degrees )."\n"; if ( $label_degrees == 0 ) { $radius = $self->minimum( $radius, abs( $y / cos( $dg2rd * $label_degrees ) ) ); } else { $radius = $self->minimum( $radius, $x / sin( $dg2rd * $label_degrees ), abs( $y / cos( $dg2rd * $label_degrees ) ) ); } $radius = int( $radius + 0.5 ); #!!DEBUG!! #$self->{'gd_obj'}->line( $centerX, $centerY, $centerX+$radius, $centerY, gdBrushed ); } if ( $radius <= 0 ) { croak "radius < 0!"; } } else { # left side of the circle # as 0 degrees means east if ( abs($label_degrees) < 0.1 ) { # too small angle $radius = $self->{'curr_x_max'} - $label_length * $fontW; } else { # $x value in respect of label arc my $x = $radius * sin( $dg2rd * $label_degrees ); my $y = $radius * cos( $dg2rd * $label_degrees ); # i.e. the startpoint is at $centerX-$x, $centerY+$y #test #$self->{'gd_obj'}->rectangle( $centerX, $centerY, $centerX-$x, $centerY+$y, $misccolor ); # theoretical right value in respect to radius and length of label my $left_pos = $centerX - $x - $label_length * $fontW; if ( $left_pos < $self->{'curr_x_min'} ) { # too far right, correct x $left_pos = $self->{'curr_x_min'}; $x = $centerX - $left_pos - $label_length * $fontW; } # theoretical top position in respect to radius and height of label # (Remark: direction to the top of the picture counts backwards!) my $top_pos = $centerY + $y - $fontH; if ( $top_pos < $self->{'curr_y_min'} ) { # too far up, correct $y $top_pos = $self->{'curr_y_min'}; $y = $centerY - $top_pos - $fontH; } my $down_pos = $centerY - $y + $fontH; if ( $down_pos > $self->{'curr_y_max'} ) { $down_pos = $self->{'curr_y_max'}; $y = $centerY + $fontH - $down_pos; } #test #$self->{'gd_obj'}->rectangle( $centerX-$x, $centerY+$y, $left_pos, $top_pos, $textcolor ); #$self->{'gd_obj'} # ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); #return; $radius = $self->minimum( $radius, $x / sin( $dg2rd * $label_degrees ), abs( $y / cos( $dg2rd * $label_degrees ) ) ); $radius = int( $radius + 0.5 ); #test #$self->{'gd_obj'}->line( $centerX, $centerY, $centerX+$radius, $centerY, gdBrushed ); } if ( $radius <= 0 ) { croak "radius < 0!"; } } # reset starting point for next dataset and continue. $start_degrees = $end_degrees; } $diameter = $radius * 2 - 2 * $labeldistance; $text_diameter = $diameter + $labeldistance; $self->{'gd_obj'}->arc( $centerX, $centerY, $diameter, $diameter, 0, 360, $misccolor ); # for DEBUG!! #$self->{'gd_obj'}->arc($centerX, $centerY, $text_diameter, $text_diameter, # 0, 360, $misccolor); # for DEBUG!! #print "-------------------------------------------\n"; # @details # Plot the pies $start_degrees = 0; $end_degrees = 0; for $j ( 0 .. ( $self->{'num_datapoints'} - 1 ) ) { next if $labelinfo{$j}{data} eq 'undefined'; # get the color for this datapoint, take the color of the datasets $color = $self->_color_role_to_index( 'dataset' . $j ); $label = $labelinfo{$j}{labelstring}; $label_length = $labelinfo{$j}{labellength}; # The first value starts at 0 degrees, each additional dataset # stops where the previous left off, and since I've already # calculated the sum_total for the whole graph, I know that # the final pie slice will end at 360 degrees. # So, get the degree offset for this dataset $end_degrees = $start_degrees + ( $data->[1][$j] / ( $dataset_sum || 1 ) * 360 ); $degrees = ( $start_degrees + $end_degrees ) / 2; # stick the label in the middle of the slice $label_degrees = $degrees; $label_degrees = $self->modulo( $label_degrees, 360.0 ); if ( $start_degrees < $end_degrees ) { # draw filled Arc # test #print "centerX=$centerX, centerY=$centerY, diameter=$diameter, $start_degrees, ".int($start_degrees) . ", $end_degrees\n"; #$self->{'gd_obj'}->filledArc( $centerX, $centerY, $diameter, $diameter, $start_degrees, $end_degrees, $color ); $self->{'gd_obj'}->filledArc( $centerX, $centerY, $diameter, $diameter, int( $start_degrees - 0.5 ), int( $end_degrees + 0.5 ), $color ); } # Figure out where to place the label # $forbidden_degrees = angle of the center, representing the height of the label if ( $j == 0 ) { $forbidden_degrees = $rd2dg * atan2( $fontH, 0.5 * $text_diameter ); $label_old_degrees = 0; } else { my $winkel; my $h; if ( ( $label_old_degrees <= 90.0 && $label_degrees > 90.0 ) || ( $label_old_degrees <= 270.0 && $label_degrees > 270.0 ) ) { # at 90 degrees there the reference point to the text changes # from the beginning to the back $forbidden_degrees = 0; } $label_degrees = $self->maximum( $label_degrees, $forbidden_degrees ); $label_old_degrees = $label_degrees; # remember old label_degrees $winkel = cos( $label_degrees * $dg2rd ); $winkel = abs($winkel); if ( abs($winkel) < 0.01 ) { $h = 0; } else { $h = $fontH / $winkel; } my $atan = atan2( $h, 0.5 * $text_diameter ); # -pi ... +pi $forbidden_degrees = $label_degrees + $rd2dg * $atan; # for debugging #printf("Index=%2d winkel=%6.2f, H=%3d atan=%5.2f label=%6.2f forbidden=%6.2f\n", # $j, $winkel*$dg2rd,$h, $atan*$rd2dg, $label_degrees,$forbidden_degrees); # end for debugging } $labelX = $centerX + $text_diameter * 0.5 * cos( $label_degrees * $dg2rd ); $labelY = $centerY + $text_diameter * 0.5 * sin( $label_degrees * $dg2rd ); #!!DEBUG!!!! #print "Text=".$label.": labelX=$labelX, y=$labelY\n"; # # For debugging # # Draw Point # # reset the brush for points # my $brush = $self->_prepare_brush($color, 'point', # $self->{'pointStyle' . '0'}); # $self->{'gd_obj'}->setBrush($brush); # # # draw the point # $self->{'gd_obj'}->line($labelX, $labelY, $labelX, $labelY, gdBrushed); # # end for debugging # Okay, if a bunch of very small datasets are close together, they can # overwrite each other. The following if statement is to help keep # labels of neighbor datasets from being overlapped. It ain't perfect, # but it does a pretty good job. if ( ( $label_degrees >= 270 && $label_degrees <= 360 ) || ( $label_degrees >= 0 && $label_degrees <= 90 ) ) { # right side of the circle # as 0 degrees means east # $textcolor marks everything black $self->{'gd_obj'}->string( $font, $labelX, $labelY - $fontH * 0.5, $label, $textcolor ); } else { # $textcolor marks everything black $self->{'gd_obj'}->string( $font, $labelX - length($label) * $fontW, $labelY - $fontH * 0.5, $label, $textcolor ); } if ( $self->true( $self->{'legend_lines'} ) ) { $self->{'gd_obj'}->line( $centerX + 0.5 * $diameter * cos( $degrees * $dg2rd ), $centerY + 0.5 * $diameter * sin( $degrees * $dg2rd ), $labelX, $labelY, $color ); } # reset starting point for next dataset and continue. $start_degrees = $end_degrees; } # end for $j # print "Center $centerX, $centerY\n"; # print "Durchmesser $diameter\n"; # print "Hintergrund $background\n"; if ( defined( $self->{'ring'} ) && abs( $self->{'ring'} ) < 1 ) { # print "bground $bground\n"; my $hole = ( 1 - abs( $self->{'ring'} ) ); if ( $self->true( $self->{'grey_background'} ) ) { $insidecolor = $self->_color_role_to_index('grey_background'); } else { $insidecolor = $background; } $self->{'gd_obj'}->filledArc( $centerX, $centerY, $hole * $diameter, $hole * $diameter, 0, 360, $insidecolor ); $self->{'gd_obj'}->arc( $centerX, $centerY, $hole * $diameter, $hole * $diameter, 0, 360, $misccolor ); } # and finaly box it off $self->{'gd_obj'} ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); return; } ## @fn private _draw_right_legend # Overwrite the legend methods to get the right legend sub _draw_right_legend { my $self = shift; my $data = $self->{'dataref'}; my @labels = @{ $data->[0] }; my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush ); my $font = $self->{'legend_font'}; my $l1 = 0; my $l2 = 0; my ( $i, $j, $label, $dataset_sum ); my $max_label_len = 1; # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # get the miscellaneous color $misccolor = $self->_color_role_to_index('misc'); #find out what the sum of all datapoits is, needed for the Labels with percent $dataset_sum = 0; for my $j ( 0 .. $self->{'num_datapoints'} ) { if ( defined $data->[1][$j] ) { $dataset_sum += $data->[1][$j]; } } # find out how who wide the largest label text is foreach (@labels) { if ( length($_) > $l1 ) { $l1 = length($_); } } for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ ) { if ( length( $data->[1][$i] ) > $l2 ) { $l2 = length( $data->[1][$i] ); } } if ( $self->{'legend_label_values'} =~ /^value$/i ) { $max_label_len = $l1 + $l2 + 1; } elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) { $max_label_len = $l1 + 7; } elsif ( $self->{'legend_label_values'} =~ /^both$/i ) { $max_label_len = $l1 + $l2 + 9; } else { $max_label_len = $l1; } # find out how wide the largest label is $width = ( 2 * $self->{'text_space'} ) #+ ($self->{'max_legend_label'} * $w) + $max_label_len * $w + $self->{'legend_example_size'} + ( 2 * $self->{'legend_space'} ); # get some starting x-y values $x1 = $self->{'curr_x_max'} - $width; $x2 = $self->{'curr_x_max'}; $y1 = $self->{'curr_y_min'} + $self->{'graph_border'}; $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'} + ( $self->{'num_datapoints'} * ( $h + $self->{'text_space'} ) ) + ( 2 * $self->{'legend_space'} ); # box the legend off $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor ); # leave that nice space inside the legend box $x1 += $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; # now draw the actual legend for ( 0 .. $#labels ) { # get the color $color = $self->_color_role_to_index( 'dataset' . $_ ); # find the x-y coords $x2 = $x1; $x3 = $x2 + $self->{'legend_example_size'}; $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2; # do the line first $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color ); # reset the brush for points $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $_ } ); $self->{'gd_obj'}->setBrush($brush); # draw the point $self->{'gd_obj'}->line( int( ( $x3 + $x2 ) / 2 ), $y2, int( ( $x3 + $x2 ) / 2 ), $y2, gdBrushed ); # now the label $x2 = $x3 + ( 2 * $self->{'text_space'} ); $y2 -= $h / 2; if ( defined $data->[1][$_] ) { if ( $self->{'legend_label_values'} =~ /^value$/i ) { $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_] . ' ' . $data->[1][$_], $color ); } elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) { $label = sprintf( "%s %4.2f%%", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100 ); $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color ); } elsif ( $self->{'legend_label_values'} =~ /^both$/i ) { if ( $data->[1][$_] =~ /\./ ) { $label = sprintf( "%s %4.2f%% %.2f", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] ); } else { $label = sprintf( "%s %4.2f%% %d", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] ); } $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color ); } else { $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color ); } } } # mark off the used space $self->{'curr_x_max'} -= $width; # and return return 1; } ## @fn private _draw_left_legend # put the legend on the left of the chart sub _draw_left_legend { my $self = shift; my $data = $self->{'dataref'}; my @labels = @{ $data->[0] }; my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush ); my $font = $self->{'legend_font'}; my $max_label_len = 1; my $l1 = 0; my $l2 = 0; my ( $dataset_sum, $label ); # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # get the miscellaneous color $misccolor = $self->_color_role_to_index('misc'); #find out what the sum of all datapoits is, needed for the Labels with percent $dataset_sum = 0; for my $j ( 0 .. $self->{'num_datapoints'} ) { if ( defined $data->[1][$j] ) { $dataset_sum += $data->[1][$j]; } } # find out how who wide the largest label text is foreach (@labels) { if ( length($_) > $l1 ) { $l1 = length($_); } } for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ ) { if ( length( $data->[1][$i] ) > $l2 ) { $l2 = length( $data->[1][$i] ); } } if ( $self->{'legend_label_values'} =~ /^value$/i ) { $max_label_len = $l1 + $l2 + 1; } elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) { $max_label_len = $l1 + 7; } elsif ( $self->{'legend_label_values'} =~ /^both$/i ) { $max_label_len = $l1 + $l2 + 9; } else { $max_label_len = $l1; } # find out how wide the largest label is $width = ( 2 * $self->{'text_space'} ) + ( $max_label_len * $w ) + $self->{'legend_example_size'} + ( 2 * $self->{'legend_space'} ); # get some base x-y coordinates $x1 = $self->{'curr_x_min'}; $x2 = $self->{'curr_x_min'} + $width; $y1 = $self->{'curr_y_min'} + $self->{'graph_border'}; $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'} + ( $self->{'num_datapoints'} * ( $h + $self->{'text_space'} ) ) + ( 2 * $self->{'legend_space'} ); # box the legend off $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor ); # leave that nice space inside the legend box $x1 += $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; # now draw the actual legend for ( 0 .. $#labels ) { # get the color $color = $self->_color_role_to_index( 'dataset' . $_ ); # find the x-y coords $x2 = $x1; $x3 = $x2 + $self->{'legend_example_size'}; $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2; # do the line first $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color ); # reset the brush for points $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $_ } ); $self->{'gd_obj'}->setBrush($brush); # draw the point $self->{'gd_obj'}->line( int( ( $x3 + $x2 ) / 2 ), $y2, int( ( $x3 + $x2 ) / 2 ), $y2, gdBrushed ); # now the label $x2 = $x3 + ( 2 * $self->{'text_space'} ); $y2 -= $h / 2; if ( $self->{'legend_label_values'} =~ /^value$/i ) { $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_] . ' ' . $data->[1][$_], $color ); } elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) { $label = sprintf( "%s %4.2f%%", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100 ); $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color ); } elsif ( $self->{'legend_label_values'} =~ /^both$/i ) { if ( $data->[1][$_] =~ /\./ ) { $label = sprintf( "%s %4.2f%% %.2f", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] ); } else { $label = sprintf( "%s %4.2f%% %d", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] ); } $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color ); } else { $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color ); } } # mark off the used space $self->{'curr_x_min'} += $width; # and return return 1; } ## @fn private _draw_bottom_legend # put the legend on the bottom of the chart sub _draw_bottom_legend { my $self = shift; my $data = $self->{'dataref'}; my @labels = @{ $data->[0] }; my ( $x1, $y1, $x2, $x3, $y2, $empty_width, $max_label_width, $cols, $rows, $color, $brush ); my ( $col_width, $row_height, $r, $c, $index, $x, $y, $w, $h ); my $font = $self->{'legend_font'}; my $max_label_len; my $l1 = 0; my $l2 = 0; my ( $dataset_sum, $j ); my $label; my $text_color = $self->_color_role_to_index( 'text' ); # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); # find the base x values $x1 = $self->{'curr_x_min'} + $self->{'graph_border'}; # + ($self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width) # + $self->{'tick_len'} + (3 * $self->{'text_space'}); $x2 = $self->{'curr_x_max'} - $self->{'graph_border'}; if ( $self->{'y_label'} ) { $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'}; } if ( $self->{'y_label2'} ) { $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'}; } #find out what the sum of all datapoits is, needed for the Labels with percent $dataset_sum = 0; for $j ( 0 .. $self->{'num_datapoints'} ) { if ( defined $data->[1][$j] ) { $dataset_sum += $data->[1][$j]; } } # find out how who wide the largest label text is, especially look what kind of # label is needed foreach (@labels) { if ( length($_) > $l1 ) { $l1 = length($_); } } for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ ) { if ( length( $data->[1][$i] ) > $l2 ) { $l2 = length( $data->[1][$i] ); } } if ( $self->{'legend_label_values'} =~ /^value$/i ) { $max_label_len = $l1 + $l2 + 1; } elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) { $max_label_len = $l1 + 7; } elsif ( $self->{'legend_label_values'} =~ /^both$/i ) { $max_label_len = $l1 + $l2 + 9; } else { $max_label_len = $l1; } # figure out how wide the columns need to be, and how many we # can fit in the space available $empty_width = ( $x2 - $x1 ) - ( 2 * $self->{'legend_space'} ); $max_label_width = $max_label_len * $w #$self->{'max_legend_label'} * $w + ( 4 * $self->{'text_space'} ) + $self->{'legend_example_size'}; $cols = int( $empty_width / $max_label_width ); unless ($cols) { $cols = 1; } $col_width = $empty_width / $cols; # figure out how many rows we need, remember how tall they are $rows = int( $self->{'num_datapoints'} / $cols ); unless ( ( $self->{'num_datapoints'} % $cols ) == 0 ) { $rows++; } unless ($rows) { $rows = 1; } $row_height = $h + $self->{'text_space'}; # box the legend off $y1 = $self->{'curr_y_max'} - $self->{'text_space'} - ( $rows * $row_height ) - ( 2 * $self->{'legend_space'} ); $y2 = $self->{'curr_y_max'}; $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $self->_color_role_to_index('misc') ); $x1 += $self->{'legend_space'} + $self->{'text_space'}; $x2 -= $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; $y2 -= $self->{'legend_space'} + $self->{'text_space'}; # draw in the actual legend for $r ( 0 .. $rows - 1 ) { for $c ( 0 .. $cols - 1 ) { $index = ( $r * $cols ) + $c; # find the index in the label array if ( $labels[$index] ) { # get the color $color = $self->_color_role_to_index( 'dataset' . $index ); # get the x-y coordinate for the start of the example line $x = $x1 + ( $col_width * $c ); $y = $y1 + ( $row_height * $r ) + $h / 2; # now draw the example line $self->{'gd_obj'}->line( $x, $y, $x + $self->{'legend_example_size'}, $y, $color ); # reset the brush for points $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $index } ); $self->{'gd_obj'}->setBrush($brush); # draw the point $x3 = int( $x + $self->{'legend_example_size'} / 2 ); $self->{'gd_obj'}->line( $x3, $y, $x3, $y, gdBrushed ); # adjust the x-y coordinates for the start of the label $x += $self->{'legend_example_size'} + ( 2 * $self->{'text_space'} ); $y = $y1 + ( $row_height * $r ); # now draw the label if ( $self->{'legend_label_values'} =~ /^value$/i ) { $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index] . ' ' . $data->[1][$index], $text_color ); #$self->{'gd_obj'}->stringTTF($color, FONT, 10, 0, $x, $y+10, $labels[$index].' '.$data->[1][$index]); ############ } elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) { $label = sprintf( "%s %4.2f%%", $labels[$index], $data->[1][$index] / ( $dataset_sum || 1 ) * 100 ); $self->{'gd_obj'}->string( $font, $x, $y, $label, $color ); } elsif ( $self->{'legend_label_values'} =~ /^both$/i ) { if ( $data->[1][$index] =~ /\./ ) { $label = sprintf( "%s %4.2f%% %.2f", $labels[$index], $data->[1][$index] / ( $dataset_sum || 1 ) * 100, $data->[1][$index] ); } else { $label = sprintf( "%s %4.2f%% %d", $labels[$index], $data->[1][$index] / ( $dataset_sum || 1 ) * 100, $data->[1][$index] ); } $self->{'gd_obj'}->string( $font, $x, $y, $label, $text_color ); ### # $self->{'gd_obj'}->stringTTF($color, FONT, 10, 0, $x, $y, $label); } else { $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index], $text_color ); } } } } # mark off the space used $self->{'curr_y_max'} -= ( $rows * $row_height ) + $self->{'text_space'} + ( 2 * $self->{'legend_space'} ); # now return return 1; } ## @fn private _draw_top_legend # put the legend on top of the chart sub _draw_top_legend { my $self = shift; my $data = $self->{'dataref'}; my ($max_label_len); my $l1 = 0; my $l2 = 0; my @labels = @{ $data->[0] }; my ( $x1, $y1, $x2, $x3, $y2, $empty_width, $max_label_width, $cols, $rows, $color, $brush ); my ( $col_width, $row_height, $r, $c, $index, $x, $y, $w, $h, $dataset_sum, $label ); my $font = $self->{'legend_font'}; # make sure we're using a real font unless ( ( ref($font) ) eq 'GD::Font' ) { croak "The font you specified isn\'t a GD Font object"; } # get the size of the font ( $h, $w ) = ( $font->height, $font->width ); #find out what the sum of all datapoits is, needed for the Labels with percent $dataset_sum = 0; for my $j ( 0 .. $self->{'num_datapoints'} ) { if ( defined $data->[1][$j] ) { $dataset_sum += $data->[1][$j]; } } # get some base x coordinates $x1 = $self->{'curr_x_min'} + $self->{'graph_border'}; # + $self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width # + $self->{'tick_len'} + (3 * $self->{'text_space'}); $x2 = $self->{'curr_x_max'} - $self->{'graph_border'}; if ( $self->{'y_label'} ) { $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'}; } if ( $self->{'y_label2'} ) { $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'}; } # find out how who wide the largest label text is foreach (@labels) { if ( length($_) > $l1 ) { $l1 = length($_); } } for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ ) { if ( length( $data->[1][$i] ) > $l2 ) { $l2 = length( $data->[1][$i] ); } } if ( $self->{'legend_label_values'} =~ /^value$/i ) { $max_label_len = $l1 + $l2 + 1; } elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) { $max_label_len = $l1 + 7; } elsif ( $self->{'legend_label_values'} =~ /^both$/i ) { $max_label_len = $l1 + $l2 + 9; } else { $max_label_len = $l1; } # figure out how wide the columns can be, and how many will fit $empty_width = ( $x2 - $x1 ) - ( 2 * $self->{'legend_space'} ); $max_label_width = ( 4 * $self->{'text_space'} ) + $max_label_len * $w + $self->{'legend_example_size'}; $cols = int( $empty_width / $max_label_width ); unless ($cols) { $cols = 1; } $col_width = $empty_width / $cols; # figure out how many rows we need and remember how tall they are $rows = int( $self->{'num_datapoints'} / $cols ); unless ( ( $self->{'num_datapoints'} % $cols ) == 0 ) { $rows++; } unless ($rows) { $rows = 1; } $row_height = $h + $self->{'text_space'}; # box the legend off $y1 = $self->{'curr_y_min'}; $y2 = $self->{'curr_y_min'} + $self->{'text_space'} + ( $rows * $row_height ) + ( 2 * $self->{'legend_space'} ); $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $self->_color_role_to_index('misc') ); # leave some space inside the legend $x1 += $self->{'legend_space'} + $self->{'text_space'}; $x2 -= $self->{'legend_space'}; $y1 += $self->{'legend_space'} + $self->{'text_space'}; $y2 -= $self->{'legend_space'} + $self->{'text_space'}; # draw in the actual legend for $r ( 0 .. $rows - 1 ) { for $c ( 0 .. $cols - 1 ) { $index = ( $r * $cols ) + $c; # find the index in the label array if ( $labels[$index] ) { # get the color $color = $self->_color_role_to_index( 'dataset' . $index ); # find the x-y coords $x = $x1 + ( $col_width * $c ); $y = $y1 + ( $row_height * $r ) + $h / 2; # draw the line first $self->{'gd_obj'}->line( $x, $y, $x + $self->{'legend_example_size'}, $y, $color ); # reset the brush for points $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $index } ); $self->{'gd_obj'}->setBrush($brush); # draw the point $x3 = int( $x + $self->{'legend_example_size'} / 2 ); $self->{'gd_obj'}->line( $x3, $y, $x3, $y, gdBrushed ); # now the label $x += $self->{'legend_example_size'} + ( 2 * $self->{'text_space'} ); $y -= $h / 2; if ( $self->{'legend_label_values'} =~ /^value$/i ) { $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index] . ' ' . $data->[1][$index], $color ); } elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) { $label = sprintf( "%s %4.2f%%", $labels[$index], $data->[1][$index] / ( $dataset_sum || 1 ) * 100 ); $self->{'gd_obj'}->string( $font, $x, $y, $label, $color ); } elsif ( $self->{'legend_label_values'} =~ /^both$/i ) { if ( $data->[1][$index] =~ /\./ ) { $label = sprintf( "%s %4.2f%% %.2f", $labels[$index], $data->[1][$index] / ( $dataset_sum || 1 ) * 100, $data->[1][$index] ); } else { $label = sprintf( "%s %4.2f%% %d", $labels[$index], $data->[1][$index] / ( $dataset_sum || 1 ) * 100, $data->[1][$index] ); } $self->{'gd_obj'}->string( $font, $x, $y, $label, $color ); } else { $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index], $color ); } } } } # mark off the space used $self->{'curr_y_min'} += ( $rows * $row_height ) + $self->{'text_space'} + 2 * $self->{'legend_space'}; # now return return 1; } ## @fn private _draw_x_ticks # Override the ticks methods for the pie charts.\n # Here: do nothing sub _draw_x_ticks { my $self = shift; return; } ## @fn private _draw_y_ticks # Override the ticks methods for the pie charts.\n sub _draw_y_ticks { my $self = shift; return; } ## @fn private _find_y_scale # Override the find_y_scale methods for the pie charts.\n # Here: do nothing sub _find_y_scale { my $self = shift; return; } ## be a good module and return 1 1; Chart-v2.403.9/lib/Chart/HorizontalBars.pm0000644000175000017500000005536214341172251020111 0ustar herbertherbert use v5.12; package Chart::HorizontalBars; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Chart::Base; use GD; use Carp; #>>>>>>>>>>>>>>>>>>>>>>>>>># # public methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<# #>>>>>>>>>>>>>>>>>>>>>>>>>>># # private methods go here # #<<<<<<<<<<<<<<<<<<<<<<<<<<<# ## @method private int _draw_x_ticks() # draw the x-ticks and their labels # Overwrites this function of Chart::Base # @return status # sub _draw_x_ticks { my $self = shift; my $data = $self->{'dataref'}; my $font = $self->{'tick_label_font'}; my $textcolor = $self->_color_role_to_index('text'); my $misccolor = $self->_color_role_to_index('misc'); my ( $h, $w, $x1, $y1, $y2, $x2, $delta, $width, $label ); my @labels = @{ $self->{'y_tick_labels'} }; $self->{'grid_data'}->{'x'} = []; #make sure we have a real font unless ( ( ref $font ) eq 'GD::Font' ) { croak "The tick label font you specified isn't a GD font object"; } #get height and width of the font ( $h, $w ) = ( $font->height, $font->width ); #get the right x-value and width if ( $self->{'y_axes'} =~ /^right$/i ) { $x1 = $self->{'curr_x_min'}; $width = $self->{'curr_x_max'} - $x1 - $self->{'tick_len'} - $self->{'text_space'} - $w * $self->{'x_tick_label_length'}; } elsif ( $self->{'y_axes'} =~ /^both$/i ) { $x1 = $self->{'curr_x_min'} + $self->{'text_space'} + $w * $self->{'x_tick_label_length'} + $self->{'tick_len'}; $width = $self->{'curr_x_max'} - $x1 - $self->{'tick_len'} - $self->{'text_space'} - $w * $self->{'x_tick_label_length'}; } else { $x1 = $self->{'curr_x_min'} + $self->{'text_space'} + $w * $self->{'x_tick_label_length'} + $self->{'tick_len'}; $width = $self->{'curr_x_max'} - $x1; } #get the delta value $delta = $width / ( $self->{'y_ticks'} - 1 ); #draw the labels $y2 = $y1; if ( $self->{'x_ticks'} =~ /^normal/i ) { # just normal ticks # get the point for updating later $y1 = $self->{'curr_y_max'} - 2 * $self->{'text_space'} - $h - $self->{'tick_len'}; #get the start point $y2 = $y1 + $self->{'tick_len'} + $self->{'text_space'}; for ( 0 .. $#labels ) { $label = $self->{'y_tick_labels'}[$_]; $x2 = $x1 + ( $delta * $_ ) - ( $w * length($label) / 2 ); $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor ); } } elsif ( $self->{'x_ticks'} =~ /^staggered/i ) { # staggered ticks # get the point for updating later $y1 = $self->{'curr_y_max'} - 3 * $self->{'text_space'} - 2 * $h - $self->{'tick_len'}; for ( 0 .. $#labels ) { $label = $self->{'y_tick_labels'}[$_]; $x2 = $x1 + ( $delta * $_ ) - ( $w * length($label) / 2 ); unless ( $_ % 2 ) { $y2 = $y1 + $self->{'text_space'} + $self->{'tick_len'}; $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor ); } else { $y2 = $y1 + $h + 2 * $self->{'text_space'} + $self->{'tick_len'}; $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $textcolor ); } } } elsif ( $self->{'x_ticks'} =~ /^vertical/i ) { # vertical ticks # get the point for updating later $y1 = $self->{'curr_y_max'} - 2 * $self->{'text_space'} - $w * $self->{'y_tick_label_length'} - $self->{'tick_len'}; for ( 0 .. $#labels ) { $label = $self->{'y_tick_labels'}[$_]; # get the start point $y2 = $y1 + $self->{'tick_len'} + $w * length($label) + $self->{'text_space'}; $x2 = $x1 + ( $delta * $_ ) - ( $h / 2 ); $self->{'gd_obj'}->stringUp( $font, $x2, $y2, $label, $textcolor ); } } else { carp "I don't understand the type of x-ticks you specified" } # update the curr x and y max value $self->{'curr_y_max'} = $y1; $self->{'curr_x_max'} = $x1 + $width; # draw the ticks $y1 = $self->{'curr_y_max'}; $y2 = $self->{'curr_y_max'} + $self->{'tick_len'}; for ( 0 .. $#labels ) { $x2 = $x1 + ( $delta * $_ ); $self->{'gd_obj'}->line( $x2, $y1, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'x'}->[$_] = $x2; } } return 1; } ## @fn private int _draw_y_ticks() # draw the y-ticks and their labels # Overwrites this function of Chart::Base # @return status sub _draw_y_ticks { my $self = shift; my $side = shift || 'left'; my $data = $self->{'dataref'}; my $font = $self->{'tick_label_font'}; my $textcolor = $self->_color_role_to_index('text'); my $misccolor = $self->_color_role_to_index('misc'); my ( $h, $w, $x1, $x2, $y1, $y2 ); my ( $width, $height, $delta ); $self->{'grid_data'}->{'y'} = []; #make sure that is a real font unless ( ( ref $font ) eq 'GD::Font' ) { croak "The tick label font isn't a GD Font object!"; } #get the size of the font ( $h, $w ) = ( $font->height, $font->width ); #figure out, where to draw if ( $side =~ /^right$/i ) { #get the right startposition $x1 = $self->{'curr_x_max'}; $y1 = $self->{'curr_y_max'} - $h / 2; #get the delta values $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta = ($height) / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); $y1 -= ( $delta / 2 ); #look if skipping is desired if ( !defined( $self->{'skip_y_ticks'} ) ) { $self->{'skip_y_ticks'} = 1; } #draw the labels for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_y_ticks'} ) ) { $y2 = $y1 - ($delta) * ( $_ * $self->{'skip_y_ticks'} ); $x2 = $x1 + $self->{'tick_len'} + $self->{'text_space'}; $self->{'gd_obj'} ->string( $font, $x2, $y2, $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ), $textcolor ); } #draw the ticks $x1 = $self->{'curr_x_max'}; $x2 = $self->{'curr_x_max'} + $self->{'tick_len'}; $y1 += $h / 2; for ( 0 .. ( $self->{'num_datapoints'} - 1 / $self->{'skip_y_ticks'} ) ) { $y2 = $y1 - ( $delta * $_ ); $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'y'}->[$_] = $y2; } } } elsif ( $side =~ /^both$/i ) { #get the right startposition $x1 = $self->{'curr_x_max'}; $y1 = $self->{'curr_y_max'} - $h / 2; #get the delta values $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta = ($height) / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); $y1 -= ( $delta / 2 ); #look if skipping is desired if ( !defined( $self->{'skip_y_ticks'} ) ) { $self->{'skip_y_ticks'} = 1; } #first draw the right labels for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_y_ticks'} ) ) { $y2 = $y1 - ($delta) * ( $_ * $self->{'skip_y_ticks'} ); $x2 = $x1 + $self->{'tick_len'} + $self->{'text_space'}; $self->{'gd_obj'} ->string( $font, $x2, $y2, $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ), $textcolor ); } #then draw the right ticks $x1 = $self->{'curr_x_max'}; $x2 = $self->{'curr_x_max'} + $self->{'tick_len'}; $y1 += $h / 2; for ( 0 .. ( $self->{'num_datapoints'} - 1 / $self->{'skip_y_ticks'} ) ) { $y2 = $y1 - ( $delta * $_ ); $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'y'}->[$_] = $y2; } } #get the right startposition $x1 = $self->{'curr_x_min'}; $y1 = $self->{'curr_y_max'} - $h / 2; #get the delta values for positioning $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta = ($height) / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); $y1 -= ( $delta / 2 ); #then draw the left labels for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_y_ticks'} ) ) { $y2 = $y1 - ($delta) * ( $_ * $self->{'skip_y_ticks'} ); $x2 = $x1 - $w * length( $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ) ) #print the Labels right-sided + $w * $self->{'x_tick_label_length'}; $self->{'gd_obj'} ->string( $font, $x2, $y2, $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ), $textcolor ); } #update the curr_x_min val $self->{'curr_x_min'} = $x1 + $self->{'text_space'} + $w * $self->{'x_tick_label_length'} + $self->{'tick_len'}; #finally draw the left ticks $x1 = $self->{'curr_x_min'}; $x2 = $self->{'curr_x_min'} - $self->{'tick_len'}; $y1 += $h / 2; for ( 0 .. ( $self->{'num_datapoints'} - 1 / $self->{'skip_y_ticks'} ) ) { $y2 = $y1 - ( $delta * $_ ); $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'y'}->[$_] = $y2; } } } else { #get the right startposition $x1 = $self->{'curr_x_min'}; $y1 = $self->{'curr_y_max'} - $h / 2; #get the delta values for positioning $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta = ($height) / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); $y1 -= ( $delta / 2 ); if ( !defined( $self->{'skip_y_ticks'} ) ) { $self->{'skip_y_ticks'} = 1; } #draw the labels for ( 0 .. int( ( $self->{'num_datapoints'} - 1 ) / $self->{'skip_y_ticks'} ) ) { $y2 = $y1 - ($delta) * ( $_ * $self->{'skip_y_ticks'} ); $x2 = $x1 - $w * length( $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ) ) #print the Labels right-sided + $w * $self->{'x_tick_label_length'}; $self->{'gd_obj'} ->string( $font, $x2, $y2, $self->{f_y_tick}->( $data->[0][ $_ * $self->{'skip_y_ticks'} ] ), $textcolor ); } #update the curr_x_min val $self->{'curr_x_min'} = $x1 + $self->{'text_space'} + $w * $self->{'x_tick_label_length'} + $self->{'tick_len'}; #draw the ticks $x1 = $self->{'curr_x_min'}; $x2 = $self->{'curr_x_min'} - $self->{'tick_len'}; $y1 += $h / 2; for ( 0 .. ( $self->{'num_datapoints'} - 1 / $self->{'skip_y_ticks'} ) ) { $y2 = $y1 - ( $delta * $_ ); $self->{'gd_obj'}->line( $x1, $y2, $x2, $y2, $misccolor ); if ( $self->true( $self->{'grid_lines'} ) or $self->true( $self->{'x_grid_lines'} ) ) { $self->{'grid_data'}->{'y'}->[$_] = $y2; } } } #now return return 1; } ## @fn private int _find_y_scale() # find good values for the minimum and maximum y-value on the chart # overwrite the find_y_scale function, only to get the right f_x_ticks !!!!! # @return status sub _find_y_scale { my $self = shift; # Predeclare vars. my ( $d_min, $d_max ); # Dataset min & max. my ( $p_min, $p_max ); # Plot min & max. my ( $tickInterval, $tickCount ); my @tickLabels; # List of labels for each tick. my $maxtickLabelLen = 0; # The length of the longest tick label. # Find the datatset minimum and maximum. ( $d_min, $d_max ) = $self->_find_y_range(); # Force the inclusion of zero if the user has requested it. if ( $self->true( $self->{'include_zero'} ) ) { if ( ( $d_min * $d_max ) > 0 ) # If both are non zero and of the same sign. { if ( $d_min > 0 ) # If the whole scale is positive. { $d_min = 0; } else # The scale is entirely negative. { $d_max = 0; } } } if ( $self->{'integer_ticks_only'} =~ /^\d$/ ) { if ( $self->{'integer_ticks_only'} == 1 ) { $self->{'integer_ticks_only'} = 'true'; } else { $self->{'integer_ticks_only'} = 'false'; } } if ( $self->true( $self->{'integer_ticks_only'} ) ) { # Allow the dataset range to be overidden by the user. # f_min/max are booleans which indicate that the min & max should not be modified. my $f_min = defined $self->{'min_val'}; $d_min = $self->{'min_val'} if $f_min; my $f_max = defined $self->{'max_val'}; $d_max = $self->{'max_val'} if $f_max; # Assert against the min is larger than the max. if ( $d_min > $d_max ) { croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)"; } # The user asked for integer ticks, force the limits to integers. # & work out the range directly. $p_min = $self->_round2Tick( $d_min, 1, -1 ); $p_max = $self->_round2Tick( $d_max, 1, 1 ); my $skip = $self->{skip_int_ticks}; $tickInterval = $skip; $tickCount = ( $p_max - $p_min ) / $skip + 1; # Now sort out an array of tick labels. for ( my $labelNum = $p_min ; $labelNum <= $p_max ; $labelNum += $tickInterval ) { my $labelText; if ( defined $self->{f_x_tick} ) { # Is _default_f_tick function used? if ( $self->{f_x_tick} == \&_default_f_tick ) { $labelText = sprintf( "%d", $labelNum ); } else { $labelText = $self->{f_x_tick}->($labelNum); } } else { $labelText = sprintf( "%d", $labelNum ); } #print "labelText = $labelText\n"; push @tickLabels, $labelText; $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText; } } else { # Allow the dataset range to be overidden by the user. # f_min/max are booleans which indicate that the min & max should not be modified. my $f_min = defined $self->{'min_val'}; $d_min = $self->{'min_val'} if $f_min; my $f_max = defined $self->{'max_val'}; $d_max = $self->{'max_val'} if $f_max; # Assert against the min is larger than the max. if ( $d_min > $d_max ) { croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)"; } # Calculate the width of the dataset. (posibly modified by the user) my $d_width = $d_max - $d_min; # If the width of the range is zero, forcibly widen it # (to avoid division by zero errors elsewhere in the code). if ( 0 == $d_width ) { $d_min--; $d_max++; $d_width = 2; } # Descale the range by converting the dataset width into # a floating point exponent & mantisa pair. my ( $rangeExponent, $rangeMantisa ) = $self->_sepFP($d_width); my $rangeMuliplier = 10**$rangeExponent; # Find what tick # to use & how many ticks to plot, # round the plot min & max to suatable round numbers. ( $tickInterval, $tickCount, $p_min, $p_max ) = $self->_calcTickInterval( $d_min / $rangeMuliplier, $d_max / $rangeMuliplier, $f_min, $f_max, $self->{'min_y_ticks'}, $self->{'max_y_ticks'} ); # Restore the tickInterval etc to the correct scale $_ *= $rangeMuliplier foreach ( $tickInterval, $p_min, $p_max ); #get teh precision for the labels my $precision = $self->{'precision'}; # Now sort out an array of tick labels. for ( my $labelNum = $p_min ; $labelNum <= $p_max ; $labelNum += $tickInterval ) { my $labelText; if ( defined $self->{f_x_tick} ) { # Is _default_f_tick function used? if ( $self->{f_x_tick} == \&_default_f_tick ) { $labelText = sprintf( "%." . $precision . "f", $labelNum ); } else { $labelText = $self->{f_x_tick}->($labelNum); } } else { $labelText = sprintf( "%." . $precision . "f", $labelNum ); } $labelText = 0 if abs $labelText < (0.1 ** $precision); #print "labelText = $labelText\n"; push @tickLabels, $labelText; $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText; } } # Store the calculated data. $self->{'min_val'} = $p_min; $self->{'max_val'} = $p_max; $self->{'y_ticks'} = $tickCount; $self->{'y_tick_labels'} = \@tickLabels; $self->{'y_tick_label_length'} = $maxtickLabelLen; # and return. return 1; } ## @fn private _draw_data # finally get around to plotting the data for (horizontal) bars sub _draw_data { my $self = shift; my $data = $self->{'dataref'}; my $misccolor = $self->_color_role_to_index('misc'); my ( $x1, $x2, $x3, $y1, $y2, $y3 ); my $cut = 0; my ( $width, $height, $delta1, $delta2, $map, $mod, $pink ); my ( $i, $j, $color ); # init the imagemap data field if they wanted it if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'} = []; } # find both delta values ($delta1 for stepping between different # datapoint names, $delta2 for setpping between datasets for that # point) and the mapping constant $width = $self->{'curr_x_max'} - $self->{'curr_x_min'}; $height = $self->{'curr_y_max'} - $self->{'curr_y_min'}; $delta1 = $height / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); $map = $width / ( $self->{'max_val'} - $self->{'min_val'} ); if ( $self->true( $self->{'spaced_bars'} ) ) { $delta2 = $delta1 / ( $self->{'num_datasets'} + 2 ); } else { $delta2 = $delta1 / $self->{'num_datasets'}; } # get the base x-y values $y1 = $self->{'curr_y_max'} - $delta2; if ( $self->{'min_val'} >= 0 ) { $x1 = $self->{'curr_x_min'}; $mod = $self->{'min_val'}; } elsif ( $self->{'max_val'} <= 0 ){ $x1 = $self->{'curr_x_max'}; $mod = $self->{'max_val'}; } else { $x1 = $self->{'curr_x_min'} + abs( $map * $self->{'min_val'} ); $mod = 0; $self->{'gd_obj'}->line( $x1, $self->{'curr_y_min'}, $x1, $self->{'curr_y_max'}, $misccolor ); } # draw the bars for $i ( 1 .. $self->{'num_datasets'} ) { # get the color for this dataset $color = $self->_color_role_to_index( 'dataset' . ( $i - 1 ) ); # draw every bar for this dataset for $j ( 0 .. $self->{'num_datapoints'} ) { # don't try to draw anything if there's no data if ( defined( $data->[$i][$j] ) ) { # find the bounds of the rectangle if ( $self->true( $self->{'spaced_bars'} ) ) { $y2 = $y1 - ( $j * $delta1 ) - ( $self->{'num_datasets'} * $delta2 ) + ( ( $i - 1 ) * $delta2 ); } else { $y2 = $y1 - ( $j * $delta1 ) - ( $self->{'num_datasets'} * $delta2 ) + ( ($i) * $delta2 ); } $x2 = $x1; $y3 = $y2 + $delta2; #cut the bars off, if needed if ( $data->[$i][$j] > $self->{'max_val'} ) { $x3 = $x1 + ( ( $self->{'max_val'} - $mod ) * $map ) - 1; $cut = 1; } elsif ( $data->[$i][$j] < $self->{'min_val'} ) { $x3 = $x1 + ( ( $self->{'min_val'} - $mod ) * $map ) + 1; $cut = 1; } else { $x3 = $x1 + ( ( $data->[$i][$j] - $mod ) * $map ); $cut = 0; } # draw the bar ## y2 and y3 are reversed in some cases because GD's fill ## algorithm is lame if ( $data->[$i][$j] < 0 ) { $self->{'gd_obj'}->filledRectangle( $x3, $y2, $x2, $y3, $color ); if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ $x3, $y2, $x2, $y3 ]; } $self->{'gd_obj'}->filledRectangle( $x3, $y2, $x2, $y3, $color ); if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ $x3, $y2, $x2, $y3 ]; } } else { $self->{'gd_obj'}->filledRectangle( $x2, $y2, $x3, $y3, $color ); if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ $x2, $y2, $x3, $y3 ]; } } # now outline it. outline red if the bar had been cut off unless ($cut) { $self->{'gd_obj'}->rectangle( $x2, $y3, $x3, $y2, $misccolor ); } else { $pink = $self->{'gd_obj'}->colorAllocate( 255, 0, 255 ); $self->{'gd_obj'}->rectangle( $x2, $y3, $x3, $y2, $pink ); } } else { if ( $self->true( $self->{'imagemap'} ) ) { $self->{'imagemap_data'}->[$i][$j] = [ undef(), undef(), undef(), undef() ]; } } } } # and finaly box it off $self->{'gd_obj'} ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor ); return; } 1; # be a good module and return 1 Chart-v2.403.9/lib/Chart/Mountain.pm0000644000175000017500000002071214341172251016731 0ustar herbertherbert # Some Mountain chart details: # # The effective y data value for a given x point and dataset # is the sum of the actual y data values of that dataset and # all datasets "below" it (i.e., with higher dataset indexes). # # If the y data value in any dataset is undef or negative for # a given x, then all datasets are treated as missing for that x. # # The y minimum is always forced to zero. # # To avoid a dataset area "cutting into" the area of the dataset below # it, the y pixel for each dataset point will never be below the y pixel for # the same point in the dataset below the dataset. # This probably should have a custom legend method, because each # dataset is identified by the fill color (and optional pattern) # of its area, not just a line color. So the legend shou a square # of the color and pattern for each dataset. use v5.12; package Chart::Mountain; our @ISA = qw(Chart::Base); our $VERSION = 'v2.403.9'; use Chart::Base; use GD; use Carp; #===================# # private methods # #===================# ## @fn private array _find_y_range() # Find minimum and maximum value of y data sets. # # @return ( min, max, flag_all_integers ) sub _find_y_range { my $self = shift; # This finds the maximum point-sum over all x points, # where the point-sum is the sum of the dataset values at that point. # If the y value in any dataset is undef for a given x, then all datasets # are treated as missing for that x. my $data = $self->{'dataref'}; my $max = undef; for my $i ( 0 .. $#{ $data->[0] } ) { my $y_sum = $data->[1]->[$i]; if ( defined $y_sum && $y_sum >= 0 ) { for my $dataset ( @$data[ 2 .. $#$data ] ) { # order not important my $datum = $dataset->[$i]; if ( defined $datum && $datum >= 0 ) { $y_sum += $datum; } else { # undef or negative, treat all at same x as missing. $y_sum = undef; last; } } } if ( defined $y_sum ) { $max = $y_sum unless defined $max && $y_sum <= $max; } } ( 0, $max ); } ## @fn private _draw_data # draw the data sub _draw_data { my $self = shift; my $data = $self->{'dataref'}; my @patterns = @{ $self->{'patterns'} || [] }; # Calculate array of x pixel positions (@x). my $x_step = ( $self->{'curr_x_max'} - $self->{'curr_x_min'} ) / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 ); my $x_min = $self->{'curr_x_min'} + $x_step / 2; my $x_max = $self->{'curr_x_max'} - $x_step / 2; my @x = map { $_ * $x_step + $x_min } 0 .. $self->{'num_datapoints'} - 1; my ( $t_x_min, $t_x_max, $t_y_min, $t_y_max, $abs_x_max, $abs_y_max ); my $repair_top_flag = 0; # Calculate array of y pixel positions for upper boundary each dataset (@y). my $map = ( $self->{'max_val'} ) ? ( $self->{'curr_y_max'} - $self->{'curr_y_min'} ) / $self->{'max_val'} : ( $self->{'curr_y_max'} - $self->{'curr_y_min'} ) / 10; my $y_max = $self->{'curr_y_max'}; # max pixel point (lower y values) my @y; for my $j ( 0 .. $#{ $data->[0] } ) { my $sum = 0; for my $i ( reverse 1 .. $#{$data} ) { # bottom to top of chart my $datum = $data->[$i][$j]; #set the repair flag, if the datum is out of the borders of the chart if ( defined $datum && $datum > $self->{'max_val'} ) { $repair_top_flag = 1; } if ( defined $datum && $datum >= 0 ) { $sum += $datum; $y[ $i - 1 ][$j] = $y_max - $map * $sum; } else { # missing value, force all to undefined foreach my $k ( 1 .. $#{$data} ) { $y[ $k - 1 ][$j] = undef } last; } } } # Find first and last x where y is defined in the bottom dataset. my $x_begin = 0; my $x_end = $self->{'num_datapoints'} - 1; while ( $x_begin <= $x_end && !defined $y[-1]->[$x_begin] ) { $x_begin++ } while ( $x_begin <= $x_end && !defined $y[-1]->[$x_end] ) { $x_end-- } if ( $x_begin > $x_end ) { croak "Internal error: x_begin > x_end ($x_begin > $x_end)"; } # For each dataset, generate a polygon for the dataset's area of the chart, # and fill the polygon with the dataset's color/pattern. my $poly = GD::Polygon->new; $poly->addPt( $x[$x_end], $y_max ); # right end of x axis $poly->addPt( $x[$x_begin], $y_max ); # left end of x axis (right-to-left) for my $dataset ( reverse 0 .. @y - 1 ) { my $y_ref = $y[$dataset]; # Append points for this dataset to polygon, direction depends on $dataset % 2. my $last_vertex_count = $poly->length; if ( ( @y - 1 - $dataset ) % 2 ) { # right-to-left for ( reverse $x_begin .. $x_end ) { $poly->addPt( $x[$_], $y_ref->[$_] ) if defined $y_ref->[$_]; } } else { # left-to-right for ( $x_begin .. $x_end ) { $poly->addPt( $x[$_], $y_ref->[$_] ) if defined $y_ref->[$_]; } } # draw the polygon my $color = $self->_color_role_to_index( 'dataset' . $dataset ); if ( $patterns[$dataset] ) { $self->{'gd_obj'}->filledPolygon( $poly, $color ) if $patterns[$dataset]->transparent >= 0; $self->{'gd_obj'}->setTile( $patterns[$dataset] ); $self->{'gd_obj'}->filledPolygon( $poly, gdTiled ); } else { $self->{'gd_obj'}->filledPolygon( $poly, $color ); } # delete previous dataset's points from the polygon, update $last_vertex_count. unless ( $dataset == 0 ) { # don't bother do delete points after last area while ($last_vertex_count) { $poly->deletePt(0); $last_vertex_count-- } } } # Enclose the plots $self->{'gd_obj'}->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $self->_color_role_to_index('misc') ); #get the width and the heigth of the complete picture ( $abs_x_max, $abs_y_max ) = $self->{'gd_obj'}->getBounds(); #repair the chart, if the lines are out of the borders of the chart if ($repair_top_flag) { #overwrite the ugly mistakes $self->{'gd_obj'}->filledRectangle( $self->{'curr_x_min'}, 0, $self->{'curr_x_max'}, $self->{'curr_y_min'} - 1, $self->_color_role_to_index('background') ); #save the actual x and y values $t_x_min = $self->{'curr_x_min'}; $t_x_max = $self->{'curr_x_max'}; $t_y_min = $self->{'curr_y_min'}; $t_y_max = $self->{'curr_y_max'}; #get back to the point, where everything began $self->{'curr_x_min'} = 0; $self->{'curr_y_min'} = 0; $self->{'curr_x_max'} = $abs_x_max; $self->{'curr_y_max'} = $abs_y_max; #draw the title again if ( $self->{'title'} ) { $self->_draw_title(); } #draw the sub title again if ( $self->{'sub_title'} ) { $self->_draw_sub_title(); } #draw the top legend again if ( $self->{'legend'} =~ /^top$/i ) { $self->_draw_top_legend(); } #reset the actual values $self->{'curr_x_min'} = $t_x_min; $self->{'curr_x_max'} = $t_x_max; $self->{'curr_y_min'} = $t_y_min; $self->{'curr_y_max'} = $t_y_max; } } ############################################################### ### Fix a bug in GD::Polygon. ### A patch has been submitted to Lincoln Stein. require GD; unless ( defined &GD::Polygon::deletePt ) { *GD::Polygon::deletePt = sub { my ( $self, $index ) = @_; unless ( ( $index >= 0 ) && ( $index < @{ $self->{'points'} } ) ) { warn "Attempt to set an undefined polygon vertex"; return undef; } my ($vertex) = splice( @{ $self->{'points'} }, $index, 1 ); $self->{'length'}--; return @$vertex; } } ############################################################### 1; Chart-v2.403.9/lib/Chart.pm0000644000175000017500000003227214341172251015143 0ustar herbertherbertuse v5.12; package Chart; our $VERSION = 'v2.403.9'; use Chart::Points; use Chart::Lines; use Chart::LinesPoints; use Chart::Mountain; use Chart::ErrorBars; use Chart::Direction; use Chart::Split; use Chart::Pareto; use Chart::Composite; use Chart::Bars; use Chart::HorizontalBars; use Chart::StackedBars; use Chart::Pie; 1; =pod =head1 NAME Chart - a series of charting modules =head1 SYNOPSIS use Chart::type; (type is one of: Points, Lines, Bars, LinesPoints, Composite, StackedBars, Mountain, Pie, HorizontalBars, Split, ErrorBars, Pareto, Direction) $obj = Chart::type->new; $obj = Chart::type->new ( $png_width, $png_height ); $obj->set ( $key_1, $val_1, ... ,$key_n, $val_n ); $obj->set ( $key_1 => $val_1, ... $key_n => $val_n ); $obj->set ( %hash ); # GIFgraph.pm-style API to produce png formatted charts @data = ( \@x_tick_labels, \@dataset1, ... , \@dataset_n ); $obj->png ( "filename", \@data ); $obj->png ( $filehandle, \@data ); $obj->png ( FILEHANDLE, \@data ); $obj->cgi_png ( \@data ); # Graph.pm-style API $obj->add_pt ($label, $val_1, ... , $val_n); $obj->add_dataset ($val_1, ... , $val_n); $obj->png ( "filename" ); $obj->png ( $filehandle ); $obj->png ( FILEHANDLE ); $obj->cgi_png (); The similar functions are available for j-peg # Retrieve image map information $obj->set ( 'imagemap' => 'true' ); $imagemap_ref = $obj->imagemap_dump (); =head1 DESCRIPTION Chart helps you to create PNG and JPG images with visualizations of numeric data. This page gives you a summary how to use it. For a more thorough documentation and lots of example code please visit the L. =for HTML

point chart composite of bars and lines stacked bars multi bar chart horizontal bar chart polar chart pie chart mountain chart split chart error bar chart

=head2 use-ing Chart Okay, so you caught me. There's really no Chart::type module. All of the different chart types (Points, Lines, Bars, LinesPoints, Composite, StackedBars, Pie, Pareto, HorizontalBars, Split, ErrorBars, Direction and Mountain so far) are classes by themselves, each inheriting a bunch of methods from the Chart::Base class. Simply replace the word type with the type of chart you want and you're on your way. For example, use Chart::Lines; would invoke the lines module. Alternatively load all chart types at ones and write: use Chart; =head2 Getting an object The new method can either be called without arguments, in which case it returns an object with the default image size (400x300 pixels), or you can specify the width and height of the image. Just remember to replace type with the type of graph you want. For example, $obj = Chart::Bars->new (600,400); would return a Chart::Bars object containing a 600x400 pixel image. New also initializes most of the default variables, which you can subsequently change with the set method. =head2 Setting different options This is where the fun begins. Set looks for a hash of keys and values. You can pass it a hash that you've already constructed, like %hash = ( property_name => 'new value' ); $obj->set (%hash); or you can try just constructing the hash inside the set call, like $obj->set ( property_name => 'new value' ); L lists all currently supported keys and values. =head2 GIFgraph.pm-style API =over 4 =item Sending the image to a file Invoking the png method causes the graph to be plotted and saved to a file. It takes the name of the output file and a reference to the data as arguments. For example, $obj->png ("foo.png", \@data); would plot the data in @data, and the save the image to foo.png. Of course, this then beggars the question "What should @data look like?". Well, just like GIFgraph, @data should contain references to arrays of data, with the first array reference pointing to an array of x-tick labels. For example, @data = ( [ 'foo', 'bar', 'junk' ], [ 30.2, 23.5, 92.1 ] ); would set up a graph with one dataset, and three data points in that set. In general, the @data array should look something like @data = ( \@x_tick_labels, \@dataset1, ... , \@dataset_n ); And no worries, I make my own internal copy of the data, so that it doesn't mess with yours. =item CGI and Chart Okay, so you're probably thinking, "Do I always have to save these images to disk? What if I want to use Chart to create dynamic images for my web site?" Well, here's the answer to that. $obj->cgi_png ( \@data ); The cgi_png method will print the chart, along with the appropriate http header, to stdout, allowing you to call chart-generating scripts directly from your html pages (ie. with a img src=image.pl HTML tag). The @data array should be set up the same way as for the normal png method. =back =head2 column based API You might ask, "But what if I just want to add a few points to the graph, and then display it, without all those references to references?". Well, friend, the solution is simple. Borrowing the add_pt idea from Matt Kruse's Graph module, you simply make a few calls to the add_pt method, like so: $obj->add_pt ('foo', 30, 25); $obj->add_pt ('bar', 16, 32); Or, if you want to be able to add entire datasets, simply use the add_dataset method: $obj->add_dataset ('foo', 'bar'); $obj->add_dataset (30, 16); $obj->add_dataset (25, 32); These methods check to make sure that the points and datasets you are adding are the same size as the ones already there. So, if you have two datasets currently stored, and try to add a data point with three different values, it will carp (per the Carp module) an error message. Similarly, if you try to add a dataset with 4 data points, and all the other datasets have 3 data points, it will carp an error message. Don't forget, when using this API, that I treat the first dataset as a series of x-tick labels. So, in the above examples, the graph would have two x-ticks, labeled 'foo' and 'bar', each with two data points. Pie and ErrorBars handle it different, look at the documentation to see how it works. =over 4 =item Adding a datafile You can also add a complete datafile to a chart object. Just use the add_datafile() method. $obj->add_datafile('file', 'set' or 'pt'); file can be the name of the data file or a filehandle. 'set' or 'pt is the type of the datafile. If the parameter is 'set' then each line in the data file has to be a complete data set. The value of the set has to be separated by white spaces. For example the file looks like this: 'foo' 'bar' 30 16 25 32 If the parameter is 'pt', one line has to include all values of one data point separated by white spaces. For example: 'foo' 30 25 'bar' 16 32 =item Clearing the data A simple call to the clear_data method empties any values that may have been entered. $obj->clear_data (); =item Getting a copy of the data If you want a copy of the data that has been added so far, make a call to the get_data method like so: $dataref = $obj->get_data; It returns (you guessed it!) a reference to an array of references to datasets. So the x-tick labels would be stored as @x_labels = @{$dataref->[0]}; =item Sending the image to a file If you just want to print this chart to a file, all you have to do is pass the name of the file to the png() method. $obj->png ("foo.png"); =item Sending the image to a filehandle If you want to do something else with the image, you can also pass a filehandle (either a typeglob or a FileHandle object) to png, and it will print directly to that. $obj->png ($filehandle); $obj->png (FILEHANDLE); =item CGI and Chart Okay, so you're probably thinking (again), "Do I always have to save these images to disk? What if I want to use Chart to create dynamic images for my web site?" Well, here's the answer to that. $obj->cgi_png (); The cgi_png method will print the chart, along with the appropriate http header, to stdout, allowing you to call chart-generating scripts directly from your html pages (ie. with a img src=image.pl HTML tag). =item Produce a png image as a scalar Like scalar_jpeg() the image is produced as a scalar so that the programmer-user can do whatever the heck s/he wants to with it: $obj-scalar_png($dataref) =item Produce a jpeg image as a scalar Like scalar_png() the image is produced as a scalar so that the programmer-user can do whatever the heck s/he wants to with it: $obj-scalar_jpeg($dataref) =back =head2 Imagemap Support Chart can also return the pixel positioning information so that you can create image maps from the pngs Chart generates. Simply set the 'imagemap' option to 'true' before you generate the png, then call the imagemap_dump() method afterwards to retrieve the information. You will be returned a data structure almost identical to the @data array described above to pass the data into Chart. $imagemap_data = $obj->imagemap_dump (); Instead of single data values, you will be passed references to arrays of pixel information. For Bars, HorizontalBars and StackedBars charts, the arrays will contain two x-y pairs (specifying the upper left and lower right corner of the bar), like so ( $x1, $y1, $x2, $y2 ) = @{ $imagemap_data->[$dataset][$datapoint] }; For Lines, Points, ErrorBars, Split and LinesPoints, the arrays will contain a single x-y pair (specifying the center of the point), like so ( $x, $y ) = @{ $imagemap_data->[$dataset][$datapoint] }; A few caveats apply here. First of all, GD treats the upper-left corner of the png as the (0,0) point, so positive y values are measured from the top of the png, not the bottom. Second, these values will most likely contain long decimal values. GD, of course, has to truncate these to single pixel values. Since I don't know how GD does it, I can't truncate it the same way he does. In a worst-case scenario, this will result in an error of one pixel on your imagemap. If this is really an issue, your only option is to either experiment with it, or to contact Lincoln Stein and ask him. Third, please remember that the 0th dataset will be empty, since that's the place in the @data array for the data point labels. =head1 PLAN This module is currently under a complete rebuild, that will take place in two phases. First: rewrite all functionality within a modular architecture and hierarchical property system. This will be accessed via a central API using the so far unutilized Chart module 'my $c = Chart->new(...);'. This API will have in part different method and property names, but the old API will not be touched. In a second phase we will see hoch much new code can be used by the old modules and which new features can be brought to the legacy parts, which will be than discouraged, but not scrapped. =head1 TO DO =over 4 =item * Include True Type Fonts =item * Violine and Box plots =item * Add some 3-D graphs. =back For more please check the TODO file. =head1 BUGS Probably quite a few, since it's been completely rewritten. As usual, please mail me with any bugs, patches, suggestions, comments, flames, death threats, etc. =head1 AUTHOR David Bonner (dbonner@cs.bu.edu) =head1 MAINTAINER =over 4 =item * Chart Group (Chart@fs.wettzell.de) =item * Herbert Breunung (lichtkind@cpan.org) =back =head1 CONTRIBUTORS =over 4 =item * Gregor Herrmann (gregoa@debian.org) =item * Chris Dolan (chris+rt@chrisdolan.net) =item * (jarmzet@yahoo.com) =item * Ricardo Signes (rjbs@cpan.org) =item * Petr Pisar (ppisar@redhat.com) =back =head1 COPYRIGHT Copyright(c) 1997-1998 by David Bonner, 1999 by Peter Clark, 2001 by the Chart group at BKG-Wettzell. 2022 by Herbert Breunung and Chart group All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Chart-v2.403.9/Makefile.PL0000644000175000017500000000232314341172251014742 0ustar herbertherbert# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.025. use strict; use warnings; use 5.012000; use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "a series of charting modules", "AUTHOR" => "Chart-Group ", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "Chart", "LICENSE" => "perl", "MIN_PERL_VERSION" => "5.012000", "NAME" => "Chart", "PREREQ_PM" => { "Carp" => "1.35", "GD" => 2, "Graphics::Toolkit::Color" => 1 }, "TEST_REQUIRES" => { "File::Temp" => "0.19", "Test::More" => "1.3", "Test::Warn" => "0.30" }, "VERSION" => "v2.403.9", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "Carp" => "1.35", "File::Temp" => "0.19", "GD" => 2, "Graphics::Toolkit::Color" => 1, "Test::More" => "1.3", "Test::Warn" => "0.30" ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); Chart-v2.403.9/META.yml0000644000175000017500000000376114341172251014250 0ustar herbertherbert--- abstract: 'a series of charting modules' author: - 'Chart-Group ' build_requires: File::Temp: '0.19' Test::More: '1.3' Test::Warn: '0.30' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.025, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Chart no_index: directory: - t namespace: - Chart::Manual - Chart::Property package: - Chart::Base - Chart::BrushStyles - Chart::Constants provides: Chart: file: lib/Chart.pm version: v2.403.9 Chart::Bars: file: lib/Chart/Bars.pm version: v2.403.9 Chart::Composite: file: lib/Chart/Composite.pm version: v2.403.9 Chart::Direction: file: lib/Chart/Direction.pm version: v2.403.9 Chart::ErrorBars: file: lib/Chart/ErrorBars.pm version: v2.403.9 Chart::HorizontalBars: file: lib/Chart/HorizontalBars.pm version: v2.403.9 Chart::Lines: file: lib/Chart/Lines.pm version: v2.403.9 Chart::LinesPoints: file: lib/Chart/LinesPoints.pm version: v2.403.9 Chart::Mountain: file: lib/Chart/Mountain.pm version: v2.403.9 Chart::Pareto: file: lib/Chart/Pareto.pm version: v2.403.9 Chart::Pie: file: lib/Chart/Pie.pm version: v2.403.9 Chart::Points: file: lib/Chart/Points.pm version: v2.403.9 Chart::Property: file: lib/Chart/Property.pm version: v2.403.9 Chart::Split: file: lib/Chart/Split.pm version: v2.403.9 Chart::StackedBars: file: lib/Chart/StackedBars.pm version: v2.403.9 requires: Carp: '1.35' GD: '2' Graphics::Toolkit::Color: '1' perl: v5.12.0 resources: repository: git://github.com/lichtkind/Chart.git version: v2.403.9 x_generated_by_perl: v5.30.0 x_maintainers: - 'Herbert Breunung ' x_serialization_backend: 'YAML::Tiny version 1.73' x_spdx_expression: 'Artistic-1.0-Perl OR GPL-1.0-or-later' Chart-v2.403.9/t/0000775000175000017500000000000014341172251013235 5ustar herbertherbertChart-v2.403.9/t/direction_2.t0000644000175000017500000000125414341172251015623 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Direction; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Direction->new( 450, 450 ); $g->add_dataset( 0, 10, 30, 100, 110, 200, 250, 300, 350 ); $g->add_dataset( 10, 4, 11, 40, 20, 35, 5, 45, 20 ); $g->add_dataset( 20, 8, 22, 80, 40, 70, 10, 90, 40 ); $g->add_dataset( 30, 18, 32, 85, 45, 60, 20, 50, 25 ); $g->set( 'title' => 'Direction Demo', 'angle_interval' => 15, 'precision' => 0, 'grey_background' => 'false', 'legend' => 'top', ); $g->png("$samples/direction_2.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/lines_2.t0000644000175000017500000000213614341172251014755 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Lines; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $g; print "1..1\n"; $g = Chart::Lines->new; $g->add_dataset( 'foo', 'bar', 'whee', 'ding', 'bat', 'bit' ); $g->add_dataset( 3.2, 4.34, 9.456, 10.459, 11.24234, 14.0234 ); $g->add_dataset( -1.3, 8.4, 5.34, 3.234, 4.33, 13.09 ); $g->add_dataset( 5, 7, 2, 10, 12, 2.3445 ); $g->add_dataset( 8, 4, 5, 12, 3, 9 ); $g->set( 'title' => 'LINES' ); $g->set( 'sub_title' => 'Lines Chart' ); $g->set( 'colors' => { 'y_label' => [ 0, 0, 255 ], y_label2 => [ 0, 255, 0 ], 'y_grid_lines' => [ 127, 127, 0 ], 'dataset0' => [ 127, 0, 0 ], 'dataset1' => [ 0, 127, 0 ], 'dataset2' => [ 0, 0, 127 ] } ); $g->set( 'y_label' => 'y label 1' ); $g->set( 'y_label2' => 'y label 2' ); $g->set( 'y_grid_lines' => 'true' ); $g->set( 'legend' => 'left' ); $g->png("$samples/lines_2.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/composite_8.t0000644000175000017500000000325614341172251015657 0ustar herbertherbert#!/usr/bin/perl -w # Difference to composite_7.t is: # set(xy_plot => 1 # You will the difference in the plots! # BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Composite; use Chart::Lines; use Chart::Points; use Chart::LinesPoints; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my @y = qw( 2.6593 2.0832 2.0519 2.2257 2.4355 2.4183 3.4088 2.2899 2.4914 2.3217 2.0684 2.1328 1.8168 1.7662 1.7592 1.8624 1.2614 ); my @x = qw(4757 14055 23004 29698 32172 31038 33068 33383 33941 32451 25235 17035 12122 9868 6647 4024 944); my @x2 = qw(1.706 1.756 1.807 1.858 1.909 1.959 2.010 2.061 2.112 2.162 2.213 2.264 2.315 2.365 2.416 2.467 2.518); my %hash = qw(precision 2 title red text blue include_zero true graph_border 0 y_grid_lines true legend bottom y_axes both skip_x_ticks 1 brush_size 4 brush_size1 4 brush_size2 3 no_cache true *xy_plot true*); $hash{title} = "my title"; my $c = 0; $hash{colors} = { 'dataset0' => [ 255, 0, 0 ], 'dataset1' => [ 0, 0, 255 ], 'dataset2' => [ 173, 170, 61 ], 'dataset3' => [ 242, 2, 249 ], 'dataset4' => [ 254, 177, 192 ], 'y_label' => [ 0, 0, 0 ], 'y_label2' => [ 173, 170, 61 ], }; $hash{brushStyle1} = 'fatPlus'; $hash{brushStyle2} = 'hollowSquare'; my $g; if (0) { $g = Chart::Lines->new( 1000, 400 ); } else { $g = Chart::Composite->new( 1000, 400 ); $g->set( 'composite_info' => [ [ 'Points', [1] ], [ 'LinesPoints', [2] ] ] ); } my @s = sort { $x[$a] <=> $x[$b]; } 0 .. $#x; $g->add_dataset( @x[@s] ); $g->add_dataset( @y[@s] ); $g->add_dataset(@x2); $g->set(%hash); $g->set( xy_plot => 1 ); $g->jpeg("$samples/composite_8.jpg"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/split_2.t0000644000175000017500000000203114341172251014770 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Split; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Split->new( 500, 500 ); $last = 0; # create arrays of data for ( 1 .. 4000 ) { srand( time() / $_ * 50 ); #intialize the random number generator $y1 = ( rand(10) ); #generate the number push( @x, $_ ); #add the x-values push( @y1, $y1 ); #add the random number push( @y2, abs( $y1 - $last ) ); #add the difference to the last number $last = $y1; } $g->add_dataset(@x); $g->add_dataset(@y1); $g->add_dataset(@y2); %options = ( 'start' => 0, 'interval' => 400, 'brush_size' => 1, 'interval_ticks' => 0, 'title' => "Random Numbers Test", 'legend_labels' => [ 'random numbers', 'difference' ], 'x_label' => "4000 Random Numbers", 'legend' => 'bottom', ); $g->set(%options); $g->png("$samples/split_2.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/mapcomp.t0000644000175000017500000000337214341172251015061 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::Composite; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $png_name = "$samples/mapcomp.png"; my @legend_keys = ( "Actual ", "Goal" ); # my $Graph = new Chart::Composite( 600, 400 ); print "1..1\n"; # $Graph->add_dataset( "Oct 01", "Nov 01", "Dec 01", "Jan 02", "Feb 02", "Mar 02" ); $Graph->add_dataset( 95.1, 84.4, 90.2, 94.4, 93.8, 95.5 ); $Graph->add_dataset( 93.0, 83.0, 94.0, 94.0, 94.0, 94.0 ); # $Graph->set( composite_info => [ [ 'Bars', [1] ], [ 'Lines', [2] ] ], colors => { dataset0 => 'green', dataset1 => 'red' }, title_font => GD::Font->Giant, label_font => GD::Font->Small, legend_font => GD::Font->Large, tick_label_font => GD::Font->Large, grid_lines => 'true', graph_border => 0, imagemap => 'true', legend => 'bottom', legend_labels => \@legend_keys, max_val => 100, min_val => 80, png_border => 4, same_y_axes => 'true', spaced_bars => 'true', title => "Yield 2004", text_space => 5, transparent => 'true', x_ticks => 'vertical', integer_ticks_only => 'true', skip_int_ticks => 5, ); $Graph->png("$png_name"); # my $imagemap_data = $Graph->imagemap_dump(); # foreach my $ds ( 1 .. 1 ) { foreach my $pt ( 0 .. 5 ) { if ( defined( $imagemap_data->[$ds]->[$pt] ) ) { my @i = @{ $imagemap_data->[$ds]->[$pt] }; # ** print "Dataset:$ds - Point: $pt ---- VALUES: @i \n"; } } } print "ok\n"; exit 0; Chart-v2.403.9/t/composite.t0000644000175000017500000000177214341172251015431 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Composite; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Composite->new(); $g->add_dataset( 'foo', 'bar', 'junk', 'whee' ); $g->add_dataset( 3, 4, 9, 10 ); $g->add_dataset( 8, 6, 1, 11 ); $g->add_dataset( 5, 7, 2, 12 ); $g->add_dataset( 2, 5, 7, 2 ); $g->set( 'legend' => 'left' ); $g->set( 'title' => 'Composite Chart', 'composite_info' => [ [ 'Bars', [ 1, 2 ] ], [ 'LinesPoints', [ 3, 4 ] ] ] ); $g->set( 'y_label' => 'y label 1', 'y_label2' => 'y label 2' ); $g->set( 'colors' => { 'y_label' => [ 0, 0, 255 ], y_label2 => [ 0, 255, 0 ], 'dataset0' => [ 0, 127, 0 ], 'dataset1' => [ 0, 0, 127 ], 'dataset8', => [ 0, 255, 0 ], 'dataset9' => [ 255, 0, 0 ] } ); $g->set( 'brush_size2' => 1 ); $g->png("$samples/composite.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/composite_2.t0000644000175000017500000000376714341172251015660 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::Composite; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my ($g) = Chart::Composite->new( 788, 435 ); # 0 = X-Achse $g->add_dataset( '0:00', '1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00' ); # 1 = BZ-Werte - linie $g->add_dataset( 222, 211, 306, 175, 216, 216, 168, 161, 153, 170, 92, 134 ); # 2 = Basas - stacked bar $g->add_dataset( 0.8, 0.8, 0.9, 1.1, 1.8, 2.1, 1.8, 1.4, 1.0, 1.0, 1.0, 1.0 ); # 3 = Bolus - stacked bar $g->add_dataset( 4.4, 1.8, 6.5, 5.6, 2.4, 4.7, 6.0, 5.4, 8.9, 9.5, 8.7, 9.2 ); # 4 = KHE - stacked bar $g->add_dataset( 3.0, 2.8, 2.5, 0.6, 2.4, 4.7, 5.0, 5.4, 1.9, 3.5, 4.7, 3.2 ); $g->set( 'composite_info' => [ [ 'StackedBars', [ 2, 3, 4 ] ], [ 'Lines', [1] ] ] ); $g->set( 'legend_labels' => [ 'BZ', 'KHE', 'Bolus', 'Basal' ], 'legend' => 'bottom', 'title' => '', 'precision' => 0, 'spaced_bars' => 'false', 'include_zero' => 'true', 'legend_example_size' => 100, 'skip_int_ticks' => 3, 'min_val2' => 0, #'max_val2' => 400, 'legend_example_height' => 'true', 'legend_example_height0..2' => 10, # Reihenfolge durch composite info 'legend_example_height3' => 2, 'y_label' => 'IE/KHE', 'y_label2' => 'mg/dl (mmol/l)', 'grey_background' => 'false', ); $g->set( 'colors' => { 'y_label' => [ 51, 255, 0 ], 'y_label2' => [ 255, 0, 0 ], 'dataset2' => [ 0, 0, 244 ], # Reihenfolge durch composite info!!! 'dataset1' => [ 0, 204, 0 ], 'dataset0' => [ 255, 255, 51 ], 'dataset3' => [ 204, 0, 0 ], } ); $g->set( 'f_y_tick' => sub { return ( $_[0] . '(' . sprintf( "%.1f", $_[0] / 18.0182 ) . ')' ) } ); $g->png("$samples/composite_2.png"); print "ok 1\n"; exit 0; Chart-v2.403.9/t/pareto_1.t0000644000175000017500000000176114341172251015137 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pareto; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Pareto->new( 450, 400 ); $g->add_dataset( 'Mo', 'Tue', 'We', 'Th', 'Fr', 'Sa', 'Su' ); $g->add_dataset( 2500, 1000, 250, 700, 100, 610, 20 ); %hash = ( 'colors' => { 'dataset0' => 'green', 'dataset1' => 'red', 'x_label' => 'red', 'y_grid_lines' => 'white', 'title' => 'blue', }, 'title' => 'Sold Tickets for Beethovens 9th\n ', 'integer_ticks_only' => 'true', 'skip_int_ticks' => 250, 'sort' => 'true', 'max_val' => 5500, 'y_grid_lines' => 'true', 'y_label' => 'Sold Tickets', 'x_label' => '! sold out in the first week !', 'spaced_bars' => 'false', 'legend' => 'none', ); $g->set(%hash); $g->png("$samples/pareto_1.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/linespoints_3.t0000644000175000017500000000265614341172251016222 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::LinesPoints; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my ( @data1, @data2, @data4, @data3, @labels, %hash, $g, $hits ); @labels = qw(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17); @data1 = qw (-7 -5 -6 -8 -9 -7 -5 -4 -3 -2 -4 -6 -3 -5 -3 -4 -6); @data2 = qw (-1 -1 -1 -1 -2 -2 -3 -3 -4 -4 -6 -3 -2 -2 -2 -1 -1); @data3 = qw (-4 -4 -3 -2 -1 -1 -1 -2 -1 -1 -3 -2 -4 -3 -4 -2 -2); @data4 = qw (-6 -3 -2 -3 -3 -3 -2 -1 -2 -3 -1 -1 -1 -1 -1 -3 -3); $g = Chart::LinesPoints->new( 600, 300 ); $g->add_dataset(@labels); $g->add_dataset(@data1); $g->add_dataset(@data2); $g->add_dataset(@data3); $g->add_dataset(@data4); %hash = ( 'integer_ticks_only' => 'true', 'title' => 'Soccer Season 2002\n ', 'legend_labels' => [ 'NY Soccer Club', 'Denver Tigers', 'Houston Spacecats', 'Washington Presidents' ], 'y_label' => 'position in the table', 'x_label' => 'day of play', 'grid_lines' => 'true', 'f_y_tick' => \&formatter, 'xy_plot' => 'false', ); $g->set(%hash); $g->png("$samples/linespoints_3.png"); #just a trick, to let the y scale start at the biggest point: #initiate with negativ values, remove the minus sign! sub formatter { my $label = shift; $label = substr( $label, 1, 2 ); return $label; } print "ok 1\n"; exit(0); Chart-v2.403.9/t/linespoints_1.t0000644000175000017500000000156614341172251016217 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::LinesPoints; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::LinesPoints->new; $g->add_dataset( 'foo', 'bar', 'junk', 'ding', 'bat' ); $g->add_dataset( 3, 4, 9, 3, 4 ); $g->add_dataset( 8, 4, 3, 4, 6 ); $g->add_dataset( 5, 7, 2, 7, 9 ); $g->set( 'title' => 'Lines and Points Chart' ); $g->set( 'sub_title' => 'Change color for grid_lines' ); $g->set( 'colors' => { 'x_grid_lines' => [ 250, 0, 125 ] } ); $g->set( 'colors' => { 'y_grid_lines' => [ 250, 0, 125 ] } ); #$g->set ('colors' => {'y2_grid_lines' => [250, 0, 125]}); $g->set( 'grid_lines' => 'true' ); $g->set( 'grey_background' => 'false' ); $g->set( 'legend' => 'bottom' ); $g->png("$samples/linespoints_1.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/lines_8.t0000644000175000017500000000130014341172251014753 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Lines; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $g; print "1..1\n"; $g = Chart::Lines->new; $g->add_dataset( 'one', 'two', 'three', 'four', 'five', 'six' ); $g->add_dataset( 3, 11, 5, 10, 12, 4 ); $g->set( 'title' => "Timing" ); $g->set( 'sub_title' => 'Example for stepline' ); $g->set( 'y_grid_lines' => 'true' ); $g->set( 'legend' => 'none' ); $g->set( 'precision' => '0' ); $g->set( 'include_zero' => 'true' ); $g->set( 'stepline' => 'true' ); $g->set( 'stepline_mode' => 'begin' ); $g->png("$samples/lines_8.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/composite_4.t0000644000175000017500000000113114341172251015641 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Composite; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Composite->new( 750, 600 ); $g->add_dataset( 1, 2, 3 ); $g->add_dataset( 10, 20, 30 ); $g->add_dataset( 15, 25, 32 ); $g->add_dataset( 7, 24, 23 ); $g->add_dataset( 0.1, 0.5, 0.9 ); $g->set( 'title' => 'Composite Chart Test 2', 'composite_info' => [ [ 'Bars', [ 1, 2 ] ], [ 'LinesPoints', [ 3, 4 ] ] ], 'include_zero' => 'true', ); $g->png("$samples/composite_4.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/lines_9.t0000644000175000017500000000205714341172251014766 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Lines; use Chart::LinesPoints; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $g; print "1..1\n"; my @labels = ( [ 'Jan', 'Feb', 'Mar' ], [ '0', '20', '100' ] ); $g = Chart::Lines->new; $g->add_dataset( 10, 20, 30, 40, 50, 60 ); $g->add_dataset( 10, 40, 70, 100, 130, 10 ); $g->set( 'title' => "Basic example for Option xlabels" ); $g->set( 'y_grid_lines' => 'true' ); $g->set( 'legend' => 'none' ); $g->set( 'xy_plot' => 1 ); $g->set( 'x_ticks' => 'vertical' ); $g->png("$samples/lines_9.png"); $g = Chart::Lines->new; $g->add_dataset( 10, 20, 30, 40, 50, 60 ); $g->add_dataset( 10, 40, 70, 100, 130, 10 ); $g->set( 'title' => "Example B for Option xlabels" ); $g->set( 'y_grid_lines' => 'true' ); $g->set( 'legend' => 'none' ); $g->set( 'xy_plot' => 1 ); $g->set( 'x_ticks' => 'vertical' ); $g->set( xlabels => \@labels, xrange => [ 0, 100 ] ); $g->png("$samples/lines_9b.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/stackedbars_2.t0000644000175000017500000000262714341172251016136 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::StackedBars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my ($g) = Chart::StackedBars->new( 788, 435 ); # 0 = X-Achse $g->add_dataset( '0:00', '1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00' ); # 2 = Basas - stacked bar $g->add_dataset( 0.8, 0.8, 0.9, 1.1, 1.8, 2.1, 1.8, 1.4, 1.0, 1.0, 1.0, 1.0 ); # 3 = Bolus - stacked bar $g->add_dataset( 4.4, 1.8, 6.5, 5.6, 2.4, 4.7, 6.0, 5.4, 8.9, 9.5, 8.7, 9.2 ); # 4 = KHE - stacked bar $g->add_dataset( 3.0, 2.8, 2.5, 0.6, 2.4, 4.7, 5.0, 5.4, 1.9, 3.5, 4.7, 3.2 ); $g->set( 'legend' => 'bottom', 'title' => 'Stacked Bars', 'precision' => 0, 'spaced_bars' => 'false', 'include_zero' => 'true', 'legend_example_size' => 100, 'skip_int_ticks' => 3, 'min_val_2' => 0, 'y_label' => 'IE/KHE', 'y_label2' => 'mg/dl (mmol/l)', 'grey_background' => 'false', ); $g->set( 'colors' => { 'y_label' => [ 51, 255, 0 ], 'y_label2' => [ 255, 0, 0 ], 'dataset2' => [ 0, 0, 244 ], 'dataset1' => [ 0, 204, 0 ], 'dataset0' => [ 255, 255, 51 ], 'dataset3' => [ 204, 0, 0 ], } ); $g->png("$samples/stackedbars_2.png"); print "ok 1\n"; exit; Chart-v2.403.9/t/composite_3.t0000644000175000017500000000550514341172251015651 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::Composite; #(type is one of: Points, Lines, Bars, LinesPoints, Composite, StackedBars, Mountain) use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $obj = Chart::Composite->new( 800, 600 ); #Breite, Höhe my @legend_ary; my ( $legend, @zeile ); my @all_aryref; open( OUT, ">$samples/composite_3.png" ) or die "kann Datei nicht schreiben\n"; my $i = 0; my $e = 0; my $max_val = 0; while () { if ( $_ =~ /EOF/i ) { last; } chomp; $i++; ( $legend, @zeile ) = split /\|/, $_; $obj->add_dataset(@zeile); if ( $i != 1 ) { push @legend_ary, $legend; # Erste Zeile ist die x-Achsenbezeichnung und gehört nicht zur Legende for ( 0 .. $#zeile ) { $zeile[$_] > $max_val ? $max_val = $zeile[$_] : 1; } # den Maximalen Wert ermitteln } $all_aryref[ $e++ ] = [@zeile]; } if ( $max_val =~ /^\d+$/ ) { $max_val = 100 * int( 1 + $max_val / 100 ); } # den Scalenwert die nächste 100er Stellen setzen # Der zweite Charttyp überdeckt immer den ersten $obj->set( 'legend' => "top", 'legend_labels' => \@legend_ary, 'x_ticks' => "vertical", 'composite_info' => [ [ 'StackedBars', [ 8, 7, 6, 5 ] ], [ 'Bars', [ 1, 2, 3, 4, 9 ] ], ], 'same_y_axes' => "true", 'y_label' => "Anzahl", 'max_val1' => $max_val, 'max_val2' => $max_val, 'space_bars' => 1, 'brush_size' => 10, 'legend_example_height' => 'true', 'legend_example_height0..3' => '50', 'legend_example_height4..8' => '4', ); $obj->png( \*OUT ); print "ok 1\n"; close OUT; exit 0; __END__ Datum|01.09.2003|02.09.2003|03.09.2003|04.09.2003|05.09.2003|06.09.2003|07.09.2003|08.09.2003|09.09.2003|10.09.2003|11.09.2003|12.09.2003|13.09.2003|14.09.2003|15.09.2003|16.09.2003|17.09.2003|18.09.2003|19.09.2003|20.09.2003|21.09.2003|22.09.2003 Anzahl gesamt|322|244|227|223|167|216|290|277|206|237|256|214|192|228|218|225|146|172|140|123|174|173 Anzahl Stufe 1 bis 4 gesamt|226|173|159|145|109|148|204|188|133|184|176|137|132|157|139|155|106|115|93|76|107|106 Anzahl JL|77|46|44|61|41|54|69|63|63|38|71|68|54|59|71|61|34|40|42|38|56|57 Anzahl DL|19|25|24|17|17|14|17|26|10|15|9|9|6|12|8|9|6|17|5|9|11|10 Anzahl 1. Stufe|28|22|11|27|15|23|28|23|17|24|24|20|19|24|23|30|20|18|12|10|14|29 Anzahl 2. Stufe|12|11|4|7|8|6|16|12|8|11|10|8|4|8|3|6|7|6|5|7|8|13 Anzahl 3. Stufe|50|39|55|34|16|33|38|40|36|38|48|29|35|42|36|42|28|25|20|19|24|19 Anzahl 4. Stufe|136|101|89|77|70|86|122|113|72|111|94|80|74|83|77|77|51|66|56|40|61|45 Anzahl Formulars|547|352|249|174|138|157|262|180|136|132|94|72|59|129|88|60|61|51|42|44|79|57 EOF Chart-v2.403.9/t/bars_9.t0000644000175000017500000000220614341172251014577 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Bars; use strict; use POSIX; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Bars->new( 600, 400 ); my @data = ( 200202, 200203, 200204, 200205, 200206, 200207, 200208, 200209, 200210, 200211, 200212, 200301 ); my @data1 = ( 6626, 7090, 7580, 7671, 8764, 8664, 6343, 5518, 6257, 5391, 5401, 6002 ); $g->add_dataset(@data); $g->add_dataset(@data1); my @legend_keys = ( "Actual ", "Goal" ); $g->set( colors => { dataset0 => [ 25, 220, 147 ], }, graph_border => 0, grey_background => 'false', grid_lines => 'true', include_zero => 'true', # integer_ticks_only => 'true', legend => 'none', png_border => 4, precision => 1, skip_int_ticks => 1000, text_space => 3, title => "Tickets", title_font => GD::Font->Giant, transparent => 'false', x_ticks => 'vertical', y_axes => 'both', y_label => '# Tickets', x_label => 'Date', ); $g->png("$samples/bars_9.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/pie_10.t0000644000175000017500000000223414341172251014476 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pie; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Pie->new( 1000, 1000 ); my @labels = ( 'Pending - 0 - 0.5 hour', 'Pending - 0.5 - 1 hour', 'Pending - 1 - 2 hours', 'Pending - 2 - 5 hours', 'Pending - 5 - 12 hours', 'Pending - 12 - 24 hours', 'Pending - 1 - 2 days', 'Pending - more than 2 days', 'Queued - 0 - 0.5 hour', 'Queued - 0.5 - 1 hour', 'Queued - 1 - 2 hours', 'Queued - 2 - 5 hours', 'Queued - 5 - 12 hours', 'Queued - 12 - 24 hours', 'Queued - 1 - 2 days', 'Queued - more than 2 days', 'Queued - Future Delivery' ); $g->add_dataset(@labels); $g->add_dataset( 40, 5, 12, 2, 4, 15, 20, 31, 1, 25, 40, 40, 40, 1, 0, 2, 20 ); $g->set( 'title' => 'Pie Demo Chart' ); $g->set( 'label_values' => 'percent' ); $g->set( 'legend_label_values' => 'value' ); $g->set( 'legend' => 'right' ); $g->set( 'grey_background' => 'false' ); $g->set( 'legend_lines' => 'true' ); $g->png("$samples/pie_10.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/bars_3.t0000644000175000017500000000163414341172251014575 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Bars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Bars->new( 500, 500 ); $g->add_dataset( 'Reds', 'Blacks', 'Greens', 'Yellows', 'Browns', 'Others' ); $g->add_dataset( -2.4, 3.4, 1.9, 1.2, -1.1, -2.9 ); %hash = ( 'title' => 'Selection 2002:\nWins and Losses in percent', 'text_space' => 5, 'y_grid_lines' => 'true', 'grey_background' => 'false', 'legend' => 'none', 'min_val' => -4, 'max_val' => 4, 'min_y_ticks' => 10, 'y_axes' => 'both', 'spaced_bars' => 'false', 'colors' => { 'background' => [ 230, 255, 230 ], 'title' => 'plum', 'dataset0' => 'mauve', }, ); $g->set(%hash); $g->png("$samples/bars_3.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/hbars_4.t0000644000175000017500000000115014341172251014737 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::HorizontalBars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::HorizontalBars->new(); $g->add_dataset( 'Foo', 'bar', 'junk', 'ding', 'bat' ); $g->add_dataset( 4, 3, 4, 2, 8 ); $g->add_dataset( 2, 10, 3, 8, 3 ); %hash = ( 'title' => 'Horizontal Bars Demo', 'grid_lines' => 'true', 'x_label' => 'x-axis', 'y_label' => 'y-axis', 'include_zero' => 'true', ); $g->set(%hash); $g->png("$samples/hbars_4.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/lines_6.t0000644000175000017500000000224414341172251014761 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Lines; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Lines->new(); $g->add_dataset( 'foo', 'bar', 'junk', 'ding', 'bat' ); $g->add_dataset( -4, 3, -4, -5, -2 ); $g->add_dataset( 2, 10, -3, 8, 3 ); $g->add_dataset( -10, 2, 4, -3, -3 ); $g->add_dataset( 7, -5, -3, 4, 7 ); %hash = ( 'legend_labels' => [ '1st Quarter', '2nd Quarter', '3rd Quarter', '4th Quarter' ], 'y_axes' => 'both', 'title' => 'Lines Demo', 'grid_lines' => 'true', 'grid_lines' => 'true', 'legend' => 'left', 'legend_example_size' => 20, 'colors' => { 'text' => 'blue', 'misc' => 'blue', 'background' => 'grey', 'grid_lines' => 'light_blue', 'dataset0' => [ 220, 0, 0 ], 'dataset1' => [ 200, 0, 100 ], 'dataset2' => [ 150, 50, 175 ], 'dataset3' => [ 170, 0, 255 ], }, ); $g->set(%hash); $g->png("$samples/lines_6.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/points_5.t0000644000175000017500000000453214341172251015164 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Points; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Points->new(); @hash = ( 'title' => 'Points Chart with different brushes', 'png_border' => 10, 'pt_size' => 20, 'grid_lines' => 'false', 'brush_size' => 18, # 10 points diameter # 'brushStyle' => 'FilledCircle', # 'brushStyle' => 'circle', # 'brushStyle' => 'donut', # 'brushStyle' => 'OpenCircle', # 'brushStyle' => 'fatPlus', # 'brushStyle' => 'triangle', # 'brushStyle' => 'upsidedownTriangle', # 'brushStyle' => 'square', # 'brushStyle' => 'hollowSquare', # 'brushStyle' => 'OpenRectangle', # 'brushStyle' => 'FilledDiamond', # 'brushStyle' => 'OpenDiamond', # 'brushStyle' => 'Star', 'brushStyle' => 'OpenStar', ); my @labels = ( 'FilledCircle', 'circle', 'donut', 'OpenCircle', 'fatPlus', 'triangle', 'upsidedownTriangle', 'square', 'hollowSquare', 'OpenRectangle', 'FilledDiamond', 'OpenDiamond', 'Star', 'OpenStar' ); $g->set( brushStyles => { dataset0 => 'FilledCircle', dataset1 => 'circle', dataset2 => 'donut', dataset3 => 'OpenCircle', dataset4 => 'fatPlus', dataset5 => 'triangle', dataset6 => 'upsidedownTriangle', dataset7 => 'square', dataset8 => 'hollowSquare', dataset9 => 'OpenRectangle', dataset10 => 'FilledDiamond', dataset11 => 'OpenDiamond', dataset12 => 'Star', dataset13 => 'OpenStar', } ); $g->set(@hash); $g->add_dataset( 'foo', 'bar', 'junk' ); $g->add_dataset( 1, 1, 1 ); $g->add_dataset( 2, 2, 2 ); $g->add_dataset( 3, 3, 3 ); $g->add_dataset( 4, 4, 4 ); $g->add_dataset( 5, 5, 5 ); $g->add_dataset( 6, 6, 6 ); $g->add_dataset( 7, 7, 7 ); $g->add_dataset( 8, 8, 8 ); $g->add_dataset( 9, 9, 9 ); $g->add_dataset( 10, 10, 10 ); $g->add_dataset( 11, 11, 11 ); $g->add_dataset( 12, 12, 12 ); $g->add_dataset( 13, 13, 13 ); $g->add_dataset( 14, 14, 14 ); $g->set( 'legend_labels' => \@labels ); $g->png("$samples/points_5.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/direction_1.t0000644000175000017500000000142714341172251015624 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Direction; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Direction->new( 500, 500 ); my @labels = ( 'eins', 'zwei', 'drei', ); $g->add_dataset( 0, 10, 30, 100, 110, 200, 250, 300, 350 ); $g->add_dataset( 10, 4, 11, 40, 20, 35, 5, 45, 20 ); $g->add_dataset( 29, 49, 20, 17, 30, 42, 45, 25, 30 ); $g->add_dataset( 40, 35, 25, 30, 42, 20, 32, 16, 5 ); $g->set( 'title' => 'Direction Demo', 'grey_background' => 'false', 'line' => 'true', 'precision' => 0, 'legend_labels' => \@labels, 'legend' => 'bottom', # 'polar' => 'true', ); $g->png("$samples/direction_1.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/mountain_3.t0000644000175000017500000000776114341172251015507 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Mountain; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $obj = new Chart::Mountain( 500, 400 ); my @data = ( [ '2006-06-01', '2006-06-02', '2006-06-03', '2006-06-04', '2006-06-05', '2006-06-06', '2006-06-07', '2006-06-08', '2006-06-09', '2006-06-10', '2006-06-11', '2006-06-12', '2006-06-13', '2006-06-14', '2006-06-15', '2006-06-16', '2006-06-17', '2006-06-18', '2006-06-19', '2006-06-20', '2006-06-21', '2006-06-22', '2006-06-23', '2006-06-24', '2006-06-25', '2006-06-26', '2006-06-27', '2006-06-28', '2006-06-29', '2006-06-30', '2006-07-01', '2006-07-02', '2006-07-03', '2006-07-04', '2006-07-05', '2006-07-06', '2006-07-07', '2006-07-08', '2006-07-09', '2006-07-10', '2006-07-11', '2006-07-12', '2006-07-13', '2006-07-14', '2006-07-15', '2006-07-16', '2006-07-17', '2006-07-18', '2006-07-19', '2006-07-20', '2006-07-21', '2006-07-22', '2006-07-23', '2006-07-24', '2006-07-25', '2006-07-26', '2006-07-27', '2006-07-28', '2006-07-29', '2006-07-30', '2006-07-31', '2006-08-01', '2006-08-02', '2006-08-03', '2006-08-04', '2006-08-05', '2006-08-06', '2006-08-07', '2006-08-08', '2006-08-09', '2006-08-10', '2006-08-11', '2006-08-12', '2006-08-13', '2006-08-14', '2006-08-15', '2006-08-16', '2006-08-17', '2006-08-18', '2006-08-19', '2006-08-20', '2006-08-21', '2006-08-22', '2006-08-23', '2006-08-24', '2006-08-25', '2006-08-26', '2006-08-27', '2006-08-28', '2006-08-29', '2006-08-30', '2006-08-31' ], [ 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900 ], [ 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 220, 220, 220, 220, 220, 220, 220, 220, 220, 220, 230, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 350 ], [ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 37 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10 ] ); my @labels = ( 'NOT RUN', 'PASS', 'FAIL', 'OTHER' ); $obj->set( 'precision' => 0, 'max_y_ticks' => 10, 'max_x_ticks' => 10, 'skip_x_ticks' => 5, 'x_ticks' => 'vertical', 'x_label' => 'Date', 'y_label' => 'Cumulative Results', 'colors' => { # not run: f2e5af 'dataset0' => [ 0xf2, 0xe5, 0xaf ], # pass: a6f2a7 'dataset1' => [ 0xa6, 0xf2, 0xa7 ], # fail: e8b2b2 'dataset2' => [ 0xe8, 0xb2, 0xb2 ], # other: bcdaeb 'dataset3' => [ 0xbc, 0xda, 0xeb ] }, 'legend_labels' => \@labels, 'grey_background' => 0, ); $obj->png( "$samples/mountain_3.png", \@data ); print "ok 1\n"; exit(0); Chart-v2.403.9/t/bars_12.t0000644000175000017500000000171314341172251014653 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Bars; use strict; use POSIX; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $file = File::Spec->catfile( File::Spec->curdir, 't', 'data', "in.csv" ); my $g = Chart::Bars->new( 600, 400 ); $g->add_datafile( $file ); $g->set( colors => { dataset0 => [ 25, 220, 147 ], }, graph_border => 0, grey_background => 'false', grid_lines => 'true', include_zero => 'true', # integer_ticks_only => 'true', legend => 'none', png_border => 4, precision => 1, skip_int_ticks => 1000, text_space => 3, title => "Tickets", title_font => GD::Font->Giant, transparent => 'false', x_ticks => 'vertical', y_axes => 'both', y_label => '# Tickets', x_label => 'Date', ); $g->png("$samples/bars_11.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/lines_5.t0000644000175000017500000000223014341172251014753 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Lines; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Lines->new( 600, 300 ); @x_values = (); @y_values = (); for ( $i = 0 ; $i <= 16 ; $i += 0.05 ) { $j = sin($i); $j2 = cos($i); push( @x_values, $i ); push( @y_values, $j ); push( @y2_values, $j2 ); } $g->add_dataset(@x_values); $g->add_dataset(@y_values); $g->add_dataset(@y2_values); %hash = ( 'title' => 'The trigonometric functions sinus and cosinus', 'grid_lines' => 'true', 'legend' => 'left', 'xy_plot' => 'true', 'skip_x_ticks' => 20, 'legend_labels' => [ 'y = sin x', 'y = cos x' ], 'precision' => 2, 'integer_ticks_only' => 'true', #'custom_x_ticks' => [0,3], 'colors' => { 'title' => 'plum', 'dataset0' => 'mauve', }, 'f_x_tick' => \&formatter, ); $g->set(%hash); $g->png("$samples/lines_5.png"); sub formatter { my $d = shift; $d = sprintf "%1.2f", $d; if ( $d =~ /^0.00/ ) { return 0 } return $d; } print "ok 1\n"; exit(0); Chart-v2.403.9/t/linespoints_6.t0000644000175000017500000004131214341172251016215 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::LinesPoints; use Chart::Lines; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my @bezugszeitraum = ( '2004-06-13 00:00:00+00', '2004-06-14 00:00:00+00', '2004-06-15 00:00:00+00', '2004-06-16 00:00:00+00', '2004-06-17 00:00:00+00', '2004-06-18 00:00:00+00', '2004-06-19 00:00:00+00', '2004-06-20 00:00:00+00', '2004-06-21 00:00:00+00', '2004-06-22 00:00:00+00', '2004-06-23 00:00:00+00', '2004-06-24 00:00:00+00', '2004-06-25 00:00:00+00', '2004-06-26 00:00:00+00', '2004-06-27 00:00:00+00', '2004-06-28 00:00:00+00', '2004-06-29 00:00:00+00', '2004-06-30 00:00:00+00', '2004-07-01 00:00:00+00', '2004-07-02 00:00:00+00', '2004-07-03 00:00:00+00', '2004-07-04 00:00:00+00', '2004-07-05 00:00:00+00', '2004-07-06 00:00:00+00', '2004-07-07 00:00:00+00', '2004-07-08 00:00:00+00', '2004-07-09 00:00:00+00', '2004-07-10 00:00:00+00', '2004-07-11 00:00:00+00', '2004-07-12 00:00:00+00', '2004-07-13 00:00:00+00', '2004-07-14 00:00:00+00', '2004-07-15 00:00:00+00', '2004-07-16 00:00:00+00', '2004-07-17 00:00:00+00', '2004-07-18 00:00:00+00', '2004-07-19 00:00:00+00', '2004-07-20 00:00:00+00', '2004-07-21 00:00:00+00', '2004-07-22 00:00:00+00', '2004-07-23 00:00:00+00', '2004-07-24 00:00:00+00', '2004-07-25 00:00:00+00', '2004-07-26 00:00:00+00', '2004-07-27 00:00:00+00', '2004-07-28 00:00:00+00', '2004-07-29 00:00:00+00', '2004-07-30 00:00:00+00', '2004-07-31 00:00:00+00', '2004-08-01 00:00:00+00', '2004-08-02 00:00:00+00', '2004-08-03 00:00:00+00', '2004-08-04 00:00:00+00', '2004-08-05 00:00:00+00', '2004-08-06 00:00:00+00', '2004-08-07 00:00:00+00', '2004-08-08 00:00:00+00', '2004-08-09 00:00:00+00', '2004-08-10 00:00:00+00', '2004-08-11 00:00:00+00', '2004-08-12 00:00:00+00', '2004-08-13 00:00:00+00', '2004-08-14 00:00:00+00', '2004-08-15 00:00:00+00', '2004-08-16 00:00:00+00', '2004-08-17 00:00:00+00', '2004-08-18 00:00:00+00', '2004-08-19 00:00:00+00', '2004-08-20 00:00:00+00', '2004-08-21 00:00:00+00', '2004-08-22 00:00:00+00', '2004-08-23 00:00:00+00', '2004-08-24 00:00:00+00', '2004-08-25 00:00:00+00', '2004-08-26 00:00:00+00', '2004-08-27 00:00:00+00', '2004-08-28 00:00:00+00', '2004-08-29 00:00:00+00', '2004-08-30 00:00:00+00', '2004-08-31 00:00:00+00', '2004-09-01 00:00:00+00', '2004-09-02 00:00:00+00', '2004-09-03 00:00:00+00', '2004-09-04 00:00:00+00', '2004-09-05 00:00:00+00', '2004-09-06 00:00:00+00', '2004-09-07 00:00:00+00', '2004-09-08 00:00:00+00', '2004-09-09 00:00:00+00', '2004-09-10 00:00:00+00', '2004-09-11 00:00:00+00', '2004-09-12 00:00:00+00', '2004-09-13 00:00:00+00', '2004-09-14 00:00:00+00', '2004-09-15 00:00:00+00', '2004-09-16 00:00:00+00', '2004-09-17 00:00:00+00', '2004-09-18 00:00:00+00', '2004-09-19 00:00:00+00', '2004-09-20 00:00:00+00', '2004-09-21 00:00:00+00', '2004-09-22 00:00:00+00', '2004-09-23 00:00:00+00', '2004-09-24 00:00:00+00', '2004-09-25 00:00:00+00', '2004-09-26 00:00:00+00', '2004-09-27 00:00:00+00', '2004-09-28 00:00:00+00', '2004-09-29 00:00:00+00', '2004-09-30 00:00:00+00', '2004-10-01 00:00:00+00', '2004-10-02 00:00:00+00', '2004-10-03 00:00:00+00', '2004-10-04 00:00:00+00', '2004-10-05 00:00:00+00', '2004-10-06 00:00:00+00', '2004-10-07 00:00:00+00', '2004-10-08 00:00:00+00', '2004-10-09 00:00:00+00', '2004-10-10 00:00:00+00', '2004-10-11 00:00:00+00', '2004-10-12 00:00:00+00', '2004-10-13 00:00:00+00', '2004-10-14 00:00:00+00', '2004-10-15 00:00:00+00', '2004-10-16 00:00:00+00', '2004-10-17 00:00:00+00', '2004-10-18 00:00:00+00', '2004-10-19 00:00:00+00', '2004-10-20 00:00:00+00', '2004-10-21 00:00:00+00', '2004-10-22 00:00:00+00', '2004-10-23 00:00:00+00', '2004-10-24 00:00:00+00', '2004-10-25 00:00:00+00', '2004-10-26 00:00:00+00', '2004-10-27 00:00:00+00', '2004-10-28 00:00:00+00', '2004-10-29 00:00:00+00', '2004-10-30 00:00:00+00', '2004-10-31 00:00:00+00', '2004-11-01 00:00:00+00', '2004-11-02 00:00:00+00', '2004-11-03 00:00:00+00', '2004-11-04 00:00:00+00', '2004-11-05 00:00:00+00', '2004-11-06 00:00:00+00', '2004-11-07 00:00:00+00', '2004-11-08 00:00:00+00', '2004-11-09 00:00:00+00', '2004-11-10 00:00:00+00', '2004-11-11 00:00:00+00', '2004-11-12 00:00:00+00', '2004-11-13 00:00:00+00', '2004-11-14 00:00:00+00', '2004-11-15 00:00:00+00', '2004-11-16 00:00:00+00', '2004-11-17 00:00:00+00', '2004-11-18 00:00:00+00', '2004-11-19 00:00:00+00', '2004-11-20 00:00:00+00', '2004-11-21 00:00:00+00', '2004-11-22 00:00:00+00', '2004-11-23 00:00:00+00', '2004-11-24 00:00:00+00', '2004-11-25 00:00:00+00', '2004-11-26 00:00:00+00', '2004-11-27 00:00:00+00', '2004-11-28 00:00:00+00', '2004-11-29 00:00:00+00', '2004-11-30 00:00:00+00', '2004-12-01 00:00:00+00', '2004-12-02 00:00:00+00', '2004-12-03 00:00:00+00', '2004-12-04 00:00:00+00', '2004-12-05 00:00:00+00', '2004-12-06 00:00:00+00', '2004-12-07 00:00:00+00', '2004-12-08 00:00:00+00', '2004-12-09 00:00:00+00', '2004-12-10 00:00:00+00', '2004-12-11 00:00:00+00', '2004-12-12 00:00:00+00', '2004-12-13 00:00:00+00', '2004-12-14 00:00:00+00', '2004-12-15 00:00:00+00', '2004-12-16 00:00:00+00', '2004-12-17 00:00:00+00', '2004-12-18 00:00:00+00', '2004-12-19 00:00:00+00', '2004-12-20 00:00:00+00', '2004-12-21 00:00:00+00', '2004-12-22 00:00:00+00', '2004-12-23 00:00:00+00', '2004-12-24 00:00:00+00', '2004-12-25 00:00:00+00', '2004-12-26 00:00:00+00', '2004-12-27 00:00:00+00', '2004-12-28 00:00:00+00', '2004-12-29 00:00:00+00', '2004-12-30 00:00:00+00', '2004-12-31 00:00:00+00', '2005-01-01 00:00:00+00', '2005-01-02 00:00:00+00', '2005-01-03 00:00:00+00', '2005-01-04 00:00:00+00', '2005-01-05 00:00:00+00', '2005-01-06 00:00:00+00', '2005-01-07 00:00:00+00', '2005-01-08 00:00:00+00', '2005-01-09 00:00:00+00', '2005-01-10 00:00:00+00', '2005-01-11 00:00:00+00', '2005-01-12 00:00:00+00', '2005-01-13 00:00:00+00', '2005-01-17 00:00:00+00', '2005-01-18 00:00:00+00', '2005-01-25 00:00:00+00', '2005-01-26 00:00:00+00', '2005-01-28 00:00:00+00', '2005-01-29 00:00:00+00', '2005-01-31 00:00:00+00', '2005-02-08 00:00:00+00', '2005-02-09 00:00:00+00', '2005-02-10 00:00:00+00', '2005-02-11 00:00:00+00', '2005-02-12 00:00:00+00', '2005-02-13 00:00:00+00', '2005-02-14 00:00:00+00', '2005-02-15 00:00:00+00', '2005-02-16 00:00:00+00', '2005-02-17 00:00:00+00', '2005-02-18 00:00:00+00', '2005-02-19 00:00:00+00', '2005-02-20 00:00:00+00', '2005-02-21 00:00:00+00', '2005-02-22 00:00:00+00', '2005-02-23 00:00:00+00', '2005-02-24 00:00:00+00', '2005-02-25 00:00:00+00', '2005-02-26 00:00:00+00', '2005-02-27 00:00:00+00', '2005-02-28 00:00:00+00', '2005-03-01 00:00:00+00', '2005-03-02 00:00:00+00', '2005-03-03 00:00:00+00', '2005-03-04 00:00:00+00', '2005-03-05 00:00:00+00', '2005-03-06 00:00:00+00', '2005-03-07 00:00:00+00', '2005-03-08 00:00:00+00', '2005-03-09 00:00:00+00', '2005-03-10 00:00:00+00', '2005-03-11 00:00:00+00', '2005-03-12 00:00:00+00', '2005-03-13 00:00:00+00', '2005-03-14 00:00:00+00', '2005-03-15 00:00:00+00', '2005-03-16 00:00:00+00', '2005-03-17 00:00:00+00', '2005-03-18 00:00:00+00', '2005-03-19 00:00:00+00', '2005-03-20 00:00:00+00', '2005-03-21 00:00:00+00', '2005-03-22 00:00:00+00', '2005-03-23 00:00:00+00', '2005-03-24 00:00:00+00', '2005-03-25 00:00:00+00', '2005-03-26 00:00:00+00', '2005-03-27 00:00:00+00', '2005-03-28 00:00:00+00', '2005-03-29 00:00:00+00', '2005-03-30 00:00:00+00', '2005-03-31 00:00:00+00', '2005-04-01 00:00:00+00', '2005-04-02 00:00:00+00', '2005-04-03 00:00:00+00', '2005-04-04 00:00:00+00', '2005-04-05 00:00:00+00', '2005-04-06 00:00:00+00', '2005-04-07 00:00:00+00', '2005-04-08 00:00:00+00', '2005-04-09 00:00:00+00', '2005-04-10 00:00:00+00', '2005-04-11 00:00:00+00', '2005-04-12 00:00:00+00', '2005-04-13 00:00:00+00', '2005-04-14 00:00:00+00', '2005-04-15 00:00:00+00', '2005-04-16 00:00:00+00', '2005-04-17 00:00:00+00', '2005-04-18 00:00:00+00', '2005-04-19 00:00:00+00', '2005-04-20 00:00:00+00', '2005-04-21 00:00:00+00', '2005-04-22 00:00:00+00', '2005-04-23 00:00:00+00', '2005-04-24 00:00:00+00', '2005-04-25 00:00:00+00', '2005-04-26 00:00:00+00', '2005-04-27 00:00:00+00', '2005-04-28 00:00:00+00', '2005-04-29 00:00:00+00', '2005-04-30 00:00:00+00', '2005-05-01 00:00:00+00', '2005-05-02 00:00:00+00', '2005-05-03 00:00:00+00', '2005-05-04 00:00:00+00', '2005-05-05 00:00:00+00', '2005-05-06 00:00:00+00', '2005-05-07 00:00:00+00', '2005-05-08 00:00:00+00', '2005-05-09 00:00:00+00', '2005-05-10 00:00:00+00', '2005-05-11 00:00:00+00', '2005-05-12 00:00:00+00', '2005-05-13 00:00:00+00', '2005-05-14 00:00:00+00', '2005-05-15 00:00:00+00', '2005-05-16 00:00:00+00', '2005-05-17 00:00:00+00', '2005-05-18 00:00:00+00', '2005-05-19 00:00:00+00', '2005-05-20 00:00:00+00', '2005-05-21 00:00:00+00', '2005-05-22 00:00:00+00', '2005-05-23 00:00:00+00', '2005-05-24 00:00:00+00', '2005-05-25 00:00:00+00', '2005-05-26 00:00:00+00', '2005-05-27 00:00:00+00', '2005-05-28 00:00:00+00', '2005-05-29 00:00:00+00', '2005-05-30 00:00:00+00', '2005-05-31 00:00:00+00', '2005-06-01 00:00:00+00', '2005-06-02 00:00:00+00', '2005-06-03 00:00:00+00', '2005-06-04 00:00:00+00', '2005-06-05 00:00:00+00', '2005-06-06 00:00:00+00', ); my @obsepoch = ( 81.8670764502497, 42.4188998589563, 100, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 100, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.6528982992017, 100, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99.6180555555556, 100, 100, 100, 100, 100, 100, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652777777778, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.7223186393613, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9306037473976, 99.9306037473976, 99.9652898299202, 99.7570288094412, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9306037473976, 99.9306037473976, 99.9306037473976, 99.9652898299202, 99.9652898299202, 99.9652898299202, 99.9306037473976, 99.9306037473976, 99.9306037473976, 99.9306037473976, 99.9306037473976, 99.9306037473976, 99.9306037473976, 99.9306037473976, 94.5176960444136, 100, 98.125, 100, 100, 100, 100, 100, 99.4444444444444, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99.9652777777778, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99.6180555555556, 100, 100, 100, 100, 100, 100, 100, 100, 99.6527777777778, 100, 100, 100, 100, 100, 100, 94.9494949494949, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99.965241571081, 99.930483142162, 99.860966284324, 99.8262078554049, 99.7914494264859, 99.7566063977747, 99.6870653685674, 99.6175243393602, 99.547983310153, 99.4784422809458, 99.3741307371349, 99.5138888888889, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, ); my $g = Chart::LinesPoints->new( 700, 450 ); #my $g = Chart::Lines->new(700,450); $g->add_dataset(@bezugszeitraum); $g->add_dataset(@obsepoch); $g->set( 'x_ticks' => 'vertical' ); $g->set( 'x_label' => ' Time' ); $g->set( 'y_label' => 'actual_nr_of_obsepoch / possible_nr' ); $g->set( 'legend' => 'none' ); $g->set( 'precision' => 0 ); $g->set( 'title' => 'DRES' ); $g->set( 'grey_background' => 'false' ); $g->set( 'max_val' => '100' ); $g->set( 'min_val' => '80' ); $g->set( 'pt_size' => '20' ); $g->set( 'brush_size' => '4' ); $g->set( 'skip_x_ticks' => '50' ); $g->png("$samples/linespoints_6.png"); print "ok 1\n\n"; Chart-v2.403.9/t/bars_8.t0000644000175000017500000000244514341172251014603 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Bars; use strict; use POSIX; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $a; $a = 10**(-30); print "1..1\n"; my $g = Chart::Bars->new( 600, 400 ); my @data = ( 200202, 200203, 200204, 200205, 200206, 200207, 200208, 200209, 200210, 200211, 200212, 200301 ); my @data1 = ( 6626 * $a, -790 * $a, 7580 * $a, 7671 * $a, 8764 * $a, 8664 * $a, 6343 * $a, 5518 * $a, 6257 * $a, 5391 * $a, 5401 * $a, 6002 * $a ); #my @data1 =(6626,-790,7580,7671,8764,8664,6343,5518,6257,5391,5401,6002); $g->add_dataset(@data); $g->add_dataset(@data1); my @legend_keys = ( "Actual ", "Goal" ); $g->set( colors => { dataset0 => [ 25, 220, 147 ], }, graph_border => 0, grey_background => 'false', grid_lines => 'true', include_zero => 'true', # integer_ticks_only => 'true', legend => 'none', png_border => 4, # precision => 27, # skip_int_ticks => 1.0e-27, text_space => 3, title => "Tickets", title_font => GD::Font->Giant, transparent => 'false', x_ticks => 'vertical', y_axes => 'both', y_label => '# Tickets', x_label => 'Date', ); $g->png("$samples/bars_8.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/split_1.t0000644000175000017500000000130214341172251014767 0ustar herbertherbert#!/usr/bin/perl -w use strict; BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Split; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my ( $x, $y, @x, @y, %hash ); my $g = Chart::Split->new(); for ( my $i = 0 ; $i < 60 ; $i += .05 ){ $y = sin($i); $x = $i; push @x, $x; push @y, $y; } $g->add_dataset(@x); $g->add_dataset(@y); %hash = ( 'start' => 0, 'interval' => 20, 'interval_ticks' => 21, 'brush_size' => 1, 'legend' => 'none', 'title' => 'f(x) = sin x', 'precision' => 0, 'y_grid_lines' => 'true', ); $g->set(%hash); $g->png("$samples/split_1.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/pie_1.t0000644000175000017500000000201214341172251014410 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pie; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Pie->new( 550, 500 ); $g->add_dataset( 'The Red', 'The Black', 'The Yellow', 'The Brown', 'The Green' ); $g->add_dataset( 430, 411, 50, 10, 100 ); $g->set( 'title' => 'The Parlament' ); $g->set( 'label_values' => 'percent' ); $g->set( 'legend_label_values' => 'value' ); $g->set( 'legend' => 'top' ); $g->set( 'grey_background' => 'false' ); $g->set( 'x_label' => 'seats in the parlament' ); $g->set( 'colors' => { 'misc' => 'light_blue', 'background' => 'lavender', 'dataset0' => 'red', 'dataset1' => 'black', 'dataset2' => [ 210, 210, 0 ], 'dataset3' => 'DarkOrange', 'dataset4' => 'green' } ); $g->set( 'legend_lines' => 'true' ); $g->png("$samples/pie_1.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/mapbars.t0000644000175000017500000000316014341172251015045 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::Bars; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $png_name = "$samples/mapbars.png"; my @legend_keys = ( "Actual ", "Goal" ); my $Graph = new Chart::Bars( 600, 400 ); print "1..1\n"; $Graph->add_dataset( "Oct 01", "Nov 01", "Dec 01", "Jan 02", "Feb 02", "Mar 02" ); $Graph->add_dataset( 95.1, 84.4, 90.2, 94.4, 93.8, 95.5 ); $Graph->add_dataset( 93.0, 83.0, 94.0, 94.0, 94.0, 94.0 ); $Graph->set( composite_info => [ [ 'Bars', [1] ], [ 'Lines', [2] ] ], colors => { dataset0 => 'green', dataset1 => 'red' }, title_font => GD::Font->Giant, label_font => GD::Font->Small, legend_font => GD::Font->Large, tick_label_font => GD::Font->Large, grid_lines => 'true', graph_border => 0, imagemap => 'true', legend => 'bottom', legend_labels => \@legend_keys, max_val => 100, min_val => 80, png_border => 4, same_y_axes => 'true', spaced_bars => 'true', title => "Yield 2004", text_space => 5, transparent => 'true', x_ticks => 'vertical', integer_ticks_only => 'true', skip_int_ticks => 5, ); $Graph->png("$png_name"); my $imagemap_data = $Graph->imagemap_dump(); foreach my $ds ( 1 .. 1 ) { foreach my $pt ( 0 .. 5 ) { my @i = @{ $imagemap_data->[$ds]->[$pt] }; # ** print "Dataset:$ds - Point: $pt ---- VALUES: @i \n"; } } print "ok 1\n"; exit 0; Chart-v2.403.9/t/pareto_2.t0000644000175000017500000000177114341172251015141 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pareto; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Pareto->new( 500, 400 ); $g->add_dataset( '1st week', '2nd week', '3rd week', '4th week', '5th week', '6th week', '7th week', '8th week', '9th week', '10th week' ); $g->add_dataset( 37, 15, 9, 4, 3.5, 2.1, 1.2, 1.5, 6.2, 16 ); %hash = ( 'colors' => { 'dataset0' => 'mauve', 'dataset1' => 'light_blue', 'title' => 'orange', }, 'title' => 'Visitors at the Picasso Exhibition', 'integer_ticks_only' => 'true', 'skip_int_ticks' => 5, 'grey_background' => 'false', 'max_val' => 100, 'y_label' => 'Visitors in Thousands', 'x_ticks' => 'vertical', 'spaced_bars' => 'true', 'legend' => 'none', ); $g->set(%hash); $g->png("$samples/pareto_2.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/pie_6.t0000644000175000017500000000135014341172251014421 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pie; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Pie->new( 500, 400 ); #$g = Chart::Pie->new; $g->add_dataset( 'Free', 'Reserved', 'Deactivated', 'Leased', 'Unavailable' ); $g->add_dataset( 90, 0, 1, 216, 0 ); %opt = ( 'label_values' => 'none', 'legend_label_values' => 'both', 'legend' => 'right', 'text_space' => 10, 'png_border' => 1, 'graph_border' => 0, 'grey_background' => 'false', 'x_label' => 'Total IPs In Scope 253', ); $g->set(%opt); $g->png("$samples/pie_6.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/pie_7.t0000644000175000017500000000152514341172251014426 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pie; use GD; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Pie->new( 500, 450 ); $g->add_dataset( 'eins', 'zwei', 'drei', 'vier', 'fuenf', 'sechs', 'sieben', 'acht', 'neun', 'zehn' ); $g->add_dataset( 40, 1, 12, 37, 8, 50, 19, 23, 5, 90 ); $g->set( 'title' => 'Pie\nDemo Chart' ); $g->set( 'sub_title' => 'Ring_Pie' ); $g->set( 'label_values' => 'value' ); $g->set( 'legend_label_values' => 'value' ); $g->set( 'legend' => 'bottom' ); $g->set( 'x_label' => '' ); $g->set( 'ring' => 0.1 ); $g->set( 'colors' => { 'background' => 'grey' } ); $g->png("$samples/pie_7.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/composite_7.t0000644000175000017500000000306514341172251015654 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Composite; use Chart::Lines; use Chart::Points; use Chart::LinesPoints; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my @y = qw( 2.6593 2.0832 2.0519 2.2257 2.4355 2.4183 3.4088 2.2899 2.4914 2.3217 2.0684 2.1328 1.8168 1.7662 1.7592 1.8624 1.2614 ); my @x = qw(4757 14055 23004 29698 32172 31038 33068 33383 33941 32451 25235 17035 12122 9868 6647 4024 944); my @x2 = qw(1.706 1.756 1.807 1.858 1.909 1.959 2.010 2.061 2.112 2.162 2.213 2.264 2.315 2.365 2.416 2.467 2.518); my %hash = qw(precision 2 title red text blue include_zero true graph_border 0 y_grid_lines true legend bottom y_axes both skip_x_ticks 1 brush_size 4 brush_size1 4 brush_size2 3 no_cache true *xy_plot true*); $hash{title} = "my title"; my $c = 0; $hash{colors} = { 'dataset0' => [ 255, 0, 0 ], 'dataset1' => [ 0, 0, 255 ], 'dataset2' => [ 173, 170, 61 ], 'dataset3' => [ 242, 2, 249 ], 'dataset4' => [ 254, 177, 192 ], 'y_label' => [ 0, 0, 0 ], 'y_label2' => [ 173, 170, 61 ], }; $hash{brushStyle1} = 'fatPlus'; $hash{brushStyle2} = 'hollowSquare'; my $g; if (0) { $g = Chart::Lines->new( 1000, 400 ); } else { $g = Chart::Composite->new( 1000, 400 ); $g->set( 'composite_info' => [ [ 'Points', [1] ], [ 'LinesPoints', [2] ] ] ); } my @s = sort { $x[$a] <=> $x[$b]; } 0 .. $#x; $g->add_dataset( @x[@s] ); $g->add_dataset( @y[@s] ); $g->add_dataset(@x2); $g->set(%hash); $g->jpeg("$samples/composite_7.jpg"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/hbars_3.t0000644000175000017500000000132714341172251014744 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::HorizontalBars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::HorizontalBars->new( 500, 400 ); $g->add_dataset( 'Foo', 'bar', 'junk', 'ding', 'bat' ); $g->add_dataset( -4, -3, -4, -5, -2 ); $g->add_dataset( -2, -10, -3, -8, -3 ); %hash = ( 'y_axes' => 'right', 'title' => 'Horizontal Bars Demo', 'x_grid_lines' => 'true', 'x_label' => 'x-axis', 'y_label' => 'y-axis', 'x_ticks' => 'staggered', 'include_zero' => 'true', 'spaced_bars' => 'false', ); $g->set(%hash); $g->png("$samples/hbars_3.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/linespoints_2.t0000644000175000017500000000161214341172251016210 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::LinesPoints; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my ( @data, @data2, @labels, %hash, $g, $hits ); # create an array of labels for ( 0 .. 30 ) { push( @labels, $_ ); } # create two arrays of data for ( 0 .. 30 ) { #first array $hits = 2 * $_; if ( $_ % 2 == 0 ) { $hits = $_; } push( @data, $hits ); #second array $hits = 40 - $_ + 10 * cos($_); push( @data2, $hits ); } $g = Chart::LinesPoints->new( 600, 300 ); $g->add_dataset(@labels); $g->add_dataset(@data); $g->add_dataset(@data2); %hash = ( 'title' => 'Lines with Points Demo', 'y_axes' => 'both', 'legend' => 'none', 'precision' => 0, 'xy_plot' => 'true', ); $g->set(%hash); $g->png("$samples/linespoints_2.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/bars_11.t0000644000175000017500000000171314341172251014652 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Bars; use strict; use POSIX; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $file = File::Spec->catfile( File::Spec->curdir, 't', 'data', "in.tsv" ); my $g = Chart::Bars->new( 600, 400 ); $g->add_datafile( $file ); $g->set( colors => { dataset0 => [ 25, 220, 147 ], }, graph_border => 0, grey_background => 'false', grid_lines => 'true', include_zero => 'true', # integer_ticks_only => 'true', legend => 'none', png_border => 4, precision => 1, skip_int_ticks => 1000, text_space => 3, title => "Tickets", title_font => GD::Font->Giant, transparent => 'false', x_ticks => 'vertical', y_axes => 'both', y_label => '# Tickets', x_label => 'Date', ); $g->png("$samples/bars_11.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/mountain_2.t0000644000175000017500000000367214341172251015503 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Mountain; use File::Spec; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..2\n"; my $a = ( 10**(-6) ); my @data = ( [ "1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th" ], [ ( 3 * $a ), ( 7 * $a ), ( 8 * $a ), ( 2 * $a ), ( 4 * $a ), ( 8.5 * $a ), ( 2 * $a ), ( 5 * $a ), ( 9 * $a ) ], [ ( 4 * $a ), ( 2 * $a ), ( 5 * $a ), ( 6 * $a ), ( 3 * $a ), ( 2.5 * $a ), ( 3 * $a ), ( 3 * $a ), ( 4 * $a ) ], [ ( 7 * $a ), ( 3 * $a ), ( 2 * $a ), ( 8 * $a ), ( 8.5 * $a ), ( 2 * $a ), ( 9 * $a ), ( 4 * $a ), ( 5 * $a ) ], ); my @hex_colors = qw(0099FF 00CC00 FFCC33 FF0099 3333FF); my @colors = map { [ map { hex($_) } unpack( "a2 a2 a2", $_ ) ] } @hex_colors; my @patterns = (); foreach ( 1 .. @data - 1 ) { open( PNG, '<' . File::Spec->catfile( File::Spec->curdir, 't', 'patterns', "PATTERN$_.PNG" ) ) || die "Can't load pattern $_"; push( @patterns, GD::Image->newFromPng( \*PNG ) ); close(PNG); } my @opts = ( {}, { 'x_label' => 'X Label', 'y_label' => 'Y label', 'title' => 'Mountain Chart', 'grid_lines' => 'true', 'colors' => { map { ( "dataset$_" => $colors[$_] ) } 0 .. @colors - 1 }, 'precision' => 6, #'integer_ticks_only' => 'true', }, { 'x_label' => 'X Label', 'y_label' => 'Y label', 'title' => 'Mountain Chart with Patterns', 'grid_lines' => 'true', 'colors' => { map { ( "dataset$_" => $colors[$_] ) } 0 .. @colors - 1 }, 'patterns' => \@patterns, 'precision' => 5, }, ); foreach my $i ( 1 .. @opts - 1 ) { my $newpath = File::Spec->catfile( $samples, "mountain_2-$i.png" ); my $opts = $opts[$i]; my $g = new Chart::Mountain(); $g->set(%$opts); my $Image = $g->png( $newpath, \@data ); print "ok $i\n"; } exit(0); Chart-v2.403.9/t/linespoints_7.t0000644000175000017500000000223314341172251016215 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::LinesPoints; use Chart::Lines; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my @bezugszeitraum = ( '2004-06-13 00:00:00+00', '2004-06-14 00:00:00+00', '2004-06-15 00:00:00+00', '2004-06-16 00:00:00+00', '2004-06-17 00:00:00+00' ); my @obsepoch = ( 81.8670764502497, 42.4188998589563, 100, 0.9652898299202, 12.9652898299202 ); my $g = Chart::LinesPoints->new( 700, 450 ); #my $g = Chart::Lines->new(700,450); $g->add_dataset(@bezugszeitraum); $g->add_dataset(@obsepoch); $g->set( 'x_ticks' => 'staggered' ); $g->set( 'x_label' => ' Time' ); $g->set( 'y_label' => 'actual_nr_of_obsepoch / possible_nr' ); $g->set( 'legend' => 'none' ); $g->set( 'precision' => 0 ); $g->set( 'title' => 'Station Test' ); $g->set( 'grey_background' => 'false' ); $g->set( 'max_val' => '100' ); $g->set( 'min_val' => '0' ); $g->set( 'pt_size' => '10' ); $g->set( 'brush_size' => '3' ); $g->set( 'stepline' => 'true' ); $g->png("$samples/linespoints_7.png"); print "ok 1\n\n"; Chart-v2.403.9/t/pie_3.t0000644000175000017500000000174514341172251014426 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pie; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Pie->new( 500, 500 ); $g->add_dataset( 'Har', 'Sug', 'Ert', 'Her', 'Tar', 'Kure' ); $g->add_dataset( 12000, 20000, 13000, 15000, 9000, 11000 ); %opt = ( 'title' => 'Another Pie Demo Chart', 'label_values' => 'both', 'legend' => 'none', 'text_space' => 10, 'png_border' => 1, 'graph_border' => 0, 'colors' => { 'x_label' => 'red', 'misc' => 'plum', 'background' => 'grey', 'dataset0' => [ 120, 0, 255 ], 'dataset1' => [ 120, 100, 255 ], 'dataset2' => [ 120, 200, 255 ], 'dataset3' => [ 255, 100, 0 ], 'dataset4' => [ 255, 50, 0 ], 'dataset5' => [ 255, 0, 0 ], }, 'x_label' => 'The Winner is Team Blue!', ); $g->set(%opt); $g->png("$samples/pie_3.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/direction_3.t0000644000175000017500000000153114341172251015622 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Direction; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Direction->new( 500, 500 ); $g->add_dataset( 0, 100, 50, 200, 280, 310 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->add_dataset( 10, 110, 60, 210, 290, 320 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->add_dataset( 20, 120, 70, 220, 300, 330 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->set( 'title' => 'Direction Demo', 'angle_interval' => 45, 'precision' => 0, 'arrow' => 'true', 'point' => 'false', 'include_zero' => 'true', 'pairs' => 'true', 'legend' => 'none', 'grey_background' => 'false', ); $g->png("$samples/direction_3.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/points_3.t0000644000175000017500000000110214341172251015150 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Points; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Points->new; $g->add_dataset( 1, 2, 7, 10, 15, 19, 20 ); $g->add_dataset( -3, 0, 8, 4, 2, 1, 0 ); @hash = ( 'title' => 'Points Chart 3', # 'type_style' => 'donut', 'png_border' => 10, 'precision' => 0, 'min_val' => 0, #'max_val' => 0, 'include_zero' => 'true', 'xy_plot' => 'true', ); $g->set(@hash); $g->png("$samples/points_3.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/pie_11.t0000644000175000017500000000127314341172251014501 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pie; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Pie->new( 450, 450 ); $g->add_dataset( 'HOST A', 'HOST B', 'HOST C', 'HOST D', 'HOST E', 'HOST F' ); $g->add_dataset( 5, 2, 1, 2, 9, 12 ); $g->set( 'title' => 'Total Access Attempts' ); $g->set( 'label_values' => 'value' ); $g->set( 'legend_label_values' => 'percent' ); $g->set( 'legend' => 'bottom' ); $g->set( 'grey_background' => 'true' ); $g->set( 'legend_lines' => 'true' ); $g->png("$samples/pie_11.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/data/0000775000175000017500000000000014341172251014146 5ustar herbertherbertChart-v2.403.9/t/data/in.csv0000644000175000017500000000024614341172251015271 0ustar herbertherbert200202, 200203, 200204, 200205, 200206, 200207, 200208, 200209, 200210, 200211, 200212, 200301 6626, 7090, 7580, 7671, 8764, 8664, 6343, 5518, 6257, 5391, 5401, 6002 Chart-v2.403.9/t/data/in.tsv0000644000175000017500000000022014341172251015302 0ustar herbertherbert200202 200203 200204 200205 200206 200207 200208 200209 200210 200211 200212 200301 6626 7090 7580 7671 8764 8664 6343 5518 6257 5391 5401 6002 Chart-v2.403.9/t/error_2.t0000644000175000017500000000153314341172251014774 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::ErrorBars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::ErrorBars->new(); $g->add_dataset(qw(1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2 2.1 2.2 2.3 2.4 2.5)); $g->add_dataset(qw(1 1.1 1.2 1.1 1.14 1.15 1.26 1.2 1.1 1.19 1.2 1.4 1.6 2.0 2.5 3.1)); $g->add_dataset(qw(0.4 0.1 0.2 0.1 0.14 0.15 0.26 0.27 0.1 0.19 0.2 0.1 0.1 0.2 0.1 0.3)); $g->set( 'xy_plot' => 'true', 'precision' => 2, 'same_error' => 'true', 'pt_size' => 12, 'brush_size' => 2, 'legend' => 'none', 'title' => 'Error Bars Demo', 'include_zero' => 'true', 'max_val' => 3, 'custom_x_ticks' => [ 0, 1 ], ); $g->png("$samples/error_2.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/pie_2.t0000644000175000017500000000136714341172251014425 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pie; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $g; print "1..1\n"; $g = Chart::Pie->new( 530, 330 ); $g->add_dataset( 'Harry', 'Sally', 'Eve', 'Mark', 'John', 'Susan', 'Alex', 'Tony', 'Kimberly', 'Theresa' ); $g->add_dataset( 12, 20, 12, 15, 8, 9, 22, 14, 8, 13 ); $g->set( 'title' => 'Pie Demo' ); $g->set( 'label_values' => 'none' ); $g->set( 'legend_label_values' => 'percent' ); $g->set( 'grey_background' => 'false' ); $g->set( 'colors' => { 'title' => 'red' } ); $g->set( 'legend_lines' => 'true' ); $g->png("$samples/pie_2.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/lines.t0000644000175000017500000000263114341172251014534 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Lines; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Lines->new; $g->add_dataset( 'foo', 'bar', 'whee', 'ding', 'bat', ); $g->add_dataset( 3.2, 4.1, 9.8, 10, 11, ); $g->add_dataset( 8, 5.3, 3, 4, 5.1, ); $g->add_dataset( 5, 7, 2.3, 10, 12, ); %hash = ( 'title' => 'Lines Chart', 'legend_example_size' => 10, 'grid_lines' => 'true', 'grey_background' => 'false', 'colors' => { 'text' => [ 255, 0, 0 ], 'grid_lines' => [ 240, 0, 0 ] } ); $g->set(%hash); $g->png("$samples/lines.png"); print "ok 1\n"; # #use Data::Dumper; #my %hopts = $g->getopts(); ## print Dumper(\%hopts); # # ## OO Approach #print "Key\tContents\n"; #foreach (keys %hopts ) #{ # print "$_\t"; # myDumpIt(\$hopts{$_}); # print "\n"; # my $i=1; #} #exit(0); # #sub myPrint #{ # my $val = shift; # if ( ref(\$val) eq 'SCALAR' ) # { # print $val; # } # elsif ( ref($val) eq 'HASH' ) # { # # } #} # #sub myDumpit #{ # my $rsomething = shift; ## Reference # if ( ref $rsomething eq 'REF' ) # { # myDumpIt($$rsomething); # } # if ( ref $rsomething eq 'ARRAY' ) # { # print "["; # foreach my $val (@$rsomething) # { # # } # } #} # Chart-v2.403.9/t/pie_5.t0000644000175000017500000000147314341172251014426 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pie; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Pie->new( 550, 500 ); $g->add_dataset( 'eins', 'zwei', 'drei', 'vier' ); $g->add_dataset( 25, 30, 250, 50 ); $g->set( 'title' => 'Pie Demo Chart' ); $g->set( 'label_values' => 'percent' ); $g->set( 'legend_label_values' => 'value' ); $g->set( 'legend' => 'bottom' ); $g->set( 'grey_background' => 'false' ); $g->set( 'x_label' => '' ); $g->set( 'colors' => { 'misc' => 'light_blue', 'dataset1' => 'red', 'dataset2' => 'blue', 'dataset0' => 'yellow', 'dataset3' => 'green' } ); $g->png("$samples/pie_5.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/bars_2.t0000644000175000017500000000321314341172251014567 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Bars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Bars->new( 600, 500 ); $g->add_dataset( 'Berlin', 'Paris', 'Rome', 'London', 'Munich' ); $g->add_dataset( 14, 5, 4, 5, 11 ); $g->add_dataset( 12, 4, 6, 7, 12 ); $g->add_dataset( 18, 2, 3, 3, 9 ); $g->add_dataset( 17, 5, 7, 6, 6 ); $g->add_dataset( 15, 3, 4, 5, 11 ); $g->add_dataset( 11, 6, 5, 6, 12 ); $g->add_dataset( 12, 1, 4, 5, 15 ); $g->add_dataset( 10, 4, 6, 8, 10 ); $g->add_dataset( 14, 5, 4, 5, 11 ); $g->add_dataset( 12, 4, 6, 6, 12 ); $g->add_dataset( 18, 2, 3, 3, 9 ); $g->add_dataset( 17, 5, 7, 2, 6 ); %hash = ( 'title' => 'Sold Cars in 2001', 'x_label' => 'City', 'y_label' => 'Number of Cars', 'legend' => 'bottom', 'legend_labels' => [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ], 'grid_lines' => 'true', 'include_zero' => 'true', 'max_val' => '20', 'colors' => { 'title' => 'red', 'x_label' => 'blue', 'y_label' => 'blue', 'background' => 'grey', 'text' => 'blue', }, ); $g->set(%hash); $g->png("$samples/bars_2.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/scalarImage.t0000644000175000017500000000407714341172251015640 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Lines; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); # bytewise comparision of scalar sub imgCompare { my $scalarImage = shift; my $fileName = shift; my $result = 0; # assume error # do a bytewise compare if ( !open( FH, $fileName ) ) { return $result; } my $idx = 0; my $fileChar = ''; my $scalarChar = ''; my $bEqual = 1; while ( read FH, $fileChar, 1, $idx ) { my $length = length $fileChar; if ( $length > 1 ) { my $c = substr $scalarImage, $length - 1, 1; $fileChar = $c; } $scalarChar = substr $scalarImage, $idx, 1; if ( $scalarChar ne $fileChar ) { $bEqual = 0; last; } $idx++, $fileChar = ''; } close FH; $result = $bEqual; return $result; } my $g; print "1..1\n"; $g = Chart::Lines->new( 600, 400 ); $g->add_dataset( 'foo', 'bar', 'whee', 'ding', 'bat', 'bit' ); $g->add_dataset( 3.2, 4.34, 9.456, 10.459, 11.24234, 14.0234 ); $g->add_dataset( -1.3, 8.4, 5.34, 3.234, 4.33, 13.09 ); $g->add_dataset( 5, 7, 2, 10, 12, 2.3445 ); $g->set( 'title' => 'LINES' ); $g->set( 'sub_title' => 'Lines Chart' ); $g->set( 'colors' => { 'y_label' => [ 0, 0, 255 ], y_label2 => [ 0, 255, 0 ], 'y_grid_lines' => [ 127, 127, 0 ], 'dataset0' => [ 127, 0, 0 ], 'dataset1' => [ 0, 127, 0 ], 'dataset2' => [ 0, 0, 127 ] } ); $g->set( 'y_label' => 'y label 1' ); $g->set( 'y_label2' => 'y label 2' ); $g->set( 'y_grid_lines' => 'true' ); $g->set( 'legend' => 'bottom' ); my $FileName = "$samples/scalarImage.png"; $g->png($FileName); my $dataref = $g->get_data(); my $scalarimage = $g->scalar_png($dataref); if ( imgCompare( $scalarimage, $FileName ) ) { unlink $FileName; print "ok 1\n"; exit(0); } unlink $FileName; print "Error 1\n"; exit(1); Chart-v2.403.9/t/pie_9.t0000644000175000017500000000131314341172251014423 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pie; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Pie->new( 550, 500 ); $g->add_dataset( 'eins', 'zwei', 'drei', 'vier' ); $g->add_dataset( 0, 0, 0, 0 ); $g->set( 'title' => 'Pie Demo Chart' ); $g->set( 'sub_title' => 'Only a circle, as all values are zero' ); $g->set( 'label_values' => 'percent' ); $g->set( 'legend_label_values' => 'value' ); $g->set( 'legend' => 'bottom' ); $g->set( 'grey_background' => 'false' ); $g->set( 'legend_lines' => 'false' ); $g->png("$samples/pie_9.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/points_100.t0000644000175000017500000000104514341172251015314 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Points; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g; my @x; my @y; $x[0] = 0; $y[0] = 0; for ( my $i = 9 ; $i < 100 ; $i++ ) { $x[$i] = $i; $y[$i] = $i * 10; } $g = Chart::Points->new; $g->add_dataset(@x); $g->add_dataset(@y); $g->set( 'title' => 'Points Chart with 100 Points' ); $g->set( 'skip_x_ticks' => 10 ); #$g->set ('skip_int_ticks'=> 10); $g->png("$samples/points_100.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/bars_4.t0000644000175000017500000000430014341172251014567 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} # The Integral of the mathematical function 1/x use strict; use Chart::Bars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my @x_values = (); # x axis my @y_values = (); my $graphic; my $picture_file = "$samples/bars_4.png"; my $min_y = -5; # max. y-values my $max_y = 5; my $x; my $y; #------------------------------------------------------------------------------------ # Start #------------------------------------------------------------------------------------ #calculate the values for ( my $x = -5 ; $x <= 5 ; ( $x = $x + 0.0005 ) ) { push( @x_values, $x ); if ( $x != 0 ) { # division by zero! $y = 1 / $x; if ( $y > $max_y ) { push( @y_values, $max_y ); } elsif ( $y < $min_y ) { push( @y_values, $min_y ); } else { push( @y_values, $y ); } } else { push( @y_values, 0 ); } } #------------------------------------------------------------------------------------ # Make it #------------------------------------------------------------------------------------ $graphic = Chart::Bars->new( 600, 600 ); $graphic->add_dataset(@x_values); $graphic->add_dataset(@y_values); $graphic->set( 'min_val' => $min_y ); $graphic->set( 'max_val' => $max_y ); $graphic->set( 'min_y_ticks' => 20 ); $graphic->set( 'skip_x_ticks' => 1000 ); $graphic->set( 'graph_border' => 18 ); $graphic->set( 'title' => "The Integral of 1/x" ); $graphic->set( 'grid_lines' => 'true' ); $graphic->set( 'x_ticks' => 'vertical' ); $graphic->set( 'legend' => 'none' ); $graphic->set( 'y_label' => 'f = 1 / x' ); $graphic->set( 'xy_plot' => 'true' ); # use a special function to convert the y values to something special $graphic->set( 'f_y_tick' => \&formatter ); $graphic->set( 'f_x_tick' => \&formatter ); $graphic->png($picture_file); sub formatter { my $y_value = shift; my $label = sprintf "%1.2f", $y_value; if ( $label == '-0.00' ) { $label = '0'; } return $label; } print "ok 1\n"; exit(0); Chart-v2.403.9/t/composite_1.t0000644000175000017500000000426114341172251015645 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Composite; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $obj = Chart::Composite->new( 600, 500 ); my @legend_ary; my ( $legend, @zeile ); my @all_aryref; open( OUT, ">$samples/composite_1.png" ) or die "cannot write file $samples/composite_1.png\n"; my $i = 0; my $e = 0; my $max_val = 0; while () { if ( $_ =~ /EOF/i ) { last; } chomp; $i++; ( $legend, @zeile ) = split /\|/, $_; $obj->add_dataset(@zeile); if ( $i != 1 ) { push @legend_ary, $legend; # Erste Zeile ist die x-Achsenbezeichnung und gehört nicht zur Legende for ( 0 .. $#zeile ) { $zeile[$_] > $max_val ? $max_val = $zeile[$_] : 1; } # den Maximalen Wert ermitteln } $all_aryref[ $e++ ] = [@zeile]; } if ( $max_val =~ /^\d+$/ ) { $max_val = 100 * int( 1 + $max_val / 100 ); } # den Scalenwert die nächste 100er Stellen setzen # Der zweite Charttyp überdeckt immer den ersten $obj->set( 'legend' => "top", 'legend_labels' => \@legend_ary, 'x_ticks' => "vertical", 'composite_info' => [ [ 'StackedBars', [ 8, 7, 6, 5 ] ], [ 'Bars', [ 1, 2, 3, 4, 9 ] ], ], 'same_y_axes' => "true", 'y_label' => "Anzahl", 'min_val1' => 0, 'max_val1' => $max_val, 'max_val2' => $max_val, 'space_bars' => 1, 'brush_size' => 10, 'legend' => 'bottom', 'title' => 'Composite Demo Chart', 'legend_example_height' => 'true', 'legend_example_height0..3' => '50', 'legend_example_height4..9' => '4', ); $obj->png( \*OUT ); close OUT; print "ok 1\n"; exit 0; __END__ Datum|01.09.2003|02.09.2003|03.09.2003|04.09.2003 Anzahl gesamt|322|244|227|223 Anzahl Stufe 1 bis 4 gesamt|226|173|159|145 Anzahl JL|77|46|44|61 Anzahl DL|19|25|24|17 Anzahl 1. Stufe|28|22|11|27 Anzahl 2. Stufe|12|11|4|7 Anzahl 3. Stufe|50|39|55|34 Anzahl 4. Stufe|136|101|89|77 Anzahl Formulare|547|352|249|174 EOF Chart-v2.403.9/t/mountain.t0000644000175000017500000000323414341172251015254 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Mountain; use File::Spec; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..2\n"; my @data = ( [ "1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th" ], [ 3, 7, 8, 2, 4, 8.5, 2, 5, 9 ], [ 4, 2, 5, 6, 3, 2.5, 3, 3, 4 ], [ 7, 3, 2, 8, 8.5, 2, 9, 4, 5 ], ); my @hex_colors = qw(0099FF 00CC00 FFCC33 FF0099 3333FF); my @colors = map { [ map { hex($_) } unpack( "a2 a2 a2", $_ ) ] } @hex_colors; my @patterns = (); foreach ( 1 .. @data - 1 ) { open( PNG, '<' . File::Spec->catfile( File::Spec->curdir, 't', 'patterns', "PATTERN$_.PNG" ) ) || die "Can't load pattern $_"; push( @patterns, GD::Image->newFromPng( \*PNG ) ); close(PNG); } my @opts = ( {}, { 'x_label' => 'X Label', 'y_label' => 'Y label', 'title' => 'Mountain Chart', 'grid_lines' => 'true', 'colors' => { map { ( "dataset$_" => $colors[$_] ) } 0 .. @colors - 1 }, }, { 'x_label' => 'X Label', 'y_label' => 'Y label', 'title' => 'Mountain Chart with Patterns', 'grid_lines' => 'true', 'colors' => { map { ( "dataset$_" => $colors[$_] ) } 0 .. @colors - 1 }, 'patterns' => \@patterns, }, ); foreach my $i ( 1 .. @opts - 1 ) { my $newpath = File::Spec->catfile( $samples, "mountain-$i.png" ); my $opts = $opts[$i]; my $g = new Chart::Mountain(); $g->set(%$opts); my $Image = $g->png( $newpath, \@data ); print "ok $i\n"; } exit(0); Chart-v2.403.9/t/patterns/0000775000175000017500000000000014341172251015075 5ustar herbertherbertChart-v2.403.9/t/patterns/PATTERN0.PNG0000644000175000017500000000020614341172251016634 0ustar herbertherbert‰PNG  IHDRìtƒ&tRNS”ý®tEXtSoftwaregif2png 0.6 (beta)ªÝi’IDATxœcøÎð˜á.ƒC=Ã?†¿ ¿2k\šIEND®B`‚Chart-v2.403.9/t/patterns/PATTERN2.PNG0000644000175000017500000000020114341172251016631 0ustar herbertherbert‰PNG  IHDRìtƒ&tRNS”ý®tEXtSoftwaregif2png 0.6 (beta)ªÝi’IDATxœcxÇp—a7C9”2îûßQ÷æIEND®B`‚Chart-v2.403.9/t/patterns/PATTERN3.PNG0000644000175000017500000000017614341172251016645 0ustar herbertherbert‰PNG  IHDRìtƒ&tRNS”ý®tEXtSoftwaregif2png 0.6 (beta)ªÝi’IDATxœc(gÁ» P!Ì…^¡„ÇIEND®B`‚Chart-v2.403.9/t/patterns/PATTERN0.GIF0000644000175000017500000000006614341172251016621 0ustar herbertherbertGIF89aðÿÿÿ!ù, „wÁš–š{ª.K/(;Chart-v2.403.9/t/patterns/PATTERN3.GIF0000644000175000017500000000006514341172251016623 0ustar herbertherbertGIF89aðÿÿÿ!ù, nx¼­T›¯Fë ;Chart-v2.403.9/t/patterns/PATTERN5.PNG0000644000175000017500000000020014341172251016633 0ustar herbertherbert‰PNG  IHDRìtƒ&tRNS”ý®tEXtSoftwaregif2png 0.6 (beta)ªÝi’IDATxœcp`øÏà„þ³üБ¸êIEND®B`‚Chart-v2.403.9/t/patterns/PATTERN1.GIF0000644000175000017500000000006714341172251016623 0ustar herbertherbertGIF89aðÿÿÿ!ù,Œ ¡ÀÛž‹Å óT;Chart-v2.403.9/t/patterns/PATTERN1.PNG0000644000175000017500000000017514341172251016642 0ustar herbertherbert‰PNG  IHDRìtƒ&tRNS”ý®tEXtSoftwaregif2png 0.6 (beta)ªÝi’IDATxœc``(C( –Ë©7çÏIEND®B`‚Chart-v2.403.9/t/patterns/PATTERN4.PNG0000644000175000017500000000020214341172251016634 0ustar herbertherbert‰PNG  IHDRìtƒ&tRNS”ý®tEXtSoftwaregif2png 0.6 (beta)ªÝi’IDATxœc¨eØÍp ˆkþ ,P1Ç?e IEND®B`‚Chart-v2.403.9/t/patterns/PATTERN5.GIF0000644000175000017500000000007014341172251016621 0ustar herbertherbertGIF89aðÿÿÿ!ù, ‚ Ëk D²=î­¬;Chart-v2.403.9/t/patterns/PATTERN2.GIF0000644000175000017500000000006514341172251016622 0ustar herbertherbertGIF89aðÿÿÿ!ù, „c€š» c›ÑÑ÷ ;Chart-v2.403.9/t/patterns/PATTERN4.GIF0000644000175000017500000000006714341172251016626 0ustar herbertherbertGIF89aðÿÿÿ!ù, ~º¸¡ ‹®Õtm ;Chart-v2.403.9/t/bars_5.t0000644000175000017500000000143014341172251014571 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::Bars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my ( @data, @labels, %hash, $g, $hits ); # create an array of labels for ( 1 .. 49 ) { push( @labels, $_ ); } # create an array of data for ( 1 .. 49 ) { srand( time() / $_ ); $hits = int( rand(100) + 401 ); push( @data, $hits ); } $g = Chart::Bars->new( 700, 300 ); $g->add_dataset(@labels); $g->add_dataset(@data); %hash = ( 'legend' => 'none', 'precision' => 0, 'x_ticks' => 'vertical', 'title' => 'Lottozahlenverteilung', 'y_label' => 'Häufigkeit', 'y_grid_lines' => 'true', ); $g->set(%hash); $g->png("$samples/bars_5.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/bars.t0000644000175000017500000000245614341172251014356 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Bars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Bars->new( 600, 600 ); $g->add_dataset( 'foo', 'bar', 'junk', 'ding', 'bat' ); $g->add_dataset( 30000, 40000, 80000, 50000, 90000 ); $g->add_dataset( 80000, 60000, 30000, 30000, 40000 ); $g->add_dataset( 50000, 70000, 20200, 80000.8, 40000 ); %hash = ( 'transparent' => 'true', 'precision' => 1, 'title' => 'Bars\nChartmodul', 'y_grid_lines' => 'true', 'graph_border' => '4', 'min_val' => '0', 'text_space' => '2', 'sub_title' => 'Untertitel', 'x_label' => 'X-Achse', 'y_label' => 'Y-Achse', 'y_label2' => 'Y-Achse2', 'legend' => 'none', 'tick_len' => '3', 'x_ticks' => 'vertical', 'include_zero' => 'true', 'skip_x_ticks' => '1', 'grid_lines' => 'true', 'colors' => { 'text' => [ 100, 0, 200 ], 'y_label' => [ 2, 255, 2 ], 'y_label2' => [ 2, 255, 2 ], 'y_grid_lines' => 'black', 'x_grid_lines' => 'black', 'dataset0' => [ 255, 20, 147 ], }, 'y_ticks' => '20', ); $g->set(%hash); $g->png("$samples/bars.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/stackedbars_4.t0000644000175000017500000000201114341172251016123 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::StackedBars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::StackedBars->new( 600, 400 ); $g->add_dataset( '2007-10-01', '2007-10-02', '2007-10-03', '2007-10-04', '2007-10-05' ); my @dataset = ( 74, 78, 75, 83, 78 ); my @first_dataset = (); my @second_dataset = (); foreach my $dat (@dataset) { if ( $dat > 75 ) { push( @first_dataset, 75 ); push( @second_dataset, $dat - 75 ); } else { push( @first_dataset, $dat ); push( @second_dataset, 0 ); } } $g->add_dataset(@first_dataset); $g->add_dataset(@second_dataset); $g->set( 'title' => 'Stacked Bar Chart', 'legend' => 'none', 'grey_background' => 'false', 'min_val' => 70, ); $g->set( 'colors' => { 'dataset1' => [ 220, 20, 60 ], 'dataset0' => [ 0, 255, 0 ], } ); $g->png("$samples/stackedbars_4.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/stackedbars.t0000644000175000017500000000114314341172251015705 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::StackedBars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::StackedBars->new( 600, 400 ); $g->add_dataset( 'foo', 'bar', 'junk', 'taco', 'kcufasidog' ); $g->add_dataset( 3, 4, 9, 10, 11 ); $g->add_dataset( 8, 6, 1, 12, 1 ); $g->add_dataset( 5, 7, 2, 13, 4 ); $g->set( 'title' => 'Stacked Bar Chart', 'legend' => 'left', 'grey_background' => 'false', ); $g->png("$samples/stackedbars.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/bars_6.t0000644000175000017500000000233314341172251014575 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::Bars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Bars->new( 580, 300 ); my @data = ( 200202, 200203, 200204, 200205, 200206, 200207, 200208, 200209, 200210, 200211, 200212, 200301 ); my @data1 = ( 6626, 7662, 7580, 7671, 8064, 8664, 6343, 5518, 6257, 5391, 5401, 6002 ); $g->add_dataset(@data); $g->add_dataset(@data1); my @legend_keys = ( "Actual ", "Goal" ); $g->set( colors => { dataset0 => [ 25, 220, 147 ], }, graph_border => 0, grey_background => 'false', grid_lines => 'true', # integer_ticks_only => 'true', legend => 'none', # min_val => 0, # include_zero => 'true', png_border => 4, precision => 1, skip_int_ticks => 1000, spaced_bars => 'true', text_space => 3, title => "Tickets", title_font => GD::Font->Giant, transparent => 'false', x_ticks => 'vertical', y_axes => 'both', y_label => '# Tickets', # max_val => 9000, ); $g->png("$samples/bars_6.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/points_4.t0000644000175000017500000000245114341172251015161 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Points; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Points->new(); @hash = ( 'title' => 'Points Chart with different brushes', 'png_border' => 10, 'pt_size' => 18, 'grid_lines' => 'true', 'brush_size' => 10, # 10 points diameter # 'brushStyle' => 'FilledCircle', # 'brushStyle' => 'circle', # 'brushStyle' => 'donut', # 'brushStyle' => 'OpenCircle', # 'brushStyle' => 'fatPlus', # 'brushStyle' => 'triangle', # 'brushStyle' => 'upsidedownTriangle', # 'brushStyle' => 'square', # 'brushStyle' => 'hollowSquare', # 'brushStyle' => 'OpenRectangle', # 'brushStyle' => 'FilledDiamond', # 'brushStyle' => 'OpenDiamond', # 'brushStyle' => 'Star', 'brushStyle' => 'OpenStar', ); $g->set( colors => { dataset0 => [ 25, 220, 147 ], } ); $g->set( brushStyles => { dataset0 => 'fatPlus', dataset1 => 'hollowSquare' } ); $g->set(@hash); $g->add_dataset( 'foo', 'bar', 'junk' ); $g->add_dataset( 3, 4, 9 ); $g->add_dataset( 8, 6, 0 ); $g->add_dataset( 5, 7, 2 ); $g->png("$samples/points_4.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/f_ticks_1.t0000644000175000017500000000543414341172251015270 0ustar herbertherbert#!/usr/bin/perl -w # # Testprogram for f_x_ticks # #====================================================================== BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::LinesPoints; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my @x_values = (); # real x values my @y_values = (); # real y values my @x_plot_values = (); # x values for plot my @y_plot_values = (); # y values for plot my $graphic; my $min_x = -15; # random start my $max_x = 10; # random stop my $min_y; my $max_y; my $x; my $y; #------------------------------------------------------------------------------------ # Start #------------------------------------------------------------------------------------ $x = $min_x; for ( my $idx = 0 ; $x <= $max_x ; $idx += 0.1 ) { $x = $min_x + $idx; $x_values[$idx] = $x; $y_values[$idx] = cos( ( $x - $min_x ) ); } undef $min_y; undef $max_y; for ( my $idx = 0 ; $idx <= $#x_values ; $idx++ ) { $x_plot_values[$idx] = $x_values[$idx] + $min_x; $y_plot_values[$idx] = $y_values[$idx]; if ( !defined($min_y) ) { $min_y = $y_values[$idx]; } else { $min_y = ( $min_y < $y_values[$idx] ) ? $min_y : $y_values[$idx]; } if ( !defined($max_y) ) { $max_y = $y_values[$idx]; } else { $max_y = ( $max_y > $y_values[$idx] ) ? $max_y : $y_values[$idx]; } } #------------------------------------------------------------------------------------ # Make it #------------------------------------------------------------------------------------ $graphic = Chart::LinesPoints->new( 750, 600 ); $graphic->set( 'brush_size' => 2 ); $graphic->add_dataset(@x_plot_values); $graphic->add_dataset(@y_plot_values); $graphic->set( 'min_val' => $min_y ); $graphic->set( 'max_val' => $max_y ); #$graphic -> set ('y_ticks' => 11 ); #$graphic -> set ('skip_x_ticks' => 100); $graphic->set( 'pt_size' => 2 ); $graphic->set( 'grey_background' => 'true' ); $graphic->set( 'graph_border' => 18 ); $graphic->set( 'title' => "f_tick example for x and y values" ); $graphic->set( 'y_grid_lines' => 'false' ); $graphic->set( 'x_grid_lines' => 'false' ); $graphic->set( 'x_ticks' => 'vertical' ); $graphic->set( 'legend' => 'none' ); $graphic->set( 'x_label' => 'some x-values' ); $graphic->set( 'y_label' => 'f = cos(g(x))' ); # use a special function to convert the x values to HH:MM:SS $graphic->set( 'f_x_tick' => \&convert_to_real_x ); if ( $graphic->can('png') ) { my $picture_file = "$samples/f_ticks_1.png"; $graphic->png($picture_file); } print "ok 1\n"; exit(0); sub convert_to_real_x { my $plot_x = shift; my $result = sprintf "%6.1f", $plot_x - $min_x; return ($result); } Chart-v2.403.9/t/Humidity.t0000644000175000017500000004176614341172251015232 0ustar herbertherbert#!/usr/bin/perl -w # # Testprogram for lines # #====================================================================== BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::Lines; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my @messwerte = (); my @zeit = (); my $graphic; my $gif_name; my $titel_name; my $einheit; my $min_y; my $max_y; #------------------------------------------------------------------------------------ # Start #------------------------------------------------------------------------------------ $gif_name = "Humidity"; $titel_name = "Examples of Humidity"; $einheit = "% rH"; @zeit = ( '12:00', '12:01', '12:02', '12:03', '12:04', '12:05', '12:06', '12:07', '12:08', '12:09', #1 '12:10', '12:11', '12:12', '12:13', '12:14', '12:15', '12:16', '12:17', '12:18', '12:19', #2 '12:20', '12:21', '12:22', '12:23', '12:24', '12:25', '12:26', '12:27', '12:28', '12:29', #3 '12:30', '12:31', '12:32', '12:33', '12:34', '12:35', '12:36', '12:37', '12:38', '12:39', #4 '12:40', '12:41', '12:42', '12:43', '12:44', '12:45', '12:46', '12:47', '12:48', '12:49', #5 '12:50', '12:51', '12:52', '12:53', '12:54', '12:55', '12:56', '12:57', '12:58', '12:59', #6 '13:00', '13:01', '13:02', '13:03', '13:04', '13:05', '13:06', '13:07', '13:08', '13:09', #7 '13:10', '13:11', '13:12', '13:13', '13:14', '13:15', '13:16', '13:17', '13:18', '13:19', #8 '13:20', '13:21', '13:22', '13:23', '13:24', '13:25', '13:26', '13:27', '13:28', '13:29', #9 '13:30', '13:31', '13:32', '13:33', '13:34', '13:35', '13:36', '13:37', '13:38', '13:39', #10 '13:40', '13:41', '13:42', '13:43', '13:44', '13:45', '13:46', '13:47', '13:48', '13:49', #11 '13:50', '13:51', '13:52', '13:53', '13:54', '13:55', '13:56', '13:57', '13:58', '13:59', #12 '14:00', '14:01', '14:02', '14:03', '14:04', '14:05', '14:06', '14:07', '14:08', '14:09', #13 '14:10', '14:11', '14:12', '14:13', '14:14', '14:15', '14:16', '14:17', '14:18', '14:19', #14 '14:20', '14:21', '14:22', '14:23', '14:24', '14:25', '14:26', '14:27', '14:28', '14:29', #15 '14:30', '14:31', '14:32', '14:33', '14:34', '14:35', '14:36', '14:37', '14:38', '14:39', #16 '14:40', '14:41', '14:42', '14:43', '14:44', '14:45', '14:46', '14:47', '14:48', '14:49', #17 '14:50', '14:51', '14:52', '14:53', '14:54', '14:55', '14:56', '14:57', '14:58', '14:59', #18 '15:00', '15:01', '15:02', '15:03', '15:04', '15:05', '15:06', '15:07', '15:08', '15:09', #19 '15:10', '15:11', '15:12', '15:13', '15:14', '15:15', '15:16', '15:17', '15:18', '15:19', #20 '15:20', '15:21', '15:22', '15:23', '15:24', '15:25', '15:26', '15:27', '15:28', '15:29', #21 '15:30', '15:31', '15:32', '15:33', '15:34', '15:35', '15:36', '15:37', '15:38', '15:39', #22 '15:40', '15:41', '15:42', '15:43', '15:44', '15:45', '15:46', '15:47', '15:48', '15:49', #23 '15:50', '15:51', '15:52', '15:53', '15:54', '15:55', '15:56', '15:57', '15:58', '15:59', #24 '16:00', '16:01', '16:02', '16:03', '16:04', '16:05', '16:06', '16:07', '16:08', '16:09', #25 '16:10', '16:11', '16:12', '16:13', '16:14', '16:15', '16:16', '16:17', '16:18', '16:19', #26 '16:20', '16:21', '16:22', '16:23', '16:24', '16:25', '16:26', '16:27', '16:28', '16:29', #27 '16:30', '16:31', '16:32', '16:33', '16:34', '16:35', '16:36', '16:37', '16:38', '16:39', #28 '16:40', '16:41', '16:42', '16:43', '16:44', '16:45', '16:46', '16:47', '16:48', '16:49', #29 '16:50', '16:51', '16:52', '16:53', '16:54', '16:55', '16:56', '16:57', '16:58', '16:59', #30 '17:00', '17:01', '17:02', '17:03', '17:04', '17:05', '17:06', '17:07', '17:08', '17:09', #31 '17:10', '17:11', '17:12', '17:13', '17:14', '17:15', '17:16', '17:17', '17:18', '17:19', #32 '17:20', '17:21', '17:22', '17:23', '17:24', '17:25', '17:26', '17:27', '17:28', '17:29', #33 '17:30', '17:31', '17:32', '17:33', '17:34', '17:35', '17:36', '17:37', '17:38', '17:39', #34 '17:40', '17:41', '17:42', '17:43', '17:44', '17:45', '17:46', '17:47', '17:48', '17:49', #35 '17:50', '17:51', '17:52', '17:53', '17:54', '17:55', '17:56', '17:57', '17:58', '17:59', #36 '18:00', '18:01', '18:02', '18:03', '18:04', '18:05', '18:06', '18:07', '18:08', '18:09', #37 '18:10', '18:11', '18:12', '18:13', '18:14', '18:15', '18:16', '18:17', '18:18', '18:19', #38 '18:20', '18:21', '18:22', '18:23', '18:24', '18:25', '18:26', '18:27', '18:28', '18:29', #39 '18:30', '18:31', '18:32', '18:33', '18:34', '18:35', '18:36', '18:37', '18:38', '18:39', #40 '18:41', '18:42', '18:43', '18:44', '18:45', '18:46', '18:47', '18:48', '18:49', '18:50', #41 '18:51', '18:52', '18:53', '18:54', '18:55', '18:56', '18:57', '18:58', '18:59', '19:00', #42 '19:01', '19:02', '19:03', '19:04', '19:05', '19:06', '19:07', '19:08', '19:09', '19:10', #43 '19:11', '19:12', '19:13', '19:14', '19:15', '19:16', '19:17', '19:18', '19:19', '19:20', #44 '19:21', '19:22', '19:23', '19:24', '19:25', '19:26', '19:27', '19:28', '19:29', '19:30', #45 '19:31', '19:32', '19:33', '19:34', '19:35', '19:36', '19:37', '19:38', '19:39', '19:40', #46 '19:41', '19:42', '19:43', '19:44', '19:45', '19:46', '19:47', '19:48', '19:49', '19:50', #47 '19:51', '19:52', '19:53', '19:54', '19:55', '19:56', '19:57', '19:58', '19:59', '20:00', #48 '20:01', '20:02', '20:03', '20:04', '20:05', '20:06', '20:07', '20:08', '20:09', '20:10', #49 '20:11', '20:12', '20:13', '20:14', '20:15', '20:16', '20:17', '20:18', '20:19', '20:20', #50 '20:21', '20:22', '20:23', '20:24', '20:25', '20:26', '20:27', '20:28', '20:29', '20:30', #51 '20:31', '20:32', '20:33', '20:34', '20:35', '20:36', '20:37', '20:38', '20:39', '20:40', #52 '20:41', '20:42', '20:43', '20:44', '20:45', '20:46', '20:47', '20:48', '20:49', '20:50', #53 '20:51', '20:52', '20:53', '20:54', '20:55', '20:56', '20:57', '20:58', '20:59', '21:00', #54 '21:01', '21:02', '21:03', '21:04', '21:05', '21:06', '21:07', '21:08', '21:09', '21:10', #55 '21:11', '21:12', '21:13', '21:14', '21:15', '21:16', '21:17', '21:18', '21:19', '21:20', #56 '21:21', '21:22', '21:23', '21:24', '21:25', '21:26', '21:27', '21:28', '21:29', '21:30', #57 '21:31', '21:32', '21:33', '21:34', '21:35', '21:36', '21:37', '21:38', '21:39', '21:40', #58 '21:41', '21:42', '21:43', '21:44', '21:45', '21:46', '21:47', '21:48', '21:49', '21:50', #59 '21:51', '21:52', '21:53', '21:54', '21:55', '21:56', '21:57', '21:58', '21:59', '22:00', #60 '22:01', '22:02', '22:03', '22:04', '22:05', '22:06', '22:07', '22:08', '22:09', '22:10', #61 '22:11', '22:12', '22:13', '22:14', '22:15', '22:16', '22:17', '22:18', '22:19', '22:20', #62 '22:21', '22:22', '22:23', '22:24', '22:25', '22:26', '22:27', '22:28', '22:29', '22:30', #63 '22:31', '22:32', '22:33', '22:34', '22:35', '22:36', '22:37', '22:38', '22:39', '22:40', #64 '22:41', '22:42', '22:43', '22:44', '22:45', '22:46', '22:47', '22:48', '22:49', '22:50', #65 '22:51', '22:52', '22:53', '22:54', '22:55', '22:56', '22:57', '22:58', '22:59', '23:00', #66 '23:01', '23:02', '23:03', '23:04', '23:05', '23:06', '23:07', '23:08', '23:09', '23:10', #67 '23:11', '23:12', '23:13', '23:14', '23:15', '23:16', '23:17', '23:18', '23:19', '23:20', #68 '23:21', '23:22', '23:23', '23:24', '23:25', '23:26', '23:27', '23:28', '23:29', '23:30', #69 '23:31', '23:32', '23:33', '23:34', '23:35', '23:36', '23:37', '23:38', '23:39', '23:40', #70 '23:41', '23:42', '23:43', '23:44', '23:45', '23:46', '23:47', '23:48', '23:49', '23:50', #71 '23:51', '23:52', '23:53', '23:54', '23:55', '23:56', '23:57', '23:58', '23:59' ); #72 @messwerte = ( 36.3, 36.2, 36.2, 36.3, 36.4, 36.4, 36.3, 36.4, 36.4, 36.3, #1 36.1, 36.3, 36.2, 36.3, 36.4, 36.3, 36.3, 36.1, 36.2, 36.2, #2 36.3, 36.2, 36.2, 36.2, 36.1, 36.3, 36.3, 36.2, 36.2, 36.2, #3 36.2, 36.1, 36.5, 36.4, 36.3, 36.2, 36.2, 36.3, 36.4, 36.4, #4 36.3, 36.3, 36.3, 36.4, 36.5, 36.4, 36.4, 36.5, 36.5, 36.5, #5 36.3, 36.4, 36.3, 36.2, 36.2, 36.3, 36.2, 36.3, 36.4, 36.2, #6 36.2, 36.4, 36.3, 36.2, 36.4, 36.4, 36.4, 36.2, 36.4, 36.3, #7 36.3, 36.4, 36.4, 36.5, 36.3, 36.5, 36.5, 36.4, 36.5, 36.4, #8 36.5, 36.3, 36.4, 36.4, 36.4, 36.4, 36.5, 36.5, 36.3, 36.3, #9 36.3, 36.4, 36.4, 36.3, 36.3, 36.2, 36.3, 36.3, 36.2, 36.2, #10 36.2, 36.2, 36.2, 36.2, 36.3, 36.3, 36.2, 36.2, 36.2, 36.3, #11 36.1, 36.2, 36.2, 36.2, 36.2, 36.4, 36.2, 36.1, 36.2, 36.2, #12 36.3, 36.2, 36.3, 36.2, 36.1, 36.2, 36.2, 36.2, 36.2, 36.2, #13 36.2, 36.1, 36.2, 36.2, 36.2, 36.2, 36.2, 36.3, 36.2, 36.2, #14 36.3, 36.2, 36.3, 36.2, 36.3, 36.1, 36.2, 36.2, 36.2, 36.2, #15 36.2, 36.2, 36.2, 36.2, 36.2, 36.3, 36.2, 36.2, 36.2, 36.2, #16 36.2, 36.2, 36.3, 36.2, 36.3, 36.2, 36.3, 36.2, 36.2, 36.2, #17 36.2, 36.2, 36.2, 36.2, 36.1, 36.2, 36.2, 36.2, 36.2, 36.2, #18 36.3, 36.1, 36.2, 36.2, 36.3, 36.2, 36.3, 36.3, 36.2, 36.2, #19 36.2, 36.3, 36.2, 36.3, 36.2, 36.2, 36.2, 36.3, 36.2, 36.2, #20 36.2, 36.2, 36.2, 36.1, 36.2, 36.2, 36.2, 36.2, 36.2, 36.2, #21 36.1, 36.2, 36.2, 36.2, 36.3, 36.2, 36.2, 36.1, 36.2, 36.2, #22 36.2, 36.2, 36.2, 36.2, 36.1, 36.3, 36.2, 36.3, 36.2, 36.3, #23 36.2, 36.2, 36.3, 36.2, 36.2, 36.3, 36.2, 36.2, 36.2, 36.2, #24 36.2, 36.2, 36.2, 36.2, 36.1, 36.2, 36.36, 36.36, 36.2, 36.1, #25 36.2, 36.2, 36.2, 36.3, 36.2, 36.3, 36.2, 36.3, 36.1, 36.1, #26 36.2, 36.2, 36.2, 36.1, 36.2, 36.2, 36.1, 36.1, 36.2, 36.2, #27 36.2, 36.2, 36.2, 36.2, 36.1, 36.1, 36.0, 36.2, 36.2, 36.2, #28 36.2, 36.2, 36.2, 36.1, 36.1, 36.1, 36.1, 36.1, 36.2, 36.2, #29 36.1, 36.2, 36.1, 36.1, 36.2, 36.2, 36.2, 36.2, 36.2, 36.2, #30 36.2, 36.2, 36.3, 36.2, 36.1, 36.2, 36.2, 36.2, 36.2, 36.2, #31 36.2, 36.3, 36.3, 36.2, 36.1, 36.2, 36.2, 36.2, 36.1, 36.3, #32 36.3, 36.2, 36.3, 36.2, 36.2, 36.4, 36.3, 36.3, 36.2, 36.1, #33 36.1, 36.1, 36.1, 36.1, 36.1, 36.0, 36.1, 36.2, 36.1, 36.1, #34 36.1, 35.9, 36.2, 36.3, 36.5, 36.5, 36.5, 36.4, 36.1, 36.3, #35 36.4, 36.1, 36.2, 36.4, 36.0, 36.2, 36.1, 36.0, 36.1, 36.1, #36 36.2, 36.3, 36.4, 36.4, 36.5, 36.5, 36.5, 36.3, 36.0, 36.2, #37 36.4, 36.4, 36.3, 36.4, 36.2, 36.3, 36.2, 36.3, 36.4, 36.2, #38 36.4, 36.5, 36.4, 36.2, 36.2, 36.3, 36.1, 36.1, 36.3, 36.2, #39 36.3, 36.3, 36.2, 36.2, 36.3, 36.4, 36.3, 36.3, 36.3, 36.4, #40 36.3, 36.2, 36.3, 36.3, 36.3, 36.4, 36.3, 36.2, 36.1, 36.2, #41 36.2, 36.1, 36.2, 36.1, 36.1, 36.2, 36.2, 36.1, 36.0, 36.1, #42 36.1, 36.2, 36.2, 36.1, 36.2, 36.1, 36.1, 36.1, 36.1, 36.2, #43 36.1, 36.1, 36.2, 36.0, 36.0, 36.1, 36.1, 35.9, 35.9, 35.8, #44 36.1, 36.2, 36.2, 36.2, 36.1, 36.1, 35.9, 35.9, 35.9, 36.1, #45 36.1, 35.9, 36.1, 36.2, 36.1, 36.1, 36.1, 36.1, 36.0, 36.1, #46 36.2, 36.2, 36.1, 36.2, 36.0, 36.0, 35.9, 36.0, 36.0, 36.1, #47 36.2, 36.0, 36.0, 36.0, 36.1, 36.0, 36.0, 35.9, 36.0, 35.8, #48 35.9, 35.9, 35.9, 35.9, 35.8, 35.9, 35.7, 35.9, 35.9, 35.8, #49 35.9, 35.9, 35.7, 35.8, 36.0, 36.1, 36.2, 36.2, 36.0, 36.1, #50 36.2, 36.1, 36.2, 36.2, 36.1, 36.1, 36.0, 36.0, 35.9, 36.0, #51 36.2, 36.1, 36.1, 36.2, 36.2, 36.1, 36.1, 36.3, 36.2, 36.2, #52 36.1, 36.1, 36.1, 36.1, 36.1, 36.3, 36.4, 36.3, 36.2, 36.3, #53 36.2, 36.2, 36.2, 36.3, 36.3, 36.3, 36.2, 36.3, 36.3, 36.4, #54 36.3, 36.3, 36.4, 36.3, 36.3, 36.4, 36.4, 36.4, 36.4, 36.4, #55 36.3, 36.3, 36.4, 36.3, 36.3, 36.2, 36.3, 36.1, 36.1, 36.1, #56 36.2, 36.2, 36.2, 36.1, 36.1, 36.2, 36.2, 36.1, 36.2, 36.2, #57 36.2, 36.2, 36.2, 36.1, 36.1, 36.2, 36.1, 36.2, 36.2, 36.2, #58 36.1, 36.2, 36.2, 36.1, 36.2, 36.2, 36.2, 36.2, 36.2, 36.2, #59 36.1, 36.1, 36.2, 36.2, 36.2, 36.2, 36.2, 36.1, 36.2, 36.2, #60 36.2, 36.2, 36.1, 36.2, 36.1, 36.1, 36.2, 36.2, 36.2, 36.2, #61 36.3, 36.1, 36.2, 36.2, 36.2, 36.3, 36.2, 36.2, 36.1, 36.2, #62 36.3, 36.2, 36.3, 36.3, 36.3, 36.3, 36.3, 36.5, 36.3, 36.4, #63 36.3, 36.3, 36.3, 36.2, 36.3, 36.3, 36.3, 36.3, 36.3, 36.2, #64 36.2, 36.2, 36.2, 36.2, 36.2, 36.2, 36.2, 36.1, 36.1, 36.2, #65 36.2, 36.2, 36.2, 36.2, 36.1, 36.1, 36.2, 36.1, 36.1, 36.1, #66 36.2, 36.1, 36.2, 36.2, 36.2, 36.5, 36.3, 36.2, 36.3, 36.4, #67 36.4, 36.4, 36.4, 36.4, 36.3, 36.3, 36.4, 36.4, 36.4, 36.4, #68 36.4, 36.4, 36.3, 36.4, 36.4, 36.4, 36.3, 36.4, 36.3, 36.2, #69 36.2, 36.2, 36.3, 36.1, 36.2, 36.1, 36.1, 36.1, 36.1, 36.2, #70 36.1, 36.2, 36.1, 36.1, 36.1, 36.1, 36.1, 36.1, 36.2, 36.2, #71 36.2, 36.1, 36.2, 36.2, 36.2, 36.2, 36.2, 36.2, 36.2 ); #72 #------------------------------------------------------------------------------------ # Zeitarray aufbauen , Minimal- und Maximalwert bestimmen und X - Achse berechnen #------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------ # Graphic_objekt erstellen #------------------------------------------------------------------------------------ $min_y = $max_y = $messwerte[0]; foreach (@messwerte) { if ( $_ < $min_y ) { $min_y = $_; } if ( $_ > $max_y ) { $max_y = $_; } } $min_y = int($min_y) - 1; $max_y = int($max_y) + 1; $graphic = Chart::Lines->new( 750, 400 ); $graphic->set( 'brush_size' => 2 ); $graphic->add_dataset(@zeit); $graphic->add_dataset(@messwerte); #------------------------------------------------------------------------------------ # Diagramm Y-Achse berechnen #------------------------------------------------------------------------------------ $graphic->set( 'min_val' => $min_y ); $graphic->set( 'max_val' => $max_y ); #$graphic -> set ('y_ticks' => 11 ); $graphic->set( 'x_ticks' => 'vertical' ); $graphic->set( 'skip_x_ticks' => 30 ); $graphic->set( 'grey_background' => 'false' ); $graphic->set( 'graph_border' => 18 ); $graphic->set( 'title' => $titel_name ); $graphic->set( 'sub_title' => "over Time" ); $graphic->set( 'y_grid_lines' => 'true' ); $graphic->set( 'x_grid_lines' => 'true' ); $graphic->set( 'x_ticks' => 'vertical' ); $graphic->set( 'colors' => { 'y_grid_lines' => [ 127, 127, 0 ], 'x_grid_lines' => [ 127, 127, 0 ], 'dataset0' => [ 0, 0, 200 ] } ); $graphic->set( 'legend' => 'none' ); $graphic->set( 'x_label' => 'Time (UTC)' ); $graphic->set( 'y_label' => $einheit ); if ( $graphic->can('gif') ) { my $wettgif = "$samples/" . $gif_name . ".gif"; $graphic->gif($wettgif); } elsif ( $graphic->can('png') ) { my $wettgif = "$samples/" . $gif_name . ".png"; $graphic->png($wettgif); } print "ok 1\n"; exit(0); Chart-v2.403.9/t/bars_7.t0000644000175000017500000000235514341172251014602 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Bars; use strict; use POSIX; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $a; $a = 10**(-12); print "1..1\n"; my $g = Chart::Bars->new( 580, 300 ); my @data = ( 200202, 200203, 200204, 200205, 200206, 200207, 200208, 200209, 200210, 200211, 200212, 200301 ); my @data1 = ( 6626 * $a, -790 * $a, 7580 * $a, 7671 * $a, 8764 * $a, 8664 * $a, 6343 * $a, 5518 * $a, 6257 * $a, 5391 * $a, 5401 * $a, 6002 * $a ); #my @data1 =(6626,-790,7580,7671,8764,8664,6343,5518,6257,5391,5401,6002); $g->add_dataset(@data); $g->add_dataset(@data1); $g->set( colors => { dataset0 => [ 25, 220, 147 ], }, graph_border => 0, grey_background => 'false', grid_lines => 'true', include_zero => 'true', legend => 'none', png_border => 4, precision => 9, spaced_bars => 'true', text_space => 3, title => "Tickets", title_font => GD::Font->Giant, transparent => 'false', x_ticks => 'vertical', y_axes => 'both', y_label => '# Tickets', x_label => 'Date', ); $g->png("$samples/bars_7.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/linespoints.t0000644000175000017500000000104514341172251015767 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::LinesPoints; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::LinesPoints->new; $g->add_dataset( 'foo', 'bar', 'junk', 'ding', 'bat' ); $g->add_dataset( 3, 4, 9, 3, 4 ); $g->add_dataset( 8, 4, 3, 4, 6 ); $g->add_dataset( 5, 7, 2, 7, 9 ); $g->set( 'title' => 'Lines and Points Chart' ); $g->set( 'legend' => 'bottom' ); $g->png("$samples/linespoints.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/points.t0000644000175000017500000000110114341172251014725 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Points; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Points->new(); @hash = ( 'title' => 'Points Chart', 'png_border' => 10, 'pt_size' => 18, 'grid_lines' => 'true', 'brush_size' => 10, # 10 points diameter ); $g->set(@hash); $g->add_dataset( 'foo', 'bar', 'junk' ); $g->add_dataset( 3, 4, 9 ); $g->add_dataset( 8, 6, 0 ); $g->add_dataset( 5, 7, 2 ); $g->png("$samples/points.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/linespoints_5.t0000644000175000017500000000457414341172251016225 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::Composite; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Composite->new( 700, 350 ); my @bezugszeitraum = ( '2005-04-02', '2005-04-03', '2005-04-04', '2005-04-05', '2005-04-06', '2005-04-07', '2005-04-08', '2005-04-09', '2005-04-10', '2005-04-18', '2005-04-19', '2005-04-20', '2005-04-21', '2005-04-22', '2005-04-23', '2005-04-24', '2005-04-25' ); my @nr_of_sats = ( 27, 29, 28, 26, 27, 23, 29, 29, 23, 26, 29, 29, 29, 29, 29, 29, 29 ); my @obsinterval_abs = ( 0.555555555555556, 0.999652777777778, 0.673611111111111, 0.607291666666667, 0.638888888888889, 0.361111111111111, 0.999652777777778, 0.999652777777778, 0.377083333333333, 0.51875, 0.84375, 0.977777777777778, 0.999652777777778, 0.999652777777778, 0.999652777777778, 0.999652777777778, 0.999652777777778 ); #my @obsinterval_abs = (0.555555555555556,0.999652777777778, # 0.673611111111111,500, # 0.638888888888889,0.361111111111111, # 0.999652777777778,0.999652777777778, # 0.377083333333333,0.51875, # -500, 0.977777777777778, # 0.999652777777778,0.999652777777778, # 0.999652777777778,0.999652777777778, # 0.999652777777778); # Chart::Composite # $g = Chart::LinesPoints->new (800, 350); # $g = Chart::LinesPoints->new (700, 350); $g = Chart::Composite->new( 700, 350 ); $g->add_dataset(@bezugszeitraum); $g->add_dataset(@nr_of_sats); $g->add_dataset(@obsinterval_abs); $g->set( 'composite_info' => [ [ 'LinesPoints', [1] ], [ 'LinesPoints', [2] ] ] ); $g->set( 'x_ticks' => 'vertical' ); $g->set( 'x_label' => ' Time' ); $g->set( 'y_label' => 'red: Nr_of_sats' ); # $g-> set ('y_axes' => 'both'); $g->set( 'y_label2' => 'green: obs_interval (absolut)' ); $g->set( 'legend' => 'none' ); $g->set( 'precision' => 1 ); $g->set( 'grey_background' => 'false' ); $g->set( 'title' => 'ANKR' ); $g->set( 'sub_title' => '2005-04-02 - 2005-04-25' ); # $g-> set ('title_font' => gdGiantFont); # $g-> set ('sub_title_font' => gdMediumBoldFont); $g->set( 'include_zero' => 'true' ); $g->set( 'pt_size' => '10' ); $g->set( 'brush_size' => '4' ); # $g-> set ('skip_x_ticks' => $skip_x); $g->png("$samples/linespoints_5.png"); print "ok 1\n\n"; Chart-v2.403.9/t/lines_1.t0000644000175000017500000000205614341172251014755 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Lines; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $g; print "1..1\n"; $g = Chart::Lines->new( 600, 400 ); $g->add_dataset( 'foo', 'bar', 'whee', 'ding', 'bat', 'bit' ); $g->add_dataset( 3.2, 4.34, 9.456, 10.459, 11.24234, 14.0234 ); $g->add_dataset( -1.3, 8.4, 5.34, 3.234, 4.33, 13.09 ); $g->add_dataset( 5, 7, 2, 10, 12, 2.3445 ); $g->set( 'title' => 'LINES' ); $g->set( 'sub_title' => 'Lines Chart' ); $g->set( 'colors' => { 'y_label' => [ 0, 0, 255 ], y_label2 => [ 0, 255, 0 ], 'y_grid_lines' => [ 127, 127, 0 ], 'dataset0' => [ 127, 0, 0 ], 'dataset1' => [ 0, 127, 0 ], 'dataset2' => [ 0, 0, 127 ] } ); $g->set( 'y_label' => 'y label 1' ); $g->set( 'y_label2' => 'y label 2' ); $g->set( 'y_grid_lines' => 'true' ); $g->set( 'legend' => 'bottom' ); $g->png("$samples/lines_1.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/pareto_3.t0000644000175000017500000000157414341172251015143 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pareto; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Pareto->new( 450, 400 ); $g->add_dataset( 'Mo', 'Tue', 'We', 'Th', 'Fr', 'Sa', 'Su' ); $g->add_dataset( 3000, 1600, 1500, 400, 100, 20, 5 ); %hash = ( 'colors' => { 'dataset0' => 'green', 'dataset1' => 'red', 'x_label' => 'red', 'y_grid_lines' => 'white', 'title' => 'blue', }, 'title' => 'Pareto Chart ', 'integer_ticks_only' => 'true', 'precision' => 0, 'skip_int_ticks' => 400, 'sort' => 'true', # 'max_val' => 6800, 'y_grid_lines' => 'true', 'spaced_bars' => 'false', 'legend' => 'none', ); $g->set(%hash); $g->png("$samples/pareto_3.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/hbars_2.t0000644000175000017500000000216314341172251014742 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::HorizontalBars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::HorizontalBars->new( 500, 400 ); $g->add_dataset( 'Foo', 'bar', 'junk', 'ding', 'bat' ); $g->add_dataset( -4, 3, -4, -5.4, -2 ); $g->add_dataset( 2.2, 10, -3, 8, 3 ); $g->add_dataset( -10, 2, 4, -3, -3 ); $g->add_dataset( 7, -5, -3, 4, 7 ); %hash = ( 'transparent' => 'true', 'y_axes' => 'both', 'title' => 'Horizontal Bars Demo', 'y_grid_lines' => 'true', 'x_label' => 'x-axis', 'y_label' => 'y-axis', 'y_label2' => 'y-axis', 'tick_len' => '5', 'x_ticks' => 'vertical', 'grid_lines' => 'true', 'colors' => { 'text' => [ 100, 0, 200 ], 'y_label' => [ 2, 255, 2 ], 'y_label2' => [ 2, 255, 2 ], 'y_grid_lines' => [ 255, 255, 255 ], 'x_grid_lines' => [ 255, 255, 255 ], }, ); $g->set(%hash); $g->png("$samples/hbars_2.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/composite_5.t0000644000175000017500000000106614341172251015651 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Composite; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Composite->new( 750, 600 ); $g->add_dataset( 1, 2, 3 ); $g->add_dataset( 10, 20, 30 ); $g->add_dataset( 15, 25, 32 ); $g->add_dataset( 7, 24, 23 ); $g->add_dataset( 0.1, 0.5, 0.9 ); $g->set( 'title' => 'Composite Chart Test 2', 'composite_info' => [ [ 'Bars', [ 1 .. 3 ] ], [ 'LinesPoints', [4] ] ] ); $g->png("$samples/composite_5.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/composite_6.t0000644000175000017500000000230714341172251015651 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Composite; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my @labels = qw (06:00-10:00 10:00-14:00 14:00-18:00 18:00-22:00 22:00-02:00 02:00-06:00); my @chart_data = qw (0 140 160 155 150 145); my @chart_lowlimit = qw (120 120 120 120 120 120); my @chart_hilimit = qw (180 180 180 180 180 180); my $chart = Chart::Composite->new( 500, 300 ); $chart->add_dataset(@labels); $chart->add_dataset(@chart_data); $chart->add_dataset(@chart_lowlimit); $chart->add_dataset(@chart_hilimit); my %chart_settings = ( 'precision' => 0, 'legend' => 'none', 'graph_border' => 0, 'png_border' => 1, 'brush_size1' => 2, 'brush_size2' => 10, 'grid_lines' => 'false', 'y_grid_lines' => 'true', 'composite_info' => [ [ 'LinesPoints', [1] ], [ 'Lines', [ 2, 3 ] ] ], 'colors' => { dataset0 => 'black', dataset1 => 'red', dataset2 => 'red' }, 'sub_title' => 'Average Chart', 'min_val' => 0, 'max_val' => 200 ); $chart->set(%chart_settings); $chart->png("$samples/composite_6.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/Math_1_over_x.t0000644000175000017500000000467114341172251016123 0ustar herbertherbert#!/usr/bin/perl -w # # Testprogram for lines # Math expressions 1/x # #====================================================================== BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::Lines; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my @x_values = (); # x axis my @y1_values = (); # 1/x for x<0 my @y2_values = (); # 1/x for x>0 my $graphic; my $min_x = -5; my $max_x = 5; my $min_y = -10; my $max_y = 10; my $x; my $y; #------------------------------------------------------------------------------------ # Start #------------------------------------------------------------------------------------ $x = $min_x; for ( my $x_idx = 0 ; $x < $max_x ; $x_idx++ ) { $x = $min_x + $x_idx * 0.1; $x_values[$x_idx] = $x; } for ( my $x_idx = 0 ; $x_idx <= $#x_values ; $x_idx++ ) { if ( $x_values[$x_idx] < 0 ) { $y1_values[$x_idx] = 1 / $x_values[$x_idx]; $y2_values[$x_idx] = $max_y + 10; } elsif ( $x_values[$x_idx] > 0 ) { $y1_values[$x_idx] = $min_y - 10; $y2_values[$x_idx] = 1 / $x_values[$x_idx]; } else { $y2_values[$x_idx] = $max_y + 10; $y1_values[$x_idx] = $min_y - 10; } } #------------------------------------------------------------------------------------ # Make it #------------------------------------------------------------------------------------ $graphic = Chart::Lines->new( 750, 600 ); $graphic->set( 'brush_size' => 2 ); $graphic->add_dataset(@x_values); $graphic->add_dataset(@y1_values); $graphic->add_dataset(@y2_values); $graphic->set( 'min_val' => $min_y ); $graphic->set( 'max_val' => $max_y ); #$graphic -> set ('y_ticks' => 11 ); $graphic->set( 'x_ticks' => 'vertical' ); $graphic->set( 'skip_x_ticks' => 10 ); $graphic->set( 'grey_background' => 'true' ); $graphic->set( 'graph_border' => 18 ); $graphic->set( 'title' => "1/x" ); $graphic->set( 'y_grid_lines' => 'true' ); $graphic->set( 'x_grid_lines' => 'true' ); $graphic->set( 'x_ticks' => 'vertical' ); $graphic->set( 'legend' => 'none' ); $graphic->set( 'x_label' => 'x' ); $graphic->set( 'y_label' => 'f = 1/x' ); if ( $graphic->can('gif') ) { my $picture_file = "$samples/Math_1_over_x.gif"; $graphic->gif($picture_file); } if ( $graphic->can('png') ) { my $picture_file = "$samples/Math_1_over_x.png"; $graphic->png($picture_file); } print "ok 1\n"; exit(0); Chart-v2.403.9/t/pie_4.t0000644000175000017500000000166114341172251014424 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pie; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Pie->new( 500, 450 ); $g->add_dataset( 'eins', 'zwei', 'drei', 'vier', 'fuenf', 'sechs', 'sieben', 'acht', 'neun', 'zehn' ); $g->add_dataset( 120, 50, 100, 80, 40, 45, 150, 60, 110, 50 ); $g->set( 'title' => 'Pie\nDemo Chart' ); $g->set( 'sub_title' => 'True Type Fonts' ); $g->set( 'label_values' => 'percent' ); $g->set( 'legend_label_values' => 'value' ); $g->set( 'legend' => 'bottom' ); $g->set( 'grey_background' => 'false' ); $g->set( 'x_label' => '' ); $g->set( 'legend_font' => GD::Font->Small ); $g->set( 'title_font' => GD::Font->Giant ); $g->set( 'legend_lines' => 'false' ); $g->png("$samples/pie_4.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/composite_f.t0000644000175000017500000000204714341172251015732 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Composite; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Composite->new; $g->add_dataset( 1, 2, 3, 7, 5, 6 ); $g->add_dataset( 0.1, 0.2, 0.3, 0.2, 0.4, 0.1 ); $g->add_dataset( 0.3, 0.5, 0.2, 0.6, 0.7, 0.4 ); $g->add_dataset( 10, 11, 6, 7, 7, 8 ); $g->set( 'title' => 'Composite Chart', 'composite_info' => [ [ 'Bars', [ 1, 2 ] ], [ 'LinesPoints', [3] ] ] ); $g->set( 'include_zero' => 'true' ); $g->set( 'legend' => 'top' ); $g->set( 'legend_example_height' => 'true', ); $g->set( 'legend_example_height0..1' => '10' ); $g->set( 'legend_example_height2' => '3' ); $g->set( 'f_y_tick' => \&multiply ); $g->set( 'f_x_tick' => \&int_quadrat ); $g->png("$samples/composite_f.png"); print "ok 1\n"; exit(0); sub multiply { my $y = shift; return ( $y * 10 ); } sub int_quadrat { my $x = shift; return $x * $x; } Chart-v2.403.9/t/error_1.t0000644000175000017500000000164214341172251014774 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::ErrorBars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::ErrorBars->new(); $g->add_dataset(qw(1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2 2.1 2.2 2.3 2.4 2.5)); $g->add_dataset(qw(1 1.1 1.2 1.1 1.14 1.15 1.26 1.2 1.1 1.19 1.2 1.4 1.6 2.0 2.5 3.1)); $g->add_dataset(qw(0.4 0.1 0.2 0.1 0.14 0.15 0.26 0.27 0.1 0.19 0.2 0.1 0.1 0.2 0.1 0.3)); $g->add_dataset(qw(0.2 0.11 0.12 0.11 0.2 0.3 0.12 0.27 0.11 0.3 0.2 0.2 0.2 0.1 0.1 0.2)); $g->set( 'xy_plot' => 'true', 'precision' => 1, 'pt_size' => 12, 'brush_size' => 2, 'legend' => 'none', 'title' => 'Error Bars Demo', 'grid_lines' => 'true', 'y_axes' => 'both', 'custom_x_ticks' => [ 0, 1, 2 ], ); $g->png("$samples/error_1.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/lines_3.t0000644000175000017500000000204114341172251014751 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Lines; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $g; print "1..1\n"; $g = Chart::Lines->new; $g->add_dataset( 'foo', 'bar', 'whee', 'ding', 'bat', 'bit' ); $g->add_dataset( 3.2, 4.34, 9.456, 10.459, 11.24234, 14.0234 ); $g->add_dataset( -1.3, 8.4, 5.34, 3.234, 4.33, 13.09 ); $g->add_dataset( 5, 7, 2, 10, 12, 2.3445 ); $g->set( 'title' => 'LINES' ); $g->set( 'sub_title' => 'Lines Chart' ); $g->set( 'colors' => { 'y_label' => [ 0, 0, 255 ], y_label2 => [ 0, 255, 0 ], 'y_grid_lines' => [ 127, 127, 0 ], 'dataset0' => [ 127, 0, 0 ], 'dataset1' => [ 0, 127, 0 ], 'dataset2' => [ 0, 0, 127 ] } ); $g->set( 'y_label' => 'y label 1' ); $g->set( 'y_label2' => 'y label 2' ); $g->set( 'y_grid_lines' => 'true' ); $g->set( 'legend' => 'right' ); $g->png("$samples/lines_3.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/lines_7.t0000644000175000017500000000153714341172251014766 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Lines; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $g; print "1..1\n"; $g = Chart::Lines->new; $g->add_dataset( 'one', 'two', 'three', 'four', 'five', 'six' ); $g->add_dataset( 3, 11, 5, 10, 12, 4 ); $g->add_dataset( -1, 3, 6, -2, -8, 0 ); $g->add_dataset( 5, 5, 6, 2, 12, 9 ); $g->add_dataset( 0, 0, 0, 0, 0, 0 ); $g->add_dataset( -12, -18, 0, 0, 0, 1 ); $g->set( 'title' => "Lines Chart" ); $g->set( 'sub_title' => 'Lines Chart' ); $g->set( 'y_grid_lines' => 'true' ); $g->set( 'legend' => 'bottom' ); $g->set( 'precision' => '0' ); $g->set( 'include_zero' => 'true' ); $g->png("$samples/lines_7.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/direction_4.t0000644000175000017500000000167414341172251015633 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Direction; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Direction->new( 500, 500 ); $g->add_dataset( 210, 220, 200, 215, 225, 200 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->add_dataset( 120, 130, 110, 125, 135, 110 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->add_dataset( 300, 310, 290, 305, 315, 290 ); $g->add_dataset( 30, 40, 20, 35, 45, 20 ); $g->set( 'title' => 'Direction Demo', 'angle_interval' => 45, 'precision' => 0, 'arrow' => 'true', 'point' => 'false', 'include_zero' => 'true', 'pairs' => 'true', 'legend' => 'none', 'grey_background' => 'false', ); $g->png("$samples/direction_4.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/lines_4.t0000644000175000017500000000203714341172251014757 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Lines; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); my $g; print "1..1\n"; $g = Chart::Lines->new; $g->add_dataset( 'foo', 'bar', 'whee', 'ding', 'bat', 'bit' ); $g->add_dataset( 3.2, 4.34, 9.456, 10.459, 11.24234, 14.0234 ); $g->add_dataset( -1.3, 8.4, 5.34, 3.234, 4.33, 13.09 ); $g->add_dataset( 5, 7, 2, 10, 12, 2.3445 ); $g->set( 'title' => "LINES" ); $g->set( 'sub_title' => 'Lines Chart' ); $g->set( 'colors' => { 'y_label' => [ 0, 0, 255 ], y_label2 => [ 0, 255, 0 ], 'y_grid_lines' => [ 127, 127, 0 ], 'dataset0' => [ 127, 0, 0 ], 'dataset1' => [ 0, 127, 0 ], 'dataset2' => [ 0, 0, 127 ] } ); $g->set( 'y_label' => 'y label 1' ); $g->set( 'y_label2' => 'y label 2' ); $g->set( 'y_grid_lines' => 'true' ); $g->set( 'legend' => 'top' ); $g->png("$samples/lines_4.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/mountain_4.t0000644000175000017500000000413114341172251015474 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Mountain; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $obj = new Chart::Mountain( 1000, 500 ); my @data = ( [ '03/05/2010-00.00', '03/05/2010-00.30', '03/05/2010-01.00', '03/05/2010-01.30', '03/05/2010-02.00', '03/05/2010-02.30', '03/05/2010-03.00', '03/05/2010-03.30', '03/05/2010-04.00', '03/05/2010-04.30', '03/05/2010-05.00', '03/05/2010-05.30', '03/05/2010-06.00', '03/05/2010-06.30', '03/05/2010-07.00', '03/05/2010-07.30', '03/05/2010-08.00', '03/05/2010-08.30', '03/05/2010-09.00', '03/05/2010-09.30', '03/05/2010-10.00', '03/05/2010-10.30', '03/05/2010-11.00', '03/05/2010-11.30', '03/05/2010-12.00', '03/05/2010-12.30', '03/05/2010-13.00', '03/05/2010-13.30', '03/05/2010-14.00', '03/05/2010-14.30', '03/05/2010-15.00', '03/05/2010-15.30', '03/05/2010-16.00', '03/05/2010-16.30', '03/05/2010-17.00', '03/05/2010-17.30', '03/05/2010-18.00', '03/05/2010-18.30', '03/05/2010-19.00', '03/05/2010-19.30', '03/05/2010-20.00', '03/05/2010-20.30', '03/05/2010-21.00', '03/05/2010-21.30', '03/05/2010-22.00', '03/05/2010-22.30', '03/05/2010-23.00', '03/05/2010-23.30' ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ); my @labels = ('ROMA_3'); $obj->set( 'title' => 'Video Server Play Daily Hourly Report', 'grid_lines' => 'true', 'include_zero' => 'true', 'misc' => [ 0, 0, 0 ], 'y_label' => 'Numero Streams', 'x_ticks' => 'vertical', 'precision' => '0', 'integer_ticks_only' => 'true', 'min_val' => 0, ); $obj->set( 'colors' => { 'dataset0' => [ 0, 255, 0 ] }, 'legend_labels' => \@labels, 'tick_label_font' => ( GD::Font->Giant ), 'title_font' => ( GD::Font->Giant ) ); $obj->png( "$samples/mountain_4.png", \@data ); print "ok 1\n"; exit(0); Chart-v2.403.9/t/hbars_1.t0000644000175000017500000000264014341172251014741 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::HorizontalBars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::HorizontalBars->new( 600, 500 ); $g->add_dataset( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ); $g->add_dataset( 14, 12, 18, 17, 15, 11, 12, 10, 14, 12, 18, 17 ); $g->add_dataset( 11, 13, 14, 15, 11, 10, 9, 8, 11, 13, 11, 12 ); $g->add_dataset( 4, 8, 7, 4, 5, 4, 6, 4, 6, 10, 7, 4 ); $g->add_dataset( 5, 7, 6, 5, 6, 6, 7, 8, 6, 9, 8, 7 ); $g->add_dataset( 5, 4, 2, 5, 3, 6, 1, 4, 5, 4, 2, 5 ); %hash = ( 'y_axes' => 'both', 'title' => 'Sold Cars in 2001', 'integer_ticks_only' => 'true', 'legend' => 'bottom', 'y_label' => 'month', 'y_label2' => 'month', 'x_label' => 'number of cars', 'legend_labels' => [ 'Berlin', 'Munich', 'Rome', 'London', 'Paris' ], 'min_val' => 0, 'max_val' => 20, 'grid_lines' => 'true', 'colors' => { 'title' => 'red', 'x_label' => 'blue', 'y_label' => 'blue', 'y_label2' => 'blue', 'dataset4' => 'yellow' }, ); $g->set(%hash); $g->png("$samples/hbars_1.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/f_ticks.t0000644000175000017500000000527514341172251015053 0ustar herbertherbert#!/usr/bin/perl -w # # Testprogram for lines # converting seconds since 0 o'clock to HH:MM:SS on x # converting -1 .. +1 to -100 ... +100 on y # #====================================================================== BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::Points; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my @x_values = (); # x axis my @y_values = (); my $graphic; my $min_x = 600; # random start my $max_x = 86400; # number of seconds of a day my $min_y; my $max_y; my $x; my $y; #------------------------------------------------------------------------------------ # Start #------------------------------------------------------------------------------------ $x = $min_x; for ( my $x_idx = 0 ; $x < $max_x ; $x_idx++ ) { $x = $min_x + $x_idx * 23; $x_values[$x_idx] = $x; $y_values[$x_idx] = cos( ( $x - $min_x ) / 3000 ); } #------------------------------------------------------------------------------------ # Make it #------------------------------------------------------------------------------------ $graphic = Chart::Points->new( 750, 600 ); $graphic->set( 'brush_size' => 2 ); $graphic->add_dataset(@x_values); $graphic->add_dataset(@y_values); $graphic->set( 'min_val' => $min_y ); $graphic->set( 'max_val' => $max_y ); #$graphic -> set ('y_ticks' => 11 ); $graphic->set( 'skip_x_ticks' => 100 ); $graphic->set( 'pt_size' => 2 ); $graphic->set( 'grey_background' => 'true' ); $graphic->set( 'graph_border' => 18 ); $graphic->set( 'title' => "f_tick example for x and y values" ); $graphic->set( 'y_grid_lines' => 'false' ); $graphic->set( 'x_grid_lines' => 'false' ); $graphic->set( 'x_ticks' => 'vertical' ); $graphic->set( 'legend' => 'none' ); $graphic->set( 'x_label' => 'Time of the day' ); $graphic->set( 'y_label' => 'f = sin(x)' ); # use a special function to convert the x values to HH:MM:SS $graphic->set( 'f_x_tick' => \&seconds_to_hour_minute ); # use a special function to convert the y values to something special $graphic->set( 'f_y_tick' => \&formatter ); if ( $graphic->can('gif') ) { my $picture_file = "$samples/f_ticks.gif"; $graphic->gif($picture_file); } if ( $graphic->can('png') ) { my $picture_file = "$samples/f_ticks.png"; $graphic->png($picture_file); } print "ok 1\n"; exit(0); sub seconds_to_hour_minute { my $seconds = shift; my $hour = int( $seconds / 3600 ); my $minute = int( ( $seconds - $hour * 3600 ) / 60 ); my $sec = $seconds - $hour * 3600 - $minute * 60; sprintf "%02d:%02d:%02d", $hour, $minute, $sec; } sub formatter { my $y_value = shift; sprintf "%02d", int( $y_value * 10 ); } Chart-v2.403.9/t/bars_10.t0000644000175000017500000000172714341172251014656 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Bars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Bars->new( 600, 500 ); $g->add_dataset( 'Berlin', 'Paris', 'Rome', 'London', 'Munich' ); $g->add_dataset( 0, 0, 0, 0, 0 ); $g->add_dataset( 0, 0, 0, 0, 0 ); $g->add_dataset( 0, 0, 0, 0, 0 ); $g->add_dataset( 0, 0, 0, 0, 0 ); $g->add_dataset( 0, 0, 0, 0, 0 ); $g->add_dataset( 0, 0, 0, 0, 0 ); $g->add_dataset( 0, 0, 0, 0, 0 ); %hash = ( 'title' => 'Only a demo chart with zero data', 'legend' => 'bottom', 'grid_lines' => 'true', 'include_zero' => 'true', 'max_val' => '20', 'min_val' => '-20', 'colors' => { 'title' => 'red', 'x_label' => 'blue', 'y_label' => 'blue', 'background' => 'grey', 'text' => 'blue', }, ); $g->set(%hash); $g->png("$samples/bars_10.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/pie_8.t0000644000175000017500000000162014341172251014423 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Pie; use strict; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::Pie->new( 550, 500 ); $g->add_dataset( 'eins', 'zwei', 'drei', 'vier' ); $g->add_dataset( 25, 80, 120, 50 ); $g->set( 'title' => 'Pie Demo Chart' ); $g->set( 'label_values' => 'percent' ); $g->set( 'legend_label_values' => 'value' ); $g->set( 'legend' => 'bottom' ); $g->set( 'grey_background' => 'false' ); $g->set( 'ring' => 0.9 ); $g->set( 'legend_lines' => 'true' ); $g->set( 'x_label' => '' ); $g->set( 'colors' => { 'misc' => 'light_blue', 'dataset1' => 'red', 'dataset2' => 'blue', 'dataset0' => 'yellow', 'dataset3' => 'green' } ); $g->png("$samples/pie_8.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/linespoints_4.t0000644000175000017500000000301514341172251016211 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::LinesPoints; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my $g = Chart::LinesPoints->new( 700, 350 ); my @bezugszeitraum = ( '2005-04-02', '2005-04-03', '2005-04-04', '2005-04-05', '2005-04-06', '2005-04-07', '2005-04-08', '2005-04-09', '2005-04-10', '2005-04-11', '2005-04-12', '2005-04-13', '2005-04-14', '2005-04-15', '2005-04-16', '2005-04-17', '2005-04-18', '2005-04-19', '2005-04-20', '2005-04-21', '2005-04-22', '2005-04-23', '2005-04-24', '2005-04-25' ); my @clock_reset = ( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); #my @clock_reset = (10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10); $g->add_dataset(@bezugszeitraum); $g->add_dataset(@clock_reset); $g->set( 'x_ticks' => 'vertical' ); $g->set( 'x_label' => 'Time' ); $g->set( 'y_label' => 'Number of clock resets' ); $g->set( 'legend' => 'none' ); $g->set( 'precision' => 1 ); $g->set( 'title' => 'AURI' ); $g->set( 'sub_title' => '2005-04-01 --- 2005-04-25' ); # $g-> set ('title_font' => 'gdGiantFont'); # $g-> set ('sub_title_font' => 'gdMediumBoldFont'); $g->set( 'grey_background' => 'false' ); # $g-> set ('include_zero' => 'true'); # $g-> set ('min_val' => '0'); $g->set( 'pt_size' => '10' ); $g->set( 'brush_size' => '4' ); # $g-> set ('skip_x_ticks' => $skip_x); # $g-> set ('integer_ticks_only' => 'true'); $g->png("$samples/linespoints_4.png"); print "ok 1\n\n"; Chart-v2.403.9/t/points_2.t0000644000175000017500000000135714341172251015163 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use Chart::Points; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; $g = Chart::Points->new; $g->add_dataset( 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So' ); $g->add_dataset( -3, 0, 8, 4, 2, 1, 0 ); $g->add_dataset( 8, 0.12, 9, 2, 4, -1, 3 ); $g->add_dataset( 5, -7, 12, 5, 7, 5, 8 ); $g->add_dataset( 0, 0, 0, 0, 0, 0, 0 ); @hash = ( 'title' => 'Points Chart', # 'type_style' => 'donut', 'png_border' => 10, 'precision' => 0, 'min_val' => 0, #'max_val' => 0, 'include_zero' => 'true', ); $g->set(@hash); $g->png("$samples/points_2.png"); print "ok 1\n"; exit(0); Chart-v2.403.9/t/stackedbars_3.t0000644000175000017500000000201114341172251016122 0ustar herbertherbert#!/usr/bin/perl -w BEGIN { unshift @INC, 'lib', '../lib'} use strict; use Chart::StackedBars; use File::Temp 0.19; my $samples = File::Temp->newdir(); print "1..1\n"; my ($g) = Chart::StackedBars->new( 580, 400 ); $g->add_dataset( '1', '2', '3', '4', '5', '6', '7' ); $g->add_dataset( 5, 7, 9, 11, 9, 7, 5 ); $g->add_dataset( 15, 11, 8, 5, 8, 11, 15 ); $g->add_dataset( 5, 4, 3, 2, 3, 4, 5 ); $g->set( 'legend' => 'right', 'title' => 'Stacked Bars', 'precision' => 0, 'spaced_bars' => 'true', 'include_zero' => 'true', 'skip_int_ticks' => 3, 'max_val' => 30, 'y_label' => '', 'y_label2' => '', 'grey_background' => 'false', ); $g->set( 'colors' => { 'dataset0' => [ 0, 125, 250 ], 'dataset1' => [ 147, 112, 219 ], 'dataset2' => [ 250, 0, 125 ], 'background' => [ 230, 230, 250 ] } ); $g->png("$samples/stackedbars_3.png"); print "ok 1\n"; exit; Chart-v2.403.9/Changes0000644000175000017500000003166514341172251014276 0ustar herbertherbert2.403.9 2022-12-11 lichtkind -------- * = minor feature add * + add_datafile now reads CSV * & added test for loading data files 2.403.8 2022-10-22 lichtkind -------- * = POD rewrite release * \ moved color code to Graphics::Toolkit::Color * ? adapted POD 2.403.7 2022-07-29 lichtkind ------- * = POD rewrite release * ? move landing page examples in own category, independent from test script runs * ? reduce references to gone modules in Chart.pm POD * ? clearify some bits in POD * ? several final touches on types manual page and example images * ! fix sub title in manual demo of pie chart * ! bad dates, typos in Changes 2.403.6 2022-07-21 lichtkind ------- * = POD rewrite release * ! gap in HTML tag in POD prevented landing page demo images * ? final touches on new POD documentation 2.403.5 2022-07-18 lichtkind ------- * = POD rewrite release * ? completed workflow page in manual * ? completed methods page in manual 2.403.4 2022-07-17 lichtkind ------- * = POD rewrite release * ? completed types page in manual * ! fixed some image sizes * ! fixed dist.ini so old stuff from git don't spill into release 2.403.3 2022-07-16 lichtkind ------- * = POD rewrite release * + addded images to landing page * + data files can have # comments * ? wrote most of types page in manual * ? some small addons for properties page * ! fixed overlong tick label due rounding error near zero 2.403.2 2022-07-13 lichtkind ------- * = POD rewrite release * ? completed properties page in manual * ? started types page in manual with first test example * ! linkfixes on landing POD of Chart.pm * ! linkfixes on Color::Constant.pm POD 2.403.1 2022-07-12 lichtkind ------- * = POD rewrite release * ? completed most of properties * ? all names listed in Color::Constant.pm POD 2.403.0 2022-07-07 lichtkind ------- * = test release to check metacpan POD parser * + added Chart::Manual + property sub page * - drop PDF and HTML documentation * - drop dependency on module: POSIX 2.402.3 2022-06-18 lichtkind ------- * + string method to Color objects * ? fixed #18891 : document that y_ticks1, y_ticks2 is composite only * ! fixed (not interrupting Color::Value::distance_rgb even sub sub carped) 2.402.2 2022-06-15 lichtkind ------- * ? linking POD of Chart::Color.pm * ? POD typo f_y_ticks1 => f_y_tick1 * ? document -ring setting for ring charts * ! added shebang to new tests by Petr Pisar 2.402.1 2022-06-09 lichtkind ------- * ! fixing color value rounding in Chart::Color::Value 2.402.0 2022-06-08 lichtkind ------- * = first minor feature add under new maintainer * # main theme: colors * + added 3 classes of Graph::Color::* * + get pantone report colors by name * + access to external color libs of Graphics::ColorNames::* via syntax: 'standard_name:color_name' * + full fledged color objects (also for later use) * ~ slight change in default colors for more calm professional look * ! fixed and added colors from X11 and HTML standard * ! fixing licence issues back to Perl 5 2.401.1 2022-04-04 lichtkind ------- * = minor fix release & test for init dzil file * ~ new file headers * ! 2 patches by Petr Pisar fixing tests * ? expanded TODO 2.400.10 2022-04-02 lichtkind ------- * = Testrelease for new metafiles * + cpan file 2.400.5 2022-03-31 lichtkind ------- * = Upload fix for failed 2.4.19 .. 2.400.04 * ~ first upload by new maintainer: LICHTKIND * + files CONTRIBUTING 2.4.10 Upload to pause.perl.org failed for version 2.4.9. Therefore, the version 2.4.9 was renamed to 2.4.10 for a new upload. 2.4.9 Upload to pause.perl.org failed for version 2.4.8. Therefore, the version 2.4.8 was renamed to 2.4.9 for a new upload. 2.4.8 Bug repaired: rt.cpan.org #81171::Composite w/two ::Lines and xy_plot ==> undefined value when drawing ticks (t/composite_7.t and t/composite_8.t are showing the differences) Base.pm: defined(@array) Chart.pod corrected for deprecated @ARR->[$i] 2.4.7 Not published 2.4.6 Number of named colors extended Documentation.pdf explains the use of colors (Appendix added) Corrections in base.pm, routines _draw_bottom_legends, _draw_x_number_ticks in LinesPoints.pm, routines _draw_data 2.4.5 Typo in _draw_x_ticks corrected Methods scalar_png(), scalar_jpeg() corrected for result. Test routine t/scalarImage.t added Chart.pod corrected 2.4.4 The brush styles to points and linespoints are extended. Not only circles represent the points but a number of different brush styles, linke donut, Star and so on. 2.4.3 Corrections to imagemap production in Composite.pm and Lines.pm 2.4.2 Changes done by R. Dassing, Michael Potter Base.pm: The values for 'true' resp. 'false' may be of the following true: TRUE, true, T, t, 1 and defined false: FALSE, false, F, f, 0 or undefined Base.pm: Added new options xlabel and xrange which is valid for xy_plot and Chart::Lines, Chart::Points, Chart::LinesPoints, Chart::Split, Chart::ErrorBars. In order to use the labels, the chart module needs to have 'xlabels' AND 'xrange' set. Below is some example code for use: @labels = (['Jan', 'Feb','Mar'], ['10','40','70']); $chart->set( xlabels => \@labels, xrange => [0,100] ); This options allow to set and position labels at the x-axis arbitrary. add_dataset() and app_pt() accepts now datasets as lists and as references to list like already mentioned in the docs Pie.pm: Optimized organisation of the placement of the labels. Added flag to avoid plotting of legends at all Using GD Version 2.0.36 as this version supports 'filledArc' Composite.pm: Correction due to Request 23166 write the endmost value of input array in imagemap_data array Lines.pm: Correction due to Request 23166: write the endmost value of input array in imagemap_data array 2.4.1 Changes done by Christine Zilker, Gerhard Stuhlpfarrer, R. Dassing Added new Options: ring Draw a ring instead of a Pie legend_lines Connect Pie and the description with a line stepline Connect Lines and LinesPoints by a stepped line stepline_mode (thanks to Gerhard Stuhlpfarrer) brush_size1 Define Brush Size for Composite 1 brush_size2 and 2 Color problem in Pie fixed Positioning of the description optimized in Pie Division by zero problem fixed in Pie Module 'Bars' checks for numeric data a input on y-axis New Function added to Base: minimum - determine minimal value of an array of numeric values maximum - determine maximal value of an array of numeric values arccos - arccos arcsin - arcsin Some small bugs corrected: Base::_find_y_scale Base::_find_y_range : check for numeric values Base::_find_x_range : check for numeric values Latex sources for documentation included (We hope to get a better documentation be a native english speaking person) 2.3 Changes done by Christine Zilker: Added new Options: in Composite: legend_example_height changes thickness of the lines in the legend, f_y_tick1, f_y_tick2 analog to f_y_tick used for right and left y-axes in Direction: pairs Added the possibility to add more datasets in Chart::Direction Fixed "label space" problem in Pie Fixed dataset order (in the legend) in StackedBars Fixed problem of getting the right values if round2Tick is used Fixed problem of datavalues in _find_y_scale Some minor bugfixes Update of the Documentation (Documentation.pdf) The requested support of TruType fonts was currently dropped due to missing support in GD.pm (even in new versions) The print out of some hints and warnings where deleted not to confuse the user. 2.2: Composite.pm: imagemap_dump() repaired. 2.1: Changes done by Markus Brandl: new Modules added: ErrorBars.pm, HorizontalBars.pm, Pareto.pm, Pie.pm, Split.pm and Direction.pm Subdirectory "doc" contains a Acrobat Reader Documentation.pdf file. Function add_datafile() added. It is now possible to add a complete datafile. Added new Options: precision, xy_plot, min_x_ticks, max_x_ticks, skip_y_ticks, skip_int_ticks, legend_label_value, y_axes, scale, interval, start, interval_ticks, sort, same_error, point, line, arrow, angle_interval, min_circles, max_circles Also added: the 'title' and 'x_label' options in the colors option Documentation (Documentation.pdf) added. _find_x_scale, _find_x_range and _draw_x_number_ticks added to make xy_plots possible. _sort_data has now a body. Fixed integer ticks problem by adding the skip_int_ticks option Fixed f_x_ticks and f_y_ticks problem in Composite Fixed negative value problem in Bars Fixed min_val and max_val problem in draw_data function of all modules: Now, Chart plots the data only if the data is in the area of min_val and max_val! The border of bars in Bars, HorizontalBars and StackedBars will be plotted pink (not misccolor) if the data isn't in the min_val-max_val interval. Fixed custom_x_ticks problem in _draw_x_ticks Some other bugfixes. Updates in _find_y_scale, _round2tick, _calcTickInterval 1.1: Changes done by David Pottage: Plot scales can now have any magnitude. It does not matter if the data covers a range of 100000000000 units or 0.00000000001 units, the scale will be correctly calculated. Ticks on plot scales are now plotted on 'round' numbers. The number & spacing of ticks is chosen based on the data range. False zero graphs are now explicitly supported, and will be generated if the data range merits it. The instance field 'include_zero' should be set to zero to suppress this. Added: include_zero, min_y_ticks, max_y_ticks, integer_ticks_only 1.0.1: Fixed _draw_bottom_legend in Base.pm 0.99c-pre3 - 1.0: Fixed _draw_data in Lines.pm: lines are limited to the frame Added f_x_tick, f_y_tick Added jpeg(), cgi_jpeg() to produce the format jpeg Delete GIF support, added PNG and JPEG instead 0.99b - 0.99c-pre3: James F Miner : Added Mountain chart type Added Patterns. See t/mountain.t for details Bugfix for drifting x tick Improved internal color handling Richard Dice : Added brush shapes for Points, LinesPoints Added scalar_gif 0.99a - 0.99b: Fixed left legend in composite charts Fixed no color problem when using composite charts w/ no legend Fixed color handling for datasets Added option for http header Pragma: no-cache Netscape 4.5 has a bug that breaks it, but it works with other browsers. Any ideas for a workaround? 0.99 - 0.99a: Added use of undef() values to represent 'no data' for line breaks Added ylabel*_color options Added x_grid_lines, y_grid_lines & y2_grid_lines , and color options for each Cache disabling in cgi header: Reiner Nippes Restored grid_lines option: Heinz-Guenter Kontny Fixed a typo that broke imagemap data storage in Lines charts 0.94 - 0.99: Modified the 'title' option to correctly process newlines Deprecated the 'subtitle' option, will remove it in next release Changed the API for specifying colors Added support for printing to file handles Added Chart::Composite Added 'spaced_bars' to make it easy to differentiate the bars Added 'grey_background' to make plot background grey Added support for negative values in the datasets Added methods to remember and dump imagemap pixel information Included rgb.txt with distribution for WinXX users 0.93 - 0.94: Moved the legend down to be flush with the chart Fixed the long decimal y-tick label problem Fixed (for the last time, hopefully) the pre-5.004 compilation problem Fixed handling of undefined data points Added more colors for the default data colors Added the transparent gif option Added the option for user-specified colors Added the grid_lines option 0.92 - 0.93: Fixed the sort problem Fixed the y-axis label centering problem Fixed pre-5.004 compilation problem Added StackedBars charts Chart-v2.403.9/cpanfile0000644000175000017500000000101414341172251014470 0ustar herbertherbert# This file is generated by Dist::Zilla::Plugin::CPANFile v6.025 # Do not edit this file directly. To change prereqs, edit the `dist.ini` file. requires "Carp" => "1.35"; requires "GD" => "2"; requires "Graphics::Toolkit::Color" => "1"; requires "perl" => "v5.12.0"; on 'test' => sub { requires "File::Temp" => "0.19"; requires "Test::More" => "1.3"; requires "Test::Warn" => "0.30"; }; on 'configure' => sub { requires "ExtUtils::MakeMaker" => "0"; }; on 'develop' => sub { requires "Test::Pod" => "1.41"; };