From 1b802f32d83cf7a3fda1f8a515e9cf74a7c0791f Mon Sep 17 00:00:00 2001 From: hickert Date: Thu, 12 Aug 2010 08:23:45 +0000 Subject: [PATCH] Updated graphs and statistics page -Added PHPLot which is licensed under GPL git-svn-id: https://oss.gonicus.de/repositories/gosa/trunk@19378 594d385d-05f5-0310-b6e9-bd551577e9d8 --- .../html/plugins/statistics/getGraph.php | 27 + gosa-core/include/phplot-5.1.2/COPYING | 504 ++ gosa-core/include/phplot-5.1.2/ChangeLog | 1010 +++ .../include/phplot-5.1.2/HorizontalBars.txt | 243 + gosa-core/include/phplot-5.1.2/NEWS.txt | 814 +++ gosa-core/include/phplot-5.1.2/README.txt | 156 + .../include/phplot-5.1.2/contrib/README.txt | 31 + .../contrib/color_range.example.php | 35 + .../phplot-5.1.2/contrib/color_range.php | 100 + .../contrib/color_range.test1.php | 55 + .../contrib/color_range.test2.php | 73 + .../contrib/prune_labels.example.php | 31 + .../phplot-5.1.2/contrib/prune_labels.php | 36 + .../contrib/prune_labels.test.php | 41 + gosa-core/include/phplot-5.1.2/phplot.php | 5873 +++++++++++++++++ .../include/phplot-5.1.2/phplot_data.php | 270 + gosa-core/include/phplot-5.1.2/rgb.inc.php | 752 +++ .../generic/statistics/class_statistics.inc | 30 +- 18 files changed, 10060 insertions(+), 21 deletions(-) create mode 100644 gosa-core/html/plugins/statistics/getGraph.php create mode 100644 gosa-core/include/phplot-5.1.2/COPYING create mode 100644 gosa-core/include/phplot-5.1.2/ChangeLog create mode 100644 gosa-core/include/phplot-5.1.2/HorizontalBars.txt create mode 100644 gosa-core/include/phplot-5.1.2/NEWS.txt create mode 100644 gosa-core/include/phplot-5.1.2/README.txt create mode 100644 gosa-core/include/phplot-5.1.2/contrib/README.txt create mode 100644 gosa-core/include/phplot-5.1.2/contrib/color_range.example.php create mode 100755 gosa-core/include/phplot-5.1.2/contrib/color_range.php create mode 100644 gosa-core/include/phplot-5.1.2/contrib/color_range.test1.php create mode 100644 gosa-core/include/phplot-5.1.2/contrib/color_range.test2.php create mode 100644 gosa-core/include/phplot-5.1.2/contrib/prune_labels.example.php create mode 100644 gosa-core/include/phplot-5.1.2/contrib/prune_labels.php create mode 100644 gosa-core/include/phplot-5.1.2/contrib/prune_labels.test.php create mode 100644 gosa-core/include/phplot-5.1.2/phplot.php create mode 100644 gosa-core/include/phplot-5.1.2/phplot_data.php create mode 100644 gosa-core/include/phplot-5.1.2/rgb.inc.php diff --git a/gosa-core/html/plugins/statistics/getGraph.php b/gosa-core/html/plugins/statistics/getGraph.php new file mode 100644 index 000000000..83b55a2dc --- /dev/null +++ b/gosa-core/html/plugins/statistics/getGraph.php @@ -0,0 +1,27 @@ + diff --git a/gosa-core/include/phplot-5.1.2/COPYING b/gosa-core/include/phplot-5.1.2/COPYING new file mode 100644 index 000000000..602bfc946 --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, 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. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + 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. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this 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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; 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. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/gosa-core/include/phplot-5.1.2/ChangeLog b/gosa-core/include/phplot-5.1.2/ChangeLog new file mode 100644 index 000000000..9d318a27c --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/ChangeLog @@ -0,0 +1,1010 @@ +This is the Change Log for PHPlot. +The project home page is http://sourceforge.net/projects/phplot/ +----------------------------------------------------------------------------- + +2010-06-29 (lbayuk) ===== Released as 5.1.2 ===== + * phplot.php: Updated version + * README.txt: Updated for new release + * NEWS.txt: Add text for new release + +2010-06-26 + * Feature request 2885930 "Horizontal Bars": + Horizontal bar charts are implemented, as an experimental feature. + A new data type 'text-data-yx' was added, which works with + 'bars' plot type to produce a horizontal bar chart from a data + array with X values for each Y value. Changes were made to + FindDataLimits, CalcMargins, CalcPlotAreaWorld, CalcBarWidths, + and CalcMaxDataLabelSize to handle the new data type. Other + changes were made to handle label position defaults and grid + defaults. New drawing functions were added for horizontal bars. + + * HorizontalBars.txt: new documentation file for experimental feature. + * Makefile: List new documentation file. + +2010-06-25 + * Each plot-type drawing function now checks that it is getting a data + type that it knows how to handle. A new internal function unifies the + checking and error message. (This is associated with an upcoming, + bigger change.) + + Compatibility: If you were using an invalid data type for a plot type + whose function did not check, will now get an error. + + * Removed some dubious code from DrawLines() and DrawSquared() and + rewrote comments there. The code initialized lastx[0] and lasty[0], + but lasty was mapped using the X (rather than Y) function. This was + obviously wrong, but closer inspection showed that the values were + never, used so the code was removed. + +2010-06-13 + * Truecolor.txt: removed + * Makefile, README.txt: Removed reference to Truecolor.txt. Full + documentation for truecolor images is now in the Reference Manual. + +2010-06-02 + * Fix bug 3010116 "Bad rendering of title in multi-plot image + when using TTF": + Make sure the main title is drawn only once. (If drawn multiple + times with TrueType font text, the anti-aliasing effects result + in poor quality text.) + +2010-05-31 + * Improvements to truecolor support (from feature request 2947679): + Truecolor support is now better integrated. The derived class only + has the constructor now, and the base class itself provides the alpha + color component support through the internal functions SetIndexColor(), + SetIndexDarkColor(), and SetRGBColor(). This means alpha channel + works with palette images too (in so far as GD supports this). + + * Truecolor.txt: Updated per changes to truecolor support. + + * Image tiling with mode 'scale' in tile_img(), used with image and + plot area backgrounds, now uses imagecopyresampled() rather than + imagecopyresized(). They are the same with palette images, but the + resampled copy gets better results with truecolor images. + +2010-05-29 + * Feature request 3002606 "Add to plot and image border options": + Added options 'right', 'top', and 'bottom' to SetPlotBorderType() + (existing options are 'left', 'sides', 'none', and 'full'). This + now also accepts an array of the above options, giving complete + control over which sides to draw. + Added option 'solid' to SetImageBorderType() to use the actual + color set with SetImageBorderColor(), rather than the darker + shade as type 'plain' does (for some reason). + New function SetImageBorderWidth() sets the width of the image + border. The image border width is now accounted for in margin + calculations, although existing plots will not change. + +2010-04-04 (lbayuk) ===== Released as 5.1.1 ===== + * phplot.php: Updated version + * README.txt: Updated for new release + * NEWS.txt: Add text for new release + +2010-04-01 + * Remove & from argument in SetDataValues(). The data array is not + modified and does not need to be passed by reference. (There is + no performance advantage, either.) + +2010-03-29 + * Feature request 2947679 "Support for alpha blending/Truecolor": + Implemented truecolor image support with a new class + PHPlot_truecolor, extended color specifications to allow + specification of an alpha value, and added a new optional parameter + to SetDataColors for a default alpha value for all data colors. + This feature is *EXPERIMENTAL* (see next item). + + * Truecolor.txt: New file, documentation for the new truecolor capability. + (The Truecolor feature is experimental, which means it is subject to + change in incompatible ways and the documentation has not yet been + incorporated into the PHPlot Reference Manual.) + + * Makefile: Include new documentation file in release. + +2010-03-26 + Fixed bug 2976735 "Improvements and fixes for 'area' plots": + Rewrote DrawArea() function which handles 'area' plot. + Part 1: This is related to feature request 2947679, Truecolor support + with transparency. The area plot function was filling each area from the X + axis up to the Y value, resulting in area overlaps. This wasn't a problem + with opaque colors, but with transparency, the overlapping areas resulted + in changed colors. The rewritten function fills the area between each line + instead of from each line down to the X axis. Plots with opaque colors + will not change. + Part 2: Area plots now work when the X axis is moved up with + SetXAxisPosition(). + Part 3: Fixed FindDataLimits() for area (and stackedbars too) to + take absolute values of Y values. The drawing function was doing this, + but not FindDataLimits, resulting in incorrect limits if any Y<0. + Part 4: The rewritten DrawArea() also handles a new plot type + 'stackedarea'. This is an area plot where the Y values are stacked, + similar to 'stackedbars'. + Note: As part of the changes, it is now an error to try an area plot + with an unequal number of Y points for each X. + +2010-03-23 + * Feature request 2973995 "Add y-Data to Stackedbars": + Implemented Y Data Labels for Stacked Bar charts (stackedbars). + The labels are enabled with SetYDataLabelPos, same as with bar charts. + There are two types of labels: above the stack with the total, and + within the bars at each segment. 'plotin' turns on the upper ones, and + 'plotstack' turns both on. + + * Other changes: + + Removed unimplemented second argument to SetYDataLabelPos. + + Fixed questionable logic in SetYDataLabelPos when given an argument + that belongs with SetYTickLabelPos. + + Fix comments at top of plot-type Draw functions. + + * Fix for bug 2974639 "Stacked bars plot breaks with X axis != 0": + Stacked bar plots with non-zero X axis position no longer break apart + into segments with gaps. The bars are drawn up from the X axis, and + any segments or partial segments below the X axis are not drawn. + +2010-03-22 + * Change related to feature request 2947679 - Fix 'dot' point shape: + Use imagefilledellipse(), not imagefilledarc(), when drawing the 'dot' + point shape. The fix was needed for future support of truecolor images + with transparency, but filled dots from imagefilledellipse() look + better (rounder) with regular images and opaque colors. + Credit to mvaldez for identifying the problem and providing the fix. + +2010-03-04 + * Fix for bug 2963757 "point_counts undefined error in 5.1.0": + Fixed CheckPointParams so it sets point_counts even when the point shape + and point size arrays are already the same size and do not need padding. + +2010-01-26 + * Fix for bug 2938219 "Bars go in wrong direction": + Fixed CalcAxisPositions() to be consistent in positioning the X axis. + When all Y values are <0 and the Y=0 line is not part of the plot range, + PHPlot will now default the X axis to the top of the plot, not the + bottom. This fixes the problem with bars to negative Y values being + drawn downward if Y=0 is visible, but upward if Y=0 is not visible. + This also affects thinbarline plots. + Credit to lauryn1298 for finding the bug. + +2009-12-24 (lbayuk) ===== Released as 5.1.0 ===== + +2009-12-18 + * Change for bug 1795971 "Fix default data colors": + The default Data Color and Error Bar Color arrays now have 16 + different colors, no duplicates, and nothing so light that it + is invisible. + Using '' or False as the argument to SetDataColors, SetErrorBarColors, + and SetDataBorderColors now re-initializes the map to the defaults. + This was previously undocumented, and in some cases set the map to + something different from the default. + +2009-12-15 + * Cleanup: Remove DrawAxisLegend() - empty function marked TODO, + not really clear what it was meant to do. + +2009-12-14 + * Fix for bug 2914403 "Pie + X/Y titles: Undefined property error": + In DrawGraph(), don't try to draw X or Y titles for pie charts. + + * Feature request 2899921: "allow different format for data and tick + labels"; Bug 2906436: "Fixes for X Tick Labels vs X Data Labels", + and partial implementation of changes from user 'adoll' regarding + tick vs data labels: + + New public functions: + + SetXDataLabelType() : Sets formatting for X Data Labels + + SetYDataLabelType() : Sets formatting for Y Data Labels (bar charts) + + SetXDataLabelAngle() : Sets text angle for X Data Labels + + SetYDataLabelAngle() : Sets text angle for Y Data Label (bar charts) + The defaults for these are set up to be fully backward compatible + with previous releases of PHPlot (except see the next item). + + Re-used function name SetXDataLabelAngle(): + + This has been deprecated and undocumented since 2003-12-07, and + used to just call SetXLabelAngle(). For new behavior, see above. + + Changes to public functions: + + SetXDataLabelPos() and SetXTickLabelPos() no longer cancel each + other out (set the other control variable to 'none'). Instead, + they are both considered before plot drawing. + + Changes to internal functions: + + DrawDataLabel() now uses the font, angle, and color arguments as + provided, and does not substitute values if they are empty. + + SetLabelType() now takes mode='xd' and 'yd' for X Data and Y Data + label formatting; 'x' and 'y' are for tick labels only now. + + Functions that work on Data labels now call FormatLabels() with the + new mode parameter value 'xd' or 'yd, and use the new + data_label_angle variables. + + New CheckLabels(), used by DrawGraph to process label parameters. + + CalcMargins() - Rewritten to handle changes to Tick and Data labels. + + Changes to internal class variables: + + New: x_data_label_angle, y_data_label_angle + + Do not initialize x_tick_label_pos or x_data_label_pos, so that + CheckLabels() can tell if they were set or not and apply defaults. + + Initialize y_data_label_pos to 'none', not 'plotleft'. + + Add 2 more indexes to label_format[] array: 'xd' and 'yd'. + + * Cleanup: + + Delete unused internal class variable: draw_y_data_label_lines + + Delete unused function SetDrawYDataLabelLines() + +2009-12-07 + * Fix bug 1795972 "Fix default point shapes": + + Added 10 new point shapes to the existing 10 shapes. + + Changed the default point shape from all 'diamond' to a + selection of up to 10 different shapes. + + Fixed bug in the code that tried to set the point shapes + and sizes arrays to be the same size. This was not working, + resulting in unexpected point sizes. + + Changed default point size to 6 for all shapes. It was trying + to be "5, 5, 3" but due to several bugs this was not working. + + Do not adjust shape sizes to even numbers (was done for only two + shapes). Instead, consistently truncate size/2 when needed. + NOTE: These may change the look of 'points' and 'linepoints' plots. + + * Changed startup initialization code: + + SetDefaultStyles() was doing some odd things using a variable + called "session_set", with comments referring to non-existent + session support code. This has been removed. There should be + no visible changes from this. PHPlot does not use PHP sessions. + +2009-12-04 + * Fix for bug 2908256, errors in pie charts with bad data array: + (From a Drupal contrib module report by thekevinday.) + With pie charts only, a data array with no valid Y values resulted + in PHP error messages. All other plot types handle this by producing + an image without a graph. + Fixed DrawPieChart to behave this way too. If there are no valid Y + values, or if the sum of all Y values is 0, do not error out, but + don't draw a pie chart either. + Also, pie charts now ignore non-numeric Y values, like other plot types. + +2009-11-20 (lbayuk) + * Fix for bug 2900914 "Problem with display of 0 on Y axis": + Changed how X and Y values are stepped by tick intervals, to avoid + cumulative round-off error. This fixes the problem when Y crosses 0 with + a tick step such as 0.1 resulting in a long label for a very small but + non-zero number. Fixed DrawXTicks, DrawYTicks, and CalcMaxTickLabelSize. + (Originally reported by cncnet) + +2009-11-19 (lbayuk) + * Improve support for using callbacks to annotate plots: + Added new callback 'draw_all', called after all drawing. + Supply plot_area[] as argument to some drawing callbacks. + Added new method GetDeviceXY() to translate from world coordinates. + Allow NULL or '' for $font in DrawText() internal method, meaning to + use the generic font. If callbacks want to use DrawText, this + avoids them having to reference the internal fonts[] array. + +2009-11-01 (lbayuk) + * Address bug report 2886365 "Declare all functions and variables in + PHP5 style" + PHP5 deprecates the use of 'var' to declare a class member variable. + All initialized class member variables are now declared 'public'. + (It was tempting to make most or all 'protected' or 'private', but + that would likely break too much, including the PHPlot Test Suite.) + + Most class member functions which are meant for internal use only are + now declared 'protected', so they cannot be called from scripts + (except in child classes). (Note PHP5 does not deprecate the use of + just 'function' to mean public, so public functions were not changed.) + Internal functions are those documented in the manual under Developer's + Guide, Internal Functions. If your code breaks because you are using + a method which is now protected, please post the details on the help + forum. + + Some member variables which were set in the constructor are now + initialized with the class instead. (No impact.) + + Removed commented-out, FIXME-noted code for interim labels. + +2009-10-12 (lbayuk) + * Bug report 2839547, allow SetImageBorderType('none') to reset the image + border type. Also checked for other cases where there is no reset; + found one that exists (Set[XY]LabelType) but needs to be documented. + +2009-07-09 (lbayuk) + * Added a hook $plot->locale_override which can be set to True to prevent + PHPlot from loading locale settings from the environment with + setlocale(LC_ALL, ''). This is necessary for testing PHPlot on Windows, + where you cannot force a locale with an environment variable. It might + also be needed for people who want PHPlot's locale to differ from the + web server's locale. + +2009-06-12 (lbayuk) ===== Released as 5.0.7 ===== + +2009-06-11 (lbayuk) + * Change PHPlot license to LGPL, per Afan. + phplot.php, phplot_data.php - Change license notice. + rgb.inc.php - Change top comments and remove bottom marker. + COPYING - new file, text of LGPL. + LICENSE.* - removed files - old licenses. + Makefile - change list of distributed files. + + * Fix for bug 2803900: SetRGBArray('large') does not work. The include + file defined a different array name than the main script expected. + (This bug seems to have happened over 8 years ago.) Fixed the array + names to match. Also removed the ./ prefix from the included filename + so it will be found if on the include path but not in the script + directory. Also added error check if the rgb.inc.php include file + is needed and not found. + +2009-05-25 (lbayuk) + * Added new feature to allow partial margin or plot area specification. + You can omit, or specify as NULL, any of the 4 arguments to + SetMarginsPixels() or SetPlotAreaPixels(), and this means PHPlot + should use the automatically calculated margin on that side. + Credit to adoll for this feature. + +2009-05-17 (lbayuk) + * Fix for bug 2791502 "Error plots treat missing Y values as 0": + Plots with data type data-data-error now support missing Y values, + instead of treating them as 0. This works with lines, points, + and linepoints plot types, and also honors SetDrawBrokenLines. + + + * Fix for bug 2792860 "Wrong DataLabelLines with missing Y": + Do not draw X Data Label Lines at points with missing Y values. + + + * Fix for bug 2786350 "Missing Y data results in bad auto-range": + Rewrote FindDataLimits to ignore missing Y values, rather than + treating them as if 0, for calculating range. + Bug report and analysis by mrten. + + * Fix for bug 2786354 "Incorrect auto-range for data-data-error": + For data-data-error data type, apply the positive and negative error + amounts for each Y point to that point only, rather than applying the + largest errors to the overall minimum and maximum Y value for the row. + + Note: The two fixes above can change existing plots which rely on + automatic Y range calculation. The first fix affects plots with + missing Y values and min(Y)>0. The second fix can affect plots using + data-data-error data type and different error values for different + points. In both cases the new Y range can be smaller than before. + +2009-01-20 (lbayuk) ===== Released as 5.0.6 ===== + +2009-01-18 (lbayuk) + * Fix for bug 1891636 "Misaligned TTF X Labels": + PHPlot was using the actual bounding box of each line of text + to allocate space and set the text positioning, but was ignoring the + fact that the text baseline is not the same as the bottom of the + bounding box. This resulted in uneven alignment of the X labels if + they had different heights (for example, month names Jul and Aug). + + PHPlot now calculates the size of text for allocation (SizeText) using + the descenders on the last line, and calculates the size for drawing + (DrawText) only to the baseline. PHPlot also now uses a fixed line + spacing for each line of text in a font, rather than the actual text + height. This allows separately drawn multi-line labels to align. + + * Changes to line spacing when using multi-line labels: + PHPlot was using the class variable line_spacing to mean the + number of pixels between lines of multi-line labels. This made the + spacing too small for larger fonts, and it was not possible to adjust + line spacing for different types of text. + + PHPlot now interprets line_spacing as the number of pixels only + for GD text, and as a scale factor for the font's built-in line + spacing for TrueType text. In addition, a new optional argument is + added to SetFont, SetFontGD, and SetFontTTF to set a line spacing + specific to that type of text. + + * Changes had to be made to the legend drawing code to accommodate the + changes to font handling. + + Note: The line spacing change results in slightly looser spacing on + multi-line TrueType text labels, and slightly taller legends, compared + to version 5.0.5. + +2008-09-21 (lbayuk) + * Interim fix for bug 1932571 "Data-Data Plot fails with same X values". + PHPlot will no longer hang when the range of X values is 0 (that is, when + x_min == x_max). It will arbitrarily set an X range of 1, so the + calculated tick step is not 0. This is a temporary fix. Work on a smarter + X and Y range calculation is in progress, which will handle edge cases + like this better, but it isn't ready and this bug has been open too long. + Credit to andyl for finding the bug. + + * Fix font path: Use DIRECTORY_SEPARATOR constant not '/'. + + Extended the label formatting capabilities, adding 'printf' and 'custom' + types, added a prefix and suffix for 'data' type, and allow format controls + to be included in SetXLabelType and SetYLabelType. + + External changes: + * Added 'printf' label type. The caller specifies the print format as the + 2nd argument to SetXLabelType or SetYLabelType (default '%e'). + $plot->SetXLabelType('printf', '%5.2f'); + + * Added 'custom' label type. The caller supplies a callback (typically a + function name) and optional pass-through argument as the 2nd and 3rd + arguments to Set[XY]LabelType. The function is called as $f($value, $arg) + to return the formatted $value. + $plot->SetXLabelType('custom', 'myfunction', $arg_value); + + * In addition to Set[XY]TimeFormat, the format string for type 'time' can + now be set as the 2nd argument to Set[XY]LabelType. + $plot->SetXLabelType('time', '%H:%M'); + + * In addition to SetPrecision[XY], the precision for type 'data' can now be + set as the 2nd argument to Set[XY]LabelType. A 3rd and 4th argument + can supply a prefix and suffix for 'data' formatting. (All optional) + $plot->SetXLabelType('data', 2, '$', 'US'); + + Internal changes: + * Class variables x_precision, y_precision, x_label_type, y_label_type, + x_time_format, and y_time_format have been removed. + + * New class array variable label_format[], with elements 'x' and 'y' which + are arrays for label formatting. Elements in the sub-arrays are not + initialized until needed. + + * New function SetLabelType, which implements Set[XY]LabelType now. + + * FormatLabel() was rewritten to support the new label formatting. + + Compatibility: + * Any code that directly references class variables related to label + formatting will break, except for data_units_text. Use the documented + function methods instead. Setting data_units_text as a suffix is + deprecated but still works. + + * The 'data' type precision for 'Y' is still used for pie chart labels. + +2008-07-12 (lbayuk) + Multiple comment spelling error fixes. No functional changes. + +2008-07-06 (lbayuk) + Changes to allow mixing GD fixed-font text and TrueType Font (TTF) text + on the same plot. + (This change came from work done trying to fix TTF text positioning, + where it looks like additional information needs to be stored for TrueType + fonts. The old font data structure was awkward to extend, and allowing + mixed GD/TTF text was on the to-do list anyway.) + + External changes: + * SetFontGD(), SetFontTTF(): New functions to set a font, with type. + * SetFont(): Now calls SetFontGD or SetFontTTF depending on $use_ttf. + These changes should be fully compatible with existing programs. + + Internal changes: + * Updated comments explaining SetUseTTF() now sets the default type + (not the only type) of text used. + * Put all the font data into a class array. (Replaces $this->generic_font + with $this->fonts['generic'], etc.) + * ProcessTextGD() and ProcessTextTTF() now take the font array as one + argument, rather than separate arguments for font path and size. + +2008-01-13 (lbayuk) ===== Released as 5.0.5 ===== + * phplot.php: Updated version + * README.txt: Updated for new release + * NEWS.txt: Add text for new release + * Makefile: Remove 'Callbacks' from release target, as this material is + now in the reference manual. + +2008-01-07 (lbayuk) + Copyright updated to 2008 and PHP4 no longer listed as supported. + + Major rewrite of the margin calculation functions to address multiple + problems. Fixes for bugs 1856207 "Margin error with 'xaxis'/'yaxis' + position, 1843012 "Make margins, drawing consistent", and 945439 + "x_tick_label_height not set correctly". + + Note: These changes are inter-dependent and cannot be split up. + + * Defer all calculations to DrawGraph time, to eliminate order dependencies. + These functions now just store their arguments in the object, and all + calculations happen later: + + SetXAxisPosition, SetYAxisPosition + + SetMarginsPixels + + SetPlotAreaPixels (Stores margins, not area, now.) + + SetPlotAreaWorld + + SetXTickIncrement, SetYTickIncrement + + * A new callback 'debug_scale' was added to trace the margin and scale + calculations. + + * CalcMargins was rewritten. Actual sizes of tick and data labels are now + used, rather than guesses like "use size of biggest Y value". A minimum + value (3 x safe_margin, or 15 pixels) applies to each margin. + + * FindDataLimits no longer needs to find the longest data label, since + CalcMargins now does that more precisely. + + * DrawXTitle and DrawYTitle now use position offsets calculated by + CalcMargins. Note: These titles are now offset from the plot area, + not the image area. The titles will move if you had set the plot area + or margins. + + * DrawYTick, DrawXTick rewritten to use pre-calculated offsets, and common + code moved to new CalcTicks(). + + * DrawXDataLabel: Use pre-calculated offsets for text. + + * DrawGraph: Rewrote top section (before drawing anything) to do the + calculations in the proper order, unconditionally. + + * Class variables removed: + x_label_inc, y_label_inc, _x_label_cnt : These were never used. + title_height, x_title_height, y_title_width : Now internal to CalcMargins. + data_limits_done : No more need to remember if FindDataLimits called. + + * New class variables added: + plot_margins_set : Keeps track of user-set plot area or automatic. + x_label_top_offset, x_label_bot_offset, x_offset_axis_offset, + y_label_left_offset, y_label_right_offset, y_label_axis_offset, + x_title_top_offset, x_title_bot_offset, + y_title_left_offset, y_title_left_offset : Label offsets + + * New internal functions: + CalcPlotAreaPixels : Deferred calculations taken out of SetPlotAreaPixels + and SetMarginsPixels. + CalcPlotAreaWorld : Deferred calculations taken out of SetPlotAreaWorld. + CalcAxisPositions : Calculate axis positions, moved from CalcTranslation. + CalcTicks : Calculate X and Y tick interval. This still uses the + same simple method (basically range/10), but now we could drop in a new + algorithm much more easily. This is now also used by CalcMargins. + Code taken out of DrawXTicks and DrawYTicks. + CalcMaxTickLabelSize : So CalcMargins can use the exact tick label sizes. + CalcMaxDataLabelSize : So CalcMargins can use the exact data label sizes. + DrawXTick : Code split out from DrawXTicks for symmetry with DrawYTick. + + +2007-12-13 (lbayuk) + * Changed ProcessTextTTF() so SizeText() will return integers. It rounds + the calculated values up, so the bounding box really contains the text. + This also prevents unneeded float calculations in derived values. + +2007-12-09 (lbayuk) + Major rewrite of the text drawing functions to address multiple problems. + Note: These changes are inter-dependent and cannot be split up. + + * Fixed bug 1813070 "Bad position for multi-line TrueType text": + TTF text is now drawn line-by-line, not as a block, for proper + alignment and positioning. + + * Fixed bug 1813071 "Wrong title height for multi-line TTF text": + Corrected miscalculation of overall height of multi-line TTF titles. + This bug resulted in over-sized margins. + The height is now computed line-by-line, including the inter-line spacing. + + * Fixed bug 1813474 "DrawText alignment arguments wrong": + Corrected meaning of 'top' vs 'bottom' alignment. PHPlot now follows + the usual conventions: 'top' alignment means top of text to reference. + DrawText default for vertical alignment is still 'bottom', but the + meaning was corrected. All callers of DrawText were fixed. + + * Fixed bug 1816844 "Fix order dependency for setting titles": + Defer processing titles strings until DrawGraph(), so there is no + more order dependency (no need to set font before setting title strings). + + * Fixed bug 1819668 "Horiz. align multi-line text: GD vs TTF": + The new text routines draw TTF text line-by-line and correctly do + right-, center-, and left- alignment of each line within a text block. + + * Fixed bug 1826513 "FIXME in DrawLegend: Max label length": + Use actual width of widest legend line to calculate legend box size. + + * Partial fix for bug 945439 "x_tick_label_height not set correctly": + In FindDataLimits(), save the longest data label, not just its length, + and use the actual rendered size of that string in CalcMargins() for + the margin calculations. + Also take into account which of the tick or data labels are visible. + This is not a complete fix, but is a significant improvement. + + The following changes were made related to the above fixes: + + + Replaced internal function TTFBBoxSize(), which didn't work right, with + SizeText(). It returns the orthogonal bounding box of a block of text, + and works with both GD and TTF text. + + + DrawText() and SizeText() call a single function ProcessText(), which is + the only place GD text and TTF text are distinguished. (So eventually + we will be able to mix GD and TTF text on a plot.) + + + New internal functions ProcessTextGD() and ProcessTextTTF() draw (or size) + GD and TTF text respectively. These are only called by ProcessText(). + These are re-implementations which properly position and align text. + + + Removed class variables title_angle, x_title_angle, and y_title_angle. The + titles only work at their fixed angles anyway (0, 0, and 90 respectively). + + + Line spacing set with SetLineSpacing() now affects TTF text as well as + GD text. Previously, it only affected GD text. The default line spacing + happens to be usable for TTF text. + + + Added new callback hook 'debug_textbox' for developing, testing, and + documenting. It provides access to the text area bounding box. + + + Removed unneeded class variables x_tick_label_height, y_tick_label_width, + x_tot_margin, y_tot_margin. + +2007-11-25 + * Improve error handling: + Internal functions PrintError() and DrawError() are now the same. Both + will draw the error message into the image and output it, and then + trigger a user-level error. If no error handler has been set, it will + exit, as before. But now the error message should also get logged, or + written to the standard error stream, depending on the SAPI in use. + You can now establish an error handler to catch most PHPlot errors and + do some cleanup before exit. + + This fix also covers bug #1823774 "Default Font Path and Error Message + Output". + + Fixed the return value of most PHPlot functions, to return False on + error, else True. Since uncaught errors are fatal anyway, this only + affects code with an error handler that returns, which is not + recommended and unsupported at this time. These changes are for + possible future error handling options. + +2007-11-22 + * Fix bug 1836528 "Insufficient checking of parameter values": + Rewrote CheckOption to correctly validate option choices. + (It previously accepted substrings and other incorrect values.) + PHPlot methods that use CheckOption now must be called with valid option + values. Empty strings are also no longer accepted. + +2007-11-17 (lbayuk) + * Change to callbacks to support extra arguments. + The PHPlot class can now pass extra arguments to a callback function. + Callback functions now take the following form: + my_callback($img, $passthru_arg, ...) + Where '...' is zero or more additional arguments supplied by PHPlot to + the callback. Each implemented callback reason will define any + additional arguments it uses. The existing defined callbacks have not + changed and do not currently pass any extra arguments. + +2007-11-10 (lbayuk) + * Fix bug 1827263 "Spoiled up pie-chart if $val is close to zero": + Skip pie slices which would result in an integer angle of zero + degrees, because the GD arc filling function will draw a complete + circle for that case. + Credit to Viacheslav for finding this. + + * Removed 8 of the functions (class methods) marked 'deprecated'. Only + deprecated functions which seem to have been for internal use have + been removed. Even old scripts shouldn't be using them, and they are + becoming a problem to maintain. + Removed: SetImageArea() DrawDotSeries() DrawLineSeries() CalcXHeights() + CalcYWidths() DrawLabels() InitImage() DrawDashedLine(). + +2007-10-20 (lbayuk) ===== Released as 5.0.4 ===== + * phplot.php: Updated copyright, version, and authors comments at top. + * README.txt: Updated for new release + * NEWS.txt: Add text for new release + +2007-10-18 (lbayuk) + * Add callbacks - experimental feature: + New functions SetCallback, GetCallback, RemoveCallback. + New internal function DoCallback. + Added callback hooks to DrawGraph. + + Re-arranged code in DrawGraph to bring pie chart drawing into the main + switch on plot type, rather than a special case in its own block. This + makes it easier to follow and easier to add callback hooks. + + * Callbacks: New file, documentation for the new callbacks feature. + (This won't be in the manual while it is an experimental feature.) + +2007-10-15 (lbayuk) + * Fix for bug 1813021: Miss-positioned right-justified vertical GD text. + Fixed DrawText() to correctly position 90 degree right-justified text + drawn in a fixed GD font. This could be seen with 90 degree Y tick + labels. (Found by accident while working on TrueType text problems.) + Also some code cleanup in DrawText: use elseif where appropriate. + +2007-10-09 (lbayuk) + * Code cleanup: Simplify SetIndexColor() and SetIndexDarkColor(). + There is no need to first try ImageColorExact, then ImageColorResolve + if that fails. ImageColorResolve does all that for us. + + Code cleanup: Rewrite SetRGBColor(). It now detects if an unrecognized + color name or color value form is used, and draws an error message. + Before this it would get a PHP index error and "headers already sent" + condition. + + * Code cleanup: Remove duplicated code for loading image files. + Added new class-private function GetImage() which loads an image based + on the image type, and also returns the image size. This replaces + duplicated code in tile_img() and SetInputFile(). + Also fixed comment at top of SetImageFile which said it was deprecated. + It isn't - it is used by the constructor. Moved the function out of the + 'deprecated' area up to below where it is used. + + * Code cleanup: PHPlot should not define or affect anything outside its + own class. + - Removed the check for __FUNCTION__ (PHP 4.3 and up). This is obsolete. + - Do not set error_reporting to E_ALL. Although it is recommended that + scripts do this, it is not the place of loaded classes to do it. + - Remove unused global constant TOTY. + - Removed constants MAXY and MINY. Global constants like this are bad. + These were used as magic index values into data[] to hold min and max Y + values for the row. Instead, put them in separate arrays which are + named data_miny[] and data_maxy[]. (This seems to be only used by the + data line drawing function.) + + Comment cleanup: Remove one commented-out partial function DrawPlotLabel, + and fix another commented-out code fragment in DrawYErrorBar. Both of + these had unmatched braces in them which caused a balance-braces check + to fail. + + * Code cleanup, array padding: Get rid of functions outside the class + and remove the interim fix for PHP 5 (which changed the behavior of + array_merge). Rewrote external function array_pad_array() as a new + class function pad_array(). It does not need access to the class, + but I don't think PHPlot should add to the global namespace more + than necessary. The third argument (array to use for padding) was + never used, so it was removed. It always pads the array with itself. + It now only works on 'usual integer indexed' arrays (0-based + sequential integer index). The was previously required but + undocumented for some of the arrays (like line_widths); now it is + required for all style arrays and will be documented. Now we can pad + the array to the required length, not just N times its previous + length, and we don't need array_merge. Deleted external function + array_merge_php4() as it is no longer used. + + Deleted PHP end marker ?>. You don't need this and it can cause + problems with extra whitespace in your output. + +2007-09-24 (lbayuk) + * Code cleanup: Fix ternary operator misuse. This doesn't change + behavior, but it was annoying me so I fixed it. + Replaced all cases of code like this: $a = ($a > $b) ? $b : $a + With just: if ($a > $b) $a = $b + + * Fix Makefile 'release' target to set owner/group when creating + the tar file. This avoids having to run it as root, but it needs + GNU tar to work. + +2007-09-08 (lbayuk) + * Fix for bug 1790441: Removed the PHPlot quasi-destructor function and + the register_shutdown_function() call which arranged for it to be used. + This was preventing release of memory when a PHPlot object was unset, + because the registered shutdown function held a reference to it. + So rather than improving memory use, it had the opposite effect. + Note: It is no longer necessary or recommended to use reference + assignment ($plot =& new PHPlot) for PHPlot object creation. + Thanks to annajilly for the thorough analysis, bug report, and fix. + +2007-09-05 (lbayuk) + * Rewrote FormatLabel() to ignore blank label values. Adapted from a + patch and feature request submitted by Gerhard Reithofer (exgerhardr). + Blank labels used to produce an error if the LabelType was set to + 'time', and zero if set to 'data'. Now they are just ignored. This + provides a simple way to have labels only at selected intervals when + using time or data formats. For example, you can have a date/time + label at every 10th data point by setting the labels for the other 9 + to be empty strings. Also: Removed $which_pos values 'plotx' and + 'ploty'. These were unused by PHPlot and this is an internal-only + function so there is no compatibility issue. Removed error checking on + $which_pos for the same reason; the error message used an undefined + variable anyway so it wouldn't have worked. + +2007-08-26 (lbayuk) + * Allow SetLegendStyle colorbox_align argument to be 'none', to suppress + the colorboxes in the legend. + + Fix comment on $legend_text_align: empty means right, not left. + + Rewrote DrawLegend layout code to make it easier to understand. The + result should be within 1 or 2 pixels of the previous size and position. + + * Fixes for bug 1779115: SetLegendWorld() fails on undefined vars + Store the given coordinates and remember that they need to be converted + from world to pixel coordinates, but defer trying to actually convert + them until it is time to draw the legend. This way, there are no + problems with the scale having to being set up first (which is nearly + impossible to do). Made the following changes: + + Changed legend class variables to be uninitialized, and unset (rather + than empty string) means use the defaults. Added a new variable: + $legend_xy_world. If it is set, (legend_x_pos, legend_y_pos) need to + be converted to pixel coords. If it is unset, they are already pixel + coords (or undefined, meaning defaults). + + Changed usage of internal function DrawLegend(): removed all arguments. + X and Y were always the class variables anyway, and now it needs to + also use the new flag to tell it if X and Y are world or pixel coords. + The third argument was unused. + + Removed third, unused, default NULL argument from SetLegendPixels and + SetLegendWorld. + + Changes to DrawLegend to convert x, y coords to pixel coordinates + if they came from SetLegendWorld. Also account for new usage of + the class variables: Test for unset to mean use default. + +2007-08-04 (lbayuk) + * New feature: control legend text and color box alignment. + Adds a new function SetLegendStyle to adjust the alignment of the + text and the color boxes inside the legend. + Based on part of bug 1208054, contributed by David Hernández Sanz. + +2006-12-02 (lbayuk) + * Fixes for bug 1605555: Y Data Labels use wrong font and not formatted. + Use y_label_font (not x_label_font) for Y Data Labels. + Use the formatted value for the label, not the original text. + (This applies to bar charts only, with the new Y data labels.) + + * One fix for bug 1208054: Localization of number format. + If number formatting is enabled with 'data' format type, PHPlot previously + used dot for decimal point and comma for thousands separator, and there + was no way to change it. + + This fix adds a new function: + SetNumberFormat($decimal_point, $thousands_separator) + to set the separators. In addition, if that function is not used, + PHPlot will now try to use locale-dependent separators. If locale + information is not available, it will fall back to the old defaults + of dot and comma. + + Note: This change may have some negative effects. 1) If your locale is + "C" or "Posix", you might not get a thousands separator now by default. + You should be using a more specific locale. 2) If your PHP script is + forcing a specific locale with setlocale(), PHPlot will probably undo + that because it uses setlocale(LC_ALL, '') to import locale information + from the environment. We have to do that, or a locale set through + the environment is ignored. But it will override a manually set locale. + + * Fix for bug 937944: X/Y Tick Counts + PHPlot could draw one too few Y tick marks, and one too many X tick marks. + + Changed the code to stop drawing X (Y) tick marks when the current X (Y) + value exceeds the maximum X (Y) value plus a small fudge factor. The fudge + factor accounts for cumulative error when repeatedly adding a delta to + the X (Y) value. + + Notes: The bug report was writing about Y tick counts only, but X tick + counts can also be wrong. The proposed fix in the bug report does not + work in all cases. + + This fix changes the appearance of many plots which were missing the + top-most Y tick mark. The extra X-tick mark problem is less common. + +===== Released as 5.0rc3 ===== + +2006-11-13 (lbayuk) + * Fix for bug 1437912: x-axis label misalignment [bar charts] + The calculations were redone from scratch. + New control variable 'bar_extra_space', which works in addition to + 'group_frac_width' to control how much extra space is around the bars. + Made bar widths match for 'stackedbars' and 1-bar-per-group 'bars'. + + NOTE: This changes the appearance of charts. bars in 'stackedbars' + will now be thinner, and bars in 'bars' graphs will be thicker. I + saw no reason for them being different before. + + This fix required fixing the positioning on the new bar data labels, + which was off before. The bar data labels will now be centered. + Additional fixes to bar chart data labels: + For negative values, the label will center under the bar. + Fixed X-adjustment to account for shading. + Fixed to not suppress the data label if the value is 0. + + +2006-11-10 (lbayuk) + * Fix for bug 1594457: DrawError text wrap and background fix + Do error image white background correctly, and word-wrap the text. + + * Fix for bug 1594458: Suppress lines or points in 'linepoints' + Don't draw X data labels twice for 'linepoints'. + Allow SetPointShapes value 'none' to suppress points, and allow + SetLineStyles value 'none' to suppress lines. This allows a 'linepoints' + graph to mix lines only, points only, and both on the same graph. + + +2006-11-09 (lbayuk) + * Fixes for bug 1446523: + + Wrong variable name in deprecated SetAxisFontSize() + + Fails to properly handle error if SetDataValues() was never + called, or not called with a data array. + + * Fix for bug 1117122: Pie Chart ignores SetPlotAreaPixels + Don't let DrawGraph recalculate the plot area for pie charts if the + user already set it with SetPlotAreaPixels. + + NOTE: This fix may slightly change the appearance of some pie charts, + whether or not they use SetPlotAreaPixels. + + * Fix for bug 1103992: Wrong max Y calculated for stackedbars + Changes FindDataLimits to calculate max Y correctly. It was counting + the first Y value in each record twice, which is always wrong but + only affected stackedbars because the Y values are summed. + + * Fix for bug 1096199: Wrong error bar colors in DrawDotsError. + Rewrites DrawDotsError to make it work like DrawLinesError to + correctly increment the record and color indexes. + Also fixes uninitialized x_now_pixels. + + * Fix for bug 1096197: No borders on unshaded Draw[Stacked]Bars + Unshaded Bars and StackedBars covered the border with the rectangle. + The fix is to draw the rectangle, then the border. + + NOTE: This fix changes chart appearance. Bars and Stacked Bars + will now get a black border around each bar by default, if you + turn off the 3D-shading. If you want borderless, unshaded bars + you need to use SetDataBorderColors to set the data border colors + to be the same as the data colors. + + * Fix for bug 1333164: Negative data values, if string variables, result + in unfilled bars. The problem was a string-to-string compare of a + negative number with the empty string x_axis_position. Fixed by + initializing x_axis_y_pixels to 0 if SetXAxisPosition was not used. + + +2005-04-17 (afan) + * Fix for bug [ 1161072 ] SetInputFile warning, background overwrite + + * Bug 1182672 fixed + +2005-04-15 (afan) + * fix for bug: [ 1182666 ] Y Auto-scale rounds in wrong direction + + * Fix for bugs 1144644 TrueType font path problems and 1106328 TTF + path/filename inconsistency + + * Fix Bug: [ 1117120 ] X Title sizing uses Y Title font height + +2005-04-13 (afan) + * Error in SetLineStyles() - does not accept an array argument + + +2005-03-29 (afan) + * Small typo fixed in SetYDataLabelPos + + * Update SetDataLabelPos: For past compatibility we accept plotleft, + ...but pass it to SetTickLabelPos + +2005-03-26 (afan) + * Change to line 3802: data lables now work with multiple bars with *$idx + +2005-03-25 (afan) + * Added Function DrawDataLabels to put data labels in world coords, + added call from DrawBars and modified SetYDataLabelPos to flag + whether or not to call DrawDataLabels. + +2005-01-20 (migueldb) + * Many bugfixes reported and solved by L. J. Bayuk. Thanks! + + fixed bug #1096190 + + FindDataLimits(): fixed bug #1096192 + + CalcTranslation(): fixed bug #1101317 + + DrawImageBorder(): fixed bug 1096200 + + DrawXDataLabel(): fixed bug 1099879 + + DrawDots(): fixed bug #1096194 + +===== Released as 5.0rc2 ===== + +2004-10-24 (migueldb) + * array_merge_php4(): added to cope with the bug introduced by + the change in array_merge() from PHP4 to PHP5 (I haven't verified this) + * Fixed some divisions by zero, thanks to an old bug report. + +2004-09-09 (migueldb) + * SetPointSize(): deprecated + * SetPointSizes(): added as replacement for SetPointSize(). + Now able to set point sizes on a per line basis. + * SetPointShape(): deprecated. + * SetPointShapes(): added as replacement for SetPointShape(). + Now able to set point shape on a per line basis. + * DrawDot(): now needs record number to decide which dot shape and + size to draw. + * CalcMargins(): dirty fix for x data label placing. + * tile_img(): fixed tile placement. + +2004-06-14 (migueldb) + * SetXTickLabelPos() and others: more on the bug reported by Jo Demol. + * Fixed bug reported by Jo Demol. + +2004-05-11 (migueldb) + * SetBgImage(): added. + * SetPlotAreaBgImage(): added. + * SetInputFile(): deprecated. + * DrawBackground(): now accepts images as backgrounds. + * DrawPlotAreaBackground(): now accepts images as backgrounds. + * tile_img(): internal method added. + +.......... +Editor's Note: For older changes to PHPlot, please see the CVS logs. diff --git a/gosa-core/include/phplot-5.1.2/HorizontalBars.txt b/gosa-core/include/phplot-5.1.2/HorizontalBars.txt new file mode 100644 index 000000000..9c2fadbb9 --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/HorizontalBars.txt @@ -0,0 +1,243 @@ +phplot/HorizontalBars - Documentation for an experimental new plot type +Last updated for PHPlot-5.1.2 on 2010-06-26 +The project home page is http://sourceforge.net/projects/phplot/ +----------------------------------------------------------------------------- +Overview: + +This file documents a new plot type, Horizontal Bars. This was added to +PHPlot in version 5.1.2 as an experimental feature. + + NOTICE: + + This new plot type is experimental. This means anything about this + may change in future releases, in ways that might be incompatible + with the current implementation, or the new plot type might even + be removed completely. The new plot type is not yet documented in + the PHPlot Reference Manual. This text file is the only documentation. + +Feedback on this feature is welcome. Please use the "help & discussion" +forum at http://sourceforge.net/projects/phplot/ + +----------------------------------------------------------------------------- +Usage: + +In a horizontal bar chart, the X axis and Y axis are oriented the same as +in other PHPlot plot types: X axis is horizontal, increasing towards the +right, and Y axis is vertical, increasing towards the top. + + Y + ^ + | + |============== + | + |===== + | + |========= + | + +---------------------> X + +To make a horizontal bar chart, use the same plot type as vertical bar +charts ('bars'), but use the new data type 'text-data-yx'. The new data +type indicates your data array has a different representation, mapping Y +values to X values (instead of X values to Y values). + +For a normal (vertical) bar chart, the data array has type 'text-data' and +looks like this: + $data = array( array('Label1', Y11, Y12, ...), + array('Label2', Y21, Y22, ...), + ...); +Each entry (row) in the data array represents one group of bars. Each group +has a label and one or more Y values. The X values are implicit: the first +row has the first X value, the second row has the second X value, etc. + +For the new horizontal bar charts, the data array has the new type +'text-data-yx' and looks like this: + $data = array( array('Label1', X11, X12, ...), + array('Label2', X21, X22, ...), + ...); +Each entry (row) in the data array represents one group of bars. Each group +has a label and one or more X values. The Y values are implicit: the first +row has the first Y value, the second row has the second Y value, etc. + +As you can see, a vertical bar chart can be changed to a horizontal bar +chart simply by changing the data type passed to SetDataType(). The data +array itself does not change. Other issues with horizontal bar charts are +discussed below. + +This complete script makes a very simple horizontal bar chart: + SetDataValues(array(array('A', 10, 25), array('B', 30, 5))); + $p->SetDataType('text-data-yx'); + $p->SetPlotType('bars'); + $p->DrawGraph(); + +Note that the bars in a horizontal bar chart are ordered from bottom to top +(that is, increasing Y values). In the example above, the "A" label bar +group is drawn below the "B" label bar group. If you need bars ordered from +top to bottom, you will have to change your data array accordingly. + +----------------------------------------------------------------------------- +Ticks and Labels: + +Since the X axis and Y axis do not change positions for horizontal bar +charts, the label and tick controls still refer to the X and Y axis. +However, the independent values are now Y, and the dependent values are +now X. Also, the label strings in your data array are plotted along the Y +axis for horizontal bar charts, rather than along the X axis for regular +bar charts. + +To control the data labels positions, use SetYDataLabelPos(). New option +values have been added to this function, which was previously used only to +position bar chart value labels with the options 'plotin', 'plotstack', or +'none'. With horizontal bar charts, SetYDataLabelPos() positions the +regular data labels that go along the Y axis. The new option values are +'plotleft', 'plotright', or 'both'. + 'plotleft' : Draw data labels along the left side of the plot area. + 'plotright' : Draw data labels along the right side of the plot area. + 'both' : Draw data labels along both left and right sides. + 'none' : Do not draw data labels. + +For bar charts, it makes no sense to have ticks or tick labels along the +independent axis. This is the Y axis for horizontal bar charts. Therefore, +you should use SetYTickPos('none') to turn off the tick marks on the Y +axis. You do not normally need to use SetYTickLabelPos('none') to turn off +the tick labels along the Y axis, since PHPlot will do this automatically +if your data array has labels. + +To control the presentation of the data labels with horizontal bar charts, +use the correct functions that refer to the Y data labels, not X data +labels as with vertical bar charts. + + SetYDataLabelAngle() or SetYLabelAngle() + Set angle of data label text. Default is 0 degrees. + + SetYDataLabelType() or SetYLabelType() + Select the type of formatting for the data labels. + + SetFont(), SetFontGD(), or SetFontTTF() + Use the element name 'y_label' for the data labels. + +Note that the PHPlot Reference Manual currently says the Y Data Label +functions are only for bar chart data value labels (see Data Value Labels +below regarding this term). That information is out of date. These +functions are now also used for data labels in horizontal bar charts. + +----------------------------------------------------------------------------- +Grid: + +For horizontal bar charts, the X grid lines default on, and the Y grid lines +default off. (This is the opposite of normal bar charts, where the X grid +lines default off, and the Y grid lines default on.) + +----------------------------------------------------------------------------- +Y Axis Position: + +PHPlot uses a different default for the X axis and the Y axis positions. +This affects horizontal bar charts if you have any data values which are +less than zero. The X axis is normally positioned at Y=0, and the Y axis +is normally positioned at the left side of the plot. + +If you have both positive and negative values in your data array, both +vertical and horizontal bar charts will draw the bars away from the zero +value. For vertical bar charts, the X axis will be drawn at that zero value +(perhaps in the middle of the plot), with bars going up and down from +there. But for horizontal bar charts, the Y axis will by default remain at +the left edge of the plot; the bars will start from the X=0 value +(somewhere in the middle of the plot area) and go left and right from +there. In this situation, you might want to use SetYAxisPosition(0) to +force the Y axis to be at X=0. + +----------------------------------------------------------------------------- +Scaling: + +You can use SetPlotAreaWorld() to explicitly set any or all of the 4 limits +of the plot area. Any limits you do not provide will be calculated for you. +The algorithm is due for replacement, but it will now apply the same +calculations to Y and X values in horizontal bar charts as it currently +applies to X and Y (respectively) in vertical bar charts. That is, the +range for the independent variable will be calculated to contain and center +the bar groups, and the range for the dependent variable will include and +usually exceed the actual data range. + +The example under Usage above will produce an auto-calculated Y range of 4 +to 33, with the X range set to center the two bar groups. If you change +'text-data-yx' to 'text-data', you will get a vertical bar chart with the +same automatic range. + +----------------------------------------------------------------------------- +Data Value Labels: + +Data Value Labels are not yet available with horizontal bar charts. These +are the text labels within the plot that identify the data value just above +the bars. (See Example 5.19 "Bar Chart with Data Labels" in the manual.) + +Note: Data Value Labels are currently referred to in the manual as Y Data +Labels. If horizontal bars are accepted and documented in the manual, this +will be changed to call them 'Data Value Labels'. This is necessary to +avoid confusion with X Data Labels and Y Data Labels. + +----------------------------------------------------------------------------- +Other Plot Types Not Available: + +There is currently no corresponding horizontal analog for plot type +stackedbars, nor are there any other horizontal plot types at this time. +Using data type 'text-data-yx' with other plot types will fail. +(Starting with 5.1.2, PHPlot always checks that the selected data type +is supported for the selected plot type.) + +----------------------------------------------------------------------------- +Implementation Notes: + +The following is a summary of the changes made to PHPlot to implement +horizontal bar charts. + +1) Do not initialize y_tick_label_pos or y_data_label_pos. The defaults +have to be dynamically calculated for horizontal bar charts, so tick labels +can be suppressed. (This was already being done for X labels.) Existing +internal function CheckLabels() was extended to do this for Y also. + +2) Do not initialize the X and Y grid setting variables. The defaults have +to be dynamically calculated because they differ with swapped data arrays. +New internal function CalcGridSettings() does this. + +3) SetYDataLabelPos() accepts the new arguments plotleft, plotright, and +both. Old compatibility code that passed these values to SetYTickLabelPos() +has been removed. + +4) SetDataType() accepts a new value: 'text-data-yx'. + +5) FindDataLimits() was changed to properly calculate minimum and maximum +values from text-data-yx data arrays. The arrays data_miny and data_maxy +were renamed to just data_min and data_max, since they now describe limits +of either X or Y values, depending on the data type. + +6) Changes were made to CalcMargins() to account for the labels in the data +array being drawn on the Y axis instead of X axis, in the text-data-yx +case. + +7) CalcPlotAreaWorld() is extended to calculate defaulted plot ranges +correctly for the swapped X/Y case. The algorithm is the same (and due for +replacement). It applies a fixed range to Y and an extended range to X. It +is also ready for possible future expansion to include swapped X/Y plots +with explicit Y values. + +8) Changed CalcBarWidths() to use either the plot area width or height, +depending on the bar directions, when calculating the bar widths. + +9) Extended CalcMaxDataLabelSize() to work with both X and Y labels. +Before, it returned the maximum height of the data labels. Now it can +instead return the maximum width of the data labels; this is used for +horizontal bar charts. It also has to pick the proper font, angle, and +format code for X or Y. + +10) New internal function DrawYDataLabel() to draw data labels for +horizontal bar charts. + +11) New internal function DrawHorizBars() draws the horizontal bar chart. + +12) DrawGraph() now decides to draw a bar or horizontal bar chart based on +the data type (text-data or text-data-yx). + +----------------------------------------------------------------------------- diff --git a/gosa-core/include/phplot-5.1.2/NEWS.txt b/gosa-core/include/phplot-5.1.2/NEWS.txt new file mode 100644 index 000000000..e5fdb2496 --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/NEWS.txt @@ -0,0 +1,814 @@ +This is the NEWS file for PHPlot, with release documentation. +The project web site is http://sourceforge.net/projects/phplot/ +The project home page is http://phplot.sourceforge.net/ +Refer the the ChangeLog file for detailed source changes. +----------------------------------------------------------------------------- + +2010-06-29 Release 5.1.2 + +Overview: + +This is the current stable release of PHPlot. Truecolor image support is no +longer considered 'experimental', and is now documented in the reference +manual. There is a new experimental feature for horizontal bar charts. This +release also contains a bug fix and new feature. + + +Cautions and Important Notes: + +The advisory against using PHP-5.3.2 or PHP-5.2.13 with PHPlot if you use +TrueType fonts (TTF) continues. See the item below for PHPlot-5.1.1. The +good news is that this has been fixed by the PHP Team and will be in the +next releases. + +Compatibility of data type and plot type are now checked completely. If +you used an incorrect data type with certain plot types, your script may no +longer work until you fix the data type. Specifically, the area, squared, +and thinbarline plot types failed to check the data type they received, and +treated anything other than 'data-data' as 'text-data'. If you have a +squared plot with data type 'data-data-error', for example (which is not +supposed to work), it did produce a plot, but will now result in an error. + +The addition of horizontal bar charts should not impact any existing plot, +with one small exception. The function SetYDataLabelPos() used to accept +some additional, undocumented options (plotleft, plotright, both, yaxis) +and pass these through to SetYTickLabelPos() "for compatibility". It no +longer does so, as some of those are now used for horizontal bar chart +labels. To position Y tick labels, use only SetYTickLabelPos(). + + +New features in 5.1.2: + +#3002606 "Add to plot and image border options": + SetPlotBorderType() now accepts 'right', 'top', and 'bottom', as well + as an array of options. So you can now control exactly which of the 4 + border sides will be drawn. + SetImageBorderType() now accepts 'solid' as a choice. This will use the + actual color set with SetImageBorderColor(), rather than the darker + shade as type 'plain' does (which may have been a bug). + SetImageBorderWidth() is a new function that sets the width of the image + border. The defaults are the same as the fixed values used before: 1 + pixel for plain, 2 pixels for raised. The image border width is now + accounted for in margin calculations, if it is greater than 2 (to make + sure existing plots will not change). + +#2885930 "Horizontal Bars": + Horizontal bar charts are implemented, as an experimental feature. + 'Experimental' means they are not yet documented in the reference manual, + and subject to change or removal. + Refer to the text file HorizontalBars.txt for details. + +#2947679 (follow-up) "Support for alpha blending/Truecolor": + Truecolor support is now documented in the Reference Manual. The interim + documentation file Truecolor.txt has been removed. Alpha channel + specification now works with both constructors and both image types. This + fixes an issue if the base constructor was used with a truecolor background + image. (In PHPlot-5.1.1, the result would be a truecolor image, but the + alpha channel features were not available.) + + +Bug Fixed in 5.1.2: + +#3010116 "Bad rendering of title in multi-plot image when using TTF": + Make sure the main title is drawn only once, to avoid bad rendering of + TTF titles with multiple plots due to anti-aliasing. + +----------------------------------------------------------------------------- + +2010-04-04 Release 5.1.1 + +Overview: + +This is the current stable release of PHPlot. This release adds truecolor +image support as an experimental feature, fixes a number of bugs and adds +a few new features. + +The PHPlot reference manual has been updated to match this release. The +manual is available as a separate download from Sourceforge. The manual is +also available for on-line viewing from the project home page. + +See the ChangeLog file in the release for more about changes and bug fixes. + + +Cautions and Important Notes: + +Avoid using PHP-5.3.2 or PHP-5.2.13 with PHPlot if you use TrueType fonts +(TTF). Some new bugs were introduced in those releases that adversely +affects accurate positioning and rendering of TrueType font text. + + +New features in 5.1.1: + +#2947679 "Support for alpha blending/Truecolor": + PHPlot can now produce truecolor images, with alpha blending of colors and + other effects. This is considered an experimental feature, meaning it is + not yet documented in the PHPlot Reference Manual, and subject to change. + Refer to the text file Truecolor.txt included in the PHPlot release for + information on using truecolor. + Two drawing changes were made to improve plot appearance with Truecolor: + + Filled dots (in points & linespoints plots) are now drawn better. This + also makes them look rounder with regular (non-Truecolor) plots. + + Area plots have the areas filled without overlapping each area down to + the Y axis. This was needed to fix problems with alpha blending, and + should have no effect on non-Truecolor plots. + +#2973995 "Add y-Data to Stackedbars": + You can now have Y Data Labels with 'stackedbars' plots. These label the Y + values (incremental and total) for each bar. Refer to the reference manual + page for SetYDataLabelPos(). + + +Bug Fixes in 5.1.1: + +#2976735 "Improvements and fixes for 'area' plots": + Moving X axis works; handle Y<0 better; new 'stackedarea' plot type is a + variation on 'area' with the data represented differently. + +#2974639 "Stacked bars plot breaks with X axis != 0": + Moving X axis works. + +#2963757 "point_counts undefined error in 5.1.0": + Fixed an error introduced in PHPlot-5.1.0 when point size and shape arrays + were set to the same size. + +#2938219 "Bars go in wrong direction": + For bar charts with all Y<0, bars will still be drawn down even if Y=0 is + not in range. + +----------------------------------------------------------------------------- + +2009-12-24 Release 5.1.0 + +Overview: + +This is the current stable release of PHPlot. This release fixes a number of +bugs and adds some new features. Some of the changes in this release can +alter the appearance of plots, so be sure to review the information in this +NEWS file and test this release with your application. + +The PHPlot reference manual has been updated to match this release. The +manual is available as a separate download from Sourceforge. The manual is +also available for on-line viewing from the project home page. + +See the ChangeLog file in the release for more about changes and bug fixes. + + +New features in 5.1.0: + ++ A new "contrib" directory has been added for useful add-ons. + This currently contains: + * prune_labels : Control data label density on X axis. + * color_range : Define a gradient map for data colors. + ++ Feature Request 2899921 "Allow different format for data and tick labels" + Text angle and format can now be controlled separately for data labels. + ++ Locale loading override + New variable locale_override stops PHPlot from getting locale from system. + ++ Translating Coordinates + New function GetDeviceXY() to translate world to device coordinates. + ++ New drawing callback + New callback 'draw_all', called after all drawing is done. + The manual now contains an example of using this new callback and + the new GetDeviceXY() function to annotate a plot. + + +Bug Fixes in 5.1.0: + +#2914403 "Pie + X/Y titles: Undefined property error" + X/Y titles are now properly ignored for pie charts. + +#2908256 "Error: array_sum() should be an array" (drupal) +#2916864 "Should at least print legend on pie charts with empty data" + Pie charts with invalid data (no Y values > 0) now make an empty plot. + +#2906436 "Fixes for X Tick Labels vs X Data Labels" + Smarter determination of whether to do Tick labels, Data labels, or both. + +#2900914 "Problem with display of 0 on Y axis" + Fixed rounding error that could produce something like Y=8.12345E-16. + +#2886365 "PHP 5 patch" (Declare all functions and variables in PHP5 style) + Most internal PHPlot member functions now have "protected" visibility. + +#2839547 "SetImageBorderType('none') + You can use SetImageBorderType('none') to turn the image border back off. + +#1795972 "Fix default point shapes" + We now have 20 (vs 10) point shapes, with 10 (vs 1) used by default. + +#1795971 "Fix default data colors" + We now have 16 (vs 8) default data colors, no duplicates, all visible. + + +Visible Changes and Possible Incompatibilities: + ++ PHP5 visibility changes (Bug #2886365) +Details: Most internal PHPlot member functions now have visibility + 'protected', rather than all being public. All member variables are + still 'public'. + +Reason for the change: Use the recommended PHP5 syntax, better OO style. + +Compatibility: If you were calling a PHPlot internal function that got + changed to 'protected', this will break. Please report this. + + ++ Fix default point shapes (Bug 1795972) +Details: We now have 20 (vs 10) point shapes available, and by default we + have 10 (vs 1) different shapes in use. The default size is now 6 pixels + for all point shapes. + +Reason for the changes: Using different shapes helps distinguish the data + sets. The existing 10 defined shapes were not enough, since some of them + are not centered over the points, too small, or otherwise hard to see. + The code to synchronize the point shape and size arrays was broken, and + some dubious code to adjust sizes to even numbers needed to be fixed. + +Compatibility (1): If you have a points or linepoints plot with more than + one dataset, and you did not use SetPointShapes() to configure the + shapes, them your plot will change from using a diamond for all data + sets to using different shapes for up to 10 data sets. + +Compatibility (2): Fixing the point size/point shape array size bug may + slightly change the size of some shapes, but it now works the way it + was documented and supposed to work. + ++ Fix default data colors (Bug 1795971) +Details: Defined a new set of 16 default data colors. The colors are + different and contrast well against the default white background. + The first 4 colors were not changed. + +Reason for the change: The default 8 data colors included two instances + of orange, and one color which was invisible on a white background. + +Compatibility: Colors will change on any plot with more than 4 data sets + where you did not use SetDataColors() to set your own data colors. + ++ Re-used old function SetXDataLabelAngle() +Details: SetXDataLabelAngle() now does something different. + +Reason for the change: This name was needed for a new function, to set the + angle for the X Data Labels. The old use of this function was not + documented, and marked "deprecated" in the code since around 2003-12-07. + +Compatibility: If you are still using SetXDataLabelAngle() to set both Tick + and Data label angles, you need to use SetXLabelAngle() instead. + ++ Separate controls for tick and data labels (Feature Request 2899921) +Details: New functions SetXDataLabelAngle(), SetYDataLabelAngle(), + SetXDataLabelType(), and SetYDataLabelType() to allow separate control + over the angle and format of data labels, versus tick labels. + +Reason for the change: Allow Data Labels to use different formatting and + angle compared to Tick Labels. + +Compatibility: The default behavior has been set up such that there should + be no compatibility issues. For example: + Old behavior: SetXLabelType() sets the type for both tick and data labels. + New behavior: SetXLabelType() sets the type for tick labels and the + default type for data labels. SetXDataLabelType() sets the type for + data labels (overrides SetXLabelType). + ++ X Tick Labels vs X Data Labels (Bug 2906436) +Details: Regarding SetXTickLabelPos() and SetXDataLabelPos(): If only one + of them is called, the behavior is unchanged (only that label type will + be displayed). If both are called: Do exactly what was requested. If + neither was called: display only data labels if any data labels are + non-empty, else display only tick labels. + +Reason for the change: 1) Fix the long-standing problem behavior that by + default PHPlot overlays tick and data labels below the X axis. 2) Fix + order dependency between setting the position of tick and data labels. + 3) Prepare for future extension of data labels, and allow both tick + and data labels to be on if the programmer enables both. + +Compatibility: There are some cases where your plot will change. + (a) Calls neither SetXDataLabelPos() nor SetXTickLabelPos(): + Old behavior: Both tick and data labels displayed, possibly overlaid. + New behavior: If there are any non-blank data labels, then show only + the data labels, not the tick labels. Otherwise, show tick labels. + + (b) Calls both SetXDataLabelPos() and SetXTickLabelPos(), with other than + 'none' for each position: + Old behavior: The latter call was effective; earlier one ignored. + New behavior: Independent of order, both calls are effective. + +----------------------------------------------------------------------------- + +2009-06-14 Release 5.0.7 + +Overview: + +This is the current stable release of PHPlot. The release adds one new +feature, fixes a few bugs, and changes the license under which PHPlot +is released. + +The PHPlot reference manual has been updated to match this release. The +manual is available as a separate download from Sourceforge. The manual is +also now available for on-line viewing at http://phplot.sourceforge.net + +See the ChangeLog file for more about changes and bug fixes. + + +Licensing: + +PHPlot is now released on the terms of the GNU Lesser General Public +License, version 2.1. (Previous versions of PHPlot were released under +a dual "PHP/GPL" license.) The licensing change was authorized by the +original author and copyright holder of PHPlot. + + +New feature in 5.0.7: + ++ Plot area margins can now be partially specified, using either + SetMarginsPixels or SetPlotAreaPixels. In previous releases of + PHPlot you had to either specify all 4 margins or none. + Credit to adoll for this feature. + + +Visible Changes and Possible Incompatibilities: + ++ Y data range can change: + As a result of the bug fixes in this release, automatically-calculated + Y data ranges can change. If you have missing Y values in your data, + and you let PHPlot calculate the Y data range (that is, you do not + call SetPlotAreaWorld with a Ymin value), then the lower limit for Y + can change. If you have a plot with data-data-error data type, different + error values for different points, and let PHPlot calculate the Y data + range, then either Y limit can change. + + +Bug Fixes in 5.0.7: + + ++ Fix for bug 2803900: SetRGBArray('large') does not work: + Corrected an array name usage problem. You can now select the large + color map. Also PHPlot no longer overrides use of the PHP include + path when loading the large color map, and now reports an error if the + file is needed and not found. + ++ Fix for bug 2791502 "Error plots treat missing Y values as 0": + Missing Y values now with with data-data-error plots. + ++ Fix for bug 2792860 "Wrong DataLabelLines with missing Y": + Data label lines are now suppressed at missing Y values. + ++ Fix for bug 2786350 "Missing Y data results in bad auto-range": + Missing Y values are now ignored when calculating the Y data range. + Bug report and analysis by mrten. + ++ Fix for bug 2786354 "Incorrect auto-range for data-data-error": + The Y data range is now correctly calculated for data-data-error plots + when the error values differ from point to point. + + +----------------------------------------------------------------------------- + +2009-01-20 Release 5.0.6 + +Overview: + +This is the current stable release of PHPlot. The purpose of this release +is to fix additional problems with text spacing and positioning, and +introduce some minor new features. + +The PHPlot reference manual has been updated to match this release. The +manual is available as a separate download from Sourceforge. The manual is +also now available for on-line viewing at http://phplot.sourceforge.net + + +New features in 5.0.6: + ++ Allow mixing GD and TrueType font text on the same plot + You can use the new method functions SetFontGD() and SetFontTTF() to + select a font and font type for text element (labels, titles, etc.) For + example, you can have TrueType plot titles, and GD-fixed font labels. + SetUseTTF() now sets the default text type, TTF or GD. This is fully + backward compatible. + ++ Extended label formatting + See the reference manual for more information on these. + + New label formatting types are added: 'printf' (using a user-defined + format), and 'custom' (using a callback function). + + For 'data' type formatting, a prefix and suffix can be added. (PHPlot + previously had an undocumented suffix for 'data' type, which still + works.) + + For 'time' formatting, the format can now be specified in the same function + call rather than using SetXTimeFormat and SetYTimeFormat. + + For 'data' formatting, the precision can now be specified in the same + function call, rather than using SetPrecisionX and SetPrecisionY. + ++ Better control over line spacing in multi-line labels + + Line spacing can now be set separately for each text element using an + additional argument to SetFont, SetFontGD, and SetFontTTF. The overall + SetLineSpacing() value is the default for each text element that does not + have a specific line spacing set. + + PHPlot now interprets the value set for line spacing as the number of + pixels only for GD text. For TrueType text, it is a scale factor for the + font's built-in line spacing for TrueType text. The equation used is: + interline_spacing = line_spacing * font_natural_spacing / 6 + where line_spacing is either the global value set with SetLineSpacing + or a more specific value set with SetFont(), and font_natural_spacing + is the amount of space between lines built-in to the TrueType font. The + factor 6 should really be 4 (since PHPlot always used 4 as the default + line_spacing, this would give the natural font spacing by default). But + the text is too widely spaced with this value, and 6 was chosen to be + more compatible for typical font sizes. + +Visible Changes and Possible Incompatibilities: + ++ Line spacing + Multi-line TrueType titles and labels will have different inter-line + spacing. Since the text size affects the margin and plot area sizes, + this results in slightly different sized features on any plot with + multi-line TrueType text. + Previous versions of PHPlot used a default 4 pixels for inter-line + spacing of multi-line TrueType text, regardless of the font size. + PHPlot now uses the 'natural' font inter-line spacing, adjusted by a line + spacing parameter (per text type, with a global default). + + The same change can also increase the size of the legend box slightly. + ++ Internal changes were made to the way font information is stored. Anything + that directly references PHPlot internals regarding fonts will break. Usage + also changed for the internal functions to size and draw text (ProcessText*, + SizeText*) due to font data storage changes. + ++ Changes were made to internal class variables used to store label + formatting information. Anything relying on these internals may break. + + +Bug Fixes in 5.0.6: + +#1932571: Data-Data Plot fails with same X values + PHPlot will no longer hang if all X values are the same. But this is + interim fix to force the X range to 1 to prevent the hang. Eventually, + smarter automatic range code will handle this better. + Credit to andyl for finding this. + +#1891636: Misaligned TTF X Labels + PHPlot will now correctly line-up TrueType labels along the X axis. There + were small but very noticeable errors before, when the text had descenders + or lines with all short letters. + + +----------------------------------------------------------------------------- + +2008-01-13 Released 5.0.5 + +Overview: + +This is the current stable release of PHPlot. The emphasis of this release +is to improve text positioning, margin calculation, and error handling. + +Although this is considered a stable release, it has a large amount +of changed code compared to the previous release 5.0.4. Two of the more +complex components of PHPlot - text and margin calculations - were mostly +re-written in this release. You are advised to carefully test your own +applications with PHPlot-5.0.5 to see how your plots look. Refer to the +README.txt file included in the release for information on reporting problems. + +Starting with this release, PHPlot no longer supports PHP4, since the PHP +group officially declared end-of-life for PHP4 as of 31 December 2007. +PHPlot-5.0.5 was tested only with PHP-5.2.5 and we are unlikely to address +any issues using PHPlot with older versions of PHP. + +The PHPlot reference manual has been updated to match this release. The +manual is available as a separate download from Sourceforge. The manual is +now also now available for on-line viewing at http://phplot.sourceforge.net + +The callback feature added in 5.0.4 is now documented in the reference +manual. It is still considered experimental and subject to change, however. + + + +Visible Changes and Possible Incompatibilities: + ++ Dropped support for PHP4. + ++ Eliminated remaining order-dependent behavior related to margins and +text. PHPlot should now do nothing at all, except record parameters, until +you draw the graph with DrawGraph. I believe this was always the intended +behavior of PHPlot, but over time perhaps various pre-calculations and +dependencies crept in. Fixing this simplifies processing and should lead to +more consistent behavior. + ++ The rewritten margin calculation code now uses actual sizes of all tick +and data labels and tick marks, rather than guesses. Margins collapse to +remove unused elements, but a minimum margin (currently fixed at 15 pixels) +is applied so the plot edges don't get to close to the image edges. The +result is that most graphs with auto-calculated margins will change in +appearance. It most cases, the margins get slightly smaller. In other +cases, earlier releases mis-calculated the margins, so this release will +produce much neater margins. + ++ The X and Y titles are now offset out from the plot area, not in from the +image area. For auto-calculated margins this should not make any +difference, but if you use SetMarginsPixels or SetPlotAreaPixels to set +larger margins, the axis titles will move in closer to the plot with this +release. + ++ Changes were made to PHPlot internals, including removal of some class +variables and functions, and addition of new variables and functions. +These are documented in the ChangeLog. Relying on any internal variables +or functions in an application using PHPlot is unwise. The following +internal functions were removed: + SetImageArea() DrawDotSeries() DrawLineSeries() CalcXHeights() + CalcYWidths() DrawLabels() InitImage() DrawDashedLine() + These were marked 'deprecated', were undocumented and unmaintained. + TTFBBoxSize() + This was replaced with SizeText(). + ++ Line spacing set with SetLineSpacing() now affects TTF text as well as +GD text. Previously, it only affected GD text. The default line spacing +happens to be usable for TTF text. + ++ Changes were made to error handling. PHPlot will now trigger a user-level +error after producing an error image, instead of exiting. If no error +handler has been set, it will exit, as before. But now the error message +should also get logged, or written to the standard error stream, depending +on the SAPI in use. You can now establish an error handler to catch most +PHPlot errors and do some cleanup before exit. + ++ PHPlot no longer accepts some invalid option values (such as a substring +of a valid value, or empty strings) passed to functions. If your +application aborts in CheckOption with PHPlot-5.0.5 but 'worked' with +previous releases, them you were probably using an invalid option value. + + + +Bug Fixes in 5.0.5: + +#945439: x_tick_label_height not set correctly + Exact sizes of labels are now used to calculate margins. + +#1813070: Bad position for multi-line TrueType text + Fixed as part of text functions rewrite. Use correct basepoint + (lower left of each line) when positioning text lines. + +#1813071: Wrong title height for multi-line TTF text + Fixed as part of text functions rewrite: calculate height of + multi-line text correctly. Also now uses the line-spacing setting. + +#1813474: DrawText alignment arguments wrong + Fixed so 'top' and 'bottom' now have the usual meaning: top means + align top of text with reference, bottom means align bottom of text. + This was switched before. Changed every internal caller to compensate. + +#1816844: Fix order dependency for setting titles + Defer processing of title strings until DrawGraph(), + so it doesn't matter if fonts, etc. are set before or after. + +#1819668: Horiz. align multi-line text: GD vs TTF + The text functions were rewritten to draw TTF text line-by-line, + like GD text, and correctly align each line. + +#1823774: Default Font Path and Error Message + Error handling has been improved to make sure a message is logged, in + addition to the error image, and use error_trigger rather than exit. + +#1826513: FIXME in DrawLegend: Max label length + The actual size needed for legend text is now used. + +#1827263: Spoiled up pie-chart if $val is close to zero + Fixed by skipping over any segment that rounds to 0 degrees of + arc. (The GD function uses integer angles only, and 0 degrees + means draw a complete circle.) + +#1836528: Insufficient checking of parameter values + Rewrote validator function to reject improper parameter values. + +#1843012: Make margins, drawing consistent + Margin code logic was rewritten and checked for consistency. + +#1856207: Margin error with 'xaxis'/'yaxis' position + Margin space is now allocated for ticks and labels if their position + is 'xaxis' or 'yaxis' and the axis is at the plot edge. This is not + a perfect fix (the axis could be close but not at the edge). + + +----------------------------------------------------------------------------- + +2007-10-20 Released 5.0.4 + +Overview: + +This is the latest stable release of PHPlot. We are abandoning the 'rc' +version naming style, because we don't consider these last releases +'release candidate' versions. As we continue to make changes to PHPlot, +we are not converging toward a final "5.0" release, however we do consider +these releases stable and complete enough for production use. + +This release fixes a number of problems and introduces a few new features. + +The PHPlot reference manual has also been updated to match this release. +New material has been added documenting some of the PHPlot internals. +The manual is available as a separate download from Sourceforge. + + +Code Cleanup: + +Some code cleanup is going in to this release. It is hoped that these +changes will not impact any existing scripts using PHPlot, but will make +the PHPlot code itself easier to understand and maintain. + +PHPlot now avoids making changes outside its own class definition. There +are no longer any functions defined outside the class, nor any constants. +Three constants (MINY MAXY TOTY) were removed, and 2 functions were removed +(see Visible Changes below). Also PHPlot no longer sets the PHP error +reporting level to E_ALL. Although we highly recommend setting error +reporting to E_ALL in your php.ini file or scripts, it is not right for +PHPlot to assume that you want it. + + +Visible Changes and Possible Incompatibilities: + +Arrays containing color and style information are used with several PHPlot +functions to control the plot style array. These functions are: + SetPointShapes, SetPointSizes, SetLineWidths, SetLineStyles, + SetDataColors, SetDataBorderColors, and SetErrorBarColors. +The arrays passed to these functions MUST used sequential integer 0-based +indexes. This is what the PHP manual calls "Usual integer indices (starting +from zero, increasing by one)". This is the type of array you get in PHP by +default if you use array() without specifying key values, or use the +empty-bracket assignment operator to add values onto an array. In previous +versions of PHPlot, some of these functions would also work with +string-indexed or non-sequentially-indexed arrays, but this was not clearly +defined. Starting with PHPlot-5.0.4, only arrays with "usual integer +indices" work, and other array indexes will cause errors. + +Some internal-use-only functions have had their usage changed or been removed. +If you are using functions that are not documented in the PHPlot Function +Reference in the manual, your code may have to be changed. + +As part of the code cleanup, two functions which were defined outside the +PHPlot class were removed: array_pad_array(), and array_merge_php4(). +If your code used these, you need to fix your code. + +The routines which accept a color name, value, or array now check for a valid +color name. If you specify a color name which is not in your current color +table, PHPlot will draw an error and exit. Previously, PHP would report an +index error, continue, and get a 'headers already sent' message. + + +Bug Fixes in 5.0.4: + +#1813021: Miss-positioned right-justified vertical GD text. + Fixed DrawText() to correctly position 90 degree right-justified text + drawn in a fixed GD font. This could be seen with 90 degree Y tick labels. + +#1790441 Removed destructor/shutdown function, and no longer recommend + using reference assignment when creating a PHPlot object. This was + interfering with memory usage. + Credit to annajilly for analysis. + +#1779115 SetLegendWorld() failed because of undefined variables. The + required order dependency was too hard to meet. This is now fixed. + You can now use SetLegendWorld anywhere before DrawGraph. + +#1726810 (feature request, but actually a bug fix) Ignore empty strings + as data labels when doing time or data label formatting. These would + previously produce errors or bad formatting. Now you can omit labels + as needed even with time and data formatting. + Credit to exgerhardr for finding this. + +#1605555 Y data labels used wrong font and not formatted (bar charts only). + +#1208054 Localization of number formatting in 'data' format type. PHPlot + will attempt to format the numbers in a way appropriate to your locale. + You can also force the formatting with the new function SetNumberFormat. + Credit to David Hernández Sanz. + +#937944 X/Y Tick counts: PHPlot could draw one two few Y tick counts, and + one too many X tick counts. This is not a perfect fix, and more work is + needed here, but this fixes an error case in both X and Y values. + + +New Features in 5.0.4: + +New function SetLegendStyle allows control of the alignment of text and + color boxes within the legend. Also allows removing the color boxes. + Based on bug #1208054. + Credit to David Hernández Sanz. + +New function SetNumberFormat. See bug report #1208054 above. + +Callbacks are added. PHPlot can call back your functions while generating the + plot. This is experimental, and documented only in the file "Callbacks". + Credit to annajilly for the idea and design. + +----------------------------------------------------------------------------- + +2006-11-13 Released 5.0rc3 + +Overview: + +This is an interim release. It has been a long time since the previous +release 5.0rc2, and there have been a lot of changes. There are still more +changes likely to go in before we have "5.0", but there are enough for now. + +The PHPlot Reference Manual has also been released, and is available as a +separate download from Sourceforge. PHPlot users and developers are +strongly encouraged to read the manual. + +This release does not include the "doc/" and "examples/" directories of +previous releases. The Reference Manual contains more complete and +up-to-date information and examples, and I am unable to maintain the doc/ +and examples/ files while also maintaining the Reference Manual. If you +need those files, they can be accessed with the Sourceforge web CVS +browser. + + +New Features: + +The emphasis for this release is bug fixing, so there are few new features. + ++ You can now suppress lines or points on individual plots in a linepoints + graph. This feature was added because I needed a graph with several + linepoints lines, but also with a solid line showing an "80% goal". + Use SetPointShapes with the value 'none' in the array to suppress the + point markers for that plot (and only draw the line). + Use SetLineStyles with the value 'none' in the array to suppress the + line for that plot (and only draw the point markers). + [Bug # 1594458] + ++ Bar charts can have data labels above the bar with the value. Turn + these on with SetYDataLabelPos('plotin'). This is somewhat experimental, + since there isn't a lot of room for labels on top of the bars and you + may find the results are not useful. + + +Visible Changes: + +Here are the more significant changes in this release. These are changes +which may affect existing scripts and output from PHPlot. See the +ChangeLog file for information about all changes and bug fixes. + ++ A bug fix on bar chart bar borders results in black borders around the + bars if shading is turned off. The border was previously covered up, + but was supposed to be there. If you need borderless, unshaded bars, + you need to use SetDataBorderColors to make the borders the same colors + as the bars. [Bug # 1096197] + ++ TrueType font pathname handling was fixed. You no longer need to use + SetUseTTF(True). You can either use full paths to the font files with + SetDefaultTTFont() and SetFont(), or you can call SetTTFPath() to point + to a directory of font files, and then use simple font filenames without + paths in SetDefaultTTFont() and SetFont(). + [Bug # 1144644 plus several others] + ++ There have been several fixes regarding automatically calculated ranges + and scales. The result is that you may see less extra space and fewer + tick marks in some cases. + ++ A fix was made to bar and stackedbar graph bar widths in order to get + the X axis labels to properly center. As part of the fix, the bar widths + now match between the two graph types. (Before this fix, the bars were + narrower in bar graphs compared to the same data plotted as a stacked + bar.) As a result, bar graph bars will now be drawn with wider bars, and + stackedbar graph bars will be narrower. You can adjust this with the new + class variable bar_extra_space. [Bug # 1437912] + ++ Dot shapes and sizes were off by 1 or 2 slots in the array of shapes or + sizes. After the fix, you may get different dot shapes or sizes per + plot line. [Bug # 1096194] + + +Testing: + +Since its output is visual (graphics), and it has so many interconnected +modes and options, PHPlot is difficult to test. But at least we are now +trying. I have a collection of PHPlot scripts (currently about 60) and a +script to run through them. The script automatically checks that: + 1) Nothing was written to the standard error stream; + 2) An image file of size greater than 0 was written; + 3) Neither the test script nor PHPlot did exit(). This catches cases + where PHPlot aborts with DrawError(). + +The automated test is an easy way to check for serious regression, but you +really need to inspect the output files to validate PHPlot. This takes a +little time, and it is easy to overlook problems. + +The real issue is test coverage. Just as we can be sure that future +PHPlot releases will pass the test collection, we can also be sure that +future bug reports will be written against untested cases. + +-------------------- + +2006-11-08 PHPlot on Sourceforge has a new maintainer: lbayuk + +-------------------- + +2004-10-24 Released 5.0rc2 + +-------------------- + diff --git a/gosa-core/include/phplot-5.1.2/README.txt b/gosa-core/include/phplot-5.1.2/README.txt new file mode 100644 index 000000000..03471bfba --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/README.txt @@ -0,0 +1,156 @@ +This is the README file for PHPlot +Last updated for PHPlot-5.1.2 on 2010-06-29 +The project web site is http://sourceforge.net/projects/phplot/ +The project home page is http://phplot.sourceforge.net/ +----------------------------------------------------------------------------- + +OVERVIEW: + +PHPlot is a PHP class for creating scientific and business charts. + +The release documentation contains only summary information. For more +complete information, download the PHPlot Reference Manual from the +Sourceforge project web site. You can also view the manual online at +http://phplot.sourceforge.net + +For important changes in this release, see the NEWS.txt file. + + +CONTENTS: + + COPYING . . . . . . . . . . . . LGPL 2.1 License file + ChangeLog . . . . . . . . . . . Lists changes to the sources + HorizontalBars.txt . . . . . . . Experimental feature documentation + NEWS.txt . . . . . . . . . . . . Highlights changes in releases + README.txt . . . . . . . . . . This file + contrib . . . . . . . . . . . . "Contributed" directory, add-ons + phplot.php . . . . . . . . . . The main PHPlot source file + phplot_data.php . . . . . . . . Auxiliary and extended functions + rgb.inc.php . . . . . . . . . . Optional extended color table + +REQUIREMENTS: + +You need a recent version of PHP5. Usually, we recommend you use the latest +stable release, however due to problems with PHP-5.3.2 and PHP-5.2.13 you +are advised to use the previous releases if possible. The problems are +specific to TrueType font (TTF) text. If you are not using TTF text, you +may use PHP-5.3.2 or 5.2.13. (The PHP team already has a fix for this TTF +problem in PHP-5.3.3 development snapshots, so the fix should be in the +next releases PHP-5.3.3 and PHP-5.2.14.) + +This version of PHPlot has been tested with PHP-5.3.1 and PHP-5.2.12 on +Linux, and with PHP-5.3.1 on Windows/XP. The PHPlot Test Suite currently +contains 432 test cases. + +You need the GD extension to PHP either built in to PHP or loaded as a +module. Refer to the PHP documentation for more information - see the +Image Functions chapter in the PHP Manual. We test PHPlot only with the +PHP-supported, bundled GD library. + +If you want to display PHPlot charts on a web site, you need a PHP-enabled +web server. You can also use the PHP CLI interface without a web server. + +PHPlot supports TrueType fonts, but does not include any TrueType font +files. If you want to use TrueType fonts on your charts, you need to have +TrueType support in GD, and some TrueType font files. By default, PHPlot +uses a simple font which is built in to the GD library. + + +INSTALLATION: + +Unpack the distribution. (If you are reading this file, you have probably +already done that.) + +Installation of PHPlot simply involves copying three script files somewhere +your PHP application scripts will be able to find them. The scripts are: + phplot.php + phplot_data.php + rgb.inc.php +(Only phplot.php is necessary for most graphs.) +Make sure the permissions on these files allow the web server to read them. + +The ideal place is a directory outside your web server document area, +and on your PHP include path. You can add to the include path in the PHP +configuration file; consult the PHP manual for details. + + +KNOWN ISSUES: + +Here are some of the problems we know about in PHPlot. See the bug tracker +on the PHPlot project web site for more information. + +#1795969 The automatic range calculation for Y values needs to be rewritten. + This is especially a problem with small offset ranges (e.g. Y=[999:1001]). + You can use SetPlotAreaWorld to set a specific range instead. + +#1605558 Wide/Custom dashed lines don't work well + This is partially a GD issue, partially PHPlot's fault. + +#2919086 Improve tick interval calculations + Tick interval calculations should try for intervals of 1, 2, or 5 times + a power of 10. + +PHP Bugs #51207, #51094, and others: These are PHP bugs, not PHPlot, +on rendering of TrueType font (TTF) text in PHP-5.3.2 and 5.2.13. + + +If you think you found a problem with PHPlot, or want to ask questions or +provide feedback, please use the Help and Discussion forum at + http://sourceforge.net/projects/phplot/ +If you are sure you have found a bug, you can report it on the Bug tracker +at the same web site. There is also a Features Request tracker. + + +TESTING: + +You can test your installation by creating the following two files somewhere +in your web document area. First, the HTML file: + +------------ simpleplot.html ---------------------------- + + +Hello, PHPlot! + + +

PHPlot Test

+ + + +--------------------------------------------------------- + +Second, in the same directory, the image file producing PHP script file. +Depending on where you installed phplot.php, you may need to specify a path +in the 'require' line below. + +------------ simpleplot.php ----------------------------- +SetDataValues($data); +$plot->SetDataType('data-data'); +$plot->DrawGraph(); +--------------------------------------------------------- + +Access the URL to 'simpleplot.html' in your web browser. If you see a +simple graph, you have successfully installed PHPlot. If you see no +graph, check your web server error log for more information. + + +COPYRIGHT and LICENSE: + +PHPlot is Copyright (C) 1998-2010 Afan Ottenheimer + +This is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License. + +This software 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this software; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/gosa-core/include/phplot-5.1.2/contrib/README.txt b/gosa-core/include/phplot-5.1.2/contrib/README.txt new file mode 100644 index 000000000..3c6322520 --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/contrib/README.txt @@ -0,0 +1,31 @@ +This is the README for PHPlot Contributed Code +The project web site is http://sourceforge.net/projects/phplot/ +Last updated on 2009-12-08 +----------------------------------------------------------------------------- + +The PHPlot Contributed Code directory contains code you might find useful +with PHPlot, but that doesn't quite belong as part of PHPlot itself. + +You will have to read the comments in the code files, and see the example +files, to determine what these do and if they are useful to you. None of +these functions is documented in the PHPlot Reference Manual. + +You may include or paste these functions into your own scripts. Check the +files for details, but some of these are considered "public domain" with no +usage or license restrictions. + +----------------------------------------------------------------------------- +Contents: + +prune_labels: Reduce the number of labels along the X axis + prune_labels.php . . . . . . . . . . . . Code + prune_labels.example.php . . . . . . . . Example + prune_labels.test.php . . . . . . . . . Test + +color_range: Create a gradient color map for data colors + color_range.php . . . . . . . . . . . . Code + color_range.example.php . . . . . . . . Example + color_range.test1.php . . . . . . . . . Image creation test + color_range.test2.php . . . . . . . . . Unit test + +----------------------------------------------------------------------------- diff --git a/gosa-core/include/phplot-5.1.2/contrib/color_range.example.php b/gosa-core/include/phplot-5.1.2/contrib/color_range.example.php new file mode 100644 index 000000000..d29978b77 --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/contrib/color_range.example.php @@ -0,0 +1,35 @@ +SetTitle('Example - Bar Chart with gradient colors'); +$p->SetDataType('text-data'); +$p->SetDataValues($data); +$p->SetPlotAreaWorld(0, 0, $x_values, 100); + +# This isn't necessary, as we do know how many data sets (bars_per_group): +$n_data = count_data_sets($data, 'text-data'); +# Make a gradient color map: +$colors = color_range($p->SetRGBColor('SkyBlue'), + $p->SetRGBColor('DarkGreen'), $n_data); +$p->SetDataColors($colors); +$p->SetXTickLabelPos('none'); +$p->SetXTickPos('none'); +$p->SetPlotType('bars'); +$p->DrawGraph(); diff --git a/gosa-core/include/phplot-5.1.2/contrib/color_range.php b/gosa-core/include/phplot-5.1.2/contrib/color_range.php new file mode 100755 index 000000000..65437ed37 --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/contrib/color_range.php @@ -0,0 +1,100 @@ + + "I wrote this code to calculate the range of colors between 2 colors + to plot using the range from color_a to color_b..." + + I have changed the code and repackaged it, but the idea is the same. + Given 2 colors and number of data sets, computes an array of colors + that make up a gradient between the two provided colors, for use + with SetDataColors(). + + Provides the following functions: + $colors = color_range($color_a, $color_b, $n_intervals) + Returns a color array for SetDataColors. + + $n = count_data_sets($data, $data_type) + Counts the number of data sets in a data array. + This can be used to provide $n_intervals in color_range(). +*/ + + + +/* + Fill a color map with a gradient step between two colors. + Arguments: + $color_a : Starting color for the gradient. Array of (r, g, b) + $color_b : Ending color for the gradient. Array of (r, g, b) + $n_steps : Total number of color steps, including color_a and color_b. + + Returns: A color map array with n_steps colors in the form + $colors[i][3], suitable for SetDataColors(). + + Notes: + You may use the PHPlot internal function $plot->SetRGBColor($color) + to convert a color name or #rrggbb notation into the required array + of 3 values (r, g, b) for color_a and color_b. + +*/ +function color_range($color_a, $color_b, $n_steps) +{ + if ($n_steps < 2) $n_steps = 2; + $nc = $n_steps - 1; + # Note: $delta[] and $current[] are kept as floats. $colors is integers. + for ($i = 0; $i < 3; $i++) + $delta[$i] = ($color_b[$i] - $color_a[$i]) / $nc; + $current = $color_a; + for ($col = 0; $col < $nc; $col++) { + for ($i = 0; $i < 3; $i++) { + $colors[$col][$i] = (int)$current[$i]; + $current[$i] += $delta[$i]; + } + } + $colors[$nc] = $color_b; # Make sure the last color is exact. + return $colors; +} + + +/* + Determine the number of data sets (plot lines, bars per group, pie + segments, etc.) contained in a data array. + This can be used to determine n_steps for $color_range. + + Arguments: + $data : PHPlot data array + $data_type : PHPlot data type, describing $data. (e.g. 'data-data') + Returns: The number of data sets in the data array. + Notes: + This has to scan the entire data array. Don't use this unless you + really don't have a better way to determine the number of data sets. + + This does NOT require that the data array be integer indexed. + +*/ +function count_data_sets($data, $data_type) +{ + + if ($data_type == 'text-data-single') + return count($data); # Pie chart, 1 segment per record + + # Get the longest data record: + $max_row = 0; + foreach ($data as $row) + if (($n = count($row)) > $max_row) $max_row = $n; + + if ($data_type == 'text-data') + return ($max_row - 1); # Each record is (label Y1 Y2...) + + if ($data_type == 'data-data') + return ($max_row - 2); # Each record is (label X Y1 Y2...) + + if ($data_type == 'data-data-error') + return (($max_row - 2) / 3); # Each record is (label X Y1 Y1+ Y1-...) + + # Not a recognized data type... Just return something sane. + return $max_row; +} diff --git a/gosa-core/include/phplot-5.1.2/contrib/color_range.test1.php b/gosa-core/include/phplot-5.1.2/contrib/color_range.test1.php new file mode 100644 index 000000000..661f591ea --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/contrib/color_range.test1.php @@ -0,0 +1,55 @@ +SetTitle('Example - pruned data labels'); +$p->SetDataType('data-data'); +$p->SetDataValues($data); +$p->SetXLabelType('time', '%Y-%m-%d'); +$p->SetXLabelAngle(90); +$p->SetXDataLabelPos('plotdown'); +$p->SetXTickLabelPos('none'); +$p->SetXTickPos('none'); +$p->SetDrawXGrid(False); +$p->SetDrawYGrid(False); +$p->SetPlotType('lines'); +$p->DrawGraph(); diff --git a/gosa-core/include/phplot-5.1.2/contrib/prune_labels.php b/gosa-core/include/phplot-5.1.2/contrib/prune_labels.php new file mode 100644 index 000000000..5f272e52c --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/contrib/prune_labels.php @@ -0,0 +1,36 @@ + 0) $data[$i][0] = ''; + if (++$k >= $m) $k = 0; + } +} diff --git a/gosa-core/include/phplot-5.1.2/contrib/prune_labels.test.php b/gosa-core/include/phplot-5.1.2/contrib/prune_labels.test.php new file mode 100644 index 000000000..6d3bbb263 --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/contrib/prune_labels.test.php @@ -0,0 +1,41 @@ + $non_blank labels\n"; + echo substr($line, 0, 80) . "\n"; # Only show first 80 chars. +} + +/* Test cases for prune_labels */ +for ($n = 7; $n <= 1000; $n *= 2) test($n, 10); +for ($g = 5; $g <= 40; $g++) test(72, $g); +# Edge cases +test(80, 41); +test(80, 40); +test(80, 39); diff --git a/gosa-core/include/phplot-5.1.2/phplot.php b/gosa-core/include/phplot-5.1.2/phplot.php new file mode 100644 index 000000000..e914570c7 --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/phplot.php @@ -0,0 +1,5873 @@ + + * + * Maintainer (2006-present) + * + * + * Requires PHP 5.2.x or later. (PHP 4 is unsupported as of Jan 2008) + */ + +class PHPlot { + + /* Declare class variables which are initialized to static values. Many more class variables + * are used, defined as needed, but are unset by default. + * All these are declared as public. While it is tempting to make them private or protected, this + * is avoided for two reasons. First, it will break existing code, since all member variables + * were public in PHP4 and who knows what internal variables people used. Second, it makes + * testing harder and less effective. Nevertheless, your code should not modify these. + */ + + public $is_inline = FALSE; // FALSE = Sends headers, TRUE = sends just raw image data + public $browser_cache = FALSE; // FALSE = Sends headers for browser to not cache the image, + // (only if is_inline = FALSE also) + public $print_image = TRUE; // DrawGraph calls PrintImage. See SetPrintImage + public $background_done = FALSE; // TRUE after background image is drawn once + + public $safe_margin = 5; // Extra margin used in several places, in pixels + + public $x_axis_position = ''; // Where to draw both axis (world coordinates), + public $y_axis_position = ''; // leave blank for X axis at 0 and Y axis at left of plot. + + public $xscale_type = 'linear'; // linear, log + public $yscale_type = 'linear'; + +//Fonts + public $use_ttf = FALSE; // Use True Type Fonts by default? + public $ttf_path = '.'; // Default path to look in for TT Fonts. + public $default_ttfont = 'benjamingothic.ttf'; + public $line_spacing = 4; // Controls line spacing of multi-line labels + + // Label angles: 0 or 90 degrees for fixed fonts, any for TTF + public $x_label_angle = 0; // For X tick labels + // public $x_data_label_angle; // For X data labels; defaults to x_label_angle - see CheckLabels() + public $y_label_angle = 0; // For Y tick labels + public $y_data_label_angle = 0; // For Y data labels + +//Formats + public $file_format = 'png'; + public $output_file = ''; // For output to a file instead of stdout + +//Data + public $data_type = 'text-data'; // Structure of the data array + public $plot_type= 'linepoints'; // bars, lines, linepoints, area, points, pie, thinbarline, squared + + public $label_scale_position = 0.5; // Shifts data labels in pie charts. 1 = top, 0 = bottom + public $group_frac_width = 0.7; // Bars use this fraction (0 to 1) of a group's space + public $bar_extra_space = 0.5; // Number of extra bar's worth of space in a group + public $bar_width_adjust = 1; // 1 = bars of normal width, must be > 0 + +// Titles + public $title_txt = ''; + + public $x_title_txt = ''; + public $x_title_pos = 'none'; // plotdown, plotup, both, none + + public $y_title_txt = ''; + public $y_title_pos = 'none'; // plotleft, plotright, both, none + + +//Labels + // There are two types of labels in PHPlot: + // Tick labels: they follow the grid, next to ticks in axis. + // they are drawn at grid drawing time, by DrawXTicks() and DrawYTicks() + // Data labels: they follow the data points, and can be placed on the axis or the plot (x/y) + // they are drawn at graph plotting time, by Draw*DataLabel(), called by DrawLines(), etc. + // Draw*DataLabel() also draws H/V lines to datapoints depending on draw_*_data_label_lines + // Tick Labels + // Tick and Data label positions are not initialized, because PHPlot needs to tell if they + // defaulted or are set by the user. See CheckLabels() for details. The variables and + // effective defaults are shown here in comments (but CheckLabels adjusts the defaults). + // public $x_tick_label_pos = 'plotdown'; // X tick label position + // public $y_tick_label_pos = 'plotleft'; // Y tick label position + // public $x_data_label_pos = 'plotdown'; // X data label position + // public $y_data_label_pos = 'none'; // Y data label position + + public $draw_x_data_label_lines = FALSE; // Draw a line from the data point to the axis? + + // Label format controls: (for tick, data and plot labels) + // Unset by default, these array members are used as needed for 'x' (x tick labels), 'xd' (x data + // labels), 'y' (y tick labels), and 'yd' (y data labels). + // type, precision, prefix, suffix, time_format, printf_format, custom_callback, custom_arg. + // These replace the former: x_label_type, x_time_format, x_precision (similar for y), data_units_text. + public $label_format = array('x' => array(), 'xd' => array(), 'y' => array(), 'yd' => array()); + // data_units_text is retained for backward compatibility, because there was never a function + // to set it. Use the 'suffix' argument to Set[XY]LabelType instead. + public $data_units_text = ''; // Units text for 'data' labels (i.e: '¤', '$', etc.) + +// Legend + public $legend = ''; // An array with legend titles + // These variables are unset to take default values: + // public $legend_x_pos; // User-specified upper left coordinates of legend box + // public $legend_y_pos; + // public $legend_xy_world; // If set, legend_x/y_pos are world coords, else pixel coords + // public $legend_text_align; // left or right, Unset means right + // public $legend_colorbox_align; // left, right, or none; Unset means same as text_align + +//Ticks + public $x_tick_length = 5; // tick length in pixels for upper/lower axis + public $y_tick_length = 5; // tick length in pixels for left/right axis + + public $x_tick_cross = 3; // ticks cross x axis this many pixels + public $y_tick_cross = 3; // ticks cross y axis this many pixels + + public $x_tick_pos = 'plotdown'; // plotdown, plotup, both, xaxis, none + public $y_tick_pos = 'plotleft'; // plotright, plotleft, both, yaxis, none + + public $num_x_ticks = ''; + public $num_y_ticks = ''; + + public $x_tick_inc = ''; // Set num_x_ticks or x_tick_inc, not both. + public $y_tick_inc = ''; // Set num_y_ticks or y_tick_inc, not both. + + public $skip_top_tick = FALSE; + public $skip_bottom_tick = FALSE; + public $skip_left_tick = FALSE; + public $skip_right_tick = FALSE; + +//Grid Formatting + // public $draw_x_grid = FALSE; // Default is False except for swapped data type + // public $draw_y_grid = TRUE; // Default is True except for swapped data type + + public $dashed_grid = TRUE; + public $grid_at_foreground = FALSE; // Chooses whether to draw the grid below or above the graph + +//Colors and styles (all colors can be array (R,G,B) or named color) + public $color_array = 'small'; // 'small', 'large' or array (define your own colors) + // See rgb.inc.php and SetRGBArray() + public $i_border = array(194, 194, 194); + public $plot_bg_color = 'white'; + public $bg_color = 'white'; + public $label_color = 'black'; + public $text_color = 'black'; + public $grid_color = 'black'; + public $light_grid_color = 'gray'; + public $tick_color = 'black'; + public $title_color = 'black'; + public $default_colors = array( // The default colors for data and error bars + 'SkyBlue', 'green', 'orange', 'blue', 'red', 'DarkGreen', 'purple', 'peru', + 'cyan', 'salmon', 'SlateBlue', 'YellowGreen', 'magenta', 'aquamarine1', 'gold', 'violet'); + + // data_colors and error_bar_colors are initialized to default_colors by SetDefaultStyles. + // public $data_colors; // Data colors + // public $error_bar_colors; // Error bar colors + // data_border_colors is initialized to black by SetDefaultStyles. + // public $data_border_colors; // Data border colors + + public $line_widths = 1; // single value or array + public $line_styles = array('solid', 'solid', 'dashed'); // single value or array + public $dashed_style = '2-4'; // colored dots-transparent dots + + public $point_sizes = array(6); // Array of sizes for points. See CheckPointParams() + public $point_shapes = array( // Array of point shapes. See SetPointShapes() and DrawDot() + 'diamond', 'dot', 'delta', 'home', 'yield', 'box', 'circle', 'up', 'down', 'cross' + ); + + public $error_bar_size = 5; // right and left size of tee + public $error_bar_shape = 'tee'; // 'tee' or 'line' + public $error_bar_line_width = 1; // single value (or array TODO) + + public $plot_border_type = 'sides'; // left, right, top, bottom, sides, none, full; or array + public $image_border_type = 'none'; // 'raised', 'plain', 'none' + // public $image_border_width; // NULL, 0, or unset for default. Default depends on type. + + public $shading = 5; // 0 for no shading, > 0 is size of shadows in pixels + + public $draw_plot_area_background = FALSE; + public $draw_broken_lines = FALSE; // Tells not to draw lines for missing Y data. + + public $data_colors_alpha = 0; // Default alpha for data colors. See SetDataColors() + + +//Miscellaneous + public $callbacks = array( // Valid callback reasons (see SetCallBack) + 'draw_setup' => NULL, + 'draw_image_background' => NULL, + 'draw_plotarea_background' => NULL, + 'draw_titles' => NULL, + 'draw_axes' => NULL, + 'draw_graph' => NULL, + 'draw_border' => NULL, + 'draw_legend' => NULL, + 'draw_all' => NULL, + 'debug_textbox' => NULL, // For testing/debugging text box alignment + 'debug_scale' => NULL, // For testing/debugging scale setup + ); + + +////////////////////////////////////////////////////// +//BEGIN CODE +////////////////////////////////////////////////////// + + /*! + * Constructor: Setup img resource, colors and size of the image, and font sizes. + * + * \param which_width int Image width in pixels. + * \param which_height int Image height in pixels. + * \param which_output_file string Filename for output. + * \param which_input_file string Path to a file to be used as background. + */ + function PHPlot($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL) + { + $this->SetRGBArray($this->color_array); + + if ($which_output_file) + $this->SetOutputFile($which_output_file); + + if ($which_input_file) + $this->SetInputFile($which_input_file); + else { + $this->image_width = $which_width; + $this->image_height = $which_height; + + $this->img = ImageCreate($this->image_width, $this->image_height); + if (! $this->img) + return $this->PrintError('PHPlot(): Could not create image resource.'); + } + + $this->SetDefaultStyles(); + $this->SetDefaultFonts(); + } + + /*! + * Reads an image file. Stores width and height, and returns the image + * resource. On error, calls PrintError and returns False. + * This is used by the constructor via SetInputFile, and by tile_img(). + */ + protected function GetImage($image_filename, &$width, &$height) + { + $error = ''; + $size = getimagesize($image_filename); + if (!$size) { + $error = "Unable to query image file $image_filename"; + } else { + $image_type = $size[2]; + switch($image_type) { + case IMAGETYPE_GIF: + $img = @ ImageCreateFromGIF ($image_filename); + break; + case IMAGETYPE_PNG: + $img = @ ImageCreateFromPNG ($image_filename); + break; + case IMAGETYPE_JPEG: + $img = @ ImageCreateFromJPEG ($image_filename); + break; + default: + $error = "Unknown image type ($image_type) for image file $image_filename"; + break; + } + } + if (empty($error) && !$img) { + # getimagesize is OK, but GD won't read it. Maybe unsupported format. + $error = "Failed to read image file $image_filename"; + } + if (!empty($error)) { + return $this->PrintError("GetImage(): $error"); + } + $width = $size[0]; + $height = $size[1]; + return $img; + } + + /*! + * Selects an input file to be used as background for the whole graph. + * This resets the graph size to the image's size. + * Note: This is used by the constructor. It is deprecated for direct use. + */ + function SetInputFile($which_input_file) + { + $im = $this->GetImage($which_input_file, $this->image_width, $this->image_height); + if (!$im) + return FALSE; // GetImage already produced an error message. + + // Deallocate any resources previously allocated + if (isset($this->img)) + imagedestroy($this->img); + + $this->img = $im; + + // Do not overwrite the input file with the background color. + $this->background_done = TRUE; + + return TRUE; + } + +///////////////////////////////////////////// +////////////// COLORS +///////////////////////////////////////////// + + /* + * Returns a GD color index value to a color specified as for SetRGBColor(). + * This works with both palette and truecolor images. + * $which_color : The color specification. See SetRGBColor for formats. + * $alpha : Optional default Alpha value, if $which_color does not include alpha. + * Returns a GD color index, or NULL on error. + * Note: For palette images, this is an index into the color map. + * For truecolor images, this is a 32 bit value 0xAARRGGBB. But this difference + * is internal to GD. + */ + function SetIndexColor($which_color, $alpha = 0) + { + list ($r, $g, $b, $a) = $this->SetRGBColor($which_color, $alpha); //Translate to RGBA + if (!isset($r)) return NULL; + return ImageColorResolveAlpha($this->img, $r, $g, $b, $a); + } + + /* + * Returns an index to a slightly darker color than the one requested. + * This works with both palette and truecolor images. + * $which_color : The color specification. See SetRGBColor for formats. + * $alpha : Optional default Alpha value, if $which_color does not include alpha. + * Returns a GD color index, or NULL on error. + */ + protected function SetIndexDarkColor($which_color, $alpha = 0) + { + list ($r, $g, $b, $a) = $this->SetRGBColor($which_color, $alpha); + if (!isset($r)) return NULL; + $r = max(0, $r - 0x30); + $g = max(0, $g - 0x30); + $b = max(0, $b - 0x30); + return ImageColorResolveAlpha($this->img, $r, $g, $b, $a); + } + + /*! + * Sets/reverts all colors and styles to their defaults. + */ + protected function SetDefaultStyles() + { + /* Some of the Set*() functions use default values when they get no parameters. */ + $this->SetDefaultDashedStyle($this->dashed_style); + $this->SetImageBorderColor($this->i_border); + $this->SetPlotBgColor($this->plot_bg_color); + $this->SetBackgroundColor($this->bg_color); + $this->SetLabelColor($this->label_color); + $this->SetTextColor($this->text_color); + $this->SetGridColor($this->grid_color); + $this->SetLightGridColor($this->light_grid_color); + $this->SetTickColor($this->tick_color); + $this->SetTitleColor($this->title_color); + $this->SetDataColors(); + $this->SetErrorBarColors(); + $this->SetDataBorderColors(); + return TRUE; + } + + + /* + * + */ + function SetBackgroundColor($which_color) + { + $this->bg_color= $which_color; + $this->ndx_bg_color= $this->SetIndexColor($this->bg_color); + return isset($this->ndx_bg_color); + } + + /* + * + */ + function SetPlotBgColor($which_color) + { + $this->plot_bg_color= $which_color; + $this->ndx_plot_bg_color= $this->SetIndexColor($this->plot_bg_color); + return isset($this->ndx_plot_bg_color); + } + + /* + * + */ + function SetTitleColor($which_color) + { + $this->title_color= $which_color; + $this->ndx_title_color= $this->SetIndexColor($this->title_color); + return isset($this->ndx_title_color); + } + + /* + * + */ + function SetTickColor ($which_color) + { + $this->tick_color= $which_color; + $this->ndx_tick_color= $this->SetIndexColor($this->tick_color); + return isset($this->ndx_tick_color); + } + + + /* + * Do not use. Use SetTitleColor instead. + */ + function SetLabelColor ($which_color) + { + $this->label_color = $which_color; + $this->ndx_title_color= $this->SetIndexColor($this->label_color); + return isset($this->ndx_title_color); + } + + + /* + * + */ + function SetTextColor ($which_color) + { + $this->text_color= $which_color; + $this->ndx_text_color= $this->SetIndexColor($this->text_color); + return isset($this->ndx_text_color); + } + + + /* + * + */ + function SetLightGridColor ($which_color) + { + $this->light_grid_color= $which_color; + $this->ndx_light_grid_color= $this->SetIndexColor($this->light_grid_color); + return isset($this->ndx_light_grid_color); + } + + + /* + * + */ + function SetGridColor ($which_color) + { + $this->grid_color = $which_color; + $this->ndx_grid_color= $this->SetIndexColor($this->grid_color); + return isset($this->ndx_grid_color); + } + + + /* + * + */ + function SetImageBorderColor($which_color) + { + $this->i_border = $which_color; + $this->ndx_i_border = $this->SetIndexColor($this->i_border); + $this->ndx_i_border_dark = $this->SetIndexDarkColor($this->i_border); + return isset($this->ndx_i_border); + } + + + /* + * + */ + function SetTransparentColor($which_color) + { + $ndx = $this->SetIndexColor($which_color); + if (!isset($ndx)) + return FALSE; + ImageColorTransparent($this->img, $ndx); + return TRUE; + } + + + /*! + * Sets the array of colors to be used. It can be user defined, a small predefined one + * or a large one included from 'rgb.inc.php'. + * + * \param which_color_array If an array, the used as color array. If a string can + * be one of 'small' or 'large'. + */ + function SetRGBArray ($which_color_array) + { + if ( is_array($which_color_array) ) { // User defined array + $this->rgb_array = $which_color_array; + return TRUE; + } elseif ($which_color_array == 'small') { // Small predefined color array + $this->rgb_array = array( + 'white' => array(255, 255, 255), + 'snow' => array(255, 250, 250), + 'PeachPuff' => array(255, 218, 185), + 'ivory' => array(255, 255, 240), + 'lavender' => array(230, 230, 250), + 'black' => array( 0, 0, 0), + 'DimGrey' => array(105, 105, 105), + 'gray' => array(190, 190, 190), + 'grey' => array(190, 190, 190), + 'navy' => array( 0, 0, 128), + 'SlateBlue' => array(106, 90, 205), + 'blue' => array( 0, 0, 255), + 'SkyBlue' => array(135, 206, 235), + 'cyan' => array( 0, 255, 255), + 'DarkGreen' => array( 0, 100, 0), + 'green' => array( 0, 255, 0), + 'YellowGreen' => array(154, 205, 50), + 'yellow' => array(255, 255, 0), + 'orange' => array(255, 165, 0), + 'gold' => array(255, 215, 0), + 'peru' => array(205, 133, 63), + 'beige' => array(245, 245, 220), + 'wheat' => array(245, 222, 179), + 'tan' => array(210, 180, 140), + 'brown' => array(165, 42, 42), + 'salmon' => array(250, 128, 114), + 'red' => array(255, 0, 0), + 'pink' => array(255, 192, 203), + 'maroon' => array(176, 48, 96), + 'magenta' => array(255, 0, 255), + 'violet' => array(238, 130, 238), + 'plum' => array(221, 160, 221), + 'orchid' => array(218, 112, 214), + 'purple' => array(160, 32, 240), + 'azure1' => array(240, 255, 255), + 'aquamarine1' => array(127, 255, 212) + ); + return TRUE; + } elseif ($which_color_array === 'large') { // Large color array + if (!@include('rgb.inc.php')) { + return $this->PrintError("SetRGBArray(): Large color map could not be loaded\n" + . "from 'rgb.inc.php'."); + } + $this->rgb_array = $ColorArray; + } else { // Default to black and white only. + $this->rgb_array = array('white' => array(255, 255, 255), 'black' => array(0, 0, 0)); + } + + return TRUE; + } + + /* + * Parse a color description and return the color component values. + * Arguments: + * $color_asked : The desired color description, in one of these forms: + * Component notation: array(R, G, B) or array(R, G, B, A) with each + * in the range described below for the return value. + * Examples: (255,255,0) (204,0,0,30) + * Hex notation: "#RRGGBB" or "#RRGGBBAA" where each pair is a 2 digit hex number. + * Examples: #FF00FF (magenta) #0000FF40 (Blue with alpha=64/127) + * Named color in the current colormap, with optional suffix ":alpha" for alpha value. + * Examples: blue red:60 yellow:20 + * $alpha : optional default alpha value. This is applied to the color if it doesn't + * already have an alpha value. If not supplied, colors are opaque (alpha=0) by default. + * + * Returns an array describing a color as (R, G, B, Alpha). + * R, G, and B are integers 0-255, and Alpha is 0 (opaque) to 127 (transparent). + */ + function SetRGBColor($color_asked, $alpha = 0) + { + if (empty($color_asked)) { + $ret_val = array(0, 0, 0); + + } elseif (is_array($color_asked) && (($n = count($color_asked)) == 3 || $n == 4) ) { + // Already an array of 3 or 4 elements: + $ret_val = $color_asked; + + } elseif (preg_match('/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i', + $color_asked, $ss)) { + // #RRGGBB or #RRGGBBAA notation: + $ret_val = array(hexdec($ss[1]), hexdec($ss[2]), hexdec($ss[3])); + if (isset($ss[4])) $ret_val[] = hexdec($ss[4]); + + } elseif (isset($this->rgb_array[$color_asked])) { + // Color by name: + $ret_val = $this->rgb_array[$color_asked]; + + } elseif (preg_match('/(.+):([\d]+)$/', $color_asked, $ss) + && isset($this->rgb_array[$ss[1]])) { + // Color by name with ":alpha" suffix, alpha is a decimal number: + $ret_val = $this->rgb_array[$ss[1]]; + $ret_val[3] = (int)$ss[2]; + + } else { + return $this->PrintError("SetRGBColor(): Color '$color_asked' is not valid."); + } + + // Append alpha if not already provided for: + if (count($ret_val) == 3) + $ret_val[] = $alpha; + return $ret_val; + } + + /*! + * Sets the colors for the data, with optional default alpha value (for PHPlot_truecolor only) + * Cases are: + * SetDataColors(array(...)) : Use the supplied array as the color map. + * SetDataColors(colorname) : Use an array of just colorname as the color map. + * SetDataColors() or SetDataColors(NULL) : Load default color map if no color map is already set. + * SetDataColors('') or SetDataColors(False) : Load default color map (even if one is already set). + */ + function SetDataColors($which_data = NULL, $which_border = NULL, $alpha = NULL) + { + if (is_array($which_data)) { + $this->data_colors = $which_data; // Use supplied array + } elseif (!empty($which_data)) { + $this->data_colors = array($which_data); // Use supplied single color + } elseif (empty($this->data_colors) || !is_null($which_data)) { + $this->data_colors = $this->default_colors; // Use default color array + } // Else do nothing: which_data is NULL or missing and a color array is already set. + + // If an alpha value is supplied, use it as the new default. Need to save and restore + // this because the color indexes will be regenerated when the arrays are padded. + if (!empty($alpha)) + $this->data_colors_alpha = $alpha; + + $i = 0; + foreach ($this->data_colors as $col) { + $ndx = $this->SetIndexColor($col, $this->data_colors_alpha); + if (!isset($ndx)) + return FALSE; + $this->ndx_data_colors[$i] = $ndx; + $this->ndx_data_dark_colors[$i] = $this->SetIndexDarkColor($col, $this->data_colors_alpha); + $i++; + } + + // For past compatibility: + return $this->SetDataBorderColors($which_border); + } // function SetDataColors() + + + /*! + * Set the colors for the bars and stacked bars outlines. + * Argument usage is similar to SetDataColors(), except the default is just black. + */ + function SetDataBorderColors($which_br = NULL) + { + if (is_array($which_br)) { + $this->data_border_colors = $which_br; // Use supplied array + } elseif (!empty($which_br)) { + $this->data_border_colors = array($which_br); // Use supplied single color + } elseif (empty($this->data_border_colors) || !is_null($which_br)) { + $this->data_border_colors = array('black'); // Use default + } // Else do nothing: which_br is NULL or missing and a color array is already set. + + $i = 0; + foreach($this->data_border_colors as $col) { + $ndx = $this->SetIndexColor($col); + if (!isset($ndx)) + return FALSE; + $this->ndx_data_border_colors[$i] = $ndx; + $i++; + } + return TRUE; + } // function SetDataBorderColors() + + + /*! + * Sets the colors for the data error bars. + * Argument usage is the same as SetDataColors(). + */ + function SetErrorBarColors($which_err = NULL) + { + if (is_array($which_err)) { + $this->error_bar_colors = $which_err; // Use supplied array + } elseif (!empty($which_err)) { + $this->error_bar_colors = array($which_err); // Use supplied single color + } elseif (empty($this->error_bar_colors) || !is_null($which_err)) { + $this->error_bar_colors = $this->default_colors; // Use default color array + } // Else do nothing: which_err is NULL or missing and a color array is already set. + + $i = 0; + foreach($this->error_bar_colors as $col) { + $ndx = $this->SetIndexColor($col); + if (!isset($ndx)) + return FALSE; + $this->ndx_error_bar_colors[$i] = $ndx; + $i++; + } + return TRUE; + } // function SetErrorBarColors() + + + /*! + * Sets the default dashed style. + * \param which_style A string specifying order of colored and transparent dots, + * i.e: '4-3' means 4 colored, 3 transparent; + * '2-3-1-2' means 2 colored, 3 transparent, 1 colored, 2 transparent. + */ + function SetDefaultDashedStyle($which_style) + { + // String: "numcol-numtrans-numcol-numtrans..." + $asked = explode('-', $which_style); + + if (count($asked) < 2) { + return $this->PrintError("SetDefaultDashedStyle(): Wrong parameter '$which_style'."); + } + + // Build the string to be eval()uated later by SetDashedStyle() + $this->default_dashed_style = 'array( '; + + $t = 0; + foreach($asked as $s) { + if ($t % 2 == 0) { + $this->default_dashed_style .= str_repeat('$which_ndxcol,', $s); + } else { + $this->default_dashed_style .= str_repeat('IMG_COLOR_TRANSPARENT,', $s); + } + $t++; + } + // Remove trailing comma and add closing parenthesis + $this->default_dashed_style = substr($this->default_dashed_style, 0, -1); + $this->default_dashed_style .= ')'; + + return TRUE; + } + + + /*! + * Sets the style before drawing a dashed line. Defaults to $this->default_dashed_style + * \param which_ndxcol Color index to be used. + */ + protected function SetDashedStyle($which_ndxcol) + { + // See SetDefaultDashedStyle() to understand this. + eval ("\$style = $this->default_dashed_style;"); + return imagesetstyle($this->img, $style); + } + + + /*! + * Sets line widths on a per-line basis. + */ + function SetLineWidths($which_lw=NULL) + { + if (is_null($which_lw)) { + // Do nothing, use default value. + } else if (is_array($which_lw)) { + // Did we get an array with line widths? + $this->line_widths = $which_lw; + } else { + $this->line_widths = array($which_lw); + } + return TRUE; + } + + /*! + * + */ + function SetLineStyles($which_ls=NULL) + { + if (is_null($which_ls)) { + // Do nothing, use default value. + } else if ( is_array($which_ls)) { + // Did we get an array with line styles? + $this->line_styles = $which_ls; + } else { + $this->line_styles = ($which_ls) ? array($which_ls) : array('solid'); + } + return TRUE; + } + + +///////////////////////////////////////////// +////////////// TEXT and FONTS +///////////////////////////////////////////// + + + /*! + * Controls the line spacing of multi-line labels. + * For GD text, this is the number of pixels between lines. + * For TTF text, it controls line spacing in proportion to the normal + * spacing defined by the font. + */ + function SetLineSpacing($which_spc) + { + $this->line_spacing = $which_spc; + return TRUE; + } + + + /*! + * Select the default font type to use. + * $which_ttf : True to default to TrueType, False to default to GD (fixed) fonts. + * This also resets all font settings to the defaults. + */ + function SetUseTTF($which_ttf) + { + $this->use_ttf = $which_ttf; + return $this->SetDefaultFonts(); + } + + /*! + * Sets the directory name to look into for TrueType fonts. + */ + function SetTTFPath($which_path) + { + // Maybe someone needs really dynamic config. He'll need this: + // clearstatcache(); + + if (is_dir($which_path) && is_readable($which_path)) { + $this->ttf_path = $which_path; + return TRUE; + } + return $this->PrintError("SetTTFPath(): $which_path is not a valid path."); + } + + /*! + * Sets the default TrueType font and updates all fonts to that. + * The default font might be a full path, or relative to the TTFPath, + * so let SetFont check that it exists. + * Side effects: Enables use of TrueType fonts as the default font type, + * and resets all font settings. + */ + function SetDefaultTTFont($which_font) + { + $this->default_ttfont = $which_font; + return $this->SetUseTTF(TRUE); + } + + /*! + * Sets fonts to their defaults + */ + protected function SetDefaultFonts() + { + // TTF: + if ($this->use_ttf) { + return $this->SetFont('generic', '', 8) + && $this->SetFont('title', '', 14) + && $this->SetFont('legend', '', 8) + && $this->SetFont('x_label', '', 6) + && $this->SetFont('y_label', '', 6) + && $this->SetFont('x_title', '', 10) + && $this->SetFont('y_title', '', 10); + } + // Fixed GD Fonts: + return $this->SetFont('generic', 2) + && $this->SetFont('title', 5) + && $this->SetFont('legend', 2) + && $this->SetFont('x_label', 1) + && $this->SetFont('y_label', 1) + && $this->SetFont('x_title', 3) + && $this->SetFont('y_title', 3); + } + + /* + * Select a fixed (GD) font for an element. + * This allows using a fixed font, even with SetUseTTF(True). + * $which_elem : The element whose font is to be changed. + * One of: title legend generic x_label y_label x_title y_title + * $which_font : A GD font number 1-5 + * $which_spacing (optional) : Line spacing factor + */ + function SetFontGD($which_elem, $which_font, $which_spacing = NULL) + { + if ($which_font < 1 || 5 < $which_font) { + return $this->PrintError(__FUNCTION__ . ': Font size must be 1, 2, 3, 4 or 5'); + } + if (!$this->CheckOption($which_elem, + 'generic, title, legend, x_label, y_label, x_title, y_title', + __FUNCTION__)) { + return FALSE; + } + + # Store the font parameters: name/size, char cell height and width. + $this->fonts[$which_elem] = array('ttf' => FALSE, + 'font' => $which_font, + 'height' => ImageFontHeight($which_font), + 'width' => ImageFontWidth($which_font), + 'line_spacing' => $which_spacing); + return TRUE; + } + + /* + * Select a TrueType font for an element. + * This allows using a TrueType font, even with SetUseTTF(False). + * $which_elem : The element whose font is to be changed. + * One of: title legend generic x_label y_label x_title y_title + * $which_font : A TrueType font filename or pathname. + * $which_size : Font point size. + * $which_spacing (optional) : Line spacing factor + */ + function SetFontTTF($which_elem, $which_font, $which_size = 12, $which_spacing = NULL) + { + if (!$this->CheckOption($which_elem, + 'generic, title, legend, x_label, y_label, x_title, y_title', + __FUNCTION__)) { + return FALSE; + } + + # Empty font name means use the default font. + if (empty($which_font)) + $which_font = $this->default_ttfont; + $path = $which_font; + + # First try the font name directly, if not then try with path. + if (!is_file($path) || !is_readable($path)) { + $path = $this->ttf_path . DIRECTORY_SEPARATOR . $which_font; + if (!is_file($path) || !is_readable($path)) { + return $this->PrintError(__FUNCTION__ . ": Can't find TrueType font $which_font"); + } + } + + # Calculate the font height and inherent line spacing. TrueType fonts have this information + # internally, but PHP/GD has no way to directly access it. So get the bounding box size of + # an upper-case character without descenders, and the baseline-to-baseline height. + # Note: In practice, $which_size = $height, maybe +/-1 . But which_size is in points, + # and height is in pixels, and someday GD may be able to tell the difference. + # The character width is saved too, but not used by the normal text drawing routines - it + # isn't necessarily a fixed-space font. It is used in DrawLegend. + $bbox = ImageTTFBBox($which_size, 0, $path, "E"); + $height = $bbox[1] - $bbox[5]; + $width = $bbox[2] - $bbox[0]; + $bbox = ImageTTFBBox($which_size, 0, $path, "E\nE"); + $spacing = $bbox[1] - $bbox[5] - 2 * $height; + + # Store the font parameters: + $this->fonts[$which_elem] = array('ttf' => TRUE, + 'font' => $path, + 'size' => $which_size, + 'height' => $height, + 'width' => $width, + 'spacing' => $spacing, + 'line_spacing' => $which_spacing); + return TRUE; + } + + + /* + * Select Fixed/TrueType font for an element. Which type of font is + * selected depends on the $use_ttf class variable (see SetUseTTF()). + * Before PHPlot supported mixing font types, only this function and + * SetUseTTF were available to select an overall font type, but now + * SetFontGD() and SetFontTTF() can be used for mixing font types. + * $which_elem : The element whose font is to be changed. + * One of: title legend generic x_label y_label x_title y_title + * $which_font : A number 1-5 for fixed fonts, or a TrueType font. + * $which_size : Ignored for Fixed fonts, point size for TrueType. + * $which_spacing (optional) : Line spacing factor + */ + function SetFont($which_elem, $which_font, $which_size = 12, $line_spacing = NULL) + { + if ($this->use_ttf) + return $this->SetFontTTF($which_elem, $which_font, $which_size, $line_spacing); + return $this->SetFontGD($which_elem, $which_font, $line_spacing); + } + + /* + * Return the inter-line spacing for a font. + * This is an internal function, used by ProcessText* and DrawLegend. + * $font : A font array variable. + * Returns: Spacing, in pixels, between text lines. + */ + protected function GetLineSpacing($font) + { + # Use the per-font line spacing preference, if set, else the global value: + if (isset($font['line_spacing'])) + $line_spacing = $font['line_spacing']; + else + $line_spacing = $this->line_spacing; + + # For GD fonts, that is the spacing in pixels. + # For TTF, adjust based on the 'natural' font spacing (see SetFontTTF): + if ($font['ttf']) { + $line_spacing = (int)($line_spacing * $font['spacing'] / 6.0); + } + return $line_spacing; + } + + /*! + * Text drawing and sizing functions: + * ProcessText is meant for use only by DrawText and SizeText. + * ProcessText(True, ...) - Draw a block of text + * ProcessText(False, ...) - Just return ($width, $height) of + * the orthogonal bounding box containing the text. + * ProcessText is further split into separate functions for GD and TTF + * text, due to the size of the code. + * + * Horizontal and vertical alignment are relative to the drawing. That is: + * vertical text (90 deg) gets centered along Y position with + * v_align = 'center', and adjusted to the right of X position with + * h_align = 'right'. Another way to look at this is to say + * that text rotation happens first, then alignment. + * + * Original multiple lines code submitted by Remi Ricard. + * Original vertical code submitted by Marlin Viss. + * + * Text routines rewritten by ljb to fix alignment and position problems. + * Here is my explanation and notes. More information and pictures will be + * placed in the PHPlot Reference Manual. + * + * + Process TTF text one line at a time, not as a block. (See below) + * + Flipped top vs bottom vertical alignment. The usual interpretation + * is: bottom align means bottom of the text is at the specified Y + * coordinate. For some reason, PHPlot did left/right the correct way, + * but had top/bottom reversed. I fixed it, and left the default valign + * argument as bottom, but the meaning of the default value changed. + * + * For GD font text, only single-line text is handled by GD, and the + * basepoint is the upper left corner of each text line. + * For TTF text, multi-line text could be handled by GD, with the text + * basepoint at the lower left corner of the first line of text. + * (Behavior of TTF drawing routines on multi-line text is not documented.) + * But you cannot do left/center/right alignment on each line that way, + * or proper line spacing. + * Therefore, for either text type, we have to break up the text into + * lines and position each line independently. + * + * There are 9 alignment modes: Horizontal = left, center, or right, and + * Vertical = top, center, or bottom. Alignment is interpreted relative to + * the image, not as the text is read. This makes sense when you consider + * for example X axis labels. They need to be centered below the marks + * (center, top alignment) regardless of the text angle. + * 'Bottom' alignment really means baseline alignment. + * + * GD font text is supported (by libgd) at 0 degrees and 90 degrees only. + * Multi-line or single line text works with any of the 9 alignment modes. + * + * TTF text can be at any angle. The 9 alignment modes work for all angles, + * but the results might not be what you expect for multi-line text. See + * the PHPlot Reference Manual for pictures and details. In short, alignment + * applies to the orthogonal (aligned with X and Y axes) bounding box that + * contains the text, and to each line in the multi-line text box. Since + * alignment is relative to the image, 45 degree multi-line text aligns + * differently from 46 degree text. + * + * Note that PHPlot allows multi-line text for the 3 titles, and they + * are only drawn at 0 degrees (main and X titles) or 90 degrees (Y title). + * Data labels can also be multi-line, and they can be drawn at any angle. + * -ljb 2007-11-03 + * + */ + + /* + * ProcessTextGD() - Draw or size GD fixed-font text. + * This is intended for use only by ProcessText(). + * $draw_it : True to draw the text, False to just return the orthogonal width and height. + * $font : PHPlot font array (with 'ttf' = False) - see SetFontGD() + * $angle : Text angle in degrees. GD only supports 0 and 90. We treat >= 45 as 90, else 0. + * $x, $y : Reference point for the text (ignored if !$draw_it) + * $color : GD color index to use for drawing the text (ignored if !$draw_it) + * $text : The text to draw or size. Put a newline between lines. + * $h_factor : Horizontal alignment factor: 0(left), .5(center), or 1(right) (ignored if !$draw_it) + * $v_factor : Vertical alignment factor: 0(top), .5(center), or 1(bottom) (ignored if !$draw_it) + * Returns: True, if drawing text, or an array of ($width, $height) if not. + */ + protected function ProcessTextGD($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor) + { + # Extract font parameters: + $font_number = $font['font']; + $font_width = $font['width']; + $font_height = $font['height']; + $line_spacing = $this->GetLineSpacing($font); + + # Break up the text into lines, trim whitespace, find longest line. + # Save the lines and length for drawing below. + $longest = 0; + foreach (explode("\n", $text) as $each_line) { + $lines[] = $line = trim($each_line); + $line_lens[] = $line_len = strlen($line); + if ($line_len > $longest) $longest = $line_len; + } + $n_lines = count($lines); + + # Width, height are based on font size and longest line, line count respectively. + # These are relative to the text angle. + $total_width = $longest * $font_width; + $total_height = $n_lines * $font_height + ($n_lines - 1) * $line_spacing; + + if (!$draw_it) { + if ($angle < 45) return array($total_width, $total_height); + return array($total_height, $total_width); + } + + $interline_step = $font_height + $line_spacing; // Line-to-line step + + if ($angle >= 45) { + // Vertical text (90 degrees): + // (Remember the alignment convention with vertical text) + // For 90 degree text, alignment factors change like this: + $temp = $v_factor; + $v_factor = $h_factor; + $h_factor = 1 - $temp; + + $draw_func = 'ImageStringUp'; + + // Rotation matrix "R" for 90 degrees (with Y pointing down): + $r00 = 0; $r01 = 1; + $r10 = -1; $r11 = 0; + + } else { + // Horizontal text (0 degrees): + $draw_func = 'ImageString'; + + // Rotation matrix "R" for 0 degrees: + $r00 = 1; $r01 = 0; + $r10 = 0; $r11 = 1; + } + + // Adjust for vertical alignment (horizontal text) or horizontal alignment (vertical text): + $factor = (int)($total_height * $v_factor); + $xpos = $x - $r01 * $factor; + $ypos = $y - $r11 * $factor; + + # Debug callback provides the bounding box: + if ($this->GetCallback('debug_textbox')) { + if ($angle >= 45) { + $bbox_width = $total_height; + $bbox_height = $total_width; + $px = $xpos; + $py = $ypos - (1 - $h_factor) * $total_width; + } else { + $bbox_width = $total_width; + $bbox_height = $total_height; + $px = $xpos - $h_factor * $total_width; + $py = $ypos; + } + $this->DoCallback('debug_textbox', $px, $py, $bbox_width, $bbox_height); + } + + for ($i = 0; $i < $n_lines; $i++) { + + // Adjust for alignment of this line within the text block: + $factor = (int)($line_lens[$i] * $font_width * $h_factor); + $x = $xpos - $r00 * $factor; + $y = $ypos - $r10 * $factor; + + // Call ImageString or ImageStringUp: + $draw_func($this->img, $font_number, $x, $y, $lines[$i], $color); + + // Step to the next line of text. This is a rotation of (x=0, y=interline_spacing) + $xpos += $r01 * $interline_step; + $ypos += $r11 * $interline_step; + } + return TRUE; + } + + + /* + * ProcessTextTTF() - Draw or size TTF text. + * This is intended for use only by ProcessText(). + * $draw_it : True to draw the text, False to just return the orthogonal width and height. + * $font : PHPlot font array (with 'ttf' = True) - see SetFontTTF() + * $angle : Text angle in degrees. + * $x, $y : Reference point for the text (ignored if !$draw_it) + * $color : GD color index to use for drawing the text (ignored if !$draw_it) + * $text : The text to draw or size. Put a newline between lines. + * $h_factor : Horizontal alignment factor: 0(left), .5(center), or 1(right) (ignored if !$draw_it) + * $v_factor : Vertical alignment factor: 0(top), .5(center), or 1(bottom) (ignored if !$draw_it) + * Returns: True, if drawing text, or an array of ($width, $height) if not. + */ + protected function ProcessTextTTF($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor) + { + # Extract font parameters (see SetFontTTF): + $font_file = $font['font']; + $font_size = $font['size']; + $font_height = $font['height']; + $line_spacing = $this->GetLineSpacing($font); + + # Break up the text into lines, trim whitespace. + # Calculate the total width and height of the text box at 0 degrees. + # Save the trimmed lines and their widths for later when drawing. + # To get uniform spacing, don't use the actual line heights. + # Total height = Font-specific line heights plus inter-line spacing. + # Total width = width of widest line. + # Last Line Descent is the offset from the bottom to the text baseline. + # Note: For some reason, ImageTTFBBox uses (-1,-1) as the reference point. + # So 1+bbox[1] is the baseline to bottom distance. + $total_width = 0; + $lastline_descent = 0; + foreach (explode("\n", $text) as $each_line) { + $lines[] = $line = trim($each_line); + $bbox = ImageTTFBBox($font_size, 0, $font_file, $line); + $line_widths[] = $width = $bbox[2] - $bbox[0]; + if ($width > $total_width) $total_width = $width; + $lastline_descent = 1 + $bbox[1]; + } + $n_lines = count($lines); + $total_height = $n_lines * $font_height + ($n_lines - 1) * $line_spacing; + + # Calculate the rotation matrix for the text's angle. Remember that GD points Y down, + # so the sin() terms change sign. + $theta = deg2rad($angle); + $cos_t = cos($theta); + $sin_t = sin($theta); + $r00 = $cos_t; $r01 = $sin_t; + $r10 = -$sin_t; $r11 = $cos_t; + + # Make a bounding box of the right size, with upper left corner at (0,0). + # By convention, the point order is: LL, LR, UR, UL. + # Note this is still working with the text at 0 degrees. + # When sizing text (SizeText), use the overall size with descenders. + # This tells the caller how much room to leave for the text. + # When drawing text (DrawText), use the size without descenders - that + # is, down to the baseline. This is for accurate positioning. + $b[0] = 0; + if ($draw_it) { + $b[1] = $total_height; + } else { + $b[1] = $total_height + $lastline_descent; + } + $b[2] = $total_width; $b[3] = $b[1]; + $b[4] = $total_width; $b[5] = 0; + $b[6] = 0; $b[7] = 0; + + # Rotate the bounding box, then offset to the reference point: + for ($i = 0; $i < 8; $i += 2) { + $x_b = $b[$i]; + $y_b = $b[$i+1]; + $c[$i] = $x + $r00 * $x_b + $r01 * $y_b; + $c[$i+1] = $y + $r10 * $x_b + $r11 * $y_b; + } + + # Get an orthogonal (aligned with X and Y axes) bounding box around it, by + # finding the min and max X and Y: + $bbox_ref_x = $bbox_max_x = $c[0]; + $bbox_ref_y = $bbox_max_y = $c[1]; + for ($i = 2; $i < 8; $i += 2) { + $x_b = $c[$i]; + if ($x_b < $bbox_ref_x) $bbox_ref_x = $x_b; + elseif ($bbox_max_x < $x_b) $bbox_max_x = $x_b; + $y_b = $c[$i+1]; + if ($y_b < $bbox_ref_y) $bbox_ref_y = $y_b; + elseif ($bbox_max_y < $y_b) $bbox_max_y = $y_b; + } + $bbox_width = $bbox_max_x - $bbox_ref_x; + $bbox_height = $bbox_max_y - $bbox_ref_y; + + if (!$draw_it) { + # Return the bounding box, rounded up (so it always contains the text): + return array((int)ceil($bbox_width), (int)ceil($bbox_height)); + } + + $interline_step = $font_height + $line_spacing; // Line-to-line step + + # Calculate the offsets from the supplied reference point to the + # upper-left corner of the text. + # Start at the reference point at the upper left corner of the bounding + # box (bbox_ref_x, bbox_ref_y) then adjust it for the 9 point alignment. + # h,v_factor are 0,0 for top,left, .5,.5 for center,center, 1,1 for bottom,right. + # $off_x = $bbox_ref_x + $bbox_width * $h_factor - $x; + # $off_y = $bbox_ref_y + $bbox_height * $v_factor - $y; + # Then use that offset to calculate back to the supplied reference point x, y + # to get the text base point. + # $qx = $x - $off_x; + # $qy = $y - $off_y; + # Reduces to: + $qx = 2 * $x - $bbox_ref_x - $bbox_width * $h_factor; + $qy = 2 * $y - $bbox_ref_y - $bbox_height * $v_factor; + + # Check for debug callback. Don't calculate bounding box unless it is wanted. + if ($this->GetCallback('debug_textbox')) { + # Calculate the orthogonal bounding box coordinates for debug testing. + + # qx, qy is upper left corner relative to the text. + # Calculate px,py: upper left corner (absolute) of the bounding box. + # There are 4 equation sets for this, depending on the quadrant: + if ($sin_t > 0) { + if ($cos_t > 0) { + # Quadrant: 0d - 90d: + $px = $qx; $py = $qy - $total_width * $sin_t; + } else { + # Quadrant: 90d - 180d: + $px = $qx + $total_width * $cos_t; $py = $qy - $bbox_height; + } + } else { + if ($cos_t < 0) { + # Quadrant: 180d - 270d: + $px = $qx - $bbox_width; $py = $qy + $total_height * $cos_t; + } else { + # Quadrant: 270d - 360d: + $px = $qx + $total_height * $sin_t; $py = $qy; + } + } + $this->DoCallback('debug_textbox', $px, $py, $bbox_width, $bbox_height); + } + + # Since alignment is applied after rotation, which parameter is used + # to control alignment of each line within the text box varies with + # the angle. + # Angle (degrees): Line alignment controlled by: + # -45 < angle <= 45 h_align + # 45 < angle <= 135 reversed v_align + # 135 < angle <= 225 reversed h_align + # 225 < angle <= 315 v_align + if ($cos_t >= $sin_t) { + if ($cos_t >= -$sin_t) $line_align_factor = $h_factor; + else $line_align_factor = $v_factor; + } else { + if ($cos_t >= -$sin_t) $line_align_factor = 1-$v_factor; + else $line_align_factor = 1-$h_factor; + } + + # Now we have the start point, spacing and in-line alignment factor. + # We are finally ready to start drawing the text, line by line. + for ($i = 0; $i < $n_lines; $i++) { + + # For drawing TTF text, the reference point is the left edge of the + # text baseline (not the lower left corner of the bounding box). + # The following also adjusts for horizontal (relative to + # the text) alignment of the current line within the box. + # What is happening is rotation of this vector by the text angle: + # (x = (total_width - line_width) * factor, y = font_height) + + $width_factor = ($total_width - $line_widths[$i]) * $line_align_factor; + $rx = $qx + $r00 * $width_factor + $r01 * $font_height; + $ry = $qy + $r10 * $width_factor + $r11 * $font_height; + + # Finally, draw the text: + ImageTTFText($this->img, $font_size, $angle, $rx, $ry, $color, $font_file, $lines[$i]); + + # Step to position of next line. + # This is a rotation of (x=0,y=height+line_spacing) by $angle: + $qx += $r01 * $interline_step; + $qy += $r11 * $interline_step; + } + return True; + } + + /* + * ProcessText() - Wrapper for ProcessTextTTF() and ProcessTextGD(). See notes above. + * This is intended for use from within PHPlot only, and only by DrawText() and SizeText(). + * $draw_it : True to draw the text, False to just return the orthogonal width and height. + * $font : PHPlot font array, or NULL or empty string to use 'generic' + * $angle : Text angle in degrees + * $x, $y : Reference point for the text (ignored if !$draw_it) + * $color : GD color index to use for drawing the text (ignored if !$draw_it) + * $text : The text to draw or size. Put a newline between lines. + * $halign : Horizontal alignment: left, center, or right (ignored if !$draw_it) + * $valign : Vertical alignment: top, center, or bottom (ignored if !$draw_it) + * Note: Alignment is relative to the image, not the text. + * Returns: True, if drawing text, or an array of ($width, $height) if not. + */ + protected function ProcessText($draw_it, $font, $angle, $x, $y, $color, $text, $halign, $valign) + { + # Empty text case: + if ($text === '') { + if ($draw_it) return TRUE; + return array(0, 0); + } + + # Calculate width and height offset factors using the alignment args: + if ($valign == 'top') $v_factor = 0; + elseif ($valign == 'center') $v_factor = 0.5; + else $v_factor = 1.0; # 'bottom' + if ($halign == 'left') $h_factor = 0; + elseif ($halign == 'center') $h_factor = 0.5; + else $h_factor = 1.0; # 'right' + + # Apply a default font. This is mostly for external (callback) users. + if (empty($font)) $font = $this->fonts['generic']; + + if ($font['ttf']) { + return $this->ProcessTextTTF($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor); + } + return $this->ProcessTextGD($draw_it, $font, $angle, $x, $y, $color, $text, $h_factor, $v_factor); + } + + + /* + * Draws a block of text. See comments above before ProcessText(). + * $which_font : PHPlot font array, or NULL or empty string to use 'generic' + * $which_angle : Text angle in degrees + * $which_xpos, $which_ypos: Reference point for the text + * $which_color : GD color index to use for drawing the text + * $which_text : The text to draw, with newlines (\n) between lines. + * $which_halign : Horizontal (relative to the image) alignment: left, center, or right. + * $which_valign : Vertical (relative to the image) alignment: top, center, or bottom. + */ + function DrawText($which_font, $which_angle, $which_xpos, $which_ypos, $which_color, $which_text, + $which_halign = 'left', $which_valign = 'bottom') + { + return $this->ProcessText(True, + $which_font, $which_angle, $which_xpos, $which_ypos, + $which_color, $which_text, $which_halign, $which_valign); + } + + /* + * Returns the size of block of text. This is the orthogonal width and height of a bounding + * box aligned with the X and Y axes of the text. Only for angle=0 is this the actual + * width and height of the text block, but for any angle it is the amount of space needed + * to contain the text. + * $which_font : PHPlot font array, or NULL or empty string to use 'generic' + * $which_angle : Text angle in degrees + * $which_text : The text to draw, with newlines (\n) between lines. + * Returns a two element array with: $width, $height. + * This is just a wrapper for ProcessText() - see above. + */ + function SizeText($which_font, $which_angle, $which_text) + { + // Color, position, and alignment are not used when calculating the size. + return $this->ProcessText(False, + $which_font, $which_angle, 0, 0, 1, $which_text, '', ''); + } + + +///////////////////////////////////////////// +/////////// INPUT / OUTPUT CONTROL +///////////////////////////////////////////// + + /*! + * Sets output file format. + */ + function SetFileFormat($format) + { + $asked = $this->CheckOption($format, 'jpg, png, gif, wbmp', __FUNCTION__); + if (!$asked) return False; + switch ($asked) { + case 'jpg': + $format_test = IMG_JPG; + break; + case 'png': + $format_test = IMG_PNG; + break; + case 'gif': + $format_test = IMG_GIF; + break; + case 'wbmp': + $format_test = IMG_WBMP; + break; + } + if (!(imagetypes() & $format_test)) { + return $this->PrintError("SetFileFormat(): File format '$format' not supported"); + } + $this->file_format = $asked; + return TRUE; + } + + + /*! + * Selects an input file to be used as graph background and scales or tiles this image + * to fit the sizes. + * \param input_file string Path to the file to be used (jpeg, png and gif accepted) + * \param mode string 'centeredtile', 'tile', 'scale' (the image to the graph's size) + */ + function SetBgImage($input_file, $mode='centeredtile') + { + $this->bgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__); + $this->bgimg = $input_file; + return (boolean)$this->bgmode; + } + + /*! + * Selects an input file to be used as plot area background and scales or tiles this image + * to fit the sizes. + * \param input_file string Path to the file to be used (jpeg, png and gif accepted) + * \param mode string 'centeredtile', 'tile', 'scale' (the image to the graph's size) + */ + function SetPlotAreaBgImage($input_file, $mode='tile') + { + $this->plotbgmode = $this->CheckOption($mode, 'tile, centeredtile, scale', __FUNCTION__); + $this->plotbgimg = $input_file; + return (boolean)$this->plotbgmode; + } + + + /*! + * Sets the name of the file to be used as output file. + */ + function SetOutputFile($which_output_file) + { + $this->output_file = $which_output_file; + return TRUE; + } + + /*! + * Sets the output image as 'inline', that is: no Content-Type headers are sent + * to the browser. Needed if you want to embed the images. + */ + function SetIsInline($which_ii) + { + $this->is_inline = (bool)$which_ii; + return TRUE; + } + + + /*! + * Performs the actual outputting of the generated graph. + */ + function PrintImage() + { + // Browser cache stuff submitted by Thiemo Nagel + if ( (! $this->browser_cache) && (! $this->is_inline)) { + header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT'); + header('Cache-Control: no-cache, must-revalidate'); + header('Pragma: no-cache'); + } + + switch($this->file_format) { + case 'png': + if (! $this->is_inline) { + Header('Content-type: image/png'); + } + if ($this->is_inline && $this->output_file != '') { + ImagePng($this->img, $this->output_file); + } else { + ImagePng($this->img); + } + break; + case 'jpg': + if (! $this->is_inline) { + Header('Content-type: image/jpeg'); + } + if ($this->is_inline && $this->output_file != '') { + ImageJPEG($this->img, $this->output_file); + } else { + ImageJPEG($this->img); + } + break; + case 'gif': + if (! $this->is_inline) { + Header('Content-type: image/gif'); + } + if ($this->is_inline && $this->output_file != '') { + ImageGIF($this->img, $this->output_file); + } else { + ImageGIF($this->img); + } + + break; + case 'wbmp': // wireless bitmap, 2 bit. + if (! $this->is_inline) { + Header('Content-type: image/wbmp'); + } + if ($this->is_inline && $this->output_file != '') { + ImageWBMP($this->img, $this->output_file); + } else { + ImageWBMP($this->img); + } + + break; + default: + return $this->PrintError('PrintImage(): Please select an image type!'); + } + return TRUE; + } + + /*! + * Error handling for 'fatal' errors: + * $error_message Text of the error message + * Standard output from PHPlot is expected to be an image file, such as + * when handling an tag browser request. So it is not permitted to + * output text to standard output. (You should have display_errors=off) + * Here is how PHPlot handles fatal errors: + * + Write the error message into an image, and output the image. + * + If no image can be output, write nothing and produce an HTTP + * error header. + * + Trigger a user-level error containing the error message. + * If no error handler was set up, the script will log the + * error and exit with non-zero status. + * + * PrintError() and DrawError() are now equivalent. Both are provided for + * compatibility. (In earlier releases, PrintError sent the message to + * stdout only, and DrawError sent it in an image only.) + * + * This function does not return, unless the calling script has set up + * an error handler which does not exit. In that case, PrintError will + * return False. But not all of PHPlot will handle this correctly, so + * it is probably a bad idea for an error handler to return. + */ + protected function PrintError($error_message) + { + // Be sure not to loop recursively, e.g. PrintError - PrintImage - PrintError. + if (isset($this->in_error)) return FALSE; + $this->in_error = TRUE; + + // Output an image containing the error message: + if (!empty($this->img)) { + $ypos = $this->image_height/2; + $xpos = $this->image_width/2; + $bgcolor = ImageColorResolve($this->img, 255, 255, 255); + $fgcolor = ImageColorResolve($this->img, 0, 0, 0); + ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height, $bgcolor); + + // Switch to built-in fonts, in case of error with TrueType fonts: + $this->SetUseTTF(FALSE); + + $this->DrawText($this->fonts['generic'], 0, $xpos, $ypos, $fgcolor, + wordwrap($error_message), 'center', 'center'); + + $this->PrintImage(); + } elseif (! $this->is_inline) { + Header('HTTP/1.0 500 Internal Server Error'); + } + trigger_error($error_message, E_USER_ERROR); + unset($this->in_error); + return FALSE; # In case error handler returns, rather than doing exit(). + } + + /*! + * Display an error message and exit. + * This is provided for backward compatibility only. Use PrintError() instead. + * $error_message Text of the error message + * $where_x, $where_y Ignored, provided for compatibility. + */ + protected function DrawError($error_message, $where_x = NULL, $where_y = NULL) + { + return $this->PrintError($error_message); + } + +///////////////////////////////////////////// +/////////// LABELS +///////////////////////////////////////////// + + + /*! + * Sets position for X labels following data points. + */ + function SetXDataLabelPos($which_xdlp) + { + $which_xdlp = $this->CheckOption($which_xdlp, 'plotdown, plotup, both, xaxis, all, none', + __FUNCTION__); + if (!$which_xdlp) return FALSE; + $this->x_data_label_pos = $which_xdlp; + + return TRUE; + } + + /* + * Sets position for Y data labels. + * For bars and stackedbars, these are labels above the bars with the Y values. + * Accepted positions are: plotin, plotstack, none. + * For horizontal bars, these are the labels along the Y axis or sides. + * Accepted positions are: plotleft, plotright, both, none. + */ + function SetYDataLabelPos($which_ydlp) + { + $which_ydlp = $this->CheckOption($which_ydlp, 'plotin, plotstack, none, plotleft, plotright, both', + __FUNCTION__); + if (!$which_ydlp) return FALSE; + $this->y_data_label_pos = $which_ydlp; + + return TRUE; + } + + /*! + * Sets position for X labels following ticks (hence grid lines) + */ + function SetXTickLabelPos($which_xtlp) + { + $which_xtlp = $this->CheckOption($which_xtlp, 'plotdown, plotup, both, xaxis, all, none', + __FUNCTION__); + if (!$which_xtlp) return FALSE; + $this->x_tick_label_pos = $which_xtlp; + + return TRUE; + } + + /*! + * Sets position for Y labels following ticks (hence grid lines) + */ + function SetYTickLabelPos($which_ytlp) + { + $this->y_tick_label_pos = $this->CheckOption($which_ytlp, 'plotleft, plotright, both, yaxis, all, none', + __FUNCTION__); + return (boolean)$this->y_tick_label_pos; + } + + /*! + * Sets type for tick and data labels on X or Y axis. This is meant for use by + * SetXLabelType and SetYLabelType, but can also be called directly. + * $mode : 'x', 'y', 'xd', or 'yd' - which type of label to configure. + * 'x' and 'y' set the type for tick labels, and the default type for data labels + * if they are not separately configured. 'xd' and 'yd' set the type for data labels. + * $args : Variable arguments, passed as an array. + * [0] = $type (required) : Label type. 'data', 'time', 'printf', or 'custom'. + * For type 'data': + * [1] = $precision (optional). Numeric precision. Can also be set by SetPrecision[XY](). + * [2] = $prefix (optional) - prefix string for labels. + * [3] = $suffix (optional) - suffix string for labels. This replaces data_units_text. + * For type 'time': + * [1] = $format for strftime (optional). Can also be set by Set[XY]TimeFormat(). + * For type 'printf': + * [1] = $format (optional) for sprintf. + * For type 'custom': + * [1] = $callback (required) - Custom function or array of (instance,method) to call. + * [2] = $argument (optional) - Pass-through argument for the formatting function. + */ + protected function SetLabelType($mode, $args) + { + if (!$this->CheckOption($mode, 'x, y, xd, yd', __FUNCTION__)) + return FALSE; + + $type = isset($args[0]) ? $args[0] : ''; + $format =& $this->label_format[$mode]; // Shorthand reference to format storage variables + switch ($type) { + case 'data': + if (isset($args[1])) + $format['precision'] = $args[1]; + elseif (!isset($format['precision'])) + $format['precision'] = 1; + $format['prefix'] = isset($args[2]) ? $args[2] : ''; + $format['suffix'] = isset($args[3]) ? $args[3] : ''; + break; + + case 'time': + if (isset($args[1])) + $format['time_format'] = $args[1]; + elseif (!isset($format['time_format'])) + $format['time_format'] = '%H:%M:%S'; + break; + + case 'printf': + if (isset($args[1])) + $format['printf_format'] = $args[1]; + elseif (!isset($format['printf_format'])) + $format['printf_format'] = '%e'; + break; + + case 'custom': + if (isset($args[1])) { + $format['custom_callback'] = $args[1]; + $format['custom_arg'] = isset($args[2]) ? $args[2] : NULL; + } else { + $type = ''; // Error, 'custom' without a function, set to no-format mode. + } + break; + + case '': + case 'title': // Retained for backwards compatibility? + break; + + default: + $this->CheckOption($type, 'data, time, printf, custom', __FUNCTION__); + $type = ''; + } + $format['type'] = $type; + return (boolean)$type; + } + + + /* + * Select label formating for X tick labels, and for X data labels + * (unless SetXDataLabelType was called). + * See SetLabelType() for details. + */ + function SetXLabelType() // Variable arguments: $type, ... + { + $args = func_get_args(); + return $this->SetLabelType('x', $args); + } + + /* + * Select label formatting for X data labels, overriding SetXLabelType. + */ + function SetXDataLabelType() // Variable arguments: $type, ... + { + $args = func_get_args(); + return $this->SetLabelType('xd', $args); + } + + /* + * Select label formating for Y tick labels, and for Y data labels + * (unless SetYDataLabelType was called). + * See SetLabelType() for details. + */ + function SetYLabelType() // Variable arguments: $type, ... + { + $args = func_get_args(); + return $this->SetLabelType('y', $args); + } + + /* + * Select label formatting for Y data labels, overriding SetYLabelType. + */ + function SetYDataLabelType() // Variable arguments: $type, ... + { + $args = func_get_args(); + return $this->SetLabelType('yd', $args); + } + + function SetXTimeFormat($which_xtf) + { + $this->label_format['x']['time_format'] = $which_xtf; + return TRUE; + } + + function SetYTimeFormat($which_ytf) + { + $this->label_format['y']['time_format'] = $which_ytf; + return TRUE; + } + + function SetNumberFormat($decimal_point, $thousands_sep) + { + $this->decimal_point = $decimal_point; + $this->thousands_sep = $thousands_sep; + return TRUE; + } + + + function SetXLabelAngle($which_xla) + { + $this->x_label_angle = $which_xla; + return TRUE; + } + + function SetYLabelAngle($which_yla) + { + $this->y_label_angle = $which_yla; + return TRUE; + } + + // If used, this sets the angle for X Data Labels only, separately from tick labels. + function SetXDataLabelAngle($which_xdla) + { + $this->x_data_label_angle = $which_xdla; + return TRUE; + } + + // Sets the angle for Y Data Labels. Unlike X Data Labels, these default to zero. + function SetYDataLabelAngle($which_ydla) + { + $this->y_data_label_angle = $which_ydla; + return TRUE; + } + + +///////////////////////////////////////////// +/////////// MISC +///////////////////////////////////////////// + + /*! + * Checks the validity of an option. + * $which_opt String to check, such as the provided value of a function argument. + * $which_acc String of accepted choices. Must be lower-case, and separated + * by exactly ', ' (comma, space). + * $which_func Name of the calling function, for error messages. + * Returns the supplied option value, downcased and trimmed, if it is valid. + * Reports an error if the supplied option is not valid. + */ + protected function CheckOption($which_opt, $which_acc, $which_func) + { + $asked = strtolower(trim($which_opt)); + + # Look for the supplied value in a comma/space separated list. + if (strpos(", $which_acc,", ", $asked,") !== False) + return $asked; + + $this->PrintError("$which_func(): '$which_opt' not in available choices: '$which_acc'."); + return NULL; + } + + /* + * Checks the validity of an array of options. + * $opt Array or string to check. + * $acc String of accepted choices. Must be lower-case, and separated + * by exactly ', ' (comma, space). + * $func Name of the calling function, for error messages. + * Returns a array option value(s), downcased and trimmed, if all entries in $opt are valid. + * Reports an error if any supplied option is not valid. Returns NULL if the error handler returns. + */ + protected function CheckOptionArray($opt, $acc, $func) + { + $opt_array = (array)$opt; + $result = array(); + foreach ($opt_array as $option) { + $choice = $this->CheckOption($option, $acc, $func); + if (is_null($choice)) return NULL; // In case CheckOption error handler returns + $result[] = $choice; + } + return $result; + } + + /* + * Check compatibility of a plot type and data type. + * This is called by the plot-type-specific drawing functions. + * $valid_types String of supported data types. Multiple values must be + * separated by exactly ', ' (comma, space). + * Returns True if the type is valid for this plot. + * Reports an error if the data type is not value. If the error is handled and + * the handler returns, this returns False. + */ + protected function CheckDataType($valid_types) + { + if (strpos(", $valid_types,", ", $this->data_type,") !== False) + return TRUE; + + $this->PrintError("Data type '$this->data_type' is not valid for '$this->plot_type' plots." + . " Supported data type(s): '$valid_types'"); + return FALSE; + } + + /* + * Decode the data type into variables used to determine how to process a data array. + * Returns a numeric-indexed array with these elements in order: + * swapped_xy (boolean) True if Y is the independent variable, X dependent (e.g. horizontal bars). + * implied_x (boolean) True if data array has an implied X value for each set of Y. + * implied_y (boolean) True if data array has an implied Y value for each set of X. + * err_bars (boolean) True if data array contains error bars values for each point. + * Note these are not independent. implied_y means swapped_xy, and implied_x means !swapped_xy. + * Also some possibilities are reserved for future use: swapped_xy && !implied_y would be a + * future swapped X/Y equivalent to data-data or data-data-error. + */ + protected function DecodeDataType() + { + $swapped_xy = ($this->data_type == 'text-data-yx'); // Only swapped type available now + $implied_x = ($this->data_type == 'text-data'); + $implied_y = ($this->data_type == 'text-data-yx'); + $err_bars = ($this->data_type == 'data-data-error'); + + return array($swapped_xy, $implied_x, $implied_y, $err_bars); + } + + /*! + * \note Submitted by Thiemo Nagel + */ + function SetBrowserCache($which_browser_cache) + { + $this->browser_cache = $which_browser_cache; + return TRUE; + } + + /*! + * Whether to show the final image or not + */ + function SetPrintImage($which_pi) + { + $this->print_image = $which_pi; + return TRUE; + } + + /*! + * Sets the graph's legend. If argument is not an array, appends it to the legend. + */ + function SetLegend($which_leg) + { + if (is_array($which_leg)) { // use array + $this->legend = $which_leg; + } elseif (! is_null($which_leg)) { // append string + $this->legend[] = $which_leg; + } else { + return $this->PrintError("SetLegend(): argument must not be null."); + } + return TRUE; + } + + /*! + * Specifies the position of the legend's upper/leftmost corner, + * in pixel (device) coordinates. + */ + function SetLegendPixels($which_x, $which_y) + { + $this->legend_x_pos = $which_x; + $this->legend_y_pos = $which_y; + // Make sure this is unset, meaning we have pixel coords: + unset($this->legend_xy_world); + + return TRUE; + } + + /*! + * Specifies the position of the legend's upper/leftmost corner, + * in world (data space) coordinates. + * Since the scale factor to convert world to pixel coordinates + * is probably not available, set a flag and defer conversion + * to later. + */ + function SetLegendWorld($which_x, $which_y) + { + $this->legend_x_pos = $which_x; + $this->legend_y_pos = $which_y; + $this->legend_xy_world = True; + + return TRUE; + } + + /* + * Set legend text alignment, color box alignment, and style options + * $text_align accepts 'left' or 'right'. + * $colorbox_align accepts 'left', 'right', 'none', or missing/empty. If missing or empty, + * the same alignment as $text_align is used. Color box is positioned first. + * $style is reserved for future use. + */ + function SetLegendStyle($text_align, $colorbox_align = '', $style = '') + { + $this->legend_text_align = $this->CheckOption($text_align, 'left, right', __FUNCTION__); + if (empty($colorbox_align)) + $this->legend_colorbox_align = $this->legend_text_align; + else + $this->legend_colorbox_align = $this->CheckOption($colorbox_align, 'left, right, none', __FUNCTION__); + return ((boolean)$this->legend_text_align && (boolean)$this->legend_colorbox_align); + } + + /* + * Set border for the plot area. + * Accepted values are: left, right, top, bottom, sides, none, full or an array of those. + */ + function SetPlotBorderType($pbt) + { + $this->plot_border_type = $this->CheckOptionArray($pbt, 'left, right, top, bottom, sides, none, full', + __FUNCTION__); + return !empty($this->plot_border_type); + } + + /* + * Set border style for the image. + * Accepted values are: raised, plain, solid, none + * 'solid' is the same as 'plain' except it fixes the color (see DrawImageBorder) + */ + function SetImageBorderType($sibt) + { + $this->image_border_type = $this->CheckOption($sibt, 'raised, plain, solid, none', __FUNCTION__); + return (boolean)$this->image_border_type; + } + + /* + * Set border width for the image. + */ + function SetImageBorderWidth($width) + { + $this->image_border_width = $width; + return TRUE; + } + + /*! + * \param dpab bool + */ + function SetDrawPlotAreaBackground($dpab) + { + $this->draw_plot_area_background = (bool)$dpab; + return TRUE; + } + + + /*! + * \param dyg bool + */ + function SetDrawYGrid($dyg) + { + $this->draw_y_grid = (bool)$dyg; + return TRUE; + } + + + /*! + * \param dxg bool + */ + function SetDrawXGrid($dxg) + { + $this->draw_x_grid = (bool)$dxg; + return TRUE; + } + + + /*! + * \param ddg bool + */ + function SetDrawDashedGrid($ddg) + { + $this->dashed_grid = (bool)$ddg; + return TRUE; + } + + + /*! + * \param dxdl bool + */ + function SetDrawXDataLabelLines($dxdl) + { + $this->draw_x_data_label_lines = (bool)$dxdl; + return TRUE; + } + + /*! + * Sets the graph's title. + * TODO: add parameter to choose title placement: left, right, centered= + */ + function SetTitle($which_title) + { + $this->title_txt = $which_title; + return TRUE; + } + + /*! + * Sets the X axis title and position. + */ + function SetXTitle($which_xtitle, $which_xpos = 'plotdown') + { + if ($which_xtitle == '') + $which_xpos = 'none'; + + $this->x_title_pos = $this->CheckOption($which_xpos, 'plotdown, plotup, both, none', __FUNCTION__); + if (!$this->x_title_pos) return FALSE; + $this->x_title_txt = $which_xtitle; + return TRUE; + } + + + /*! + * Sets the Y axis title and position. + */ + function SetYTitle($which_ytitle, $which_ypos = 'plotleft') + { + if ($which_ytitle == '') + $which_ypos = 'none'; + + $this->y_title_pos = $this->CheckOption($which_ypos, 'plotleft, plotright, both, none', __FUNCTION__); + if (!$this->y_title_pos) return FALSE; + $this->y_title_txt = $which_ytitle; + return TRUE; + } + + /*! + * Sets the size of the drop shadow for bar and pie charts. + * \param which_s int Size in pixels. + */ + function SetShading($which_s) + { + $this->shading = (int)$which_s; + return TRUE; + } + + function SetPlotType($which_pt) + { + $this->plot_type = $this->CheckOption($which_pt, 'bars, stackedbars, lines, linepoints,' + . ' area, points, pie, thinbarline, squared, stackedarea', + __FUNCTION__); + return (boolean)$this->plot_type; + } + + /*! + * Sets the position of Y axis. + * \param pos int Position in world coordinates. + */ + function SetYAxisPosition($pos) + { + $this->y_axis_position = (int)$pos; + return TRUE; + } + + /*! + * Sets the position of X axis. + * \param pos int Position in world coordinates. + */ + function SetXAxisPosition($pos) + { + $this->x_axis_position = (int)$pos; + return TRUE; + } + + + function SetXScaleType($which_xst) + { + $this->xscale_type = $this->CheckOption($which_xst, 'linear, log', __FUNCTION__); + return (boolean)$this->xscale_type; + } + + function SetYScaleType($which_yst) + { + $this->yscale_type = $this->CheckOption($which_yst, 'linear, log', __FUNCTION__); + return (boolean)$this->yscale_type; + } + + function SetPrecisionX($which_prec) + { + return $this->SetXLabelType('data', $which_prec); + } + + function SetPrecisionY($which_prec) + { + return $this->SetYLabelType('data', $which_prec); + } + + function SetErrorBarLineWidth($which_seblw) + { + $this->error_bar_line_width = $which_seblw; + return TRUE; + } + + function SetLabelScalePosition($which_blp) + { + //0 to 1 + $this->label_scale_position = $which_blp; + return TRUE; + } + + function SetErrorBarSize($which_ebs) + { + //in pixels + $this->error_bar_size = $which_ebs; + return TRUE; + } + + /*! + * Can be one of: 'tee', 'line' + */ + function SetErrorBarShape($which_ebs) + { + $this->error_bar_shape = $this->CheckOption($which_ebs, 'tee, line', __FUNCTION__); + return (boolean)$this->error_bar_shape; + } + + /* + * Synchronize the point shape and point size arrays. + * This is called just before drawing any plot that needs 'points'. + */ + protected function CheckPointParams() + { + // Make both point_shapes and point_sizes the same size, by padding the smaller. + $ps = count($this->point_sizes); + $pt = count($this->point_shapes); + + if ($ps < $pt) { + $this->pad_array($this->point_sizes, $pt); + $this->point_counts = $pt; + } else if ($ps > $pt) { + $this->pad_array($this->point_shapes, $ps); + $this->point_counts = $ps; + } else { + $this->point_counts = $ps; + } + + // Note: PHPlot used to check and adjust point_sizes to be an even number here, + // for all 'diamond' and 'triangle' shapes. The reason for this having been + // lost, and the current maintainer seeing no sense it doing this for only + // some shapes, the code has been removed. But see what DrawDot() does. + } + + /*! + * Sets point shape for each data set via an array. + * For a list of valid shapes, see the CheckOption call below. + * The point shape and point sizes arrays are synchronized before drawing a graph + * that uses points. See CheckPointParams() + */ + function SetPointShapes($which_pt) + { + if (is_array($which_pt)) { + // Use provided array: + $this->point_shapes = $which_pt; + } elseif (!is_null($which_pt)) { + // Make the single value into an array: + $this->point_shapes = array($which_pt); + } + + // Validate all the shapes. This list must agree with DrawDot(). + foreach ($this->point_shapes as $shape) + { + if (!$this->CheckOption($shape, 'halfline, line, plus, cross, rect, circle, dot,' + . ' diamond, triangle, trianglemid, delta, yield, star, hourglass,' + . ' bowtie, target, box, home, up, down, none', __FUNCTION__)) + return FALSE; + } + return TRUE; + } + + /*! + * Sets the point size for point plots. + * The point shape and point sizes arrays are synchronized before drawing a graph + * that uses points. See CheckPointParams() + */ + function SetPointSizes($which_ps) + { + if (is_array($which_ps)) { + // Use provided array: + $this->point_sizes = $which_ps; + } elseif (!is_null($which_ps)) { + // Make the single value into an array: + $this->point_sizes = array($which_ps); + } + return TRUE; + } + + /*! + * Tells not to draw lines for missing Y data. Only works with 'lines' and 'squared' plots. + * \param bl bool + */ + function SetDrawBrokenLines($bl) + { + $this->draw_broken_lines = (bool)$bl; + return TRUE; + } + + + /* + * Set the data type, which defines the structure of the data array + * text-data: ('label', y1, y2, y3, ...) + * text-data-single: ('label', data), for some pie charts. + * data-data: ('label', x, y1, y2, y3, ...) + * data-data-error: ('label', x1, y1, e1+, e2-, y2, e2+, e2-, y3, e3+, e3-, ...) + * text-data-yx: ('label', x1, x2, x3, ...) + */ + function SetDataType($which_dt) + { + //The next four lines are for past compatibility. + if ($which_dt == 'text-linear') { $which_dt = 'text-data'; } + elseif ($which_dt == 'linear-linear') { $which_dt = 'data-data'; } + elseif ($which_dt == 'linear-linear-error') { $which_dt = 'data-data-error'; } + elseif ($which_dt == 'text-data-pie') { $which_dt = 'text-data-single'; } + + $this->data_type = $this->CheckOption($which_dt, 'text-data, text-data-single, '. + 'data-data, data-data-error, text-data-yx', + __FUNCTION__); + return (boolean)$this->data_type; + } + + /*! + * Copy the array passed as data values. We convert to numerical indexes, for its + * use for (or while) loops, which sometimes are faster. Performance improvements + * vary from 28% in DrawLines() to 49% in DrawArea() for plot drawing functions. + */ + function SetDataValues($which_dv) + { + $this->num_data_rows = count($which_dv); + $this->total_records = 0; // Perform some useful calculations. + $this->records_per_group = 1; + for ($i = 0, $recs = 0; $i < $this->num_data_rows; $i++) { + // Copy + $this->data[$i] = array_values($which_dv[$i]); // convert to numerical indices. + + // Compute some values + $recs = count($this->data[$i]); + $this->total_records += $recs; + + if ($recs > $this->records_per_group) + $this->records_per_group = $recs; + + $this->num_recs[$i] = $recs; + } + return TRUE; + } + + /*! + * Pad styles arrays for later use by plot drawing functions: + * This removes the need for $max_data_colors, etc. and $color_index = $color_index % $max_data_colors + * in DrawBars(), DrawLines(), etc. + */ + protected function PadArrays() + { + $this->pad_array($this->line_widths, $this->records_per_group); + $this->pad_array($this->line_styles, $this->records_per_group); + + $this->pad_array($this->data_colors, $this->records_per_group); + $this->pad_array($this->data_border_colors, $this->records_per_group); + $this->pad_array($this->error_bar_colors, $this->records_per_group); + + $this->SetDataColors(); + $this->SetDataBorderColors(); + $this->SetErrorBarColors(); + + return TRUE; + } + + /*! + * Pads an array with itself. This only works on 0-based sequential integer indexed arrays. + * \param arr array Original array (reference), or scalar. + * \param size int Minimum size of the resulting array. + * If $arr is a scalar, it will be converted first to a single element array. + * If $arr has at least $size elements, it is unchanged. + * Otherwise, append elements of $arr to itself until it reaches $size elements. + */ + protected function pad_array(&$arr, $size) + { + if (! is_array($arr)) { + $arr = array($arr); + } + $n = count($arr); + $base = 0; + while ($n < $size) $arr[$n++] = $arr[$base++]; + } + + /* + * Format a floating-point number. + * This is like PHP's number_format, but uses class variables for separators. + * The separators will default to locale-specific values, if available. + * Note: This method should be 'protected', but is called from test script(s). + */ + function number_format($number, $decimals=0) + { + if (!isset($this->decimal_point) || !isset($this->thousands_sep)) { + // Load locale-specific values from environment, unless disabled: + if (empty($this->locale_override)) + @setlocale(LC_ALL, ''); + // Fetch locale settings: + $locale = @localeconv(); + if (!empty($locale) && isset($locale['decimal_point']) && + isset($locale['thousands_sep'])) { + $this->decimal_point = $locale['decimal_point']; + $this->thousands_sep = $locale['thousands_sep']; + } else { + // Locale information not available. + $this->decimal_point = '.'; + $this->thousands_sep = ','; + } + } + return number_format($number, $decimals, $this->decimal_point, $this->thousands_sep); + } + + /* + * Register a callback (hook) function + * reason - A pre-defined name where a callback can be defined. + * function - The name of a function to register for callback, or an instance/method + * pair in an array (see 'callbacks' in the PHP reference manual). + * arg - Optional argument to supply to the callback function when it is triggered. + * (Often called "clientData") + * Returns: True if the callback reason is valid, else False. + */ + function SetCallback($reason, $function, $arg = NULL) + { + // Use array_key_exists because valid reason keys have NULL as value. + if (!array_key_exists($reason, $this->callbacks)) + return False; + $this->callbacks[$reason] = array($function, $arg); + return True; + } + + /* + * Return the name of a function registered for callback. See SetCallBack. + * reason - A pre-defined name where a callback can be defined. + * Returns the current callback function (name or array) for the given reason, + * or False if there was no active callback or the reason is not valid. + * Note you can safely test the return value with a simple 'if', as + * no valid function name evaluates to false. + */ + function GetCallback($reason) + { + if (isset($this->callbacks[$reason])) + return $this->callbacks[$reason][0]; + return False; + } + + /* + * Un-register (remove) a function registered for callback. + * reason - A pre-defined name where a callback can be defined. + * Returns: True if it was a valid callback reason, else False. + * Note: Returns True whether or not there was a callback registered. + */ + function RemoveCallback($reason) + { + if (!array_key_exists($reason, $this->callbacks)) + return False; + $this->callbacks[$reason] = NULL; + return True; + } + + /* + * Invoke a callback, if one is registered. + * Accepts a variable number of arguments >= 1: + * reason : A string naming the callback. + * ... : Zero or more additional arguments to be passed to the + * callback function, after the passthru argument: + * callback_function($image, $passthru, ...) + * Returns: nothing. + */ + protected function DoCallback() # Note: Variable arguments + { + $args = func_get_args(); + $reason = $args[0]; + if (!isset($this->callbacks[$reason])) + return; + list($function, $args[0]) = $this->callbacks[$reason]; + array_unshift($args, $this->img); + # Now args[] looks like: img, passthru, extra args... + call_user_func_array($function, $args); + } + + +////////////////////////////////////////////////////////// +/////////// DATA ANALYSIS, SCALING AND TRANSLATION +////////////////////////////////////////////////////////// + + /* + * Analyzes the data array and calculates the minimum and maximum values. + * In this function, IV refers to the independent variable, and DV the dependent variable. + * For most plots, IV is X and DV is Y. For swapped X/Y plots, IV is Y and DV is X. + * At the end of the function, IV and DV ranges get assigned into X or Y. + * + * Plot types 'stackedbars' and 'stackedarea' are special cases, because the bars or area always + * start at 0 (although they may be clipped below the X axis, if the axis is moved up), + * absolute values are used, and the Y values in each row accumulate. + * + * This calculates min_x, max_x, min_y, and max_y. It also calculates two arrays + * data_min[] and data_max[] with per-row min and max values. These are used for + * data label lines. For normal (unswapped) data, these are the Y range for each X. + * For swapped X/Y data, they are the X range for each Y. + * + * Note: This method should be 'protected', but is called from test script(s). + */ + function FindDataLimits() + { + # Determine how to process the data array: + list($swapped_xy, $implied_x, $implied_y, $err_bars) = $this->DecodeDataType(); + // process_iv is true if the data array contains independent variable values: + $process_iv = !($implied_x || $implied_y); + + # Special case processing for certain plot types: + $process_stacked = ($this->plot_type == 'stackedbars' || $this->plot_type == 'stackedarea'); + $abs_val = ($this->plot_type == 'area' || $this->plot_type == 'pie'); + + # These need to be initialized in case there are multiple plots and + # missing data points. + $this->data_min = array(); + $this->data_max = array(); + + # Independent values are in the data array or assumed? + if ($process_iv) { + $all_iv = array(); + } else { + $all_iv = array(0, $this->num_data_rows - 1); + } + + # Process all rows of data: + for ($i = 0; $i < $this->num_data_rows; $i++) { + $n_vals = $this->num_recs[$i]; + $j = 1; # Skips label at [0] + + if ($process_iv) { + $all_iv[] = (double)$this->data[$i][$j++]; + } + + if ($process_stacked) { + $all_dv = array(0, 0); # Min (always 0) and max + } else { + $all_dv = array(); + } + while ($j < $n_vals) { + if (is_numeric($this->data[$i][$j])) { + $val = (double)$this->data[$i][$j++]; + + if ($err_bars) { + $all_dv[] = $val + (double)$this->data[$i][$j++]; + $all_dv[] = $val - (double)$this->data[$i][$j++]; + } elseif ($process_stacked) { + $all_dv[1] += abs($val); + } elseif ($abs_val) { + $all_dv[] = abs($val); + } else { + $all_dv[] = $val; + } + } else { # Missing DV value + $j++; + if ($err_bars) $j += 2; + } + } + if (!empty($all_dv)) { + $this->data_min[$i] = min($all_dv); # Store per-row DV range + $this->data_max[$i] = max($all_dv); + } + } + + if ($swapped_xy) { + // Assign min and max for swapped X/Y plots: IV=Y and DV=X + $this->min_y = min($all_iv); + $this->max_y = max($all_iv); + if (empty($this->data_min)) { # Guard against regressive case: No X at all + $this->min_x = 0; + $this->max_x = 0; + } else { + $this->min_x = min($this->data_min); # Store global X range + $this->max_x = max($this->data_max); + } + } else { + // Assign min and max for normal plots: IV=X and DV=Y + $this->min_x = min($all_iv); + $this->max_x = max($all_iv); + if (empty($this->data_min)) { # Guard against regressive case: No Y at all + $this->min_y = 0; + $this->max_y = 0; + } else { + $this->min_y = min($this->data_min); # Store global Y range + $this->max_y = max($this->data_max); + } + } + + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'min_x' => $this->min_x, 'min_y' => $this->min_y, + 'max_x' => $this->max_x, 'max_y' => $this->max_y)); + } + return TRUE; + } + + /*! + * Calculates image margins on the fly from title positions and sizes, + * and tick labels positions and sizes. + * + * A picture of the locations of elements and spacing can be found in the + * PHPlot Reference Manual. + * + * Calculates the following (class variables unless noted): + * + * Plot area margins (see note below): + * y_top_margin + * y_bot_margin + * x_left_margin + * x_right_margin + * + * Title sizes (these are now local, not class variables, since they are not used elsewhere): + * title_height : Height of main title + * x_title_height : Height of X axis title, 0 if no X title + * y_title_width : Width of Y axis title, 0 if no Y title + * + * Tick/Data label offsets, relative to plot_area: + * x_label_top_offset, x_label_bot_offset, x_label_axis_offset + * y_label_left_offset, y_label_right_offset, y_label_axis_offset + * + * Title offsets, relative to plot area: + * x_title_top_offset, x_title_bot_offset + * y_title_left_offset, y_title_left_offset + * title_offset (for main title, relative to image edge) + * + * Note: The margins are calculated, but not stored, if margins or plot area were + * set by the user with SetPlotAreaPixels or SetMarginsPixels. The margin + * calculation is mixed in with the offset variables, so it doesn't seem worth the + * trouble to separate them. + * + * If the $maximize argument is true, we use the full image size, minus safe_margin + * and main title, for the plot. This is for pie charts which have no axes or X/Y titles. + */ + protected function CalcMargins($maximize) + { + // This is the line-to-line or line-to-text spacing: + $gap = $this->safe_margin; + // Initial margin on each side takes into account a possible image border. + // For compatibility, if border is 1 or 2, don't increase the margins. + $base_margin = max($gap, $this->GetImageBorderWidth() + 3); + $this->title_offset = $base_margin; // For use in DrawTitle + + // Minimum margin on each side. This reduces the chance that the + // right-most tick label (for example) will run off the image edge + // if there are no titles on that side. + $min_margin = 2 * $gap + $base_margin; + + // Calculate the title sizes: + list($unused, $title_height) = $this->SizeText($this->fonts['title'], 0, $this->title_txt); + list($unused, $x_title_height) = $this->SizeText($this->fonts['x_title'], 0, $this->x_title_txt); + list($y_title_width, $unused) = $this->SizeText($this->fonts['y_title'], 90, $this->y_title_txt); + + // Special case for maximum area usage with no X/Y titles or labels, only main title: + if ($maximize) { + if (!isset($this->x_left_margin)) + $this->x_left_margin = $base_margin; + if (!isset($this->x_right_margin)) + $this->x_right_margin = $base_margin; + if (!isset($this->y_top_margin)) { + $this->y_top_margin = $base_margin; + if ($title_height > 0) + $this->y_top_margin += $title_height + $gap; + } + if (!isset($this->y_bot_margin)) + $this->y_bot_margin = $base_margin; + + return TRUE; + } + + // Make local variables for these. (They get used a lot and I'm tired of this, this, this.) + $x_tick_label_pos = $this->x_tick_label_pos; + $x_data_label_pos = $this->x_data_label_pos; + $x_tick_pos = $this->x_tick_pos; + $x_tick_len = $this->x_tick_length; + $y_tick_label_pos = $this->y_tick_label_pos; + $y_tick_pos = $this->y_tick_pos; + $y_tick_len = $this->y_tick_length; + $y_data_label_pos = $this->y_data_label_pos; + + // For X/Y tick and label position of 'xaxis' or 'yaxis', determine if the axis happens to be + // on an edge of a plot. If it is, we need to account for the margins there. + if ($this->x_axis_position <= $this->plot_min_y) + $x_axis_pos = 'bottom'; + elseif ($this->x_axis_position >= $this->plot_max_y) + $x_axis_pos = 'top'; + else + $x_axis_pos = 'none'; + if ($this->y_axis_position <= $this->plot_min_x) + $y_axis_pos = 'left'; + elseif ($this->y_axis_position >= $this->plot_max_x) + $y_axis_pos = 'right'; + else + $y_axis_pos = 'none'; + + // Calculate the heights for X tick and data labels, and the max (used if they are overlaid): + $x_data_label_height = ($x_data_label_pos == 'none') ? 0 : $this->CalcMaxDataLabelSize('x'); + $x_tick_label_height = ($x_tick_label_pos == 'none') ? 0 : $this->CalcMaxTickLabelSize('x'); + $x_max_label_height = max($x_data_label_height, $x_tick_label_height); + + // Calculate the space needed above and below the plot for X tick and X data labels: + + // Above the plot: + $tick_labels_above = ($x_tick_label_pos == 'plotup' || $x_tick_label_pos == 'both' + || ($x_tick_label_pos == 'xaxis' && $x_axis_pos == 'top')); + $data_labels_above = ($x_data_label_pos == 'plotup' || $x_data_label_pos == 'both'); + if ($tick_labels_above) { + if ($data_labels_above) { + $label_height_above = $x_max_label_height; + } else { + $label_height_above = $x_tick_label_height; + } + } elseif ($data_labels_above) { + $label_height_above = $x_data_label_height; + } else { + $label_height_above = 0; + } + + // Below the plot: + $tick_labels_below = ($x_tick_label_pos == 'plotdown' || $x_tick_label_pos == 'both' + || ($x_tick_label_pos == 'xaxis' && $x_axis_pos == 'bottom')); + $data_labels_below = ($x_data_label_pos == 'plotdown' || $x_data_label_pos == 'both'); + if ($tick_labels_below) { + if ($data_labels_below) { + $label_height_below = $x_max_label_height; + } else { + $label_height_below = $x_tick_label_height; + } + } elseif ($data_labels_below) { + $label_height_below = $x_data_label_height; + } else { + $label_height_below = 0; + } + + // Calculate the width for Y tick and data labels, if on, and the max: + // Note CalcMaxDataLabelSize('y') returns 0 except for swapped X/Y plots. + $y_data_label_width = ($y_data_label_pos == 'none') ? 0 : $this->CalcMaxDataLabelSize('y'); + $y_tick_label_width = ($y_tick_label_pos == 'none') ? 0 : $this->CalcMaxTickLabelSize('y'); + $y_max_label_width = max($y_data_label_width, $y_tick_label_width); + + // Calculate the space needed left and right of the plot for Y tick and Y data labels: + // (Y data labels here are for swapped X/Y plots such has horizontal bars) + + // Left of the plot: + $tick_labels_left = ($y_tick_label_pos == 'plotleft' || $y_tick_label_pos == 'both' + || ($y_tick_label_pos == 'yaxis' && $y_axis_pos == 'left')); + $data_labels_left = ($y_data_label_pos == 'plotleft' || $y_data_label_pos == 'both'); + if ($tick_labels_left) { + if ($data_labels_left) { + $label_width_left = $y_max_label_width; + } else { + $label_width_left = $y_tick_label_width; + } + } elseif ($data_labels_left) { + $label_width_left = $y_data_label_width; + } else { + $label_width_left = 0; + } + + // Right of the plot: + $tick_labels_right = ($y_tick_label_pos == 'plotright' || $y_tick_label_pos == 'both' + || ($y_tick_label_pos == 'yaxis' && $y_axis_pos == 'right')); + $data_labels_right = ($y_data_label_pos == 'plotright' || $y_data_label_pos == 'both'); + if ($tick_labels_right) { + if ($data_labels_right) { + $label_width_right = $y_max_label_width; + } else { + $label_width_right = $y_tick_label_width; + } + } elseif ($data_labels_right) { + $label_width_right = $y_data_label_width; + } else { + $label_width_right = 0; + } + + ///////// Calculate margins: + + // Calculating Top and Bottom margins: + // y_top_margin: Main title, Upper X title, X ticks and tick labels, and X data labels: + // y_bot_margin: Lower title, ticks and tick labels, and data labels: + $top_margin = $base_margin; + $bot_margin = $base_margin; + $this->x_title_top_offset = $gap; + $this->x_title_bot_offset = $gap; + + // Space for main title? + if ($title_height > 0) + $top_margin += $title_height + $gap; + + // Space for X Title? + if ($x_title_height > 0) { + $pos = $this->x_title_pos; + if ($pos == 'plotup' || $pos == 'both') + $top_margin += $x_title_height + $gap; + if ($pos == 'plotdown' || $pos == 'both') + $bot_margin += $x_title_height + $gap; + } + + // Space for X Labels above the plot? + if ($label_height_above > 0) { + $top_margin += $label_height_above + $gap; + $this->x_title_top_offset += $label_height_above + $gap; + } + + // Space for X Labels below the plot? + if ($label_height_below > 0) { + $bot_margin += $label_height_below + $gap; + $this->x_title_bot_offset += $label_height_below + $gap; + } + + // Space for X Ticks above the plot? + if ($x_tick_pos == 'plotup' || $x_tick_pos == 'both' + || ($x_tick_pos == 'xaxis' && $x_axis_pos == 'top')) { + $top_margin += $x_tick_len; + $this->x_label_top_offset = $x_tick_len + $gap; + $this->x_title_top_offset += $x_tick_len; + } else { + // No X Ticks above the plot: + $this->x_label_top_offset = $gap; + } + + // Space for X Ticks below the plot? + if ($x_tick_pos == 'plotdown' || $x_tick_pos == 'both' + || ($x_tick_pos == 'xaxis' && $x_axis_pos == 'bottom')) { + $bot_margin += $x_tick_len; + $this->x_label_bot_offset = $x_tick_len + $gap; + $this->x_title_bot_offset += $x_tick_len; + } else { + // No X Ticks below the plot: + $this->x_label_bot_offset = $gap; + } + // Label offsets for on-axis ticks: + if ($x_tick_pos == 'xaxis') { + $this->x_label_axis_offset = $x_tick_len + $gap; + } else { + $this->x_label_axis_offset = $gap; + } + + // Calculating Left and Right margins: + // x_left_margin: Left Y title, Y ticks and tick labels: + // x_right_margin: Right Y title, Y ticks and tick labels: + $left_margin = $base_margin; + $right_margin = $base_margin; + $this->y_title_left_offset = $gap; + $this->y_title_right_offset = $gap; + + // Space for Y Title? + if ($y_title_width > 0) { + $pos = $this->y_title_pos; + if ($pos == 'plotleft' || $pos == 'both') + $left_margin += $y_title_width + $gap; + if ($pos == 'plotright' || $pos == 'both') + $right_margin += $y_title_width + $gap; + } + + // Space for Y Labels left of the plot? + if ($label_width_left > 0) { + $left_margin += $label_width_left + $gap; + $this->y_title_left_offset += $label_width_left + $gap; + } + + // Space for Y Labels right of the plot? + if ($label_width_right > 0) { + $right_margin += $label_width_right + $gap; + $this->y_title_right_offset += $label_width_right + $gap; + } + + // Space for Y Ticks left of plot? + if ($y_tick_pos == 'plotleft' || $y_tick_pos == 'both' + || ($y_tick_pos == 'yaxis' && $y_axis_pos == 'left')) { + $left_margin += $y_tick_len; + $this->y_label_left_offset = $y_tick_len + $gap; + $this->y_title_left_offset += $y_tick_len; + } else { + // No Y Ticks left of plot: + $this->y_label_left_offset = $gap; + } + + // Space for Y Ticks right of plot? + if ($y_tick_pos == 'plotright' || $y_tick_pos == 'both' + || ($y_tick_pos == 'yaxis' && $y_axis_pos == 'right')) { + $right_margin += $y_tick_len; + $this->y_label_right_offset = $y_tick_len + $gap; + $this->y_title_right_offset += $y_tick_len; + } else { + // No Y Ticks right of plot: + $this->y_label_right_offset = $gap; + } + + // Label offsets for on-axis ticks: + if ($x_tick_pos == 'yaxis') { + $this->y_label_axis_offset = $y_tick_len + $gap; + } else { + $this->y_label_axis_offset = $gap; + } + + // Apply the minimum margins and store in the object. + // Do not set margins which were user-defined (see note at top of function). + if (!isset($this->y_top_margin)) + $this->y_top_margin = max($min_margin, $top_margin); + if (!isset($this->y_bot_margin)) + $this->y_bot_margin = max($min_margin, $bot_margin); + if (!isset($this->x_left_margin)) + $this->x_left_margin = max($min_margin, $left_margin); + if (!isset($this->x_right_margin)) + $this->x_right_margin = max($min_margin, $right_margin); + + if ($this->GetCallback('debug_scale')) { + // (Too bad compact() doesn't work on class member variables...) + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'label_height_above' => $label_height_above, + 'label_height_below' => $label_height_below, + 'label_width_left' => $label_width_left, + 'label_width_right' => $label_width_right, + 'x_tick_len' => $x_tick_len, + 'y_tick_len' => $y_tick_len, + 'x_left_margin' => $this->x_left_margin, + 'x_right_margin' => $this->x_right_margin, + 'y_top_margin' => $this->y_top_margin, + 'y_bot_margin' => $this->y_bot_margin, + 'x_label_top_offset' => $this->x_label_top_offset, + 'x_label_bot_offset' => $this->x_label_bot_offset, + 'y_label_left_offset' => $this->y_label_left_offset, + 'y_label_right_offset' => $this->y_label_right_offset, + 'x_title_top_offset' => $this->x_title_top_offset, + 'x_title_bot_offset' => $this->x_title_bot_offset, + 'y_title_left_offset' => $this->y_title_left_offset, + 'y_title_right_offset' => $this->y_title_right_offset)); + } + + return TRUE; + } + + /* + * Calculate the plot area (device coordinates) from the margins. + * (This used to be part of SetPlotAreaPixels.) + * The margins might come from SetMarginsPixels, SetPlotAreaPixels, + * or CalcMargins. + */ + protected function CalcPlotAreaPixels() + { + $this->plot_area = array($this->x_left_margin, $this->y_top_margin, + $this->image_width - $this->x_right_margin, + $this->image_height - $this->y_bot_margin); + $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0]; + $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1]; + + $this->DoCallback('debug_scale', __FUNCTION__, $this->plot_area); + return TRUE; + } + + + /*! + * Set the margins in pixels (left, right, top, bottom) + * This determines the plot area, equivalent to SetPlotAreaPixels(). + * Deferred calculations now occur in CalcPlotAreaPixels(). + */ + function SetMarginsPixels($which_lm = NULL, $which_rm = NULL, $which_tm = NULL, $which_bm = NULL) + { + $this->x_left_margin = $which_lm; + $this->x_right_margin = $which_rm; + $this->y_top_margin = $which_tm; + $this->y_bot_margin = $which_bm; + + return TRUE; + } + + /*! + * Sets the limits for the plot area. + * This stores the margins, not the area. That may seem odd, but + * the idea is to make SetPlotAreaPixels and SetMarginsPixels two + * ways to accomplish the same thing, and the deferred calculations + * in CalcMargins and CalcPlotAreaPixels don't need to know which + * was used. + * (x1, y1) - Upper left corner of the plot area + * (x2, y2) - Lower right corner of the plot area + */ + function SetPlotAreaPixels($x1 = NULL, $y1 = NULL, $x2 = NULL, $y2 = NULL) + { + $this->x_left_margin = $x1; + if (isset($x2)) $this->x_right_margin = $this->image_width - $x2; + else unset($this->x_right_margin); + $this->y_top_margin = $y1; + if (isset($y2)) $this->y_bot_margin = $this->image_height - $y2; + else unset($this->y_bot_margin); + + return TRUE; + } + + /* + * Calculate the World Coordinate limits of the plot area. + * This goes with SetPlotAreaWorld, but the calculations are + * deferred until the graph is being drawn. + * Uses and sets: plot_min_x, plot_max_x, plot_min_y, plot_max_y + * These can be user-supplied or NULL to auto-calculate. + * Pre-requisites: FindDataLimits() calculates min_x, max_x, min_y, max_y + * which are the limits of the data to be plotted. + * + * Note: $implied_y and $swapped_xy are currently equivalent, but in the + * future there may be a data type with swapped X/Y and explicit Y values. + * The 4 code blocks below for plot_min_x, plot_max_x, plot_min_y, and + * plot_max_y already contain logic for this case. + * The general method is this: + * If any part of the range is user-defined (via SetPlotAreaWorld), + * use the user-defined value. + * Else, if this is an implicitly-defined independent variable, + * use the fixed range of 0 to (max+1). + * Else, if this is an explicitly-defined independent variable, + * use the exact data range (min to max). + * Else, this is the dependent variable, so define a range which + * includes and exceeds the data range by a bit. + */ + protected function CalcPlotAreaWorld() + { + list($swapped_xy, $implied_x, $implied_y) = $this->DecodeDataType(); + + if (isset($this->plot_min_x) && $this->plot_min_x !== '') + $xmin = $this->plot_min_x; // Use user-provided value + elseif ($implied_x) + $xmin = 0; // Implied X starts at zero + elseif ($swapped_xy) + // If X is the dependent variable, leave some room below. + $xmin = floor($this->min_x - abs($this->min_x) * 0.1); + else + $xmin = $this->min_x; // Otherwise just start at the min data X + + if (isset($this->plot_max_x) && $this->plot_max_x !== '') + $xmax = $this->plot_max_x; // Use user-provided value + elseif ($implied_x) + $xmax = $this->max_x + 1; // Implied X ends after last value + elseif ($swapped_xy) + // If X is the dependent variable, leave some room above. + $xmax = ceil($this->max_x + abs($this->max_x) * 0.1); + else + $xmax = $this->max_x; // Otherwise just end at the max data X + + if (isset($this->plot_min_y) && $this->plot_min_y !== '') + $ymin = $this->plot_min_y; // Use user-provided value + elseif ($implied_y) + $ymin = 0; // Implied Y starts at zero + elseif ($swapped_xy) + $ymin = $this->min_y; // Start at min data Y + else + // If Y is the dependent variable, leave some room below. + $ymin = floor($this->min_y - abs($this->min_y) * 0.1); + + if (isset($this->plot_max_y) && $this->plot_max_y !== '') + $ymax = $this->plot_max_y; // Use user-provided value + elseif ($implied_y) + $ymax = $this->max_y + 1; // Implied Y ends after last value + elseif ($swapped_xy) + $ymax = $this->max_y; // End at max data Y + else + // If Y is the dependent variable, leave some room above. + $ymax = ceil($this->max_y + abs($this->max_y) * 0.1); + + // Error checking + + if ($ymin == $ymax) + $ymax++; + if ($xmin == $xmax) + $xmax++; + + if ($this->yscale_type == 'log') { + if ($ymin <= 0) { + $ymin = 1; + } + if ($ymax <= 0) { + // Note: Error messages reference the user function, not this function. + return $this->PrintError('SetPlotAreaWorld(): Log plots need data greater than 0'); + } + } + + if ($ymax <= $ymin) { + return $this->PrintError('SetPlotAreaWorld(): Error in data - max not greater than min'); + } + + $this->plot_min_x = $xmin; + $this->plot_max_x = $xmax; + $this->plot_min_y = $ymin; + $this->plot_max_y = $ymax; + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'plot_min_x' => $this->plot_min_x, 'plot_min_y' => $this->plot_min_y, + 'plot_max_x' => $this->plot_max_x, 'plot_max_y' => $this->plot_max_y)); + } + + return TRUE; + } + + /*! + * Stores the desired World Coordinate range of the plot. + * The user calls this to force one or more of the range limits to + * specific values. Anything not set will be calculated in CalcPlotAreaWorld(). + */ + function SetPlotAreaWorld($xmin=NULL, $ymin=NULL, $xmax=NULL, $ymax=NULL) + { + $this->plot_min_x = $xmin; + $this->plot_max_x = $xmax; + $this->plot_min_y = $ymin; + $this->plot_max_y = $ymax; + return TRUE; + } //function SetPlotAreaWorld + + + /* + * Calculate the width (or height) of bars for bar plots. + * This calculates: + * record_bar_width : Allocated width for each bar (including gaps) + * actual_bar_width : Actual drawn width of each bar + * bar_adjust_gap : Gap on each side of each bar (0 if they touch) + * For the case $verticals=False, horizontal bars are being drawn, + * but the same variable names are used. Think of "bar_width" as being + * the width if you are standing on the Y axis looking towards positive X. + */ + protected function CalcBarWidths($verticals = True) + { + // group_width is the width of a group, including padding + if ($verticals) { + $group_width = $this->plot_area_width / $this->num_data_rows; + } else { + $group_width = $this->plot_area_height / $this->num_data_rows; + } + + // Actual number of bar spaces in the group. This includes the drawn bars, and + // 'bar_extra_space'-worth of extra bars. + // Note that 'records_per_group' includes the label, so subtract one to get + // the number of points in the group. 'stackedbars' have 1 bar space per group. + if ($this->plot_type == 'stackedbars') { + $num_spots = 1 + $this->bar_extra_space; + } else { + $num_spots = $this->records_per_group - 1 + $this->bar_extra_space; + } + + // record_bar_width is the width of each bar's allocated area. + // If bar_width_adjust=1 this is the width of the bar, otherwise + // the bar is centered inside record_bar_width. + // The equation is: + // group_frac_width * group_width = record_bar_width * num_spots + $this->record_bar_width = $this->group_frac_width * $group_width / $num_spots; + + // Note that the extra space due to group_frac_width and bar_extra_space will be + // evenly divided on each side of the group: the drawn bars are centered in the group. + + // Within each bar's allocated space, if bar_width_adjust=1 the bar fills the + // space, otherwise it is centered. + // This is the actual drawn bar width: + $this->actual_bar_width = $this->record_bar_width * $this->bar_width_adjust; + // This is the gap on each side of the bar (0 if bar_width_adjust=1): + $this->bar_adjust_gap = ($this->record_bar_width - $this->actual_bar_width) / 2; + + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'record_bar_width' => $this->record_bar_width, + 'actual_bar_width' => $this->actual_bar_width, + 'bar_adjust_gap' => $this->bar_adjust_gap)); + } + return TRUE; + } + + /* + * Calculate X and Y Axis Positions, world coordinates. + * This needs the min/max x/y range set by CalcPlotAreaWorld. + * It adjusts or sets x_axis_position and y_axis_position per the data. + * Empty string means the values need to be calculated; otherwise they + * are supplied but need to be validated against the World area. + * + * Note: This used to be in CalcTranslation, but CalcMargins needs it too. + * This does not calculate the pixel values of the axes. That happens in + * CalcTranslation, after scaling is set up (which has to happen after + * margins are set up). + */ + protected function CalcAxisPositions() + { + // If no user-provided Y axis position, default to axis on left side. + // Otherwise, make sure user-provided position is inside the plot area. + if ($this->y_axis_position === '') + $this->y_axis_position = $this->plot_min_x; + else + $this->y_axis_position = min(max($this->plot_min_x, $this->y_axis_position), $this->plot_max_x); + + // Validate user-provided X axis position, or auto-calculate if not: + if ($this->x_axis_position !== '') { + // User-provided X axis position: make sure it is inside the range. + $this->x_axis_position = min(max($this->plot_min_y, $this->x_axis_position), $this->plot_max_y); + } elseif ($this->yscale_type == 'log') { + // Always use 1 for X axis position on log scale plots. + $this->x_axis_position = 1; + } elseif ($this->plot_min_y <= 0 && 0 <= $this->plot_max_y) { + // Plot range includes Y=0, so use that for X axis: + $this->x_axis_position = 0; + } elseif ($this->plot_min_y > 0) { + // The entire Y range is > 0, so use the bottom for the X axis: + $this->x_axis_position = $this->plot_min_y; + } else { + // The entire Y range is < 0, so use the top for the X axis: + $this->x_axis_position = $this->plot_max_y; + } + + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'x_axis_position' => $this->x_axis_position, + 'y_axis_position' => $this->y_axis_position)); + } + + return TRUE; + } + + /*! + * Calculates scaling stuff... + */ + protected function CalcTranslation() + { + if ($this->plot_max_x - $this->plot_min_x == 0) { // Check for div by 0 + $this->xscale = 0; + } else { + if ($this->xscale_type == 'log') { + $this->xscale = ($this->plot_area_width)/(log10($this->plot_max_x) - log10($this->plot_min_x)); + } else { + $this->xscale = ($this->plot_area_width)/($this->plot_max_x - $this->plot_min_x); + } + } + + if ($this->plot_max_y - $this->plot_min_y == 0) { // Check for div by 0 + $this->yscale = 0; + } else { + if ($this->yscale_type == 'log') { + $this->yscale = ($this->plot_area_height)/(log10($this->plot_max_y) - log10($this->plot_min_y)); + } else { + $this->yscale = ($this->plot_area_height)/($this->plot_max_y - $this->plot_min_y); + } + } + // GD defines x = 0 at left and y = 0 at TOP so -/+ respectively + if ($this->xscale_type == 'log') { + $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * log10($this->plot_min_x) ); + } else { + $this->plot_origin_x = $this->plot_area[0] - ($this->xscale * $this->plot_min_x); + } + if ($this->yscale_type == 'log') { + $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * log10($this->plot_min_y)); + } else { + $this->plot_origin_y = $this->plot_area[3] + ($this->yscale * $this->plot_min_y); + } + + // Convert axis positions to device coordinates: + $this->y_axis_x_pixels = $this->xtr($this->y_axis_position); + $this->x_axis_y_pixels = $this->ytr($this->x_axis_position); + + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'xscale' => $this->xscale, 'yscale' => $this->yscale, + 'plot_origin_x' => $this->plot_origin_x, 'plot_origin_y' => $this->plot_origin_y, + 'y_axis_x_pixels' => $this->y_axis_x_pixels, + 'x_axis_y_pixels' => $this->x_axis_y_pixels)); + } + + return TRUE; + } // function CalcTranslation() + + + /*! + * Translate X world coordinate into pixel coordinate + * See CalcTranslation() for calculation of xscale. + */ + function xtr($x_world) + { + if ($this->xscale_type == 'log') { + $x_pixels = $this->plot_origin_x + log10($x_world) * $this->xscale ; + } else { + $x_pixels = $this->plot_origin_x + $x_world * $this->xscale ; + } + return round($x_pixels); + } + + + /*! + * Translate Y world coordinate into pixel coordinate. + * See CalcTranslation() for calculation of yscale. + */ + function ytr($y_world) + { + if ($this->yscale_type == 'log') { + //minus because GD defines y = 0 at top. doh! + $y_pixels = $this->plot_origin_y - log10($y_world) * $this->yscale ; + } else { + $y_pixels = $this->plot_origin_y - $y_world * $this->yscale ; + } + return round($y_pixels); + } + + /* A public interface to xtr and ytr. Translates (x,y) in world coordinates + * to (x,y) in device coordinates and returns them as an array. + * Usage is: list($x_pixel, $y_pixel) = $plot->GetDeviceXY($x_world, $y_world) + */ + function GetDeviceXY($x_world, $y_world) + { + if (!isset($this->xscale)) { + return $this->PrintError("GetDeviceXY() was called before translation factors were calculated"); + } + return array($this->xtr($x_world), $this->ytr($y_world)); + } + + /* + * Calculate tick parameters: Start, end, and delta values. This is used + * by both DrawXTicks() and DrawYTicks(). + * This currently uses the same simplistic method previously used by + * PHPlot (basically just range/10), but splitting this out into its + * own function is the first step in replacing the method. + * This is also used by CalcMaxTickSize() for CalcMargins(). + * + * $which : 'x' or 'y' : Which tick parameters to calculate + * + * Returns an array of 3 elements: tick_start, tick_end, tick_step + */ + protected function CalcTicks($which) + { + if ($which == 'x') { + $num_ticks = $this->num_x_ticks; + $tick_inc = $this->x_tick_inc; + $data_max = $this->plot_max_x; + $data_min = $this->plot_min_x; + $skip_lo = $this->skip_left_tick; + $skip_hi = $this->skip_right_tick; + } elseif ($which == 'y') { + $num_ticks = $this->num_y_ticks; + $tick_inc = $this->y_tick_inc; + $data_max = $this->plot_max_y; + $data_min = $this->plot_min_y; + $skip_lo = $this->skip_bottom_tick; + $skip_hi = $this->skip_top_tick; + } else { + return $this->PrintError("CalcTicks: Invalid usage ($which)"); + } + + if (!empty($tick_inc)) { + $tick_step = $tick_inc; + } elseif (!empty($num_ticks)) { + $tick_step = ($data_max - $data_min) / $num_ticks; + } else { + $tick_step = ($data_max - $data_min) / 10; + } + + // NOTE: When working with floats, because of approximations when adding $tick_step, + // the value may not quite reach the end, or may exceed it very slightly. + // So apply a "fudge" factor. + $tick_start = (double)$data_min; + $tick_end = (double)$data_max + ($data_max - $data_min) / 10000.0; + + if ($skip_lo) + $tick_start += $tick_step; + + if ($skip_hi) + $tick_end -= $tick_step; + + return array($tick_start, $tick_end, $tick_step); + } + + /* + * Calculate the size of the biggest tick label. This is used by CalcMargins(). + * For 'x' ticks, it returns the height . For 'y' ticks, it returns the width. + * This means height along Y, or width along X - not relative to the text angle. + * That is what we need to calculate the needed margin space. + * (Previous versions of PHPlot estimated this, using the maximum X or Y value, + * or maybe the longest string. That doesn't work. -10 is longer than 9, etc. + * So this gets the actual size of each label, slow as that may be. + */ + protected function CalcMaxTickLabelSize($which) + { + list($tick_start, $tick_end, $tick_step) = $this->CalcTicks($which); + + if ($which == 'x') { + $font = $this->fonts['x_label']; + $angle = $this->x_label_angle; + } elseif ($which == 'y') { + $font = $this->fonts['y_label']; + $angle = $this->y_label_angle; + } else { + return $this->PrintError("CalcMaxTickLabelSize: Invalid usage ($which)"); + } + + $max_width = 0; + $max_height = 0; + + // Loop over ticks, same as DrawXTicks and DrawYTicks: + // Avoid cumulative round-off errors from $val += $delta + $n = 0; + $tick_val = $tick_start; + while ($tick_val <= $tick_end) { + $tick_label = $this->FormatLabel($which, $tick_val); + list($width, $height) = $this->SizeText($font, $angle, $tick_label); + if ($width > $max_width) $max_width = $width; + if ($height > $max_height) $max_height = $height; + $tick_val = $tick_start + ++$n * $tick_step; + } + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'which' => $which, 'height' => $max_height, 'width' => $max_width)); + } + + if ($which == 'x') + return $max_height; + return $max_width; + } + + /* + * Calculate the size of the biggest data label. This is used by CalcMargins(). + * For $which='x', it returns the height of labels along the top or bottom. + * For $which='y', it returns the width of labels along the left or right sides. + * There is only one set of data labels (the first position in each data record). + * They normally go along the top or bottom (or both). If the data type indicates + * X/Y swapping (which is used for horizontal bar charts), the data labels go + * along the sides instead. So CalcMaxDataLabelSize('x') returns 0 if the + * data is X/Y swapped, and CalcMaxDataLabelSize('y') returns 0 if the data is + * is not X/Y swapped. + */ + protected function CalcMaxDataLabelSize($which = 'x') + { + list($swapped_xy) = $this->DecodeDataType(); + if ($which == 'x') { + if ($swapped_xy) + return 0; // Shortcut: labels aren't on top/bottom. + $font = $this->fonts['x_label']; + $angle = $this->x_data_label_angle; + $format_code = 'xd'; + } elseif ($which == 'y') { + if (!$swapped_xy) + return 0; // Shortcut: labels aren't on left/right. + $font = $this->fonts['y_label']; + $angle = $this->y_data_label_angle; + $format_code = 'yd'; + } else { + return $this->PrintError("CalcMaxDataLabelSize: Invalid usage ($which)"); + } + $max_width = 0; + $max_height = 0; + + // Loop over all data labels and find the biggest: + for ($i = 0; $i < $this->num_data_rows; $i++) { + $label = $this->FormatLabel($format_code, $this->data[$i][0]); + list($width, $height) = $this->SizeText($font, $angle, $label); + if ($width > $max_width) $max_width = $width; + if ($height > $max_height) $max_height = $height; + } + if ($this->GetCallback('debug_scale')) { + $this->DoCallback('debug_scale', __FUNCTION__, array( + 'height' => $max_height, 'width' => $max_width)); + } + + if ($swapped_xy) + return $max_width; + return $max_height; + } + + /* + * Set grid control defaults. + * X grid defaults off, Y grid defaults on, except the reverse is true + * with swapped graphs such as horizontal bars. + */ + protected function CalcGridSettings() + { + list($swapped_xy) = $this->DecodeDataType(); + if (!isset($this->draw_x_grid)) + $this->draw_x_grid = $swapped_xy; + if (!isset($this->draw_y_grid)) + $this->draw_y_grid = !$swapped_xy; + } + + /* + * Helper for CheckLabels() - determine if there are any non-empty labels. + * Returns True if all data labels are empty, else False. + */ + protected function CheckLabelsAllEmpty() + { + for ($i = 0; $i < $this->num_data_rows; $i++) + if ($this->data[$i][0] !== '') return False; + return True; + } + + /* + * Check and set label parameters. This handles deferred processing for label + * positioning and other label-related parameters. + * Copy label_format from 'x' to 'xd', and 'y' to 'yd', if not already set. + * Set x_data_label_angle from x_label_angle, if not already set. + * Apply defaults to X and Y tick and data label positions. + * Note: the label strings in the data array are used as X data labels in + * the normal case, but as Y data labels in the swapped X/Y case. + */ + protected function CheckLabels() + { + // The X and Y data labels are formatted the same as X and Y tick labels, + // unless overridden. Check and apply defaults for FormatLabel here: + if (empty($this->label_format['xd']) && !empty($this->label_format['x'])) + $this->label_format['xd'] = $this->label_format['x']; + if (empty($this->label_format['yd']) && !empty($this->label_format['y'])) + $this->label_format['yd'] = $this->label_format['y']; + + // The X tick label angle setting controls X data label angles too, + // unless overridden. Check and apply the default here: + if (!isset($this->x_data_label_angle)) + $this->x_data_label_angle = $this->x_label_angle; + // Note: Y data label angle defaults to zero, unlike X, + // for compatibility with older releases. + + list($swapped_xy) = $this->DecodeDataType(); // Determine if X->Y or Y->X. + + // X Label position fixups, for x_data_label_pos and x_tick_label_pos: + if ($swapped_xy) { + // Just apply defaults - there is no position conflict for X labels. + if (!isset($this->x_tick_label_pos)) + $this->x_tick_label_pos = 'plotdown'; + if (!isset($this->x_data_label_pos)) + $this->x_data_label_pos = 'none'; + } else { + // Apply defaults but do not allow conflict between tick and data labels. + if (isset($this->x_data_label_pos)) { + if (!isset($this->x_tick_label_pos)) { + // Case: data_label_pos is set, tick_label_pos needs a default: + if ($this->x_data_label_pos == 'none') + $this->x_tick_label_pos = 'plotdown'; + else + $this->x_tick_label_pos = 'none'; + } + } elseif (isset($this->x_tick_label_pos)) { + // Case: tick_label_pos is set, data_label_pos needs a default: + if ($this->x_tick_label_pos == 'none') + $this->x_data_label_pos = 'plotdown'; + else + $this->x_data_label_pos = 'none'; + } else { + // Case: Neither tick_label_pos nor data_label_pos is set. + // We do not want them to be both on (as PHPlot used to do in this case). + // Turn on data labels if any were supplied, else tick labels. + if ($this->CheckLabelsAllEmpty()) { + $this->x_data_label_pos = 'none'; + $this->x_tick_label_pos = 'plotdown'; + } else { + $this->x_data_label_pos = 'plotdown'; + $this->x_tick_label_pos = 'none'; + } + } + } + + // Y Label position fixups, for y_data_label_pos and y_tick_label_pos: + if (!$swapped_xy) { + // Just apply defaults - there is no position conflict. + if (!isset($this->y_tick_label_pos)) + $this->y_tick_label_pos = 'plotleft'; + if (!isset($this->y_data_label_pos)) + $this->y_data_label_pos = 'none'; + } else { + // Apply defaults but do not allow conflict between tick and data labels. + if (isset($this->y_data_label_pos)) { + if (!isset($this->y_tick_label_pos)) { + // Case: data_label_pos is set, tick_label_pos needs a default: + if ($this->y_data_label_pos == 'none') + $this->y_tick_label_pos = 'plotleft'; + else + $this->y_tick_label_pos = 'none'; + } + } elseif (isset($this->y_tick_label_pos)) { + // Case: tick_label_pos is set, data_label_pos needs a default: + if ($this->y_tick_label_pos == 'none') + $this->y_data_label_pos = 'plotleft'; + else + $this->y_data_label_pos = 'none'; + } else { + // Case: Neither tick_label_pos nor data_label_pos is set. + // Turn on data labels if any were supplied, else tick labels. + if ($this->CheckLabelsAllEmpty()) { + $this->y_data_label_pos = 'none'; + $this->y_tick_label_pos = 'plotleft'; + } else { + $this->y_data_label_pos = 'plotleft'; + $this->y_tick_label_pos = 'none'; + } + } + } + return TRUE; + } + + /*! + * Formats a tick or data label. + * which_pos - 'x', 'xd', 'y', or 'yd', selects formatting controls. + * x, y are for tick labels; xd, yd are for data labels. + * which_lab - String to format as a label. + * Credits: Time formatting suggested by Marlin Viss + * Custom formatting suggested by zer0x333 + * Notes: + * Type 'title' is obsolete and retained for compatibility. + * Class variable 'data_units_text' is retained as a suffix for 'data' type formatting for + * backward compatibility. Since there was never a function/method to set it, there + * could be somebody out there who sets it directly in the object. + */ + protected function FormatLabel($which_pos, $which_lab) + { + // Assign a reference shortcut to the label format controls. + // Note CheckLabels() made sure the 'xd' and 'yd' arrays are set. + $format =& $this->label_format[$which_pos]; + + // Don't format empty strings (especially as time or numbers), or if no type was set. + if ($which_lab !== '' && !empty($format['type'])) { + switch ($format['type']) { + case 'title': // Note: This is obsolete + $which_lab = @ $this->data[$which_lab][0]; + break; + case 'data': + $which_lab = $format['prefix'] + . $this->number_format($which_lab, $format['precision']) + . $this->data_units_text // Obsolete + . $format['suffix']; + break; + case 'time': + $which_lab = strftime($format['time_format'], $which_lab); + break; + case 'printf': + $which_lab = sprintf($format['printf_format'], $which_lab); + break; + case 'custom': + $which_lab = call_user_func($format['custom_callback'], $which_lab, $format['custom_arg']); + break; + + } + } + return $which_lab; + } //function FormatLabel + +///////////////////////////////////////////// +/////////////// TICKS +///////////////////////////////////////////// + + /*! + * Use either this or SetNumXTicks() to set where to place x tick marks + */ + function SetXTickIncrement($which_ti='') + { + $this->x_tick_inc = $which_ti; + if (!empty($which_ti)) { + $this->num_x_ticks = ''; //either use num_x_ticks or x_tick_inc, not both + } + return TRUE; + } + + /*! + * Use either this or SetNumYTicks() to set where to place y tick marks + */ + function SetYTickIncrement($which_ti='') + { + $this->y_tick_inc = $which_ti; + if (!empty($which_ti)) { + $this->num_y_ticks = ''; //either use num_y_ticks or y_tick_inc, not both + } + return TRUE; + } + + + function SetNumXTicks($which_nt) + { + $this->num_x_ticks = $which_nt; + if (!empty($which_nt)) { + $this->x_tick_inc = ''; //either use num_x_ticks or x_tick_inc, not both + } + return TRUE; + } + + function SetNumYTicks($which_nt) + { + $this->num_y_ticks = $which_nt; + if (!empty($which_nt)) { + $this->y_tick_inc = ''; //either use num_y_ticks or y_tick_inc, not both + } + return TRUE; + } + + /*! + * + */ + function SetYTickPos($which_tp) + { + $this->y_tick_pos = $this->CheckOption($which_tp, 'plotleft, plotright, both, yaxis, none', __FUNCTION__); + return (boolean)$this->y_tick_pos; + } + /*! + * + */ + function SetXTickPos($which_tp) + { + $this->x_tick_pos = $this->CheckOption($which_tp, 'plotdown, plotup, both, xaxis, none', __FUNCTION__); + return (boolean)$this->x_tick_pos; + } + + /*! + * \param skip bool + */ + function SetSkipTopTick($skip) + { + $this->skip_top_tick = (bool)$skip; + return TRUE; + } + + /*! + * \param skip bool + */ + function SetSkipBottomTick($skip) + { + $this->skip_bottom_tick = (bool)$skip; + return TRUE; + } + + /*! + * \param skip bool + */ + function SetSkipLeftTick($skip) + { + $this->skip_left_tick = (bool)$skip; + return TRUE; + } + + /*! + * \param skip bool + */ + function SetSkipRightTick($skip) + { + $this->skip_right_tick = (bool)$skip; + return TRUE; + } + + function SetXTickLength($which_xln) + { + $this->x_tick_length = $which_xln; + return TRUE; + } + + function SetYTickLength($which_yln) + { + $this->y_tick_length = $which_yln; + return TRUE; + } + + function SetXTickCrossing($which_xc) + { + $this->x_tick_cross = $which_xc; + return TRUE; + } + + function SetYTickCrossing($which_yc) + { + $this->y_tick_cross = $which_yc; + return TRUE; + } + + +///////////////////////////////////////////// +//////////////////// GENERIC DRAWING +///////////////////////////////////////////// + + /*! + * Fills the background. + * Note: This method should be 'protected', but is called from test script(s). + */ + function DrawBackground() + { + // Don't draw this twice if drawing two plots on one image + if (! $this->background_done) { + if (isset($this->bgimg)) { // If bgimg is defined, use it + $this->tile_img($this->bgimg, 0, 0, $this->image_width, $this->image_height, $this->bgmode); + } else { // Else use solid color + ImageFilledRectangle($this->img, 0, 0, $this->image_width, $this->image_height, + $this->ndx_bg_color); + } + $this->background_done = TRUE; + } + return TRUE; + } + + + /*! + * Fills the plot area background. + */ + protected function DrawPlotAreaBackground() + { + if (isset($this->plotbgimg)) { + $this->tile_img($this->plotbgimg, $this->plot_area[0], $this->plot_area[1], + $this->plot_area_width, $this->plot_area_height, $this->plotbgmode); + } + else { + if ($this->draw_plot_area_background) { + ImageFilledRectangle($this->img, $this->plot_area[0], $this->plot_area[1], + $this->plot_area[2], $this->plot_area[3], $this->ndx_plot_bg_color); + } + } + + return TRUE; + } + + + /* + * Tiles an image at some given coordinates. + * + * $file string Filename of the picture to be used as tile. + * $xorig int X coordinate of the plot where the tile is to begin. + * $yorig int Y coordinate of the plot where the tile is to begin. + * $width int Width of the area to be tiled. + * $height int Height of the area to be tiled. + * $mode string One of 'centeredtile', 'tile', 'scale'. + */ + protected function tile_img($file, $xorig, $yorig, $width, $height, $mode) + { + $im = $this->GetImage($file, $tile_width, $tile_height); + if (!$im) + return FALSE; // GetImage already produced an error message. + + if ($mode == 'scale') { + imagecopyresampled($this->img, $im, $xorig, $yorig, 0, 0, $width, $height, + $tile_width, $tile_height); + return TRUE; + } + + if ($mode == 'centeredtile') { + $x0 = - floor($tile_width/2); // Make the tile look better + $y0 = - floor($tile_height/2); + } else { // Accept anything else as $mode == 'tile' + $x0 = 0; + $y0 = 0; + } + + // Actually draw the tile + + // But first on a temporal image. + $tmp = imagecreate($width, $height); + if (! $tmp) + return $this->PrintError('tile_img(): Could not create image resource.'); + + for ($x = $x0; $x < $width; $x += $tile_width) + for ($y = $y0; $y < $height; $y += $tile_height) + imagecopy($tmp, $im, $x, $y, 0, 0, $tile_width, $tile_height); + + // Copy the temporal image onto the final one. + imagecopy($this->img, $tmp, $xorig, $yorig, 0,0, $width, $height); + + // Free resources + imagedestroy($tmp); + imagedestroy($im); + + return TRUE; + } // function tile_img + + + /* + * Return the image border width. + * This is used by CalcMargins() and DrawImageBorder(). + */ + protected function GetImageBorderWidth() + { + if ($this->image_border_type == 'none') + return 0; // No border + if (!empty($this->image_border_width)) + return $this->image_border_width; // Specified border width + if ($this->image_border_type == 'raised') + return 2; // Default for raised border is 2 pixels. + return 1; // Default for other border types is 1 pixel. + } + + /* + * Draws a border around the final image. + * Note: 'plain' draws a flat border using the dark shade of the border color. + * This probably should have been written to use the actual border color, but + * it is too late to fix it without changing plot appearances. Therefore a + * new type 'solid' was added to use the SetImageBorderColor color. + */ + protected function DrawImageBorder() + { + if ($this->image_border_type == 'none') + return TRUE; // Early test for default case. + $width = $this->GetImageBorderWidth(); + $color1 = $this->ndx_i_border; + $color2 = $this->ndx_i_border_dark; + $ex = $this->image_width - 1; + $ey = $this->image_height - 1; + switch ($this->image_border_type) { + case 'raised': + // Top and left lines use border color, right and bottom use the darker shade. + // Drawing order matters in the upper right and lower left corners. + for ($i = 0; $i < $width; $i++, $ex--, $ey--) { + imageline($this->img, $i, $i, $ex, $i, $color1); // Top + imageline($this->img, $ex, $i, $ex, $ey, $color2); // Right + imageline($this->img, $i, $i, $i, $ey, $color1); // Left + imageline($this->img, $i, $ey, $ex, $ey, $color2); // Bottom + } + break; + case 'plain': // See note above re colors + $color1 = $color2; + // Fall through + case 'solid': + for ($i = 0; $i < $width; $i++, $ex--, $ey--) { + imagerectangle($this->img, $i, $i, $ex, $ey, $color1); + } + break; + default: + return $this->PrintError("DrawImageBorder(): unknown image_border_type: '$this->image_border_type'"); + } + return TRUE; + } + + + /* + * Draws the main title on the graph. + * The title must not be drawn more than once (in the case of multiple plots + * on the image), because TTF text antialiasing makes it look bad. + */ + protected function DrawTitle() + { + if (isset($this->title_done) || $this->title_txt === '') + return TRUE; + + // Center of the image: + $xpos = $this->image_width / 2; + + // Place it at almost at the top + $ypos = $this->title_offset; + + $this->DrawText($this->fonts['title'], 0, $xpos, $ypos, + $this->ndx_title_color, $this->title_txt, 'center', 'top'); + + $this->title_done = TRUE; + return TRUE; + } + + + /*! + * Draws the X-Axis Title + */ + protected function DrawXTitle() + { + if ($this->x_title_pos == 'none') + return TRUE; + + // Center of the plot + $xpos = ($this->plot_area[2] + $this->plot_area[0]) / 2; + + // Upper title + if ($this->x_title_pos == 'plotup' || $this->x_title_pos == 'both') { + $ypos = $this->plot_area[1] - $this->x_title_top_offset; + $this->DrawText($this->fonts['x_title'], 0, $xpos, $ypos, $this->ndx_title_color, + $this->x_title_txt, 'center', 'bottom'); + } + // Lower title + if ($this->x_title_pos == 'plotdown' || $this->x_title_pos == 'both') { + $ypos = $this->plot_area[3] + $this->x_title_bot_offset; + $this->DrawText($this->fonts['x_title'], 0, $xpos, $ypos, $this->ndx_title_color, + $this->x_title_txt, 'center', 'top'); + } + return TRUE; + } + + /*! + * Draws the Y-Axis Title + */ + protected function DrawYTitle() + { + if ($this->y_title_pos == 'none') + return TRUE; + + // Center the title vertically to the plot area + $ypos = ($this->plot_area[3] + $this->plot_area[1]) / 2; + + if ($this->y_title_pos == 'plotleft' || $this->y_title_pos == 'both') { + $xpos = $this->plot_area[0] - $this->y_title_left_offset; + $this->DrawText($this->fonts['y_title'], 90, $xpos, $ypos, $this->ndx_title_color, + $this->y_title_txt, 'right', 'center'); + } + if ($this->y_title_pos == 'plotright' || $this->y_title_pos == 'both') { + $xpos = $this->plot_area[2] + $this->y_title_right_offset; + $this->DrawText($this->fonts['y_title'], 90, $xpos, $ypos, $this->ndx_title_color, + $this->y_title_txt, 'left', 'center'); + } + + return TRUE; + } + + + /* + * \note Horizontal grid lines overwrite horizontal axis with y=0, so call this first, then DrawXAxis() + */ + protected function DrawYAxis() + { + // Draw ticks, labels and grid, if any + $this->DrawYTicks(); + + // Draw Y axis at X = y_axis_x_pixels + ImageLine($this->img, $this->y_axis_x_pixels, $this->plot_area[1], + $this->y_axis_x_pixels, $this->plot_area[3], $this->ndx_grid_color); + + return TRUE; + } + + /* + * + */ + protected function DrawXAxis() + { + // Draw ticks, labels and grid + $this->DrawXTicks(); + + /* This tick and label tend to overlap with regular Y Axis labels, + * as Mike Pullen pointed out. + * + //Draw Tick and Label for X axis + if (! $this->skip_bottom_tick) { + $ylab =$this->FormatLabel('y', $this->x_axis_position); + $this->DrawYTick($ylab, $this->x_axis_y_pixels); + } + */ + //Draw X Axis at Y = x_axis_y_pixels + ImageLine($this->img, $this->plot_area[0]+1, $this->x_axis_y_pixels, + $this->plot_area[2]-1, $this->x_axis_y_pixels, $this->ndx_grid_color); + + return TRUE; + } + + /*! + * Draw one Y tick mark and its tick label. Called from DrawYTicks() and DrawXAxis() + */ + protected function DrawYTick($which_ylab, $which_ypix) + { + // Ticks on Y axis + if ($this->y_tick_pos == 'yaxis') { + ImageLine($this->img, $this->y_axis_x_pixels - $this->y_tick_length, $which_ypix, + $this->y_axis_x_pixels + $this->y_tick_cross, $which_ypix, $this->ndx_tick_color); + } + + // Ticks to the left of the Plot Area + if (($this->y_tick_pos == 'plotleft') || ($this->y_tick_pos == 'both') ) { + ImageLine($this->img, $this->plot_area[0] - $this->y_tick_length, $which_ypix, + $this->plot_area[0] + $this->y_tick_cross, $which_ypix, $this->ndx_tick_color); + } + + // Ticks to the right of the Plot Area + if (($this->y_tick_pos == 'plotright') || ($this->y_tick_pos == 'both') ) { + ImageLine($this->img, $this->plot_area[2] + $this->y_tick_length, $which_ypix, + $this->plot_area[2] - $this->y_tick_cross, $which_ypix, $this->ndx_tick_color); + } + + // Labels on Y axis + if ($this->y_tick_label_pos == 'yaxis') { + $this->DrawText($this->fonts['y_label'], $this->y_label_angle, + $this->y_axis_x_pixels - $this->y_label_axis_offset, $which_ypix, + $this->ndx_text_color, $which_ylab, 'right', 'center'); + } + + // Labels to the left of the plot area + if ($this->y_tick_label_pos == 'plotleft' || $this->y_tick_label_pos == 'both') { + $this->DrawText($this->fonts['y_label'], $this->y_label_angle, + $this->plot_area[0] - $this->y_label_left_offset, $which_ypix, + $this->ndx_text_color, $which_ylab, 'right', 'center'); + } + // Labels to the right of the plot area + if ($this->y_tick_label_pos == 'plotright' || $this->y_tick_label_pos == 'both') { + $this->DrawText($this->fonts['y_label'], $this->y_label_angle, + $this->plot_area[2] + $this->y_label_right_offset, $which_ypix, + $this->ndx_text_color, $which_ylab, 'left', 'center'); + } + return TRUE; + } // Function DrawYTick() + + + /*! + * Draws Grid, Ticks and Tick Labels along Y-Axis + * Ticks and ticklabels can be left of plot only, right of plot only, + * both on the left and right of plot, or crossing a user defined Y-axis + */ + protected function DrawYTicks() + { + // Sets the line style for IMG_COLOR_STYLED lines (grid) + if ($this->dashed_grid) { + $this->SetDashedStyle($this->ndx_light_grid_color); + $style = IMG_COLOR_STYLED; + } else { + $style = $this->ndx_light_grid_color; + } + + // Calculate the tick start, end, and step: + list($y_start, $y_end, $delta_y) = $this->CalcTicks('y'); + + // Loop, avoiding cumulative round-off errors from $y_tmp += $delta_y + $n = 0; + $y_tmp = $y_start; + while ($y_tmp <= $y_end) { + $ylab = $this->FormatLabel('y', $y_tmp); + $y_pixels = $this->ytr($y_tmp); + + // Horizontal grid line + if ($this->draw_y_grid) { + ImageLine($this->img, $this->plot_area[0]+1, $y_pixels, $this->plot_area[2]-1, $y_pixels, $style); + } + + // Draw tick mark(s) + $this->DrawYTick($ylab, $y_pixels); + + // Step to next Y, without accumulating error + $y_tmp = $y_start + ++$n * $delta_y; + } + return TRUE; + } // function DrawYTicks + + /*! + * Draw one X tick mark and its tick label. + */ + protected function DrawXTick($which_xlab, $which_xpix) + { + // Ticks on X axis + if ($this->x_tick_pos == 'xaxis') { + ImageLine($this->img, $which_xpix, $this->x_axis_y_pixels - $this->x_tick_cross, + $which_xpix, $this->x_axis_y_pixels + $this->x_tick_length, $this->ndx_tick_color); + } + + // Ticks on top of the Plot Area + if ($this->x_tick_pos == 'plotup' || $this->x_tick_pos == 'both') { + ImageLine($this->img, $which_xpix, $this->plot_area[1] - $this->x_tick_length, + $which_xpix, $this->plot_area[1] + $this->x_tick_cross, $this->ndx_tick_color); + } + + // Ticks on bottom of Plot Area + if ($this->x_tick_pos == 'plotdown' || $this->x_tick_pos == 'both') { + ImageLine($this->img, $which_xpix, $this->plot_area[3] + $this->x_tick_length, + $which_xpix, $this->plot_area[3] - $this->x_tick_cross, $this->ndx_tick_color); + } + + // Label on X axis + if ($this->x_tick_label_pos == 'xaxis') { + $this->DrawText($this->fonts['x_label'], $this->x_label_angle, + $which_xpix, $this->x_axis_y_pixels + $this->x_label_axis_offset, + $this->ndx_text_color, $which_xlab, 'center', 'top'); + } + + // Label on top of the Plot Area + if ($this->x_tick_label_pos == 'plotup' || $this->x_tick_label_pos == 'both') { + $this->DrawText($this->fonts['x_label'], $this->x_label_angle, + $which_xpix, $this->plot_area[1] - $this->x_label_top_offset, + $this->ndx_text_color, $which_xlab, 'center', 'bottom'); + } + + // Label on bottom of the Plot Area + if ($this->x_tick_label_pos == 'plotdown' || $this->x_tick_label_pos == 'both') { + $this->DrawText($this->fonts['x_label'], $this->x_label_angle, + $which_xpix, $this->plot_area[3] + $this->x_label_bot_offset, + $this->ndx_text_color, $which_xlab, 'center', 'top'); + } + return TRUE; + } + + /*! + * Draws Grid, Ticks and Tick Labels along X-Axis + * Ticks and tick labels can be down of plot only, up of plot only, + * both on up and down of plot, or crossing a user defined X-axis + * + * \note Original vertical code submitted by Marlin Viss + */ + protected function DrawXTicks() + { + // Sets the line style for IMG_COLOR_STYLED lines (grid) + if ($this->dashed_grid) { + $this->SetDashedStyle($this->ndx_light_grid_color); + $style = IMG_COLOR_STYLED; + } else { + $style = $this->ndx_light_grid_color; + } + + // Calculate the tick start, end, and step: + list($x_start, $x_end, $delta_x) = $this->CalcTicks('x'); + + // Loop, avoiding cumulative round-off errors from $x_tmp += $delta_x + $n = 0; + $x_tmp = $x_start; + while ($x_tmp <= $x_end) { + $xlab = $this->FormatLabel('x', $x_tmp); + $x_pixels = $this->xtr($x_tmp); + + // Vertical grid lines + if ($this->draw_x_grid) { + ImageLine($this->img, $x_pixels, $this->plot_area[1], $x_pixels, $this->plot_area[3], $style); + } + + // Draw tick mark(s) + $this->DrawXTick($xlab, $x_pixels); + + // Step to next X, without accumulating error + $x_tmp = $x_start + ++$n * $delta_x; + } + return TRUE; + } // function DrawXTicks + + + /* + * Draw a border around the plot area. See SetPlotBorderType. + * Note: SetPlotBorderType sets plot_border_type to an array, but + * it won't be an array if it defaults or is set directly (backward compatibility). + */ + protected function DrawPlotBorder() + { + $pbt = (array)$this->plot_border_type; + $sides = 0; // Bitmap: 1=left 2=top 4=right 8=bottom + $map = array('left' => 1, 'plotleft' => 1, 'right' => 4, 'plotright' => 4, 'top' => 2, + 'bottom' => 8, 'both' => 5, 'sides' => 5, 'full' => 15, 'none' => 0); + foreach ($pbt as $option) $sides |= $map[$option]; + if ($sides == 15) { // Border on all 4 sides + imagerectangle($this->img, $this->plot_area[0], $this->plot_area[1], + $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color); + } else { + if ($sides & 1) // Left + imageline($this->img, $this->plot_area[0], $this->plot_area[1], + $this->plot_area[0], $this->plot_area[3], $this->ndx_grid_color); + if ($sides & 2) // Top + imageline($this->img, $this->plot_area[0], $this->plot_area[1], + $this->plot_area[2], $this->plot_area[1], $this->ndx_grid_color); + if ($sides & 4) // Right + imageline($this->img, $this->plot_area[2], $this->plot_area[1], + $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color); + if ($sides & 8) // Bottom + imageline($this->img, $this->plot_area[0], $this->plot_area[3], + $this->plot_area[2], $this->plot_area[3], $this->ndx_grid_color); + } + return TRUE; + } + + + /*! + * Draws the data label associated with a point in the plot at specified x/y world position. + * This is currently only used for Y data labels for bar charts. + */ + protected function DrawDataLabel($which_font, $which_angle, $x_world, $y_world, $which_color, $which_text, + $which_halign = 'center', $which_valign = 'bottom', $x_adjustment=0, $y_adjustment=0) + { + $this->DrawText($which_font, $which_angle, + $this->xtr($x_world) + $x_adjustment, $this->ytr($y_world) + $y_adjustment, + $which_color, $this->FormatLabel('yd', $which_text), $which_halign, $which_valign); + + return TRUE; + } + + /*! + * Draws the data label associated with a point in the plot. + * This is different from x_labels drawn by DrawXTicks() and care + * should be taken not to draw both, as they'd probably overlap. + * Calling of this function in DrawLines(), etc is decided after x_data_label_pos value. + * Leave the last parameter out, to avoid the drawing of vertical lines, no matter + * what the setting is (for plots that need it, like DrawSquared()) + */ + protected function DrawXDataLabel($xlab, $xpos, $row=FALSE) + { + $xlab = $this->FormatLabel('xd', $xlab); + + // Labels below the plot area + if ($this->x_data_label_pos == 'plotdown' || $this->x_data_label_pos == 'both') + $this->DrawText($this->fonts['x_label'], $this->x_data_label_angle, + $xpos, $this->plot_area[3] + $this->x_label_bot_offset, + $this->ndx_text_color, $xlab, 'center', 'top'); + + // Labels above the plot area + if ($this->x_data_label_pos == 'plotup' || $this->x_data_label_pos == 'both') + $this->DrawText($this->fonts['x_label'], $this->x_data_label_angle, + $xpos, $this->plot_area[1] - $this->x_label_top_offset, + $this->ndx_text_color, $xlab, 'center', 'bottom'); + + // $row=0 means this is the first row. $row=FALSE means don't do any rows. + if ($row !== FALSE && $this->draw_x_data_label_lines) + $this->DrawXDataLine($xpos, $row); + return TRUE; + } + + /* + * Draw a data label along the Y axis or side. + * This is only used by horizontal bar charts. + */ + protected function DrawYDataLabel($ylab, $ypos) + { + $ylab = $this->FormatLabel('yd', $ylab); + + // Labels left of the plot area + if ($this->y_data_label_pos == 'plotleft' || $this->y_data_label_pos == 'both') + $this->DrawText($this->fonts['y_label'], $this->y_data_label_angle, + $this->plot_area[0] - $this->y_label_left_offset, $ypos, + $this->ndx_text_color, $ylab, 'right', 'center'); + + // Labels right of the plot area + if ($this->y_data_label_pos == 'plotright' || $this->y_data_label_pos == 'both') + $this->DrawText($this->fonts['y_label'], $this->y_data_label_angle, + $this->plot_area[2] + $this->y_label_right_offset, $ypos, + $this->ndx_text_color, $ylab, 'left', 'center'); + return TRUE; + } + + /*! + * Draws Vertical lines from data points up and down. + * Which lines are drawn depends on the value of x_data_label_pos, + * and whether this is at all done or not, on draw_x_data_label_lines + * + * \param xpos int position in pixels of the line. + * \param row int index of the data row being drawn. + */ + protected function DrawXDataLine($xpos, $row) + { + // Sets the line style for IMG_COLOR_STYLED lines (grid) + if($this->dashed_grid) { + $this->SetDashedStyle($this->ndx_light_grid_color); + $style = IMG_COLOR_STYLED; + } else { + $style = $this->ndx_light_grid_color; + } + + // Lines from the bottom up + if ($this->x_data_label_pos == 'both') { + ImageLine($this->img, $xpos, $this->plot_area[3], $xpos, $this->plot_area[1], $style); + } + // Lines from the bottom of the plot up to the max Y value at this X: + else if ($this->x_data_label_pos == 'plotdown' && isset($this->data_max[$row])) { + $ypos = $this->ytr($this->data_max[$row]); + ImageLine($this->img, $xpos, $ypos, $xpos, $this->plot_area[3], $style); + } + // Lines from the top of the plot down to the min Y value at this X: + else if ($this->x_data_label_pos == 'plotup' && isset($this->data_min[$row])) { + $ypos = $this->ytr($this->data_min[$row]); + ImageLine($this->img, $xpos, $this->plot_area[1], $xpos, $ypos, $style); + } + return TRUE; + } + + + /*! + * Draws the graph legend + * + * \note Base code submitted by Marlin Viss + */ + protected function DrawLegend() + { + $font = &$this->fonts['legend']; + + // Find maximum legend label line width. + $max_width = 0; + foreach ($this->legend as $line) { + list($width, $unused) = $this->SizeText($font, 0, $line); + if ($width > $max_width) $max_width = $width; + } + + // Use the font parameters to size the color boxes: + $char_w = $font['width']; + $char_h = $font['height']; + $line_spacing = $this->GetLineSpacing($font); + + // Normalize text alignment and colorbox alignment variables: + $text_align = isset($this->legend_text_align) ? $this->legend_text_align : 'right'; + $colorbox_align = isset($this->legend_colorbox_align) ? $this->legend_colorbox_align : 'right'; + + // Sizing parameters: + $v_margin = $char_h/2; // Between vertical borders and labels + $dot_height = $char_h + $line_spacing; // Height of the small colored boxes + // Overall legend box width e.g.: | space colorbox space text space | + // where colorbox and each space are 1 char width. + if ($colorbox_align != 'none') { + $width = $max_width + 4 * $char_w; + $draw_colorbox = True; + } else { + $width = $max_width + 2 * $char_w; + $draw_colorbox = False; + } + + //////// Calculate box position + // User-defined position specified? + if ( !isset($this->legend_x_pos) || !isset($this->legend_y_pos)) { + // No, use default + $box_start_x = $this->plot_area[2] - $width - $this->safe_margin; + $box_start_y = $this->plot_area[1] + $this->safe_margin; + } elseif (isset($this->legend_xy_world)) { + // User-defined position in world-coordinates (See SetLegendWorld). + $box_start_x = $this->xtr($this->legend_x_pos); + $box_start_y = $this->ytr($this->legend_y_pos); + unset($this->legend_xy_world); + } else { + // User-defined position in pixel coordinates. + $box_start_x = $this->legend_x_pos; + $box_start_y = $this->legend_y_pos; + } + + // Lower right corner + $box_end_y = $box_start_y + $dot_height*(count($this->legend)) + 2*$v_margin; + $box_end_x = $box_start_x + $width; + + // Draw outer box + ImageFilledRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, $this->ndx_bg_color); + ImageRectangle($this->img, $box_start_x, $box_start_y, $box_end_x, $box_end_y, $this->ndx_grid_color); + + $color_index = 0; + $max_color_index = count($this->ndx_data_colors) - 1; + + // Calculate color box and text horizontal positions. + if (!$draw_colorbox) { + if ($text_align == 'left') + $x_pos = $box_start_x + $char_w; + else + $x_pos = $box_end_x - $char_w; + } elseif ($colorbox_align == 'left') { + $dot_left_x = $box_start_x + $char_w; + $dot_right_x = $dot_left_x + $char_w; + if ($text_align == 'left') + $x_pos = $dot_left_x + 2 * $char_w; + else + $x_pos = $box_end_x - $char_w; + } else { + $dot_left_x = $box_end_x - 2 * $char_w; + $dot_right_x = $dot_left_x + $char_w; + if ($text_align == 'left') + $x_pos = $box_start_x + $char_w; + else + $x_pos = $dot_left_x - $char_w; + } + + // Calculate starting position of first text line. The bottom of each color box + // lines up with the bottom (baseline) of its text line. + $y_pos = $box_start_y + $v_margin + $dot_height; + + foreach ($this->legend as $leg) { + // Draw text with requested alignment: + $this->DrawText($font, 0, $x_pos, $y_pos, $this->ndx_text_color, $leg, $text_align, 'bottom'); + if ($draw_colorbox) { + // Draw a box in the data color + $y1 = $y_pos - $dot_height + 1; + $y2 = $y_pos - 1; + ImageFilledRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2, + $this->ndx_data_colors[$color_index]); + // Draw a rectangle around the box + ImageRectangle($this->img, $dot_left_x, $y1, $dot_right_x, $y2, + $this->ndx_text_color); + } + $y_pos += $dot_height; + + $color_index++; + if ($color_index > $max_color_index) + $color_index = 0; + } + return TRUE; + } // Function DrawLegend() + + +///////////////////////////////////////////// +//////////////////// PLOT DRAWING +///////////////////////////////////////////// + + + /*! + * Draws a pie chart. Data is 'text-data', 'data-data', or 'text-data-single'. + * + * For text-data-single, the data array contains records with an ignored label, + * and one Y value. Each record defines a sector of the pie, as a portion of + * the sum of all Y values. + * + * For text-data and data-data, the data array contains records with an ignored label, + * an ignored X value (for data-data only), and N (N>=1) Y values per record. + * The pie chart will be produced with N segments. The relative size of the first + * sector of the pie is the sum of the first Y data value in each record, etc. + * + * Note: With text-data-single, the data labels could be used, but are not currently. + * + * If there are no valid data points > 0 at all, just draw nothing. It may seem more correct to + * raise an error, but all of the other plot types handle it this way implicitly. DrawGraph + * checks for an empty data array, but this is different: a non-empty data array with no Y values, + * or all Y=0. + */ + protected function DrawPieChart() + { + if (!$this->CheckDataType('text-data, text-data-single, data-data')) + return FALSE; + + $xpos = $this->plot_area[0] + $this->plot_area_width/2; + $ypos = $this->plot_area[1] + $this->plot_area_height/2; + $diameter = min($this->plot_area_width, $this->plot_area_height); + $radius = $diameter/2; + + if ($this->data_type == 'text-data') { + // Get sum of each column - One pie slice per column: + $num_slices = $this->records_per_group - 1; // records_per_group is the maximum row size + if ($num_slices < 1) return TRUE; // Give up early if there is no data at all. + $sumarr = array_fill(0, $num_slices, 0); + for ($i = 0; $i < $this->num_data_rows; $i++) { + for ($j = 1; $j < $this->num_recs[$i]; $j++) { // Skip label at [0] + if (is_numeric($this->data[$i][$j])) + $sumarr[$j-1] += abs($this->data[$i][$j]); + } + } + } elseif ($this->data_type == 'text-data-single') { + // Or only one column per row, one pie slice per row: + $num_slices = $this->num_data_rows; + if ($num_slices < 1) return TRUE; // Give up early if there is no data at all. + $sumarr = array_fill(0, $num_slices, 0); + for ($i = 0; $i < $num_slices; $i++) { + // $legend[$i] = $this->data[$i][0]; // Note: Labels are not used yet + if (is_numeric($this->data[$i][1])) + $sumarr[$i] = abs($this->data[$i][1]); + } + } else { // $this->data_type == 'data-data' + $num_slices = $this->records_per_group - 2; // records_per_group is the maximum row size + if ($num_slices < 1) return TRUE; // Give up early if there is no data at all. + $sumarr = array_fill(0, $num_slices, 0); + for ($i = 0; $i < $this->num_data_rows; $i++) { + for ($j = 2; $j < $this->num_recs[$i]; $j++) { // Skip label at [0] an X and [1] + if (is_numeric($this->data[$i][$j])) + $sumarr[$j-2] += abs($this->data[$i][$j]); + } + } + } + + $total = array_sum($sumarr); + + if ($total == 0) { + // There are either no valid data points, or all are 0. + // See top comment about why not to make this an error. + return TRUE; + } + + if ($this->shading) { + $diam2 = $diameter / 2; + } else { + $diam2 = $diameter; + } + $max_data_colors = count ($this->data_colors); + + // Use the Y label format precision, with default value: + if (isset($this->label_format['y']['precision'])) + $precision = $this->label_format['y']['precision']; + else + $precision = 1; + + + for ($h = $this->shading; $h >= 0; $h--) { + $color_index = 0; + $start_angle = 0; + $end_angle = 0; + for ($j = 0; $j < $num_slices; $j++) { + $val = $sumarr[$j]; + + // For shaded pies: the last one (at the top of the "stack") has a brighter color: + if ($h == 0) + $slicecol = $this->ndx_data_colors[$color_index]; + else + $slicecol = $this->ndx_data_dark_colors[$color_index]; + + $label_txt = $this->number_format(($val / $total * 100), $precision) . '%'; + $val = 360 * ($val / $total); + + // NOTE that imagefilledarc measures angles CLOCKWISE (go figure why), + // so the pie chart would start clockwise from 3 o'clock, would it not be + // for the reversal of start and end angles in imagefilledarc() + // Also note ImageFilledArc only takes angles in integer degrees, and if the + // the start and end angles match then you get a full circle not a zero-width + // pie. This is bad. So skip any zero-size wedge. On the other hand, we cannot + // let cumulative error from rounding to integer result in missing wedges. So + // keep the running total as a float, and round the angles. It should not + // be necessary to check that the last wedge ends at 360 degrees. + $start_angle = $end_angle; + $end_angle += $val; + // This method of conversion to integer - truncate after reversing it - was + // chosen to match the implicit method of PHPlot<=5.0.4 to get the same slices. + $arc_start_angle = (int)(360 - $start_angle); + $arc_end_angle = (int)(360 - $end_angle); + + if ($arc_start_angle > $arc_end_angle) { + $mid_angle = deg2rad($end_angle - ($val / 2)); + + // Draw the slice + ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2, + $arc_end_angle, $arc_start_angle, + $slicecol, IMG_ARC_PIE); + + // Draw the labels only once + if ($h == 0) { + // Draw the outline + if (! $this->shading) + ImageFilledArc($this->img, $xpos, $ypos+$h, $diameter, $diam2, + $arc_end_angle, $arc_start_angle, + $this->ndx_grid_color, IMG_ARC_PIE | IMG_ARC_EDGED |IMG_ARC_NOFILL); + + + // The '* 1.2' trick is to get labels out of the pie chart so there are more + // chances they can be seen in small sectors. + $label_x = $xpos + ($diameter * 1.2 * cos($mid_angle)) * $this->label_scale_position; + $label_y = $ypos+$h - ($diam2 * 1.2 * sin($mid_angle)) * $this->label_scale_position; + + $this->DrawText($this->fonts['generic'], 0, $label_x, $label_y, $this->ndx_grid_color, + $label_txt, 'center', 'center'); + } + } + if (++$color_index >= $max_data_colors) + $color_index = 0; + } // end for + } // end for + return TRUE; + } + + + /* + * Draw the points and errors bars for an error plot of types points and linepoints + * Supports only data-data-error format, with each row of the form + * array("title", x, y1, error1+, error1-, y2, error2+, error2-, ...) + * Note: plot type and data type were already checked by the calling function. + */ + protected function DrawDotsError() + { + // Adjust the point shapes and point sizes arrays: + $this->CheckPointParams(); + + // Suppress duplicate X data labels in linepoints mode; let DrawLinesError() do them. + $do_labels = ($this->plot_type != 'linepoints'); + + for($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (title) + + $x_now = $this->data[$row][$record++]; // Read it, advance record index + + $x_now_pixels = $this->xtr($x_now); // Absolute coordinates. + + // Draw X Data labels? + if ($this->x_data_label_pos != 'none' && $do_labels) + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); + + // Now go for Y, E+, E- + for ($idx = 0; $record < $this->num_recs[$row]; $idx++) { + if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data + + // Y: + $y_now = $this->data[$row][$record++]; + $this->DrawDot($x_now, $y_now, $idx, $this->ndx_data_colors[$idx]); + + // Error + + $val = $this->data[$row][$record++]; + $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, + $this->ndx_error_bar_colors[$idx]); + // Error - + $val = $this->data[$row][$record++]; + $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, + $this->ndx_error_bar_colors[$idx]); + } else { + $record += 3; // Skip over missing Y and its error values + } + } + } + return TRUE; + } // function DrawDotsError() + + + /* + * Draw the points for plots of type points and linepoints + * Supported data types: + * - data-data ("title", x, y1, y2, y3, ...) + * - text-data ("title", y1, y2, y3, ...) + */ + protected function DrawDots() + { + if (!$this->CheckDataType('text-data, data-data')) + return FALSE; + + // Adjust the point shapes and point sizes arrays: + $this->CheckPointParams(); + + // Suppress duplicate X data labels in linepoints mode; let DrawLines() do them. + $do_labels = ($this->plot_type != 'linepoints'); + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $rec = 1; // Skip record #0 (data label) + + // Do we have a value for X? + if ($this->data_type == 'data-data') + $x_now = $this->data[$row][$rec++]; // Read it, advance record index + else + $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... + + $x_now_pixels = $this->xtr($x_now); + + // Draw X Data labels? + if ($this->x_data_label_pos != 'none' && $do_labels) + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); + + // Proceed with Y values + for($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) { + if (is_numeric($this->data[$row][$rec])) { // Allow for missing Y data + $this->DrawDot($x_now, $this->data[$row][$rec], + $idx, $this->ndx_data_colors[$idx]); + } + } + } + return TRUE; + } //function DrawDots + + + /* + * Draw a Thin Bar Line plot, also known as an Impulse plot. + * A clean, fast routine for when you just want charts like stock volume charts + * Supports data-data and text-data formats. + */ + protected function DrawThinBarLines() + { + if (!$this->CheckDataType('text-data, data-data')) + return FALSE; + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $rec = 1; // Skip record #0 (data label) + + // Do we have a value for X? + if ($this->data_type == 'data-data') + $x_now = $this->data[$row][$rec++]; // Read it, advance record index + else + $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... + + $x_now_pixels = $this->xtr($x_now); + + // Draw X Data labels? + if ($this->x_data_label_pos != 'none') + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); + + // Proceed with Y values + for($idx = 0;$rec < $this->num_recs[$row]; $rec++, $idx++) { + if (is_numeric($this->data[$row][$rec])) { // Allow for missing Y data + ImageSetThickness($this->img, $this->line_widths[$idx]); + // Draws a line from user defined x axis position up to ytr($val) + ImageLine($this->img, $x_now_pixels, $this->x_axis_y_pixels, $x_now_pixels, + $this->ytr($this->data[$row][$rec]), $this->ndx_data_colors[$idx]); + } + } + } + + ImageSetThickness($this->img, 1); + return TRUE; + } //function DrawThinBarLines + + /* + * Draw an Error Bar set. Used by DrawDotsError and DrawLinesError + */ + protected function DrawYErrorBar($x_world, $y_world, $error_height, $error_bar_type, $color) + { + /* + // TODO: add a parameter to show datalabels next to error bars? + // something like this: + if ($this->x_data_label_pos == 'plot') + $this->DrawText($this->fonts['error'], 90, $x1, $y2, + $color, $label, 'center', 'bottom'); + */ + + $x1 = $this->xtr($x_world); + $y1 = $this->ytr($y_world); + $y2 = $this->ytr($y_world+$error_height) ; + + ImageSetThickness($this->img, $this->error_bar_line_width); + ImageLine($this->img, $x1, $y1 , $x1, $y2, $color); + + switch ($error_bar_type) { + case 'line': + break; + case 'tee': + ImageLine($this->img, $x1-$this->error_bar_size, $y2, $x1+$this->error_bar_size, $y2, $color); + break; + default: + ImageLine($this->img, $x1-$this->error_bar_size, $y2, $x1+$this->error_bar_size, $y2, $color); + break; + } + + ImageSetThickness($this->img, 1); + return TRUE; + } + + /* + * Draws a styled dot. Uses world coordinates. + * The list of supported shapes can also be found in SetPointShapes(). + * All shapes are drawn using a 3x3 grid, centered on the data point. + * The center is (x_mid, y_mid) and the corners are (x1, y1) and (x2, y2). + * $record is the 0-based index that selects the shape and size. + */ + protected function DrawDot($x_world, $y_world, $record, $color) + { + $index = $record % $this->point_counts; + $point_size = $this->point_sizes[$index]; + + $half_point = (int)($point_size / 2); + + $x_mid = $this->xtr($x_world); + $y_mid = $this->ytr($y_world); + + $x1 = $x_mid - $half_point; + $x2 = $x_mid + $half_point; + $y1 = $y_mid - $half_point; + $y2 = $y_mid + $half_point; + + switch ($this->point_shapes[$index]) { + case 'halfline': + ImageLine($this->img, $x1, $y_mid, $x_mid, $y_mid, $color); + break; + case 'line': + ImageLine($this->img, $x1, $y_mid, $x2, $y_mid, $color); + break; + case 'plus': + ImageLine($this->img, $x1, $y_mid, $x2, $y_mid, $color); + ImageLine($this->img, $x_mid, $y1, $x_mid, $y2, $color); + break; + case 'cross': + ImageLine($this->img, $x1, $y1, $x2, $y2, $color); + ImageLine($this->img, $x1, $y2, $x2, $y1, $color); + break; + case 'circle': + ImageArc($this->img, $x_mid, $y_mid, $point_size, $point_size, 0, 360, $color); + break; + case 'dot': + ImageFilledEllipse($this->img, $x_mid, $y_mid, $point_size, $point_size, $color); + break; + case 'diamond': + $arrpoints = array( $x1, $y_mid, $x_mid, $y1, $x2, $y_mid, $x_mid, $y2); + ImageFilledPolygon($this->img, $arrpoints, 4, $color); + break; + case 'triangle': + $arrpoints = array( $x1, $y_mid, $x2, $y_mid, $x_mid, $y2); + ImageFilledPolygon($this->img, $arrpoints, 3, $color); + break; + case 'trianglemid': + $arrpoints = array( $x1, $y1, $x2, $y1, $x_mid, $y_mid); + ImageFilledPolygon($this->img, $arrpoints, 3, $color); + break; + case 'yield': + $arrpoints = array( $x1, $y1, $x2, $y1, $x_mid, $y2); + ImageFilledPolygon($this->img, $arrpoints, 3, $color); + break; + case 'delta': + $arrpoints = array( $x1, $y2, $x2, $y2, $x_mid, $y1); + ImageFilledPolygon($this->img, $arrpoints, 3, $color); + break; + case 'star': + ImageLine($this->img, $x1, $y_mid, $x2, $y_mid, $color); + ImageLine($this->img, $x_mid, $y1, $x_mid, $y2, $color); + ImageLine($this->img, $x1, $y1, $x2, $y2, $color); + ImageLine($this->img, $x1, $y2, $x2, $y1, $color); + break; + case 'hourglass': + $arrpoints = array( $x1, $y1, $x2, $y1, $x1, $y2, $x2, $y2); + ImageFilledPolygon($this->img, $arrpoints, 4, $color); + break; + case 'bowtie': + $arrpoints = array( $x1, $y1, $x1, $y2, $x2, $y1, $x2, $y2); + ImageFilledPolygon($this->img, $arrpoints, 4, $color); + break; + case 'target': + ImageFilledRectangle($this->img, $x1, $y1, $x_mid, $y_mid, $color); + ImageFilledRectangle($this->img, $x_mid, $y_mid, $x2, $y2, $color); + ImageRectangle($this->img, $x1, $y1, $x2, $y2, $color); + break; + case 'box': + ImageRectangle($this->img, $x1, $y1, $x2, $y2, $color); + break; + case 'home': /* As in: "home plate" (baseball), also looks sort of like a house. */ + $arrpoints = array( $x1, $y2, $x2, $y2, $x2, $y_mid, $x_mid, $y1, $x1, $y_mid); + ImageFilledPolygon($this->img, $arrpoints, 5, $color); + break; + case 'up': + ImagePolygon($this->img, array($x_mid, $y1, $x2, $y2, $x1, $y2), 3, $color); + break; + case 'down': + ImagePolygon($this->img, array($x_mid, $y2, $x1, $y1, $x2, $y1), 3, $color); + break; + case 'none': /* Special case, no point shape here */ + break; + default: /* Also 'rect' */ + ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $color); + break; + } + return TRUE; + } + + /* + * Draw an 'area' or 'stacked area' plot. + * Both of these fill the area between lines, but in the stacked area graph the Y values + * are accumulated for each X, same as stacked bars. In the regular area graph, the areas + * are filled in order from the X axis up to each Y (so the Y values for each X need to be + * in decreasing order in this case). + * Data format can be text-data (label, y1, y2, ...) or data-data (label, x, y1, y2, ...) + * Notes: + * All Y values must be >= 0. (If any Y<0 the absolute value is used.) + * Missing data points are NOT handled. (They are counted as 0.) + * All rows must have the same number of Y points, or an error image will be produced. + */ + protected function DrawArea($do_stacked = False) + { + if (!$this->CheckDataType('text-data, data-data')) + return FALSE; + + $n = $this->num_data_rows; // Number of X values + + // These arrays store the device X and Y coordinates for all lines: + $xd = array(); + $yd = array(); + if ($this->data_type == 'data-data') { // Explicit X values? + $x_supplied = True; + $skip_index = 2; + } else { + $x_supplied = False; + $skip_index = 1; + } + $num_recs = max($this->num_recs); // Number of 'records' (label + x + y's) for each X. + $num_points = $num_recs - $skip_index; // Number of Y values per X, max + $min_points = min($this->num_recs) - $skip_index; // To check for uniform number of Y values + if ($num_points != $min_points) { + return $this->PrintError("DrawArea(): Data array must contain the same number" + . " of Y values for each X ($num_points != $min_points)"); + } + + // Calculate the Y value for each X, and store the device + // coordinates into the xd and yd arrays. + // For stacked area plots, the Y values accumulate. + for ($row = 0; $row < $n; $row++) { + $rec = 1; // Skip record #0 (data label) + + if ($x_supplied) + $x_now = $this->data[$row][$rec++]; + else + $x_now = 0.5 + $row; + + $x_now_pixels = $this->xtr($x_now); + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); + + // Store the X value. + // There is an artificial Y value at the axis. For 'area' it goes + // at the end; for stackedarea it goes before the start. + $xd[$row] = $x_now_pixels; + $yd[$row] = array(); + if ($do_stacked) + $yd[$row][] = $this->x_axis_y_pixels; + + // Store the Y values for this X. + // All Y values are clipped to the x axis which should be zero but can be moved. + $y = 0; + while ($rec < $num_recs) { + if (is_numeric($this->data[$row][$rec])) { // Treat missing values as 0. + $y += abs($this->data[$row][$rec]); + } + $yd[$row][] = $this->ytr(max($this->x_axis_position, $y)); + if (!$do_stacked) $y = 0; + $rec++; + } + + if (!$do_stacked) + $yd[$row][] = $this->x_axis_y_pixels; + } + + // Now draw the filled polygons. + $prev_row = 0; + for ($row = 1; $row <= $num_points; $row++) { // 1 extra for X axis artificial row + $pts = array(); + // Previous data set forms top (for area) or bottom (for stackedarea): + for ($j = 0; $j < $n; $j++) { + $pts[] = $xd[$j]; + $pts[] = $yd[$j][$prev_row]; + } + // Current data set forms bottom (for area) or top (for stackedarea): + for ($j = $n- 1; $j >= 0; $j--) { + $pts[] = $xd[$j]; + $pts[] = $yd[$j][$row]; + } + // Draw it: + ImageFilledPolygon($this->img, $pts, $n * 2, $this->ndx_data_colors[$prev_row]); + + $prev_row = $row; + } + return TRUE; + } // function DrawArea() + + + /* + * Draw a Line plot + * Supported data-types are 'data-data' and 'text-data'. + */ + protected function DrawLines() + { + if (!$this->CheckDataType('text-data, data-data')) + return FALSE; + + // Flag array telling if the current point is valid, one element per plot line. + // If start_lines[i] is true, then (lastx[i], lasty[i]) is the previous point. + $start_lines = array_fill(0, $this->records_per_group, FALSE); + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + if ($this->data_type == 'data-data') // Do we have a value for X? + $x_now = $this->data[$row][$record++]; // Read it, advance record index + else + $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... + + $x_now_pixels = $this->xtr($x_now); // Absolute coordinates + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); + + for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { + if (($line_style = $this->line_styles[$idx]) == 'none') + continue; //Allow suppressing entire line, useful with linepoints + if (is_numeric($this->data[$row][$record])) { //Allow for missing Y data + $y_now_pixels = $this->ytr($this->data[$row][$record]); + + if ($start_lines[$idx]) { + // Set line width, revert it to normal at the end + ImageSetThickness($this->img, $this->line_widths[$idx]); + + if ($line_style == 'dashed') { + $this->SetDashedStyle($this->ndx_data_colors[$idx]); + ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], + IMG_COLOR_STYLED); + } else { + ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], + $this->ndx_data_colors[$idx]); + } + + } + $lasty[$idx] = $y_now_pixels; + $lastx[$idx] = $x_now_pixels; + $start_lines[$idx] = TRUE; + } + // Y data missing... should we leave a blank or not? + else if ($this->draw_broken_lines) { + $start_lines[$idx] = FALSE; + } + } // end for + } // end for + + ImageSetThickness($this->img, 1); // Revert to original state for lines to be drawn later. + return TRUE; + } // function DrawLines() + + + /* + * Draw lines with error bars for an error plot of types lines and linepoints + * Supports only data-data-error format, with each row of the form + * array("title", x, y1, error1+, error1-, y2, error2+, error2-, ...) + * Note: plot type and data type were already checked by the calling function. + */ + protected function DrawLinesError() + { + $start_lines = array_fill(0, $this->records_per_group, FALSE); + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + $x_now = $this->data[$row][$record++]; // Read X value, advance record index + + $x_now_pixels = $this->xtr($x_now); // Absolute coordinates. + + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels, $row); + + // Now go for Y, E+, E- + for ($idx = 0; $record < $this->num_recs[$row]; $idx++) { + if (($line_style = $this->line_styles[$idx]) == 'none') + continue; //Allow suppressing entire line, useful with linepoints + if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data + + // Y + $y_now = $this->data[$row][$record++]; + $y_now_pixels = $this->ytr($y_now); + + if ($start_lines[$idx]) { + ImageSetThickness($this->img, $this->line_widths[$idx]); + + if ($line_style == 'dashed') { + $this->SetDashedStyle($this->ndx_data_colors[$idx]); + ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], + IMG_COLOR_STYLED); + } else { + ImageLine($this->img, $x_now_pixels, $y_now_pixels, $lastx[$idx], $lasty[$idx], + $this->ndx_data_colors[$idx]); + } + } + + // Error+ + $val = $this->data[$row][$record++]; + $this->DrawYErrorBar($x_now, $y_now, $val, $this->error_bar_shape, + $this->ndx_error_bar_colors[$idx]); + + // Error- + $val = $this->data[$row][$record++]; + $this->DrawYErrorBar($x_now, $y_now, -$val, $this->error_bar_shape, + $this->ndx_error_bar_colors[$idx]); + + // Update indexes: + $start_lines[$idx] = TRUE; // Tells us if we already drew the first column of points, + // thus having $lastx and $lasty ready for the next column. + $lastx[$idx] = $x_now_pixels; + $lasty[$idx] = $y_now_pixels; + + } else { + $record += 3; // Skip over missing Y and its error values + if ($this->draw_broken_lines) { + $start_lines[$idx] = FALSE; + } + } + } // end for + } // end for + + ImageSetThickness($this->img, 1); // Revert to original state for lines to be drawn later. + return TRUE; + } // function DrawLinesError() + + + + /* + * Draw a Squared Line plot. + * Supported data-types are 'data-data' and 'text-data'. + * This is based on DrawLines(), with one more line drawn for each point. + */ + protected function DrawSquared() + { + if (!$this->CheckDataType('text-data, data-data')) + return FALSE; + + // Flag array telling if the current point is valid, one element per plot line. + // If start_lines[i] is true, then (lastx[i], lasty[i]) is the previous point. + $start_lines = array_fill(0, $this->records_per_group, FALSE); + + for ($row = 0, $cnt = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + if ($this->data_type == 'data-data') // Do we have a value for X? + $x_now = $this->data[$row][$record++]; // Read it, advance record index + else + $x_now = 0.5 + $cnt++; // Place text-data at X = 0.5, 1.5, 2.5, etc... + + $x_now_pixels = $this->xtr($x_now); // Absolute coordinates + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); // notice there is no last param. + + // Draw Lines + for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { + if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data + $y_now_pixels = $this->ytr($this->data[$row][$record]); + + if ($start_lines[$idx] == TRUE) { + // Set line width, revert it to normal at the end + ImageSetThickness($this->img, $this->line_widths[$idx]); + + if ($this->line_styles[$idx] == 'dashed') { + $this->SetDashedStyle($this->ndx_data_colors[$idx]); + ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx], + IMG_COLOR_STYLED); + ImageLine($this->img, $x_now_pixels, $lasty[$idx], $x_now_pixels, $y_now_pixels, + IMG_COLOR_STYLED); + } else { + ImageLine($this->img, $lastx[$idx], $lasty[$idx], $x_now_pixels, $lasty[$idx], + $this->ndx_data_colors[$idx]); + ImageLine($this->img, $x_now_pixels, $lasty[$idx], $x_now_pixels, $y_now_pixels, + $this->ndx_data_colors[$idx]); + } + } + $lastx[$idx] = $x_now_pixels; + $lasty[$idx] = $y_now_pixels; + $start_lines[$idx] = TRUE; + } + // Y data missing... should we leave a blank or not? + else if ($this->draw_broken_lines) { + $start_lines[$idx] = FALSE; + } + } + } // end while + + ImageSetThickness($this->img, 1); + return TRUE; + } // function DrawSquared() + + + /* + * Draw a Bar chart + * Supports only text-data format, with each row in the form array("title", y1, y2, y3, ...) + */ + protected function DrawBars() + { + if (!$this->CheckDataType('text-data')) + return False; + + // This is the X offset from the bar group's label center point to the left side of the first bar + // in the group. See also CalcBarWidths above. + $x_first_bar = (($this->records_per_group - 1) * $this->record_bar_width) / 2 - $this->bar_adjust_gap; + + for ($row = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + $x_now_pixels = $this->xtr(0.5 + $row); // Place text-data at X = 0.5, 1.5, 2.5, etc... + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); + + // Lower left X of first bar in the group: + $x1 = $x_now_pixels - $x_first_bar; + + // Draw the bars in the group: + for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { + if (is_numeric($this->data[$row][$record])) { // Allow for missing Y data + $x2 = $x1 + $this->actual_bar_width; + + if ($this->data[$row][$record] < $this->x_axis_position) { + $y1 = $this->x_axis_y_pixels; + $y2 = $this->ytr($this->data[$row][$record]); + $upgoing_bar = False; + } else { + $y1 = $this->ytr($this->data[$row][$record]); + $y2 = $this->x_axis_y_pixels; + $upgoing_bar = True; + } + + // Draw the bar + ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]); + + if ($this->shading) { // Draw the shade? + ImageFilledPolygon($this->img, array($x1, $y1, + $x1 + $this->shading, $y1 - $this->shading, + $x2 + $this->shading, $y1 - $this->shading, + $x2 + $this->shading, $y2 - $this->shading, + $x2, $y2, + $x2, $y1), + 6, $this->ndx_data_dark_colors[$idx]); + } + // Or draw a border? + else { + ImageRectangle($this->img, $x1, $y1, $x2,$y2, $this->ndx_data_border_colors[$idx]); + } + + // Draw optional data labels above the bars (or below, for negative values). + if ( $this->y_data_label_pos == 'plotin') { + if ($upgoing_bar) { + $v_align = 'bottom'; + $y_offset = -5 - $this->shading; + } else { + $v_align = 'top'; + $y_offset = 2; + } + $this->DrawDataLabel($this->fonts['y_label'], $this->y_data_label_angle, + $row+0.5, $this->data[$row][$record], $this->ndx_title_color, + $this->data[$row][$record], 'center', $v_align, + ($idx + 0.5) * $this->record_bar_width - $x_first_bar, $y_offset); + } + + } + // Step to next bar in group: + $x1 += $this->record_bar_width; + } // end for + } // end for + return TRUE; + } //function DrawBars + + /* + * Draw a Horizontal Bar chart + * Supports only text-data-yx format, with each row in the form array("title", x1, x2, x3, ...) + * This is an unusual case, because the data values are X not Y. + * Note: plot type and data type were already checked by the calling function. + */ + protected function DrawHorizBars() + { + // This is the Y offset from the bar group's label center point to the bottom of the first bar + // in the group. See also CalcBarWidths above. + $y_first_bar = (($this->records_per_group - 1) * $this->record_bar_width) / 2 - $this->bar_adjust_gap; + + for ($row = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + $y_now_pixels = $this->ytr(0.5 + $row); // Place bars at Y=0.5, 1.5, 2.5, etc... + + if ($this->y_data_label_pos != 'none') // Draw Y Data Labels? + $this->DrawYDataLabel($this->data[$row][0], $y_now_pixels); + + // Lower left Y of first bar in the group: + $y1 = $y_now_pixels + $y_first_bar; + + // Draw the bars in the group: + for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { + if (is_numeric($this->data[$row][$record])) { // Allow for missing X data + $y2 = $y1 - $this->actual_bar_width; + + if ($this->data[$row][$record] < $this->y_axis_position) { + $x1 = $this->y_axis_x_pixels; + $x2 = $this->xtr($this->data[$row][$record]); + $rightwards_bar = False; + } else { + $x1 = $this->xtr($this->data[$row][$record]); + $x2 = $this->y_axis_x_pixels; + $rightwards_bar = True; + } + + // Draw the bar + ImageFilledRectangle($this->img, $x2, $y2, $x1, $y1, $this->ndx_data_colors[$idx]); + + if ($this->shading) { // Draw the shade? + ImageFilledPolygon($this->img, array($x2, $y2, + $x2 + $this->shading, $y2 - $this->shading, + $x1 + $this->shading, $y2 - $this->shading, + $x1 + $this->shading, $y1 - $this->shading, + $x1, $y1, + $x1, $y2), + 6, $this->ndx_data_dark_colors[$idx]); + } + // Or draw a border? + else { + ImageRectangle($this->img, $x1, $y1, $x2,$y2, $this->ndx_data_border_colors[$idx]); + } + + // Draw optional data labels above the bars (or below, for negative values). + // DELETED + + } + // Step to next bar in group: + $y1 -= $this->record_bar_width; + } // end for + } // end for + + return TRUE; + } + + /* + * Draw a Stacked Bar chart + * Supports only text-data format, with each row in the form array("title", y1, y2, y3, ...) + * Original stacked bars idea by Laurent Kruk < lolok at users.sourceforge.net > + */ + protected function DrawStackedBars() + { + if (!$this->CheckDataType('text-data')) + return False; + + // This is the X offset from the bar's label center point to the left side of the bar. + $x_first_bar = $this->record_bar_width / 2 - $this->bar_adjust_gap; + + // Copy shading value to local variable + $shade = $this->shading; + + // Determine if any data labels are on: + if ($this->y_data_label_pos == 'plotin') { + $data_labels_above = True; + $data_labels_within = False; + } elseif ($this->y_data_label_pos == 'plotstack') { + $data_labels_above = True; + $data_labels_within = True; + // Get the text label height, plus a little bit, so we can omit labels in + // segments that are too short for the label to fit. + $data_labels_min_height = $this->fonts['y_label']['height'] + 2; + } else { + $data_labels_above = False; + $data_labels_within = False; + } + if ($data_labels_above || $data_labels_within) { + $data_label_y_offset = -5 - $shade; + } + + for ($row = 0; $row < $this->num_data_rows; $row++) { + $record = 1; // Skip record #0 (data label) + + $x_now_pixels = $this->xtr(0.5 + $row); // Place text-data at X = 0.5, 1.5, 2.5, etc... + + if ($this->x_data_label_pos != 'none') // Draw X Data labels? + $this->DrawXDataLabel($this->data[$row][0], $x_now_pixels); + + // Lower left and lower right X of the bars in this stack: + $x1 = $x_now_pixels - $x_first_bar; + $x2 = $x1 + $this->actual_bar_width; + + // Draw the bar segments in this stack. The first segment is drawn from the X axis to Y1. + // Each additional segment is drawn from the top of the previous segment to the new + // cumulative Y. Skip over any segment of 0 size or part below the X axis. + $wy1 = 0; // World coordinates Y1, upper value + $wy2 = $this->x_axis_position; // World coordinates Y2, lower value + + for ($idx = 0; $record < $this->num_recs[$row]; $record++, $idx++) { + + // Skip missing Y values, and ignore Y=0 values. + if (is_numeric($this->data[$row][$record]) + && ($this_y = abs($this->data[$row][$record])) > 0) { + + $wy1 += $this_y; // Accumulate Y value in world coordinates - top of current segment. + + // Draw nothing if top of segment is below X axis. + // Bottom (wy2) will not go below X axis, so we will get a partial + // segment from X axis up if the segment would cross the X axis. + if ($wy1 > $this->x_axis_position) { + + $y1 = $this->ytr($wy1); + $y2 = $this->ytr($wy2); + + // Draw the bar + ImageFilledRectangle($this->img, $x1, $y1, $x2, $y2, $this->ndx_data_colors[$idx]); + + if ($shade > 0) { // Draw the shade? + ImageFilledPolygon($this->img, + array($x1, $y1, $x1 + $shade, $y1 - $shade, + $x2 + $shade, $y1 - $shade, $x2 + $shade, $y2 - $shade, + $x2, $y2, $x2, $y1), + 6, $this->ndx_data_dark_colors[$idx]); + } else { // Or draw a border? + ImageRectangle($this->img, $x1, $y1, $x2,$y2, $this->ndx_data_border_colors[$idx]); + } + + // Draw optional data label for this bar segment just below the line. + // Text value is the current Y, but position is the cumulative Y. + // Skip the label if the segment is too short for the label to fit. + if ($data_labels_within && ($y2 - $y1) >= $data_labels_min_height) { + $this->DrawDataLabel($this->fonts['y_label'], $this->y_data_label_angle, + $row+0.5, $wy1, $this->ndx_title_color, + $this_y, 'center', 'top', 0, 3); + } + } + // Make the top of this segment become the bottom of the next segment, but not if + // it is still below the X axis. + $wy2 = max($this->x_axis_position, $wy1); + } + } // end for + + // Draw optional data label above the bar with the total value. + // Value is wy1, but position is wy2. This only makes a difference when + // the stacked bar ends completely below the X axis. Then we see the actual + // cumulative value (wy1), positioned above the axis, with no bar at all. + if ($data_labels_above) { + $this->DrawDataLabel($this->fonts['y_label'], $this->y_data_label_angle, + $row+0.5, $wy2, $this->ndx_title_color, + $wy1, 'center', 'bottom', 0, $data_label_y_offset); + } + } // end for + return TRUE; + } //function DrawStackedBars + + + /* + * Draw the graph. + * This is the function that performs the actual drawing, after all + * the parameters and data are set up. + * It also outputs the finished image, unless told not to. + */ + function DrawGraph() + { + // Test for missing image, missing data, empty data: + if (! $this->img) { + return $this->PrintError('DrawGraph(): No image resource allocated'); + } + if (empty($this->data) || ! is_array($this->data)) { + return $this->PrintError("DrawGraph(): No data array"); + } + if ($this->total_records == 0) { + return $this->PrintError('DrawGraph(): Empty data set'); + } + + // For pie charts: don't draw grid or border or axes, and maximize area usage. + // These controls can be split up in the future if needed. + $draw_axes = ($this->plot_type != 'pie'); + + // Get maxima and minima for scaling: + if (!$this->FindDataLimits()) + return FALSE; + + // Set plot area world values (plot_max_x, etc.): + if (!$this->CalcPlotAreaWorld()) + return FALSE; + + // Calculate X and Y axis positions in World Coordinates: + $this->CalcAxisPositions(); + + // Process label-related parameters: + $this->CheckLabels(); + + // Apply grid defaults: + $this->CalcGridSettings(); + + // Calculate the plot margins, if needed. + // For pie charts, set the $maximize argument to maximize space usage. + $this->CalcMargins(!$draw_axes); + + // Calculate the actual plot area in device coordinates: + $this->CalcPlotAreaPixels(); + + // Calculate the mapping between world and device coordinates: + $this->CalcTranslation(); + + // Pad color and style arrays to fit records per group: + $this->PadArrays(); + $this->DoCallback('draw_setup'); + + $this->DrawBackground(); + $this->DrawImageBorder(); + $this->DoCallback('draw_image_background'); + + $this->DrawPlotAreaBackground(); + $this->DoCallback('draw_plotarea_background', $this->plot_area); + + $this->DrawTitle(); + if ($draw_axes) { // If no axes (pie chart), no axis titles either + $this->DrawXTitle(); + $this->DrawYTitle(); + } + $this->DoCallback('draw_titles'); + + if ($draw_axes && ! $this->grid_at_foreground) { // Usually one wants grids to go back, but... + $this->DrawYAxis(); // Y axis must be drawn before X axis (see DrawYAxis()) + $this->DrawXAxis(); + $this->DoCallback('draw_axes'); + } + + switch ($this->plot_type) { + case 'thinbarline': + $this->DrawThinBarLines(); + break; + case 'area': + $this->DrawArea(); + break; + case 'squared': + $this->DrawSquared(); + break; + case 'lines': + if ( $this->data_type == 'data-data-error') { + $this->DrawLinesError(); + } else { + $this->DrawLines(); + } + break; + case 'linepoints': + if ( $this->data_type == 'data-data-error') { + $this->DrawLinesError(); + $this->DrawDotsError(); + } else { + $this->DrawLines(); + $this->DrawDots(); + } + break; + case 'points'; + if ( $this->data_type == 'data-data-error') { + $this->DrawDotsError(); + } else { + $this->DrawDots(); + } + break; + case 'pie': + $this->DrawPieChart(); + break; + case 'stackedbars': + $this->CalcBarWidths(); + $this->DrawStackedBars(); + break; + case 'stackedarea': + $this->DrawArea(True); + break; + // case 'bars': + default: + $this->plot_type = 'bars'; // Make sure it is set + if ($this->data_type == 'text-data-yx') { + $this->CalcBarWidths(False); + $this->DrawHorizBars(); + } else { + $this->CalcBarWidths(); + $this->DrawBars(); + } + break; + } // end switch + $this->DoCallback('draw_graph', $this->plot_area); + + if ($draw_axes && $this->grid_at_foreground) { // Usually one wants grids to go back, but... + $this->DrawYAxis(); // Y axis must be drawn before X axis (see DrawYAxis()) + $this->DrawXAxis(); + $this->DoCallback('draw_axes'); + } + + if ($draw_axes) { + $this->DrawPlotBorder(); + $this->DoCallback('draw_border'); + } + + if ($this->legend) { + $this->DrawLegend(); + $this->DoCallback('draw_legend'); + } + $this->DoCallback('draw_all', $this->plot_area); + + if ($this->print_image && !$this->PrintImage()) + return FALSE; + + return TRUE; + } //function DrawGraph() + +///////////////////////////////////////////// +////////////////// DEPRECATED METHODS +///////////////////////////////////////////// + + /*! + * Deprecated, use SetYTickPos() + */ + function SetDrawVertTicks($which_dvt) + { + if ($which_dvt != 1) + $this->SetYTickPos('none'); + return TRUE; + } + + /*! + * Deprecated, use SetXTickPos() + */ + function SetDrawHorizTicks($which_dht) + { + if ($which_dht != 1) + $this->SetXTickPos('none'); + return TRUE; + } + + /*! + * \deprecated Use SetNumXTicks() + */ + function SetNumHorizTicks($n) + { + return $this->SetNumXTicks($n); + } + + /*! + * \deprecated Use SetNumYTicks() + */ + function SetNumVertTicks($n) + { + return $this->SetNumYTicks($n); + } + + /*! + * \deprecated Use SetXTickIncrement() + */ + function SetHorizTickIncrement($inc) + { + return $this->SetXTickIncrement($inc); + } + + + /*! + * \deprecated Use SetYTickIncrement() + */ + function SetVertTickIncrement($inc) + { + return $this->SetYTickIncrement($inc); + } + + /*! + * \deprecated Use SetYTickPos() + */ + function SetVertTickPosition($which_tp) + { + return $this->SetYTickPos($which_tp); + } + + /*! + * \deprecated Use SetXTickPos() + */ + function SetHorizTickPosition($which_tp) + { + return $this->SetXTickPos($which_tp); + } + + /*! + * \deprecated Use SetFont() + */ + function SetTitleFontSize($which_size) + { + return $this->SetFont('title', $which_size); + } + + /*! + * \deprecated Use SetFont() + */ + function SetAxisFontSize($which_size) + { + $this->SetFont('x_label', $which_size); + $this->SetFont('y_label', $which_size); + } + + /*! + * \deprecated Use SetFont() + */ + function SetSmallFontSize($which_size) + { + return $this->SetFont('generic', $which_size); + } + + /*! + * \deprecated Use SetFont() + */ + function SetXLabelFontSize($which_size) + { + return $this->SetFont('x_title', $which_size); + } + + /*! + * \deprecated Use SetFont() + */ + function SetYLabelFontSize($which_size) + { + return $this->SetFont('y_title', $which_size); + } + + /*! + * \deprecated Use SetXTitle() + */ + function SetXLabel($which_xlab) + { + return $this->SetXTitle($which_xlab); + } + + /*! + * \deprecated Use SetYTitle() + */ + function SetYLabel($which_ylab) + { + return $this->SetYTitle($which_ylab); + } + + /*! + * \deprecated Use SetXTickLength() and SetYTickLength() instead. + */ + function SetTickLength($which_tl) + { + $this->SetXTickLength($which_tl); + $this->SetYTickLength($which_tl); + return TRUE; + } + + /*! + * \deprecated Use SetYLabelType() + */ + function SetYGridLabelType($which_yglt) + { + return $this->SetYLabelType($which_yglt); + } + + /*! + * \deprecated Use SetXLabelType() + */ + function SetXGridLabelType($which_xglt) + { + return $this->SetXLabelType($which_xglt); + } + /*! + * \deprecated Use SetYTickLabelPos() + */ + function SetYGridLabelPos($which_yglp) + { + return $this->SetYTickLabelPos($which_yglp); + } + /*! + * \deprecated Use SetXTickLabelPos() + */ + function SetXGridLabelPos($which_xglp) + { + return $this->SetXTickLabelPos($which_xglp); + } + + + /*! + * \deprecated Use SetXtitle() + */ + function SetXTitlePos($xpos) + { + $this->x_title_pos = $xpos; + return TRUE; + } + + /*! + * \deprecated Use SetYTitle() + */ + function SetYTitlePos($xpos) + { + $this->y_title_pos = $xpos; + return TRUE; + } + + /*! + * Draw Labels (not grid labels) on X Axis, following data points. Default position is + * down of plot. Care must be taken not to draw these and x_tick_labels as they'd probably overlap. + * + * \deprecated Use SetXDataLabelPos() + */ + function SetDrawXDataLabels($which_dxdl) + { + if ($which_dxdl == '1' ) + $this->SetXDataLabelPos('plotdown'); + else + $this->SetXDataLabelPos('none'); + } + + /*! + * \deprecated + */ + function SetNewPlotAreaPixels($x1, $y1, $x2, $y2) + { + //Like in GD 0, 0 is upper left set via pixel Coordinates + $this->plot_area = array($x1, $y1, $x2, $y2); + $this->plot_area_width = $this->plot_area[2] - $this->plot_area[0]; + $this->plot_area_height = $this->plot_area[3] - $this->plot_area[1]; + $this->y_top_margin = $this->plot_area[1]; + + if (isset($this->plot_max_x)) + $this->CalcTranslation(); + + return TRUE; + } + + /*! + * \deprecated Use _SetRGBColor() + */ + function SetColor($which_color) + { + $this->SetRGBColor($which_color); + return TRUE; + } + + /* + * \deprecated Use SetLineWidths(). + */ + function SetLineWidth($which_lw) + { + + $this->SetLineWidths($which_lw); + + if (!$this->error_bar_line_width) { + $this->SetErrorBarLineWidth($which_lw); + } + return TRUE; + } + + /* + * \deprecated Use SetPointShapes(). + */ + function SetPointShape($which_pt) + { + $this->SetPointShapes($which_pt); + return TRUE; + } + + /* + * \deprecated Use SetPointSizes(). + */ + function SetPointSize($which_ps) + { + $this->SetPointSizes($which_ps); + return TRUE; + } +} // class PHPlot + + +/* + * The PHPlot_truecolor class extends PHPlot to use GD truecolor images. + */ + +class PHPlot_truecolor extends PHPlot { + /* + * PHPlot Truecolor variation constructor: Create a PHPlot_truecolor object and initialize it. + * Note this does NOT call the parent (PHPlot) constructor. It duplicates the code here. + * Everything is the same as the PHPlot constructor except for imagecreatetruecolor. + * + * Parameters are the same as PHPlot: + * which_width, which_height Image width and height in pixels. + * which_output_file Optional filename for output. + * which_input_file Optional path to a file to be used as background. + * + */ + function __construct($which_width=600, $which_height=400, $which_output_file=NULL, $which_input_file=NULL) + { + $this->SetRGBArray($this->color_array); + + if ($which_output_file) + $this->SetOutputFile($which_output_file); + + if ($which_input_file) + $this->SetInputFile($which_input_file); + else { + $this->image_width = $which_width; + $this->image_height = $which_height; + + $this->img = imagecreatetruecolor($this->image_width, $this->image_height); + if (! $this->img) + return $this->PrintError('PHPlot_truecolor(): Could not create image resource.'); + } + + $this->SetDefaultStyles(); + $this->SetDefaultFonts(); + } +} diff --git a/gosa-core/include/phplot-5.1.2/phplot_data.php b/gosa-core/include/phplot-5.1.2/phplot_data.php new file mode 100644 index 000000000..d42c27aee --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/phplot_data.php @@ -0,0 +1,270 @@ +img)) { + $this->PHPlot($which_width, $which_height, $which_output_file, $which_input_file); + } + } + + /*! + * Will scale all data rows + * Maybe later I will do a function that only scales some rows + * if $even is TRUE, data will be scaled with "even" factors. + * \note Original code by Thiemo Nagel + */ + function DoScaleData($even, $show_in_legend) + { + $offset = 0; // We use this not to read labels in text-data + + if ($this->data_type == 'text-data') { + $offset = 1; + } elseif ($this->data_type != 'data-data') { + $this->DrawError('wrong data type!!'); + return FALSE; + } + + // Determine maxima for each data row in array $max + // Put maximum of the maxima in $maxmax + $maxmax = 0; + for($i=0; $i < $this->num_data_rows; $i++) { + $rowsize = count($this->data[$i]); + for ($j=$offset; $j < $rowsize; $j++) { + if ($this->data[$i][$j] > @ $max[$j]) + $max[$j] = $this->data[$i][$j]; + if (@ $max[$j] > $maxmax) + $maxmax = $max[$j]; + } + } + + // determine amplification factor $amplify + $end = count($max) + $offset; + for ($i=$offset; $i < $end; $i++) { + if ($max[$i] == 0 || $max[$i] == $maxmax) { + $amplify[$i] = 1; // no divide by zero + } else { + if ($even) { + $amp = pow(10,round(log10($maxmax / $max[$i]))-1); + if ($amp * $max[$i] * 5 < $maxmax) { + $amp *= 5; + } elseif ($amp * $max[$i] * 2 < $maxmax) { + $amp *= 2; + } + } else { + $amp = $maxmax / $max[$i]; + $digits = floor(log10($amp)); + $amp = round($amp/pow(10,$digits-1))*pow(10,$digits-1); + } + $amplify[$i] = $amp; + } + if ($amplify[$i] != 1 && $show_in_legend) + @ $this->legend[$i] .= "*$amplify[$i]"; + } + + // Amplify data + // On my machine, running 1000 iterations over 1000 rows of 12 elements each, + // the for loops were 43.2% faster (MBD) + for ($i = 0; $i < $this->num_data_rows; $i++) { + $rowsize = count($this->data[$i]); + for ($j=$offset; $j < $rowsize; $j++) { + $this->data[$i][$j] *= $amplify[$j]; + } + } + + //Re-Scale Vertical Ticks if not already set + //Removed; does nothing and wrong variable name? + // if ( ! $this->y_tick_increment) { + // $this->SetYTickIncrement() ; + // } + + return TRUE; + } //function DoScaleData + + + /*! + * Computes a moving average of strength $interval for + * data row number $datarow, where 0 denotes the first + * row of y-data. + * + * \param int datarow Index of the row whereupon to make calculations + * \param int interval Number of elements to use in average ("strength") + * \param bool show Whether to tell about the moving average in the legend. + * \param string color Color for the line to be drawn. This color is darkened. + * Can be named or #RRGGBB. + * \param int width Width of the line to be drawn. + * + * \note Original idea by Theimo Nagel + */ + function DoMovingAverage($datarow, $interval, $show=TRUE, $color=NULL, $width=NULL) + { + $off = 1; // Skip record #0 (data label) + + $this->PadArrays(); + + if ($interval == 0) { + $this->DrawError('DoMovingAverage(): interval can\'t be 0'); + return FALSE; + } + + if ($datarow >= $this->records_per_group) { + $this->DrawError("DoMovingAverage(): Data row out of bounds ($datarow >= $this->records_per_group)"); + return FALSE; + } + + if ($this->data_type == 'text-data') { + // Ok. No need to set the offset to skip more records. + } elseif ($this->data_type == 'data-data') { + $off++; // first Y value at $data[][2] + } else { + $this->DrawError('DoMovingAverage(): wrong data type!!'); + return FALSE; + } + + // Set color: + if ($color) { + array_push($this->ndx_data_colors, $this->SetIndexDarkColor($color)); + } else { + array_push($this->ndx_data_colors, $this->SetIndexDarkColor($this->data_colors[$datarow])); + } + // Set line width: + if ($width) { + array_push($this->line_widths, $width); + } else { + array_push($this->line_widths, $this->line_widths[$datarow] * 2); + } + // Show in legend? + if ($show) { + $this->legend[$this->records_per_group-1] = "(MA[$datarow]:$interval)"; + } + + $datarow += $off; + for ($i = 0; $i < $this->num_data_rows; $i++) { + $storage[$i % $interval] = @ $this->data[$i][$datarow]; + $ma = array_sum($storage); + $ma /= count($storage); + array_push($this->data[$i], $ma); // Push the data onto the array + $this->num_recs[$i]++; // Tell the drawing functions it is there + } + $this->records_per_group++; +// $this->FindDataLimits(); + return TRUE; + } //function DoMovingAverage() + + + /** + * Computes an exponentially smoothed moving average. + * @param int perc "smoothing percentage" + * FIXME!!! I haven't checked this. + */ + function DoExponentialMovingAverage($datarow, $perc, $show_in_legend) + { + if ($this->data_type == 'text-data') { + $datarow++; + } elseif ($this->data_type != 'data-data') { + $this->DrawError('DoWeightedMovingAverage(): wrong data type!!'); + return FALSE; + } + + if ($show_in_legend) { + $this->legend[$datarow] .= " (MA: $interval)"; + } + + $storage[0] = $this->data[0][$datarow]; + for ($i=1;$i < $this->num_data_rows; $i++) { + $storage[$i] = @ $storage[$i-1] + $perc * ($this->data[$i][$datarow] - $storage[$i-1]); + $ma = array_sum($storage); + $ma /= count($storage); + $this->data[$i][$datarow] = $ma; + } + return TRUE; + } // function DoExponentialMovingAverage() + + + /*! + * Removes the DataSet of number $index + */ + function DoRemoveDataSet($index) + { + $offset = 1; + if ($this->data_type == 'data-data') { + $offset++; + } elseif ($this->data_type != 'text-data') { + $this->DrawError('wrong data type!!'); + return FALSE; + } + + $index += $offset; + foreach ($this->data as $key=>$val) { + foreach ($val as $key2=>$val2) { + if ($key2 >= $index) { + if (isset($this->data[$key][$key2+1])) { + $this->data[$key][$key2] = $this->data[$key][$key2+1]; + } else { + unset($this->data[$key][$key2]); + } + } + } + } + } // function DoRemoveDataSet + + + /*! + * Computes row x divided by row y, stores the result in row x + * and deletes row y + */ + function DoDivision($x,$y) + { + $offset = 1; + if ($this->data_type == 'data-data') { + $offset++; + } elseif ($this->data_type != 'text-data') { + $this->DrawError('wrong data type!!'); + return FALSE; + } + + $x += $offset; $y += $offset; + reset($this->data); + while (list($key, $val) = each($this->data)) { + if ($this->data[$key][$y] == 0) { + $this->data[$key][$x] = 0; + } else { + $this->data[$key][$x] /= $this->data[$key][$y]; + } + } + + $this->DoRemoveDataSet($y-$offset); + } // function DoDivision + +} // class PHPlot_Data extends PHPlot diff --git a/gosa-core/include/phplot-5.1.2/rgb.inc.php b/gosa-core/include/phplot-5.1.2/rgb.inc.php new file mode 100644 index 000000000..41256ee25 --- /dev/null +++ b/gosa-core/include/phplot-5.1.2/rgb.inc.php @@ -0,0 +1,752 @@ +SetRGBArray('large') + * For more information on PHPlot see http://sourceforge.net/projects/phplot/ + * + * rgb.inc.php comes with PHPlot but is derived from the X11 rgb.txt color + * database file, which contains no specific copyright notice. It may be + * covered by X.Org, XFree86, or MIT/X11 copyright and license, all of which + * allow redistribution on terms which are less strict than the LGPL which + * covers PHPlot. + */ +$ColorArray = array( + "snow" => array(255, 250, 250), + "ghost white" => array(248, 248, 255), + "GhostWhite" => array(248, 248, 255), + "white smoke" => array(245, 245, 245), + "WhiteSmoke" => array(245, 245, 245), + "gainsboro" => array(220, 220, 220), + "floral white" => array(255, 250, 240), + "FloralWhite" => array(255, 250, 240), + "old lace" => array(253, 245, 230), + "OldLace" => array(253, 245, 230), + "linen" => array(250, 240, 230), + "antique white" => array(250, 235, 215), + "AntiqueWhite" => array(250, 235, 215), + "papaya whip" => array(255, 239, 213), + "PapayaWhip" => array(255, 239, 213), + "blanched almond" => array(255, 235, 205), + "BlanchedAlmond" => array(255, 235, 205), + "bisque" => array(255, 228, 196), + "peach puff" => array(255, 218, 185), + "PeachPuff" => array(255, 218, 185), + "navajo white" => array(255, 222, 173), + "NavajoWhite" => array(255, 222, 173), + "moccasin" => array(255, 228, 181), + "cornsilk" => array(255, 248, 220), + "ivory" => array(255, 255, 240), + "lemon chiffon" => array(255, 250, 205), + "LemonChiffon" => array(255, 250, 205), + "seashell" => array(255, 245, 238), + "honeydew" => array(240, 255, 240), + "mint cream" => array(245, 255, 250), + "MintCream" => array(245, 255, 250), + "azure" => array(240, 255, 255), + "alice blue" => array(240, 248, 255), + "AliceBlue" => array(240, 248, 255), + "lavender" => array(230, 230, 250), + "lavender blush" => array(255, 240, 245), + "LavenderBlush" => array(255, 240, 245), + "misty rose" => array(255, 228, 225), + "MistyRose" => array(255, 228, 225), + "white" => array(255, 255, 255), + "black" => array( 0, 0, 0), + "dark slate gray" => array( 47, 79, 79), + "DarkSlateGray" => array( 47, 79, 79), + "dark slate grey" => array( 47, 79, 79), + "DarkSlateGrey" => array( 47, 79, 79), + "dim gray" => array(105, 105, 105), + "DimGray" => array(105, 105, 105), + "dim grey" => array(105, 105, 105), + "DimGrey" => array(105, 105, 105), + "slate gray" => array(112, 128, 144), + "SlateGray" => array(112, 128, 144), + "slate grey" => array(112, 128, 144), + "SlateGrey" => array(112, 128, 144), + "light slate gray" => array(119, 136, 153), + "LightSlateGray" => array(119, 136, 153), + "light slate grey" => array(119, 136, 153), + "LightSlateGrey" => array(119, 136, 153), + "gray" => array(190, 190, 190), + "grey" => array(190, 190, 190), + "light grey" => array(211, 211, 211), + "LightGrey" => array(211, 211, 211), + "light gray" => array(211, 211, 211), + "LightGray" => array(211, 211, 211), + "midnight blue" => array( 25, 25, 112), + "MidnightBlue" => array( 25, 25, 112), + "navy" => array( 0, 0, 128), + "navy blue" => array( 0, 0, 128), + "NavyBlue" => array( 0, 0, 128), + "cornflower blue" => array(100, 149, 237), + "CornflowerBlue" => array(100, 149, 237), + "dark slate blue" => array( 72, 61, 139), + "DarkSlateBlue" => array( 72, 61, 139), + "slate blue" => array(106, 90, 205), + "SlateBlue" => array(106, 90, 205), + "medium slate blue" => array(123, 104, 238), + "MediumSlateBlue" => array(123, 104, 238), + "light slate blue" => array(132, 112, 255), + "LightSlateBlue" => array(132, 112, 255), + "medium blue" => array( 0, 0, 205), + "MediumBlue" => array( 0, 0, 205), + "royal blue" => array( 65, 105, 225), + "RoyalBlue" => array( 65, 105, 225), + "blue" => array( 0, 0, 255), + "dodger blue" => array( 30, 144, 255), + "DodgerBlue" => array( 30, 144, 255), + "deep sky blue" => array( 0, 191, 255), + "DeepSkyBlue" => array( 0, 191, 255), + "sky blue" => array(135, 206, 235), + "SkyBlue" => array(135, 206, 235), + "light sky blue" => array(135, 206, 250), + "LightSkyBlue" => array(135, 206, 250), + "steel blue" => array( 70, 130, 180), + "SteelBlue" => array( 70, 130, 180), + "light steel blue" => array(176, 196, 222), + "LightSteelBlue" => array(176, 196, 222), + "light blue" => array(173, 216, 230), + "LightBlue" => array(173, 216, 230), + "powder blue" => array(176, 224, 230), + "PowderBlue" => array(176, 224, 230), + "pale turquoise" => array(175, 238, 238), + "PaleTurquoise" => array(175, 238, 238), + "dark turquoise" => array( 0, 206, 209), + "DarkTurquoise" => array( 0, 206, 209), + "medium turquoise" => array( 72, 209, 204), + "MediumTurquoise" => array( 72, 209, 204), + "turquoise" => array( 64, 224, 208), + "cyan" => array( 0, 255, 255), + "light cyan" => array(224, 255, 255), + "LightCyan" => array(224, 255, 255), + "cadet blue" => array( 95, 158, 160), + "CadetBlue" => array( 95, 158, 160), + "medium aquamarine" => array(102, 205, 170), + "MediumAquamarine" => array(102, 205, 170), + "aquamarine" => array(127, 255, 212), + "dark green" => array( 0, 100, 0), + "DarkGreen" => array( 0, 100, 0), + "dark olive green" => array( 85, 107, 47), + "DarkOliveGreen" => array( 85, 107, 47), + "dark sea green" => array(143, 188, 143), + "DarkSeaGreen" => array(143, 188, 143), + "sea green" => array( 46, 139, 87), + "SeaGreen" => array( 46, 139, 87), + "medium sea green" => array( 60, 179, 113), + "MediumSeaGreen" => array( 60, 179, 113), + "light sea green" => array( 32, 178, 170), + "LightSeaGreen" => array( 32, 178, 170), + "pale green" => array(152, 251, 152), + "PaleGreen" => array(152, 251, 152), + "spring green" => array( 0, 255, 127), + "SpringGreen" => array( 0, 255, 127), + "lawn green" => array(124, 252, 0), + "LawnGreen" => array(124, 252, 0), + "green" => array( 0, 255, 0), + "chartreuse" => array(127, 255, 0), + "medium spring green" => array( 0, 250, 154), + "MediumSpringGreen" => array( 0, 250, 154), + "green yellow" => array(173, 255, 47), + "GreenYellow" => array(173, 255, 47), + "lime green" => array( 50, 205, 50), + "LimeGreen" => array( 50, 205, 50), + "yellow green" => array(154, 205, 50), + "YellowGreen" => array(154, 205, 50), + "forest green" => array( 34, 139, 34), + "ForestGreen" => array( 34, 139, 34), + "olive drab" => array(107, 142, 35), + "OliveDrab" => array(107, 142, 35), + "dark khaki" => array(189, 183, 107), + "DarkKhaki" => array(189, 183, 107), + "khaki" => array(240, 230, 140), + "pale goldenrod" => array(238, 232, 170), + "PaleGoldenrod" => array(238, 232, 170), + "light goldenrod yellow" => array(250, 250, 210), + "LightGoldenrodYellow" => array(250, 250, 210), + "light yellow" => array(255, 255, 224), + "LightYellow" => array(255, 255, 224), + "yellow" => array(255, 255, 0), + "gold" => array(255, 215, 0), + "light goldenrod" => array(238, 221, 130), + "LightGoldenrod" => array(238, 221, 130), + "goldenrod" => array(218, 165, 32), + "dark goldenrod" => array(184, 134, 11), + "DarkGoldenrod" => array(184, 134, 11), + "rosy brown" => array(188, 143, 143), + "RosyBrown" => array(188, 143, 143), + "indian red" => array(205, 92, 92), + "IndianRed" => array(205, 92, 92), + "saddle brown" => array(139, 69, 19), + "SaddleBrown" => array(139, 69, 19), + "sienna" => array(160, 82, 45), + "peru" => array(205, 133, 63), + "burlywood" => array(222, 184, 135), + "beige" => array(245, 245, 220), + "wheat" => array(245, 222, 179), + "sandy brown" => array(244, 164, 96), + "SandyBrown" => array(244, 164, 96), + "tan" => array(210, 180, 140), + "chocolate" => array(210, 105, 30), + "firebrick" => array(178, 34, 34), + "brown" => array(165, 42, 42), + "dark salmon" => array(233, 150, 122), + "DarkSalmon" => array(233, 150, 122), + "salmon" => array(250, 128, 114), + "light salmon" => array(255, 160, 122), + "LightSalmon" => array(255, 160, 122), + "orange" => array(255, 165, 0), + "dark orange" => array(255, 140, 0), + "DarkOrange" => array(255, 140, 0), + "coral" => array(255, 127, 80), + "light coral" => array(240, 128, 128), + "LightCoral" => array(240, 128, 128), + "tomato" => array(255, 99, 71), + "orange red" => array(255, 69, 0), + "OrangeRed" => array(255, 69, 0), + "red" => array(255, 0, 0), + "hot pink" => array(255, 105, 180), + "HotPink" => array(255, 105, 180), + "deep pink" => array(255, 20, 147), + "DeepPink" => array(255, 20, 147), + "pink" => array(255, 192, 203), + "light pink" => array(255, 182, 193), + "LightPink" => array(255, 182, 193), + "pale violet red" => array(219, 112, 147), + "PaleVioletRed" => array(219, 112, 147), + "maroon" => array(176, 48, 96), + "medium violet red" => array(199, 21, 133), + "MediumVioletRed" => array(199, 21, 133), + "violet red" => array(208, 32, 144), + "VioletRed" => array(208, 32, 144), + "magenta" => array(255, 0, 255), + "violet" => array(238, 130, 238), + "plum" => array(221, 160, 221), + "orchid" => array(218, 112, 214), + "medium orchid" => array(186, 85, 211), + "MediumOrchid" => array(186, 85, 211), + "dark orchid" => array(153, 50, 204), + "DarkOrchid" => array(153, 50, 204), + "dark violet" => array(148, 0, 211), + "DarkViolet" => array(148, 0, 211), + "blue violet" => array(138, 43, 226), + "BlueViolet" => array(138, 43, 226), + "purple" => array(160, 32, 240), + "medium purple" => array(147, 112, 219), + "MediumPurple" => array(147, 112, 219), + "thistle" => array(216, 191, 216), + "snow1" => array(255, 250, 250), + "snow2" => array(238, 233, 233), + "snow3" => array(205, 201, 201), + "snow4" => array(139, 137, 137), + "seashell1" => array(255, 245, 238), + "seashell2" => array(238, 229, 222), + "seashell3" => array(205, 197, 191), + "seashell4" => array(139, 134, 130), + "AntiqueWhite1" => array(255, 239, 219), + "AntiqueWhite2" => array(238, 223, 204), + "AntiqueWhite3" => array(205, 192, 176), + "AntiqueWhite4" => array(139, 131, 120), + "bisque1" => array(255, 228, 196), + "bisque2" => array(238, 213, 183), + "bisque3" => array(205, 183, 158), + "bisque4" => array(139, 125, 107), + "PeachPuff1" => array(255, 218, 185), + "PeachPuff2" => array(238, 203, 173), + "PeachPuff3" => array(205, 175, 149), + "PeachPuff4" => array(139, 119, 101), + "NavajoWhite1" => array(255, 222, 173), + "NavajoWhite2" => array(238, 207, 161), + "NavajoWhite3" => array(205, 179, 139), + "NavajoWhite4" => array(139, 121, 94), + "LemonChiffon1" => array(255, 250, 205), + "LemonChiffon2" => array(238, 233, 191), + "LemonChiffon3" => array(205, 201, 165), + "LemonChiffon4" => array(139, 137, 112), + "cornsilk1" => array(255, 248, 220), + "cornsilk2" => array(238, 232, 205), + "cornsilk3" => array(205, 200, 177), + "cornsilk4" => array(139, 136, 120), + "ivory1" => array(255, 255, 240), + "ivory2" => array(238, 238, 224), + "ivory3" => array(205, 205, 193), + "ivory4" => array(139, 139, 131), + "honeydew1" => array(240, 255, 240), + "honeydew2" => array(224, 238, 224), + "honeydew3" => array(193, 205, 193), + "honeydew4" => array(131, 139, 131), + "LavenderBlush1" => array(255, 240, 245), + "LavenderBlush2" => array(238, 224, 229), + "LavenderBlush3" => array(205, 193, 197), + "LavenderBlush4" => array(139, 131, 134), + "MistyRose1" => array(255, 228, 225), + "MistyRose2" => array(238, 213, 210), + "MistyRose3" => array(205, 183, 181), + "MistyRose4" => array(139, 125, 123), + "azure1" => array(240, 255, 255), + "azure2" => array(224, 238, 238), + "azure3" => array(193, 205, 205), + "azure4" => array(131, 139, 139), + "SlateBlue1" => array(131, 111, 255), + "SlateBlue2" => array(122, 103, 238), + "SlateBlue3" => array(105, 89, 205), + "SlateBlue4" => array( 71, 60, 139), + "RoyalBlue1" => array( 72, 118, 255), + "RoyalBlue2" => array( 67, 110, 238), + "RoyalBlue3" => array( 58, 95, 205), + "RoyalBlue4" => array( 39, 64, 139), + "blue1" => array( 0, 0, 255), + "blue2" => array( 0, 0, 238), + "blue3" => array( 0, 0, 205), + "blue4" => array( 0, 0, 139), + "DodgerBlue1" => array( 30, 144, 255), + "DodgerBlue2" => array( 28, 134, 238), + "DodgerBlue3" => array( 24, 116, 205), + "DodgerBlue4" => array( 16, 78, 139), + "SteelBlue1" => array( 99, 184, 255), + "SteelBlue2" => array( 92, 172, 238), + "SteelBlue3" => array( 79, 148, 205), + "SteelBlue4" => array( 54, 100, 139), + "DeepSkyBlue1" => array( 0, 191, 255), + "DeepSkyBlue2" => array( 0, 178, 238), + "DeepSkyBlue3" => array( 0, 154, 205), + "DeepSkyBlue4" => array( 0, 104, 139), + "SkyBlue1" => array(135, 206, 255), + "SkyBlue2" => array(126, 192, 238), + "SkyBlue3" => array(108, 166, 205), + "SkyBlue4" => array( 74, 112, 139), + "LightSkyBlue1" => array(176, 226, 255), + "LightSkyBlue2" => array(164, 211, 238), + "LightSkyBlue3" => array(141, 182, 205), + "LightSkyBlue4" => array( 96, 123, 139), + "SlateGray1" => array(198, 226, 255), + "SlateGray2" => array(185, 211, 238), + "SlateGray3" => array(159, 182, 205), + "SlateGray4" => array(108, 123, 139), + "LightSteelBlue1" => array(202, 225, 255), + "LightSteelBlue2" => array(188, 210, 238), + "LightSteelBlue3" => array(162, 181, 205), + "LightSteelBlue4" => array(110, 123, 139), + "LightBlue1" => array(191, 239, 255), + "LightBlue2" => array(178, 223, 238), + "LightBlue3" => array(154, 192, 205), + "LightBlue4" => array(104, 131, 139), + "LightCyan1" => array(224, 255, 255), + "LightCyan2" => array(209, 238, 238), + "LightCyan3" => array(180, 205, 205), + "LightCyan4" => array(122, 139, 139), + "PaleTurquoise1" => array(187, 255, 255), + "PaleTurquoise2" => array(174, 238, 238), + "PaleTurquoise3" => array(150, 205, 205), + "PaleTurquoise4" => array(102, 139, 139), + "CadetBlue1" => array(152, 245, 255), + "CadetBlue2" => array(142, 229, 238), + "CadetBlue3" => array(122, 197, 205), + "CadetBlue4" => array( 83, 134, 139), + "turquoise1" => array( 0, 245, 255), + "turquoise2" => array( 0, 229, 238), + "turquoise3" => array( 0, 197, 205), + "turquoise4" => array( 0, 134, 139), + "cyan1" => array( 0, 255, 255), + "cyan2" => array( 0, 238, 238), + "cyan3" => array( 0, 205, 205), + "cyan4" => array( 0, 139, 139), + "DarkSlateGray1" => array(151, 255, 255), + "DarkSlateGray2" => array(141, 238, 238), + "DarkSlateGray3" => array(121, 205, 205), + "DarkSlateGray4" => array( 82, 139, 139), + "aquamarine1" => array(127, 255, 212), + "aquamarine2" => array(118, 238, 198), + "aquamarine3" => array(102, 205, 170), + "aquamarine4" => array( 69, 139, 116), + "DarkSeaGreen1" => array(193, 255, 193), + "DarkSeaGreen2" => array(180, 238, 180), + "DarkSeaGreen3" => array(155, 205, 155), + "DarkSeaGreen4" => array(105, 139, 105), + "SeaGreen1" => array( 84, 255, 159), + "SeaGreen2" => array( 78, 238, 148), + "SeaGreen3" => array( 67, 205, 128), + "SeaGreen4" => array( 46, 139, 87), + "PaleGreen1" => array(154, 255, 154), + "PaleGreen2" => array(144, 238, 144), + "PaleGreen3" => array(124, 205, 124), + "PaleGreen4" => array( 84, 139, 84), + "SpringGreen1" => array( 0, 255, 127), + "SpringGreen2" => array( 0, 238, 118), + "SpringGreen3" => array( 0, 205, 102), + "SpringGreen4" => array( 0, 139, 69), + "green1" => array( 0, 255, 0), + "green2" => array( 0, 238, 0), + "green3" => array( 0, 205, 0), + "green4" => array( 0, 139, 0), + "chartreuse1" => array(127, 255, 0), + "chartreuse2" => array(118, 238, 0), + "chartreuse3" => array(102, 205, 0), + "chartreuse4" => array( 69, 139, 0), + "OliveDrab1" => array(192, 255, 62), + "OliveDrab2" => array(179, 238, 58), + "OliveDrab3" => array(154, 205, 50), + "OliveDrab4" => array(105, 139, 34), + "DarkOliveGreen1" => array(202, 255, 112), + "DarkOliveGreen2" => array(188, 238, 104), + "DarkOliveGreen3" => array(162, 205, 90), + "DarkOliveGreen4" => array(110, 139, 61), + "khaki1" => array(255, 246, 143), + "khaki2" => array(238, 230, 133), + "khaki3" => array(205, 198, 115), + "khaki4" => array(139, 134, 78), + "LightGoldenrod1" => array(255, 236, 139), + "LightGoldenrod2" => array(238, 220, 130), + "LightGoldenrod3" => array(205, 190, 112), + "LightGoldenrod4" => array(139, 129, 76), + "LightYellow1" => array(255, 255, 224), + "LightYellow2" => array(238, 238, 209), + "LightYellow3" => array(205, 205, 180), + "LightYellow4" => array(139, 139, 122), + "yellow1" => array(255, 255, 0), + "yellow2" => array(238, 238, 0), + "yellow3" => array(205, 205, 0), + "yellow4" => array(139, 139, 0), + "gold1" => array(255, 215, 0), + "gold2" => array(238, 201, 0), + "gold3" => array(205, 173, 0), + "gold4" => array(139, 117, 0), + "goldenrod1" => array(255, 193, 37), + "goldenrod2" => array(238, 180, 34), + "goldenrod3" => array(205, 155, 29), + "goldenrod4" => array(139, 105, 20), + "DarkGoldenrod1" => array(255, 185, 15), + "DarkGoldenrod2" => array(238, 173, 14), + "DarkGoldenrod3" => array(205, 149, 12), + "DarkGoldenrod4" => array(139, 101, 8), + "RosyBrown1" => array(255, 193, 193), + "RosyBrown2" => array(238, 180, 180), + "RosyBrown3" => array(205, 155, 155), + "RosyBrown4" => array(139, 105, 105), + "IndianRed1" => array(255, 106, 106), + "IndianRed2" => array(238, 99, 99), + "IndianRed3" => array(205, 85, 85), + "IndianRed4" => array(139, 58, 58), + "sienna1" => array(255, 130, 71), + "sienna2" => array(238, 121, 66), + "sienna3" => array(205, 104, 57), + "sienna4" => array(139, 71, 38), + "burlywood1" => array(255, 211, 155), + "burlywood2" => array(238, 197, 145), + "burlywood3" => array(205, 170, 125), + "burlywood4" => array(139, 115, 85), + "wheat1" => array(255, 231, 186), + "wheat2" => array(238, 216, 174), + "wheat3" => array(205, 186, 150), + "wheat4" => array(139, 126, 102), + "tan1" => array(255, 165, 79), + "tan2" => array(238, 154, 73), + "tan3" => array(205, 133, 63), + "tan4" => array(139, 90, 43), + "chocolate1" => array(255, 127, 36), + "chocolate2" => array(238, 118, 33), + "chocolate3" => array(205, 102, 29), + "chocolate4" => array(139, 69, 19), + "firebrick1" => array(255, 48, 48), + "firebrick2" => array(238, 44, 44), + "firebrick3" => array(205, 38, 38), + "firebrick4" => array(139, 26, 26), + "brown1" => array(255, 64, 64), + "brown2" => array(238, 59, 59), + "brown3" => array(205, 51, 51), + "brown4" => array(139, 35, 35), + "salmon1" => array(255, 140, 105), + "salmon2" => array(238, 130, 98), + "salmon3" => array(205, 112, 84), + "salmon4" => array(139, 76, 57), + "LightSalmon1" => array(255, 160, 122), + "LightSalmon2" => array(238, 149, 114), + "LightSalmon3" => array(205, 129, 98), + "LightSalmon4" => array(139, 87, 66), + "orange1" => array(255, 165, 0), + "orange2" => array(238, 154, 0), + "orange3" => array(205, 133, 0), + "orange4" => array(139, 90, 0), + "DarkOrange1" => array(255, 127, 0), + "DarkOrange2" => array(238, 118, 0), + "DarkOrange3" => array(205, 102, 0), + "DarkOrange4" => array(139, 69, 0), + "coral1" => array(255, 114, 86), + "coral2" => array(238, 106, 80), + "coral3" => array(205, 91, 69), + "coral4" => array(139, 62, 47), + "tomato1" => array(255, 99, 71), + "tomato2" => array(238, 92, 66), + "tomato3" => array(205, 79, 57), + "tomato4" => array(139, 54, 38), + "OrangeRed1" => array(255, 69, 0), + "OrangeRed2" => array(238, 64, 0), + "OrangeRed3" => array(205, 55, 0), + "OrangeRed4" => array(139, 37, 0), + "red1" => array(255, 0, 0), + "red2" => array(238, 0, 0), + "red3" => array(205, 0, 0), + "red4" => array(139, 0, 0), + "DeepPink1" => array(255, 20, 147), + "DeepPink2" => array(238, 18, 137), + "DeepPink3" => array(205, 16, 118), + "DeepPink4" => array(139, 10, 80), + "HotPink1" => array(255, 110, 180), + "HotPink2" => array(238, 106, 167), + "HotPink3" => array(205, 96, 144), + "HotPink4" => array(139, 58, 98), + "pink1" => array(255, 181, 197), + "pink2" => array(238, 169, 184), + "pink3" => array(205, 145, 158), + "pink4" => array(139, 99, 108), + "LightPink1" => array(255, 174, 185), + "LightPink2" => array(238, 162, 173), + "LightPink3" => array(205, 140, 149), + "LightPink4" => array(139, 95, 101), + "PaleVioletRed1" => array(255, 130, 171), + "PaleVioletRed2" => array(238, 121, 159), + "PaleVioletRed3" => array(205, 104, 137), + "PaleVioletRed4" => array(139, 71, 93), + "maroon1" => array(255, 52, 179), + "maroon2" => array(238, 48, 167), + "maroon3" => array(205, 41, 144), + "maroon4" => array(139, 28, 98), + "VioletRed1" => array(255, 62, 150), + "VioletRed2" => array(238, 58, 140), + "VioletRed3" => array(205, 50, 120), + "VioletRed4" => array(139, 34, 82), + "magenta1" => array(255, 0, 255), + "magenta2" => array(238, 0, 238), + "magenta3" => array(205, 0, 205), + "magenta4" => array(139, 0, 139), + "orchid1" => array(255, 131, 250), + "orchid2" => array(238, 122, 233), + "orchid3" => array(205, 105, 201), + "orchid4" => array(139, 71, 137), + "plum1" => array(255, 187, 255), + "plum2" => array(238, 174, 238), + "plum3" => array(205, 150, 205), + "plum4" => array(139, 102, 139), + "MediumOrchid1" => array(224, 102, 255), + "MediumOrchid2" => array(209, 95, 238), + "MediumOrchid3" => array(180, 82, 205), + "MediumOrchid4" => array(122, 55, 139), + "DarkOrchid1" => array(191, 62, 255), + "DarkOrchid2" => array(178, 58, 238), + "DarkOrchid3" => array(154, 50, 205), + "DarkOrchid4" => array(104, 34, 139), + "purple1" => array(155, 48, 255), + "purple2" => array(145, 44, 238), + "purple3" => array(125, 38, 205), + "purple4" => array( 85, 26, 139), + "MediumPurple1" => array(171, 130, 255), + "MediumPurple2" => array(159, 121, 238), + "MediumPurple3" => array(137, 104, 205), + "MediumPurple4" => array( 93, 71, 139), + "thistle1" => array(255, 225, 255), + "thistle2" => array(238, 210, 238), + "thistle3" => array(205, 181, 205), + "thistle4" => array(139, 123, 139), + "gray0" => array( 0, 0, 0), + "grey0" => array( 0, 0, 0), + "gray1" => array( 3, 3, 3), + "grey1" => array( 3, 3, 3), + "gray2" => array( 5, 5, 5), + "grey2" => array( 5, 5, 5), + "gray3" => array( 8, 8, 8), + "grey3" => array( 8, 8, 8), + "gray4" => array( 10, 10, 10), + "grey4" => array( 10, 10, 10), + "gray5" => array( 13, 13, 13), + "grey5" => array( 13, 13, 13), + "gray6" => array( 15, 15, 15), + "grey6" => array( 15, 15, 15), + "gray7" => array( 18, 18, 18), + "grey7" => array( 18, 18, 18), + "gray8" => array( 20, 20, 20), + "grey8" => array( 20, 20, 20), + "gray9" => array( 23, 23, 23), + "grey9" => array( 23, 23, 23), + "gray10" => array( 26, 26, 26), + "grey10" => array( 26, 26, 26), + "gray11" => array( 28, 28, 28), + "grey11" => array( 28, 28, 28), + "gray12" => array( 31, 31, 31), + "grey12" => array( 31, 31, 31), + "gray13" => array( 33, 33, 33), + "grey13" => array( 33, 33, 33), + "gray14" => array( 36, 36, 36), + "grey14" => array( 36, 36, 36), + "gray15" => array( 38, 38, 38), + "grey15" => array( 38, 38, 38), + "gray16" => array( 41, 41, 41), + "grey16" => array( 41, 41, 41), + "gray17" => array( 43, 43, 43), + "grey17" => array( 43, 43, 43), + "gray18" => array( 46, 46, 46), + "grey18" => array( 46, 46, 46), + "gray19" => array( 48, 48, 48), + "grey19" => array( 48, 48, 48), + "gray20" => array( 51, 51, 51), + "grey20" => array( 51, 51, 51), + "gray21" => array( 54, 54, 54), + "grey21" => array( 54, 54, 54), + "gray22" => array( 56, 56, 56), + "grey22" => array( 56, 56, 56), + "gray23" => array( 59, 59, 59), + "grey23" => array( 59, 59, 59), + "gray24" => array( 61, 61, 61), + "grey24" => array( 61, 61, 61), + "gray25" => array( 64, 64, 64), + "grey25" => array( 64, 64, 64), + "gray26" => array( 66, 66, 66), + "grey26" => array( 66, 66, 66), + "gray27" => array( 69, 69, 69), + "grey27" => array( 69, 69, 69), + "gray28" => array( 71, 71, 71), + "grey28" => array( 71, 71, 71), + "gray29" => array( 74, 74, 74), + "grey29" => array( 74, 74, 74), + "gray30" => array( 77, 77, 77), + "grey30" => array( 77, 77, 77), + "gray31" => array( 79, 79, 79), + "grey31" => array( 79, 79, 79), + "gray32" => array( 82, 82, 82), + "grey32" => array( 82, 82, 82), + "gray33" => array( 84, 84, 84), + "grey33" => array( 84, 84, 84), + "gray34" => array( 87, 87, 87), + "grey34" => array( 87, 87, 87), + "gray35" => array( 89, 89, 89), + "grey35" => array( 89, 89, 89), + "gray36" => array( 92, 92, 92), + "grey36" => array( 92, 92, 92), + "gray37" => array( 94, 94, 94), + "grey37" => array( 94, 94, 94), + "gray38" => array( 97, 97, 97), + "grey38" => array( 97, 97, 97), + "gray39" => array( 99, 99, 99), + "grey39" => array( 99, 99, 99), + "gray40" => array(102, 102, 102), + "grey40" => array(102, 102, 102), + "gray41" => array(105, 105, 105), + "grey41" => array(105, 105, 105), + "gray42" => array(107, 107, 107), + "grey42" => array(107, 107, 107), + "gray43" => array(110, 110, 110), + "grey43" => array(110, 110, 110), + "gray44" => array(112, 112, 112), + "grey44" => array(112, 112, 112), + "gray45" => array(115, 115, 115), + "grey45" => array(115, 115, 115), + "gray46" => array(117, 117, 117), + "grey46" => array(117, 117, 117), + "gray47" => array(120, 120, 120), + "grey47" => array(120, 120, 120), + "gray48" => array(122, 122, 122), + "grey48" => array(122, 122, 122), + "gray49" => array(125, 125, 125), + "grey49" => array(125, 125, 125), + "gray50" => array(127, 127, 127), + "grey50" => array(127, 127, 127), + "gray51" => array(130, 130, 130), + "grey51" => array(130, 130, 130), + "gray52" => array(133, 133, 133), + "grey52" => array(133, 133, 133), + "gray53" => array(135, 135, 135), + "grey53" => array(135, 135, 135), + "gray54" => array(138, 138, 138), + "grey54" => array(138, 138, 138), + "gray55" => array(140, 140, 140), + "grey55" => array(140, 140, 140), + "gray56" => array(143, 143, 143), + "grey56" => array(143, 143, 143), + "gray57" => array(145, 145, 145), + "grey57" => array(145, 145, 145), + "gray58" => array(148, 148, 148), + "grey58" => array(148, 148, 148), + "gray59" => array(150, 150, 150), + "grey59" => array(150, 150, 150), + "gray60" => array(153, 153, 153), + "grey60" => array(153, 153, 153), + "gray61" => array(156, 156, 156), + "grey61" => array(156, 156, 156), + "gray62" => array(158, 158, 158), + "grey62" => array(158, 158, 158), + "gray63" => array(161, 161, 161), + "grey63" => array(161, 161, 161), + "gray64" => array(163, 163, 163), + "grey64" => array(163, 163, 163), + "gray65" => array(166, 166, 166), + "grey65" => array(166, 166, 166), + "gray66" => array(168, 168, 168), + "grey66" => array(168, 168, 168), + "gray67" => array(171, 171, 171), + "grey67" => array(171, 171, 171), + "gray68" => array(173, 173, 173), + "grey68" => array(173, 173, 173), + "gray69" => array(176, 176, 176), + "grey69" => array(176, 176, 176), + "gray70" => array(179, 179, 179), + "grey70" => array(179, 179, 179), + "gray71" => array(181, 181, 181), + "grey71" => array(181, 181, 181), + "gray72" => array(184, 184, 184), + "grey72" => array(184, 184, 184), + "gray73" => array(186, 186, 186), + "grey73" => array(186, 186, 186), + "gray74" => array(189, 189, 189), + "grey74" => array(189, 189, 189), + "gray75" => array(191, 191, 191), + "grey75" => array(191, 191, 191), + "gray76" => array(194, 194, 194), + "grey76" => array(194, 194, 194), + "gray77" => array(196, 196, 196), + "grey77" => array(196, 196, 196), + "gray78" => array(199, 199, 199), + "grey78" => array(199, 199, 199), + "gray79" => array(201, 201, 201), + "grey79" => array(201, 201, 201), + "gray80" => array(204, 204, 204), + "grey80" => array(204, 204, 204), + "gray81" => array(207, 207, 207), + "grey81" => array(207, 207, 207), + "gray82" => array(209, 209, 209), + "grey82" => array(209, 209, 209), + "gray83" => array(212, 212, 212), + "grey83" => array(212, 212, 212), + "gray84" => array(214, 214, 214), + "grey84" => array(214, 214, 214), + "gray85" => array(217, 217, 217), + "grey85" => array(217, 217, 217), + "gray86" => array(219, 219, 219), + "grey86" => array(219, 219, 219), + "gray87" => array(222, 222, 222), + "grey87" => array(222, 222, 222), + "gray88" => array(224, 224, 224), + "grey88" => array(224, 224, 224), + "gray89" => array(227, 227, 227), + "grey89" => array(227, 227, 227), + "gray90" => array(229, 229, 229), + "grey90" => array(229, 229, 229), + "gray91" => array(232, 232, 232), + "grey91" => array(232, 232, 232), + "gray92" => array(235, 235, 235), + "grey92" => array(235, 235, 235), + "gray93" => array(237, 237, 237), + "grey93" => array(237, 237, 237), + "gray94" => array(240, 240, 240), + "grey94" => array(240, 240, 240), + "gray95" => array(242, 242, 242), + "grey95" => array(242, 242, 242), + "gray96" => array(245, 245, 245), + "grey96" => array(245, 245, 245), + "gray97" => array(247, 247, 247), + "grey97" => array(247, 247, 247), + "gray98" => array(250, 250, 250), + "grey98" => array(250, 250, 250), + "gray99" => array(252, 252, 252), + "grey99" => array(252, 252, 252), + "gray100" => array(255, 255, 255) +); diff --git a/gosa-core/plugins/generic/statistics/class_statistics.inc b/gosa-core/plugins/generic/statistics/class_statistics.inc index 7f1bd5496..63c51f72f 100644 --- a/gosa-core/plugins/generic/statistics/class_statistics.inc +++ b/gosa-core/plugins/generic/statistics/class_statistics.inc @@ -66,33 +66,21 @@ class statistics extends plugin } $all = array_sum($res['category_count']); - $tmp = array(); - $tmpRendered = array(); + $data = array(); foreach($res['category_count'] as $category => $count){ $tmp[$category] = floor(($count / $all) * 10000) / 100; + $data[] = array($category, floor(($count / $all) * 10000) / 100); } - $data = array_values($tmp); + $this->graphID_1 = preg_replace("/[^0-9]/","",microtime(TRUE)); + $plot = new PHPlot(); + $plot->SetDataValues($data); + $plot->SetOutputFile('/tmp/graph_'.$this->graphID_1); + $plot->SetIsInline(TRUE); + $plot->DrawGraph(); - // Include jpgraph classes now. - new jpGraphInclude(); - - $graph = new PieGraph(600,500); - $graph->title->Set("Example 4 of pie plot"); - $graph->SetAntiAliasing(); - - $p1 = new PiePlot($data); - $p1 = new PiePlot3D($data); - $p1->value->SetColor("darkred"); - $p1->SetSize(0.3); - $p1->SetCenter(0.4); - $p1->SetLegends(array_keys($tmp)); - $graph->Add($p1); - - $this->graphID_1 = preg_replace("/[^0-9]/","",microtime(TRUE)); - $graph->Stroke('/tmp/graph_'.$this->graphID_1, TRUE); - session::set('statistics::graphFile'.$this->graphID_1,'/tmp/graph_'.$this->graphID_1); + session::set('statistics::graphFile'.$this->graphID_1,'/tmp/graph_'.$this->graphID_1); } } -- 2.30.2